Merge
This commit is contained in:
commit
c76b282388
@ -3806,15 +3806,24 @@ void Compile::reshape_address(AddPNode* addp) {
|
||||
// Any use that can't embed the address computation?
|
||||
for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) {
|
||||
Node* u = addp->fast_out(i);
|
||||
if (!u->is_Mem() || u->is_LoadVector() || u->is_StoreVector() || u->Opcode() == Op_StoreCM) {
|
||||
if (!u->is_Mem()) {
|
||||
return;
|
||||
}
|
||||
if (u->is_LoadVector() || u->is_StoreVector() || u->Opcode() == Op_StoreCM) {
|
||||
return;
|
||||
}
|
||||
if (addp2->in(AddPNode::Offset)->Opcode() != Op_ConvI2L) {
|
||||
int scale = 1 << addp2->in(AddPNode::Offset)->in(2)->get_int();
|
||||
if (VM_Version::expensive_load(u->as_Mem()->memory_size(), scale)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Node* off = addp->in(AddPNode::Offset);
|
||||
Node* addr2 = addp2->in(AddPNode::Address);
|
||||
Node* base = addp->in(AddPNode::Base);
|
||||
|
||||
|
||||
Node* new_addr = NULL;
|
||||
// Check whether the graph already has the new AddP we need
|
||||
// before we create one (no GVN available here).
|
||||
@ -3828,7 +3837,7 @@ void Compile::reshape_address(AddPNode* addp) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (new_addr == NULL) {
|
||||
new_addr = new AddPNode(base, addr2, off);
|
||||
}
|
||||
|
@ -272,8 +272,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||
// load pointer for resolved_references[] objArray
|
||||
ldr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||
ldr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||
// JNIHandles::resolve(obj);
|
||||
ldr(result, Address(result, 0));
|
||||
resolve_oop_handle(result);
|
||||
// Add in the index
|
||||
add(result, result, tmp);
|
||||
load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
|
||||
|
@ -3279,6 +3279,12 @@ void MacroAssembler::load_klass(Register dst, Register src) {
|
||||
}
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
ldr(result, Address(result, 0));
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror(Register dst, Register method) {
|
||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||
ldr(dst, Address(rmethod, Method::const_offset()));
|
||||
|
@ -790,6 +790,7 @@ public:
|
||||
void store_klass(Register dst, Register src);
|
||||
void cmp_klass(Register oop, Register trial_klass, Register tmp);
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror(Register dst, Register method);
|
||||
|
||||
void load_heap_oop(Register dst, Address src);
|
||||
|
@ -56,6 +56,17 @@ public:
|
||||
static void assert_is_initialized() {
|
||||
}
|
||||
|
||||
static bool expensive_load(int ld_size, int scale) {
|
||||
if (cpu_family() == CPU_ARM) {
|
||||
// Half-word load with index shift by 1 (aka scale is 2) has
|
||||
// extra cycle latency, e.g. ldrsh w0, [x1,w2,sxtw #1].
|
||||
if (ld_size == 2 && scale == 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum Family {
|
||||
CPU_ARM = 'A',
|
||||
CPU_BROADCOM = 'B',
|
||||
|
@ -300,8 +300,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||
// load pointer for resolved_references[] objArray
|
||||
ldr(cache, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||
ldr(cache, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||
// JNIHandles::resolve(result)
|
||||
ldr(cache, Address(cache, 0));
|
||||
resolve_oop_handle(cache);
|
||||
// Add in the index
|
||||
// convert from field index to resolved_references() index and from
|
||||
// word index to byte offset. Since this is a java object, it can be compressed
|
||||
|
@ -2887,6 +2887,11 @@ int MacroAssembler::patchable_call(address target, RelocationHolder const& rspec
|
||||
return offset();
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
ldr(result, Address(result, 0));
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror(Register mirror, Register method, Register tmp) {
|
||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||
@ -2896,6 +2901,7 @@ void MacroAssembler::load_mirror(Register mirror, Register method, Register tmp)
|
||||
ldr(mirror, Address(tmp, mirror_offset));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Compressed pointers
|
||||
|
@ -687,6 +687,7 @@ public:
|
||||
AbstractAssembler::emit_address((address)L.data());
|
||||
}
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror(Register mirror, Register method, Register tmp);
|
||||
|
||||
// Porting layer between 32-bit ARM and AArch64
|
||||
|
@ -464,8 +464,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result
|
||||
// Load pointer for resolved_references[] objArray.
|
||||
ld(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||
ld(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||
// JNIHandles::resolve(result)
|
||||
ld(result, 0, result);
|
||||
resolve_oop_handle(result);
|
||||
#ifdef ASSERT
|
||||
Label index_ok;
|
||||
lwa(R0, arrayOopDesc::length_offset_in_bytes(), result);
|
||||
|
@ -3372,6 +3372,12 @@ void MacroAssembler::load_klass(Register dst, Register src) {
|
||||
}
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
ld(result, 0, result);
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror_from_const_method(Register mirror, Register const_method) {
|
||||
ld(mirror, in_bytes(ConstMethod::constants_offset()), const_method);
|
||||
ld(mirror, ConstantPool::pool_holder_offset_in_bytes(), mirror);
|
||||
|
@ -725,6 +725,7 @@ class MacroAssembler: public Assembler {
|
||||
void store_klass(Register dst_oop, Register klass, Register tmp = R0);
|
||||
void store_klass_gap(Register dst_oop, Register val = noreg); // Will store 0 if val not specified.
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror_from_const_method(Register mirror, Register const_method);
|
||||
|
||||
static int instr_size_for_decode_klass_not_null();
|
||||
|
@ -56,7 +56,7 @@
|
||||
// if too small.
|
||||
// Run with +PrintInterpreter to get the VM to print out the size.
|
||||
// Max size with JVMTI
|
||||
int TemplateInterpreter::InterpreterCodeSize = 230*K;
|
||||
int TemplateInterpreter::InterpreterCodeSize = 256*K;
|
||||
|
||||
#ifdef PRODUCT
|
||||
#define BLOCK_COMMENT(str) /* nothing */
|
||||
|
@ -364,8 +364,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result
|
||||
// Load pointer for resolved_references[] objArray.
|
||||
z_lg(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||
z_lg(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||
// JNIHandles::resolve(result)
|
||||
z_lg(result, 0, result); // Load resolved references array itself.
|
||||
resolve_oop_handle(result); // Load resolved references array itself.
|
||||
#ifdef ASSERT
|
||||
NearLabel index_ok;
|
||||
z_lgf(Z_R0, Address(result, arrayOopDesc::length_offset_in_bytes()));
|
||||
|
@ -4660,6 +4660,12 @@ void MacroAssembler::oop_decoder(Register Rdst, Register Rsrc, bool maybeNULL, R
|
||||
}
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
z_lg(result, 0, result);
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||
mem2reg_opt(mirror, Address(method, Method::const_offset()));
|
||||
mem2reg_opt(mirror, Address(mirror, ConstMethod::constants_offset()));
|
||||
|
@ -832,6 +832,7 @@ class MacroAssembler: public Assembler {
|
||||
void oop_decoder(Register Rdst, Register Rsrc, bool maybeNULL,
|
||||
Register Rbase = Z_R1, int pow2_offset = -1);
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror(Register mirror, Register method);
|
||||
|
||||
//--------------------------
|
||||
|
@ -730,8 +730,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||
// load pointer for resolved_references[] objArray
|
||||
ld_ptr(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||
ld_ptr(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||
// JNIHandles::resolve(result)
|
||||
ld_ptr(result, 0, result);
|
||||
resolve_oop_handle(result);
|
||||
// Add in the index
|
||||
add(result, tmp, result);
|
||||
load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), result);
|
||||
|
@ -3822,6 +3822,12 @@ void MacroAssembler::card_write_barrier_post(Register store_addr, Register new_v
|
||||
card_table_write(bs->byte_map_base, tmp, store_addr);
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
ld_ptr(result, 0, result);
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||
ld_ptr(method, in_bytes(Method::const_offset()), mirror);
|
||||
|
@ -995,6 +995,7 @@ public:
|
||||
inline void ldbool(const Address& a, Register d);
|
||||
inline void movbool( bool boolconst, Register d);
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror(Register mirror, Register method);
|
||||
|
||||
// klass oop manipulations if compressed
|
||||
|
@ -511,8 +511,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||
// load pointer for resolved_references[] objArray
|
||||
movptr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||
movptr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||
// JNIHandles::resolve(obj);
|
||||
movptr(result, Address(result, 0));
|
||||
resolve_oop_handle(result);
|
||||
// Add in the index
|
||||
addptr(result, tmp);
|
||||
load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
|
||||
|
@ -6604,6 +6604,12 @@ void MacroAssembler::restore_cpu_control_state_after_jni() {
|
||||
#endif // _LP64
|
||||
}
|
||||
|
||||
// ((OopHandle)result).resolve();
|
||||
void MacroAssembler::resolve_oop_handle(Register result) {
|
||||
// OopHandle::resolve is an indirection.
|
||||
movptr(result, Address(result, 0));
|
||||
}
|
||||
|
||||
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||
// get mirror
|
||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||
@ -7030,7 +7036,6 @@ void MacroAssembler::reinit_heapbase() {
|
||||
|
||||
#endif // _LP64
|
||||
|
||||
|
||||
// C2 compiled method's prolog code.
|
||||
void MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b) {
|
||||
|
||||
|
@ -327,6 +327,7 @@ class MacroAssembler: public Assembler {
|
||||
void movbool(Address dst, Register src);
|
||||
void testbool(Register dst);
|
||||
|
||||
void resolve_oop_handle(Register result);
|
||||
void load_mirror(Register mirror, Register method);
|
||||
|
||||
// oop manipulations
|
||||
|
@ -158,7 +158,7 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw GraalError.shouldNotReachHere();
|
||||
throw GraalError.shouldNotReachHere(input.getPlatformKind().toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -451,7 +451,7 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
|
||||
protected Value emitZeroExtendMemory(AMD64Kind memoryKind, int resultBits, AMD64AddressValue address, LIRFrameState state) {
|
||||
// Issue a zero extending load of the proper bit size and set the result to
|
||||
// the proper kind.
|
||||
Variable result = getLIRGen().newVariable(LIRKind.value(resultBits == 32 ? AMD64Kind.DWORD : AMD64Kind.QWORD));
|
||||
Variable result = getLIRGen().newVariable(LIRKind.value(resultBits <= 32 ? AMD64Kind.DWORD : AMD64Kind.QWORD));
|
||||
switch (memoryKind) {
|
||||
case BYTE:
|
||||
getLIRGen().append(new AMD64Unary.MemoryOp(MOVZXB, DWORD, result, address, state));
|
||||
|
@ -25,16 +25,26 @@ package org.graalvm.compiler.core.common.calc;
|
||||
import org.graalvm.compiler.debug.GraalError;
|
||||
|
||||
public enum FloatConvert {
|
||||
F2I,
|
||||
D2I,
|
||||
F2L,
|
||||
D2L,
|
||||
I2F,
|
||||
L2F,
|
||||
D2F,
|
||||
I2D,
|
||||
L2D,
|
||||
F2D;
|
||||
F2I(FloatConvertCategory.FloatingPointToInteger),
|
||||
D2I(FloatConvertCategory.FloatingPointToInteger),
|
||||
F2L(FloatConvertCategory.FloatingPointToInteger),
|
||||
D2L(FloatConvertCategory.FloatingPointToInteger),
|
||||
I2F(FloatConvertCategory.IntegerToFloatingPoint),
|
||||
L2F(FloatConvertCategory.IntegerToFloatingPoint),
|
||||
D2F(FloatConvertCategory.FloatingPointToFloatingPoint),
|
||||
I2D(FloatConvertCategory.IntegerToFloatingPoint),
|
||||
L2D(FloatConvertCategory.IntegerToFloatingPoint),
|
||||
F2D(FloatConvertCategory.FloatingPointToFloatingPoint);
|
||||
|
||||
private FloatConvertCategory category;
|
||||
|
||||
FloatConvert(FloatConvertCategory category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public FloatConvertCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public FloatConvert reverse() {
|
||||
switch (this) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.graalvm.compiler.core.common.calc;
|
||||
|
||||
public enum FloatConvertCategory {
|
||||
FloatingPointToInteger,
|
||||
IntegerToFloatingPoint,
|
||||
FloatingPointToFloatingPoint;
|
||||
}
|
@ -33,9 +33,11 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Add;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Sub;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Xor;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend;
|
||||
@ -62,6 +64,8 @@ public final class ArithmeticOpTable {
|
||||
private final BinaryOp<Sub> sub;
|
||||
|
||||
private final BinaryOp<Mul> mul;
|
||||
private final BinaryOp<MulHigh> mulHigh;
|
||||
private final BinaryOp<UMulHigh> umulHigh;
|
||||
private final BinaryOp<Div> div;
|
||||
private final BinaryOp<Rem> rem;
|
||||
|
||||
@ -92,7 +96,7 @@ public final class ArithmeticOpTable {
|
||||
}
|
||||
}
|
||||
|
||||
public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
public interface ArithmeticOpWrapper {
|
||||
|
||||
@ -121,6 +125,8 @@ public final class ArithmeticOpTable {
|
||||
BinaryOp<Sub> sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub());
|
||||
|
||||
BinaryOp<Mul> mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul());
|
||||
BinaryOp<MulHigh> mulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMulHigh());
|
||||
BinaryOp<UMulHigh> umulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getUMulHigh());
|
||||
BinaryOp<Div> div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv());
|
||||
BinaryOp<Rem> rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem());
|
||||
|
||||
@ -141,16 +147,18 @@ public final class ArithmeticOpTable {
|
||||
IntegerConvertOp<Narrow> narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow());
|
||||
|
||||
FloatConvertOp[] floatConvert = CollectionsUtil.filterAndMapToArray(inner.floatConvert, Objects::nonNull, wrapper::wrapFloatConvertOp, FloatConvertOp[]::new);
|
||||
return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert);
|
||||
return new ArithmeticOpTable(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert);
|
||||
}
|
||||
|
||||
protected ArithmeticOpTable(UnaryOp<Neg> neg, BinaryOp<Add> add, BinaryOp<Sub> sub, BinaryOp<Mul> mul, BinaryOp<Div> div, BinaryOp<Rem> rem, UnaryOp<Not> not, BinaryOp<And> and, BinaryOp<Or> or,
|
||||
BinaryOp<Xor> xor, ShiftOp<Shl> shl, ShiftOp<Shr> shr, ShiftOp<UShr> ushr, UnaryOp<Abs> abs, UnaryOp<Sqrt> sqrt, IntegerConvertOp<ZeroExtend> zeroExtend,
|
||||
IntegerConvertOp<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) {
|
||||
protected ArithmeticOpTable(UnaryOp<Neg> neg, BinaryOp<Add> add, BinaryOp<Sub> sub, BinaryOp<Mul> mul, BinaryOp<MulHigh> mulHigh, BinaryOp<UMulHigh> umulHigh, BinaryOp<Div> div, BinaryOp<Rem> rem,
|
||||
UnaryOp<Not> not, BinaryOp<And> and, BinaryOp<Or> or, BinaryOp<Xor> xor, ShiftOp<Shl> shl, ShiftOp<Shr> shr, ShiftOp<UShr> ushr, UnaryOp<Abs> abs, UnaryOp<Sqrt> sqrt,
|
||||
IntegerConvertOp<ZeroExtend> zeroExtend, IntegerConvertOp<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) {
|
||||
this.neg = neg;
|
||||
this.add = add;
|
||||
this.sub = sub;
|
||||
this.mul = mul;
|
||||
this.mulHigh = mulHigh;
|
||||
this.umulHigh = umulHigh;
|
||||
this.div = div;
|
||||
this.rem = rem;
|
||||
this.not = not;
|
||||
@ -206,6 +214,20 @@ public final class ArithmeticOpTable {
|
||||
return mul;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a signed operation that multiples the upper 32-bits of two long values.
|
||||
*/
|
||||
public BinaryOp<MulHigh> getMulHigh() {
|
||||
return mulHigh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an unsigned operation that multiples the upper 32-bits of two long values.
|
||||
*/
|
||||
public BinaryOp<UMulHigh> getUMulHigh() {
|
||||
return umulHigh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the division operation.
|
||||
*/
|
||||
@ -321,6 +343,8 @@ public final class ArithmeticOpTable {
|
||||
Objects.equals(add, that.add) &&
|
||||
Objects.equals(sub, that.sub) &&
|
||||
Objects.equals(mul, that.mul) &&
|
||||
Objects.equals(mulHigh, that.mulHigh) &&
|
||||
Objects.equals(umulHigh, that.umulHigh) &&
|
||||
Objects.equals(div, that.div) &&
|
||||
Objects.equals(rem, that.rem) &&
|
||||
Objects.equals(not, that.not) &&
|
||||
@ -360,8 +384,8 @@ public final class ArithmeticOpTable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) + ",floatConvert[" +
|
||||
toString(floatConvert) + "]]";
|
||||
return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) +
|
||||
",floatConvert[" + toString(floatConvert) + "]]";
|
||||
}
|
||||
|
||||
public abstract static class Op {
|
||||
@ -479,6 +503,20 @@ public final class ArithmeticOpTable {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class MulHigh extends BinaryOp<MulHigh> {
|
||||
|
||||
protected MulHigh(boolean associative, boolean commutative) {
|
||||
super("*H", associative, commutative);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class UMulHigh extends BinaryOp<UMulHigh> {
|
||||
|
||||
protected UMulHigh(boolean associative, boolean commutative) {
|
||||
super("|*H|", associative, commutative);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class Div extends BinaryOp<Div> {
|
||||
|
||||
protected Div(boolean associative, boolean commutative) {
|
||||
|
@ -302,7 +302,7 @@ public class FloatStamp extends PrimitiveStamp {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final ArithmeticOpTable OPS = new ArithmeticOpTable(
|
||||
public static final ArithmeticOpTable OPS = new ArithmeticOpTable(
|
||||
|
||||
new UnaryOp.Neg() {
|
||||
|
||||
@ -437,6 +437,10 @@ public class FloatStamp extends PrimitiveStamp {
|
||||
}
|
||||
},
|
||||
|
||||
null,
|
||||
|
||||
null,
|
||||
|
||||
new BinaryOp.Div(false, false) {
|
||||
|
||||
@Override
|
||||
|
@ -858,6 +858,164 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||
}
|
||||
},
|
||||
|
||||
new BinaryOp.MulHigh(true, true) {
|
||||
|
||||
@Override
|
||||
public Constant foldConstant(Constant const1, Constant const2) {
|
||||
PrimitiveConstant a = (PrimitiveConstant) const1;
|
||||
PrimitiveConstant b = (PrimitiveConstant) const2;
|
||||
assert a.getJavaKind() == b.getJavaKind();
|
||||
return JavaConstant.forIntegerKind(a.getJavaKind(), multiplyHigh(a.asLong(), b.asLong(), a.getJavaKind()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stamp foldStamp(Stamp stamp1, Stamp stamp2) {
|
||||
IntegerStamp a = (IntegerStamp) stamp1;
|
||||
IntegerStamp b = (IntegerStamp) stamp2;
|
||||
JavaKind javaKind = a.getStackKind();
|
||||
|
||||
assert a.getBits() == b.getBits();
|
||||
assert javaKind == b.getStackKind();
|
||||
assert (javaKind == JavaKind.Int || javaKind == JavaKind.Long);
|
||||
|
||||
if (a.isEmpty() || b.isEmpty()) {
|
||||
return a.empty();
|
||||
} else if (a.isUnrestricted() || b.isUnrestricted()) {
|
||||
return a.unrestricted();
|
||||
}
|
||||
|
||||
long[] xExtremes = {a.lowerBound(), a.upperBound()};
|
||||
long[] yExtremes = {b.lowerBound(), b.upperBound()};
|
||||
long min = Long.MAX_VALUE;
|
||||
long max = Long.MIN_VALUE;
|
||||
for (long x : xExtremes) {
|
||||
for (long y : yExtremes) {
|
||||
long result = multiplyHigh(x, y, javaKind);
|
||||
min = Math.min(min, result);
|
||||
max = Math.max(max, result);
|
||||
}
|
||||
}
|
||||
return StampFactory.forInteger(javaKind, min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNeutral(Constant value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private long multiplyHigh(long x, long y, JavaKind javaKind) {
|
||||
if (javaKind == JavaKind.Int) {
|
||||
return (x * y) >> 32;
|
||||
} else {
|
||||
assert javaKind == JavaKind.Long;
|
||||
long x0 = x & 0xFFFFFFFFL;
|
||||
long x1 = x >> 32;
|
||||
|
||||
long y0 = y & 0xFFFFFFFFL;
|
||||
long y1 = y >> 32;
|
||||
|
||||
long z0 = x0 * y0;
|
||||
long t = x1 * y0 + (z0 >>> 32);
|
||||
long z1 = t & 0xFFFFFFFFL;
|
||||
long z2 = t >> 32;
|
||||
z1 += x0 * y1;
|
||||
|
||||
return x1 * y1 + z2 + (z1 >> 32);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new BinaryOp.UMulHigh(true, true) {
|
||||
|
||||
@Override
|
||||
public Constant foldConstant(Constant const1, Constant const2) {
|
||||
PrimitiveConstant a = (PrimitiveConstant) const1;
|
||||
PrimitiveConstant b = (PrimitiveConstant) const2;
|
||||
assert a.getJavaKind() == b.getJavaKind();
|
||||
return JavaConstant.forIntegerKind(a.getJavaKind(), multiplyHighUnsigned(a.asLong(), b.asLong(), a.getJavaKind()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stamp foldStamp(Stamp stamp1, Stamp stamp2) {
|
||||
IntegerStamp a = (IntegerStamp) stamp1;
|
||||
IntegerStamp b = (IntegerStamp) stamp2;
|
||||
JavaKind javaKind = a.getStackKind();
|
||||
|
||||
assert a.getBits() == b.getBits();
|
||||
assert javaKind == b.getStackKind();
|
||||
assert (javaKind == JavaKind.Int || javaKind == JavaKind.Long);
|
||||
|
||||
if (a.isEmpty() || b.isEmpty()) {
|
||||
return a.empty();
|
||||
} else if (a.isUnrestricted() || b.isUnrestricted()) {
|
||||
return a.unrestricted();
|
||||
}
|
||||
|
||||
// Note that the minima and maxima are calculated using signed min/max
|
||||
// functions, while the values themselves are unsigned.
|
||||
long[] xExtremes = getUnsignedExtremes(a);
|
||||
long[] yExtremes = getUnsignedExtremes(b);
|
||||
long min = Long.MAX_VALUE;
|
||||
long max = Long.MIN_VALUE;
|
||||
for (long x : xExtremes) {
|
||||
for (long y : yExtremes) {
|
||||
long result = multiplyHighUnsigned(x, y, javaKind);
|
||||
min = Math.min(min, result);
|
||||
max = Math.max(max, result);
|
||||
}
|
||||
}
|
||||
|
||||
// if min is negative, then the value can reach into the unsigned range
|
||||
if (min == max || min >= 0) {
|
||||
return StampFactory.forInteger(javaKind, min, max);
|
||||
} else {
|
||||
return StampFactory.forKind(javaKind);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNeutral(Constant value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private long[] getUnsignedExtremes(IntegerStamp stamp) {
|
||||
if (stamp.lowerBound() < 0 && stamp.upperBound() >= 0) {
|
||||
/*
|
||||
* If -1 and 0 are both in the signed range, then we can't say
|
||||
* anything about the unsigned range, so we have to return [0,
|
||||
* MAX_UNSIGNED].
|
||||
*/
|
||||
return new long[]{0, -1L};
|
||||
} else {
|
||||
return new long[]{stamp.lowerBound(), stamp.upperBound()};
|
||||
}
|
||||
}
|
||||
|
||||
private long multiplyHighUnsigned(long x, long y, JavaKind javaKind) {
|
||||
if (javaKind == JavaKind.Int) {
|
||||
long xl = x & 0xFFFFFFFFL;
|
||||
long yl = y & 0xFFFFFFFFL;
|
||||
long r = xl * yl;
|
||||
return (int) (r >>> 32);
|
||||
} else {
|
||||
assert javaKind == JavaKind.Long;
|
||||
long x0 = x & 0xFFFFFFFFL;
|
||||
long x1 = x >>> 32;
|
||||
|
||||
long y0 = y & 0xFFFFFFFFL;
|
||||
long y1 = y >>> 32;
|
||||
|
||||
long z0 = x0 * y0;
|
||||
long t = x1 * y0 + (z0 >>> 32);
|
||||
long z1 = t & 0xFFFFFFFFL;
|
||||
long z2 = t >>> 32;
|
||||
z1 += x0 * y1;
|
||||
|
||||
return x1 * y1 + z2 + (z1 >>> 32);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new BinaryOp.Div(true, false) {
|
||||
|
||||
@Override
|
||||
@ -1046,10 +1204,14 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||
IntegerStamp value = (IntegerStamp) stamp;
|
||||
int bits = value.getBits();
|
||||
long defaultMask = CodeUtil.mask(bits);
|
||||
if (value.upMask() == 0) {
|
||||
if (value.isEmpty()) {
|
||||
return value;
|
||||
} else if (shift.isEmpty()) {
|
||||
return StampFactory.forInteger(bits).empty();
|
||||
} else if (value.upMask() == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int shiftMask = getShiftAmountMask(stamp);
|
||||
int shiftBits = Integer.bitCount(shiftMask);
|
||||
if (shift.lowerBound() == shift.upperBound()) {
|
||||
@ -1068,6 +1230,7 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||
}
|
||||
}
|
||||
if ((shift.lowerBound() >>> shiftBits) == (shift.upperBound() >>> shiftBits)) {
|
||||
long defaultMask = CodeUtil.mask(bits);
|
||||
long downMask = defaultMask;
|
||||
long upMask = 0;
|
||||
for (long i = shift.lowerBound(); i <= shift.upperBound(); i++) {
|
||||
@ -1109,7 +1272,11 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||
IntegerStamp value = (IntegerStamp) stamp;
|
||||
int bits = value.getBits();
|
||||
if (shift.lowerBound() == shift.upperBound()) {
|
||||
if (value.isEmpty()) {
|
||||
return value;
|
||||
} else if (shift.isEmpty()) {
|
||||
return StampFactory.forInteger(bits).empty();
|
||||
} else if (shift.lowerBound() == shift.upperBound()) {
|
||||
long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp);
|
||||
if (shiftCount == 0) {
|
||||
return stamp;
|
||||
@ -1153,6 +1320,12 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||
IntegerStamp value = (IntegerStamp) stamp;
|
||||
int bits = value.getBits();
|
||||
if (value.isEmpty()) {
|
||||
return value;
|
||||
} else if (shift.isEmpty()) {
|
||||
return StampFactory.forInteger(bits).empty();
|
||||
}
|
||||
|
||||
if (shift.lowerBound() == shift.upperBound()) {
|
||||
long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp);
|
||||
if (shiftCount == 0) {
|
||||
|
@ -46,6 +46,16 @@ public class UnsafeReadEliminationTest extends GraalCompilerTest {
|
||||
public static double SideEffectD;
|
||||
public static double SideEffectL;
|
||||
|
||||
private static final long byteArrayBaseOffset;
|
||||
private static final long intArrayBaseOffset;
|
||||
private static final long longArrayBaseOffset;
|
||||
|
||||
static {
|
||||
byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
|
||||
intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class);
|
||||
longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class);
|
||||
}
|
||||
|
||||
public static long test1Snippet(double a) {
|
||||
final Object m = Memory;
|
||||
if (a > 0) {
|
||||
@ -130,4 +140,76 @@ public class UnsafeReadEliminationTest extends GraalCompilerTest {
|
||||
Assert.assertEquals(writes, graph.getNodes().filter(WriteNode.class).count());
|
||||
}
|
||||
|
||||
public static int testWriteIntToByteArraySnippet() {
|
||||
byte[] array = new byte[4];
|
||||
UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntToByteArray() {
|
||||
test("testWriteIntToByteArraySnippet");
|
||||
}
|
||||
|
||||
public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) {
|
||||
byte[] array = new byte[4];
|
||||
array[0] = 0x01;
|
||||
array[1] = 0x02;
|
||||
array[2] = 0x03;
|
||||
array[3] = 0x04;
|
||||
UNSAFE.putInt(array, byteArrayBaseOffset, b);
|
||||
return array[3];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteSignedExtendedByteToByteArray() {
|
||||
test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0);
|
||||
}
|
||||
|
||||
public static int testWriteLongToIntArraySnippet() {
|
||||
int[] array = new int[2];
|
||||
UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteLongToIntArray() {
|
||||
test("testWriteLongToIntArraySnippet");
|
||||
}
|
||||
|
||||
public static int testWriteByteToIntArraySnippet() {
|
||||
int[] array = new int[1];
|
||||
array[0] = 0x01020304;
|
||||
UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteByteToIntArray() {
|
||||
test("testWriteByteToIntArraySnippet");
|
||||
}
|
||||
|
||||
public static long testWriteIntToLongArraySnippet() {
|
||||
long[] array = new long[1];
|
||||
array[0] = 0x0102030405060708L;
|
||||
UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntToLongArray() {
|
||||
test("testWriteIntToLongArraySnippet");
|
||||
}
|
||||
|
||||
public static float testWriteFloatToIntArraySnippet() {
|
||||
float[] array = new float[1];
|
||||
UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f));
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFloatToIntArray() {
|
||||
test("testWriteFloatToIntArraySnippet");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,10 +38,6 @@ public class UnsafeEATest extends EATestBase {
|
||||
private static final long fieldOffset1;
|
||||
private static final long fieldOffset2;
|
||||
|
||||
private static final long byteArrayBaseOffset;
|
||||
private static final long intArrayBaseOffset;
|
||||
private static final long longArrayBaseOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x"));
|
||||
@ -55,9 +51,6 @@ public class UnsafeEATest extends EATestBase {
|
||||
fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z"));
|
||||
}
|
||||
assert fieldOffset2 == fieldOffset1 + 4;
|
||||
byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
|
||||
intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class);
|
||||
longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -203,76 +196,4 @@ public class UnsafeEATest extends EATestBase {
|
||||
return x;
|
||||
}
|
||||
|
||||
public static int testWriteIntToByteArraySnippet() {
|
||||
byte[] array = new byte[4];
|
||||
UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntToByteArray() {
|
||||
test("testWriteIntToByteArraySnippet");
|
||||
}
|
||||
|
||||
public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) {
|
||||
byte[] array = new byte[4];
|
||||
array[0] = 0x01;
|
||||
array[1] = 0x02;
|
||||
array[2] = 0x03;
|
||||
array[3] = 0x04;
|
||||
UNSAFE.putInt(array, byteArrayBaseOffset, b);
|
||||
return array[3];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteSignedExtendedByteToByteArray() {
|
||||
test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0);
|
||||
}
|
||||
|
||||
public static int testWriteLongToIntArraySnippet() {
|
||||
int[] array = new int[2];
|
||||
UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteLongToIntArray() {
|
||||
test("testWriteLongToIntArraySnippet");
|
||||
}
|
||||
|
||||
public static int testWriteByteToIntArraySnippet() {
|
||||
int[] array = new int[1];
|
||||
array[0] = 0x01020304;
|
||||
UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteByteToIntArray() {
|
||||
test("testWriteByteToIntArraySnippet");
|
||||
}
|
||||
|
||||
public static long testWriteIntToLongArraySnippet() {
|
||||
long[] array = new long[1];
|
||||
array[0] = 0x0102030405060708L;
|
||||
UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201);
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntToLongArray() {
|
||||
test("testWriteIntToLongArraySnippet");
|
||||
}
|
||||
|
||||
public static float testWriteFloatToIntArraySnippet() {
|
||||
float[] array = new float[1];
|
||||
UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f));
|
||||
return array[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFloatToIntArray() {
|
||||
test("testWriteFloatToIntArraySnippet");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,6 +58,11 @@ public final class CompilationPrinter {
|
||||
*/
|
||||
public static CompilationPrinter begin(OptionValues options, CompilationIdentifier id, JavaMethod method, int entryBCI) {
|
||||
if (PrintCompilation.getValue(options) && !TTY.isSuppressed()) {
|
||||
try {
|
||||
Class.forName("java.lang.management.ManagementFactory");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new IllegalArgumentException("PrintCompilation option requires java.management module");
|
||||
}
|
||||
return new CompilationPrinter(id, method, entryBCI);
|
||||
}
|
||||
return DISABLED;
|
||||
|
@ -82,26 +82,6 @@ public abstract class CompilationWrapper<T> {
|
||||
*/
|
||||
ExitVM;
|
||||
|
||||
static ValueHelp HELP = new ValueHelp();
|
||||
|
||||
static class ValueHelp implements EnumOptionKey.ValueHelp<ExceptionAction> {
|
||||
@Override
|
||||
public String getHelp(Object value) {
|
||||
ExceptionAction action = (ExceptionAction) value;
|
||||
switch (action) {
|
||||
case Silent:
|
||||
return action + ": Print nothing to the console.";
|
||||
case Print:
|
||||
return action + ": Print a stack trace to the console.";
|
||||
case Diagnose:
|
||||
return action + ": Retry the compilation with extra diagnostics.";
|
||||
case ExitVM:
|
||||
return action + ": Same as " + Diagnose + " except that the VM process exits after retrying.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the action that is one level less verbose than this action, bottoming out at the
|
||||
* least verbose action.
|
||||
|
@ -36,13 +36,15 @@ public class GraalCompilerOptions {
|
||||
// @formatter:off
|
||||
@Option(help = "Print an informational line to the console for each completed compilation.", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> PrintCompilation = new OptionKey<>(false);
|
||||
@Option(help = "Pattern (see MethodFilter for format) for method that will trigger an exception when compiled. " +
|
||||
"This option exists to test handling compilation crashes gracefully.", type = OptionType.Debug)
|
||||
@Option(help = "Pattern for method(s) that will trigger an exception when compiled. " +
|
||||
"This option exists to test handling compilation crashes gracefully. " +
|
||||
"See the MethodFilter option for the pattern syntax. ", type = OptionType.Debug)
|
||||
public static final OptionKey<String> CrashAt = new OptionKey<>(null);
|
||||
@Option(help = "The action to take when compilation fails with a non-bailout exception.", type = OptionType.User)
|
||||
public static final EnumOptionKey<ExceptionAction> CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose, ExceptionAction.HELP);
|
||||
@Option(help = "The action to take when compilation fails with a bailout exception.", type = OptionType.User)
|
||||
public static final EnumOptionKey<ExceptionAction> CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent, ExceptionAction.HELP);
|
||||
@Option(help = "file:doc-files/CompilationBailoutActionHelp.txt", type = OptionType.User)
|
||||
public static final EnumOptionKey<ExceptionAction> CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent);
|
||||
@Option(help = "Specifies the action to take when compilation fails with a bailout exception. " +
|
||||
"The accepted values are the same as for CompilationBailoutAction.", type = OptionType.User)
|
||||
public static final EnumOptionKey<ExceptionAction> CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose);
|
||||
@Option(help = "The maximum number of compilation failures or bailouts to handle with the action specified " +
|
||||
"by CompilationFailureAction or CompilationBailoutAction before changing to a less verbose action.", type = OptionType.User)
|
||||
public static final OptionKey<Integer> MaxCompilationProblemsPerAction = new OptionKey<>(5);
|
||||
|
@ -0,0 +1,6 @@
|
||||
Specifies the action to take when compilation fails with a bailout exception.
|
||||
The accepted values are:
|
||||
Silent - Print nothing to the console.
|
||||
Print - Print a stack trace to the console.
|
||||
Diagnose - Retry the compilation with extra diagnostics.
|
||||
ExitVM - Same as Diagnose except that the VM process exits after retrying.
|
@ -198,9 +198,22 @@ public final class DebugContext implements AutoCloseable {
|
||||
|
||||
private Immutable(OptionValues options) {
|
||||
this.options = options;
|
||||
String timeValue = Time.getValue(options);
|
||||
String trackMemUseValue = TrackMemUse.getValue(options);
|
||||
this.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false);
|
||||
this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(Time.getValue(options)), true);
|
||||
this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(TrackMemUse.getValue(options)), true);
|
||||
this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(timeValue), true);
|
||||
this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(trackMemUseValue), true);
|
||||
|
||||
if (unscopedTimers != null ||
|
||||
unscopedMemUseTrackers != null ||
|
||||
timeValue != null ||
|
||||
trackMemUseValue != null) {
|
||||
try {
|
||||
Class.forName("java.lang.management.ManagementFactory");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new IllegalArgumentException("Time, Timers, MemUseTrackers and TrackMemUse options require java.management module");
|
||||
}
|
||||
}
|
||||
|
||||
this.scopesEnabled = DumpOnError.getValue(options) ||
|
||||
Dump.getValue(options) != null ||
|
||||
|
@ -28,8 +28,11 @@ import java.util.regex.Pattern;
|
||||
import org.graalvm.compiler.debug.DebugContext.Scope;
|
||||
|
||||
/**
|
||||
* Implements the filter specified by the {@link DebugOptions#Dump}, {@link DebugOptions#Log},
|
||||
* {@link DebugOptions#Count} and {@link DebugOptions#Time} options.
|
||||
* Implements the filter specified by options such as {@link DebugOptions#Dump},
|
||||
* {@link DebugOptions#Log}, {@link DebugOptions#Count} and {@link DebugOptions#Time}.
|
||||
*
|
||||
* See <a href="DumpHelp.txt">here</a> for a description of the filter syntax.
|
||||
*
|
||||
* <p>
|
||||
* These options enable the associated debug facility if their filter matches the
|
||||
* {@linkplain Scope#getQualifiedName() name} of the current scope. For the
|
||||
@ -37,47 +40,7 @@ import org.graalvm.compiler.debug.DebugContext.Scope;
|
||||
* {@link DebugOptions#Count} and {@link DebugOptions#Time} options don't have a level, for them
|
||||
* {@code level = 0} means disabled and a {@code level > 0} means enabled.
|
||||
* <p>
|
||||
* A filter is a list of comma-separated terms of the form {@code <pattern>[:<level>]}. {@code
|
||||
* <pattern>} is interpreted as a glob pattern if it contains a "*" or "?" character. Otherwise, it
|
||||
* is interpreted as a substring. If {@code <pattern>} is empty, it matches every scope. If {@code :
|
||||
* <level>} is omitted, it defaults to {@link DebugContext#BASIC_LEVEL}. The term {@code ~<pattern>}
|
||||
* is a shorthand for {@code <pattern>:0} to disable a debug facility for a pattern.
|
||||
* <p>
|
||||
* The resulting log level of a scope is determined by the <em>last</em> matching term. If no term
|
||||
* matches, the log level is 0 (disabled). A filter with no terms matches every scope with a log
|
||||
* level of {@link DebugContext#BASIC_LEVEL}.
|
||||
*
|
||||
* <h2>Examples of filters</h2>
|
||||
*
|
||||
* <ul>
|
||||
* <li>(empty string)<br>
|
||||
* Matches any scope with log level {@link DebugContext#BASIC_LEVEL}.
|
||||
*
|
||||
* <li>{@code :1}<br>
|
||||
* Matches any scope with log level 1.
|
||||
*
|
||||
* <li>{@code *}<br>
|
||||
* Matches any scope with log level {@link DebugContext#BASIC_LEVEL}.
|
||||
*
|
||||
* <li>{@code CodeGen,CodeInstall}<br>
|
||||
* Matches scopes containing "CodeGen" or "CodeInstall", both with log level
|
||||
* {@link DebugContext#BASIC_LEVEL}.
|
||||
*
|
||||
* <li>{@code CodeGen:2,CodeInstall:1}<br>
|
||||
* Matches scopes containing "CodeGen" with log level 2, or "CodeInstall" with log level 1.
|
||||
*
|
||||
* <li>{@code :1,Dead:2}<br>
|
||||
* Matches scopes containing "Dead" with log level 2, and all other scopes with log level 1.
|
||||
*
|
||||
* <li>{@code :1,Dead:0}<br>
|
||||
* Matches all scopes with log level 1, except those containing "Dead".
|
||||
*
|
||||
* <li>{@code Code*}<br>
|
||||
* Matches scopes starting with "Code" with log level {@link DebugContext#BASIC_LEVEL}.
|
||||
*
|
||||
* <li>{@code Code,~Dead}<br>
|
||||
* Matches scopes containing "Code" but not "Dead", with log level {@link DebugContext#BASIC_LEVEL}.
|
||||
* </ul>
|
||||
* The syntax for a filter is explained <a href="file:doc-files/DumpHelp.txt">here</a>.
|
||||
*/
|
||||
final class DebugFilter {
|
||||
|
||||
@ -148,13 +111,16 @@ final class DebugFilter {
|
||||
if (terms == null) {
|
||||
return DebugContext.BASIC_LEVEL;
|
||||
} else {
|
||||
int level = 0;
|
||||
int defaultLevel = 0;
|
||||
int level = -1;
|
||||
for (Term t : terms) {
|
||||
if (t.matches(input)) {
|
||||
if (t.isMatchAny()) {
|
||||
defaultLevel = t.level;
|
||||
} else if (t.matches(input)) {
|
||||
level = t.level;
|
||||
}
|
||||
}
|
||||
return level;
|
||||
return level == -1 ? defaultLevel : level;
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +142,7 @@ final class DebugFilter {
|
||||
|
||||
Term(String filter, int level) {
|
||||
this.level = level;
|
||||
if (filter.isEmpty()) {
|
||||
if (filter.isEmpty() || filter.equals("*")) {
|
||||
this.pattern = null;
|
||||
} else if (filter.contains("*") || filter.contains("?")) {
|
||||
this.pattern = Pattern.compile(MethodFilter.createGlobString(filter));
|
||||
@ -192,6 +158,10 @@ final class DebugFilter {
|
||||
return pattern == null || pattern.matcher(input).matches();
|
||||
}
|
||||
|
||||
public boolean isMatchAny() {
|
||||
return pattern == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (pattern == null ? ".*" : pattern.toString()) + ":" + level;
|
||||
|
@ -64,24 +64,28 @@ public class DebugOptions {
|
||||
"An empty value enables all memory usage trackers unconditionally.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> MemUseTrackers = new OptionKey<>(null);
|
||||
|
||||
@Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " +
|
||||
@Option(help = "Pattern for specifying scopes in which counters are enabled. " +
|
||||
"See the Dump option for the pattern syntax. " +
|
||||
"An empty value enables all counters unconditionally.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> Count = new OptionKey<>(null);
|
||||
@Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " +
|
||||
@Option(help = "Pattern for specifying scopes in which memory use tracking is enabled. " +
|
||||
"See the Dump option for the pattern syntax. " +
|
||||
"An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> TrackMemUse = new OptionKey<>(null);
|
||||
@Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " +
|
||||
@Option(help = "Pattern for specifying scopes in which timing is enabled. " +
|
||||
"See the Dump option for the pattern syntax. " +
|
||||
"An empty value enables all timers unconditionally.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> Time = new OptionKey<>(null);
|
||||
|
||||
@Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug)
|
||||
@Option(help = "Pattern for specifying scopes in which logging is enabled. " +
|
||||
"See the Dump option for the pattern syntax.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> Verify = new OptionKey<>(null);
|
||||
@Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug)
|
||||
@Option(help = "file:doc-files/DumpHelp.txt", type = OptionType.Debug)
|
||||
public static final OptionKey<String> Dump = new OptionKey<>(null);
|
||||
@Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug)
|
||||
@Option(help = "Pattern for specifying scopes in which logging is enabled. " +
|
||||
"See the Dump option for the pattern syntax.", type = OptionType.Debug)
|
||||
public static final OptionKey<String> Log = new OptionKey<>(null);
|
||||
|
||||
@Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug)
|
||||
@Option(help = "file:doc-files/MethodFilterHelp.txt")
|
||||
public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
|
||||
@Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> MethodFilterRootOnly = new OptionKey<>(false);
|
||||
@ -89,13 +93,11 @@ public class DebugOptions {
|
||||
"The argument is substring matched against the simple name of the phase class", type = OptionType.Debug)
|
||||
public static final OptionKey<String> DumpOnPhaseChange = new OptionKey<>(null);
|
||||
|
||||
@Option(help = "Listst the console at VM shutdown the metric names available to the Timers, Counters and MemUseTrackers option. " +
|
||||
@Option(help = "Lists on the console at VM shutdown the metric names available to the Timers, Counters and MemUseTrackers options. " +
|
||||
"Note that this only lists the metrics that were initialized during the VM execution and so " +
|
||||
"will not include metrics for compiler code that is not executed.", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> ListMetrics = new OptionKey<>(false);
|
||||
@Option(help = "File to which metrics are dumped per compilation. A CSV format is used if the file ends with .csv " +
|
||||
"otherwise a more human readable format is used. The fields in the CSV format are: " +
|
||||
"compilable, compilable_identity, compilation_nr, compilation_id, metric_name, metric_value", type = OptionType.Debug)
|
||||
@Option(help = "file:doc-files/MetricsFileHelp.txt", type = OptionType.Debug)
|
||||
public static final OptionKey<String> MetricsFile = new OptionKey<>(null);
|
||||
@Option(help = "File to which aggregated metrics are dumped at shutdown. A CSV format is used if the file ends with .csv " +
|
||||
"otherwise a more human readable format is used. If not specified, metrics are dumped to the console.", type = OptionType.Debug)
|
||||
@ -149,7 +151,7 @@ public class DebugOptions {
|
||||
@Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> PrintCanonicalGraphStrings = new OptionKey<>(false);
|
||||
@Option(help = "Choose format used when dumping canonical text for graphs: " +
|
||||
"0 gives a scheduled graph (better for spotting changes involving the schedule)" +
|
||||
"0 gives a scheduled graph (better for spotting changes involving the schedule) " +
|
||||
"while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug)
|
||||
public static final OptionKey<Integer> PrintCanonicalGraphStringFlavor = new OptionKey<>(0);
|
||||
@Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
|
||||
|
@ -31,66 +31,7 @@ import jdk.vm.ci.meta.Signature;
|
||||
|
||||
/**
|
||||
* This class implements a method filter that can filter based on class name, method name and
|
||||
* parameters. The syntax for the source pattern that is passed to the constructor is as follows:
|
||||
*
|
||||
* <pre>
|
||||
* SourcePatterns = SourcePattern ["," SourcePatterns] .
|
||||
* SourcePattern = [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] .
|
||||
* Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" .
|
||||
* Class = { package "." } class .
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* Glob pattern matching (*, ?) is allowed in all parts of the source pattern. Examples for valid
|
||||
* filters are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* visit(Argument;BlockScope)
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods named "visit", with the first parameter of type "Argument", and the second
|
||||
* parameter of type "BlockScope". The packages of the parameter types are irrelevant.</li>
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* arraycopy(Object;;;;)
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods named "arraycopy", with the first parameter of type "Object", and four more
|
||||
* parameters of any type. The packages of the parameter types are irrelevant.</li>
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* org.graalvm.compiler.core.graph.PostOrderNodeIterator.*
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods in the class "org.graalvm.compiler.core.graph.PostOrderNodeIterator".</li>
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* *
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods in all classes</li>
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* org.graalvm.compiler.core.graph.*.visit
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods named "visit" in classes in the package "org.graalvm.compiler.core.graph".
|
||||
* <li>
|
||||
*
|
||||
* <pre>
|
||||
* arraycopy,toString
|
||||
* </pre>
|
||||
*
|
||||
* Matches all methods named "arraycopy" or "toString", meaning that ',' acts as an <i>or</i>
|
||||
* operator.</li>
|
||||
* </ul>
|
||||
* parameters. The syntax for a filter is explained <a href="MethodFilterHelp.txt">here</a>.
|
||||
*/
|
||||
public class MethodFilter {
|
||||
|
||||
|
@ -0,0 +1,61 @@
|
||||
Filter pattern for specifying scopes in which dumping is enabled.
|
||||
|
||||
A filter is a list of comma-separated terms of the form:
|
||||
|
||||
<pattern>[:<level>]
|
||||
|
||||
If <pattern> contains a "*" or "?" character, it is interpreted as a glob pattern.
|
||||
Otherwise, it is interpreted as a substring. If <pattern> is empty, it
|
||||
matches every scope. If :<level> is omitted, it defaults to 1. The term
|
||||
~<pattern> is a shorthand for <pattern>:0 to disable a debug facility for a pattern.
|
||||
|
||||
The default log level is 0 (disabled). Terms with an empty pattern set
|
||||
the default log level to the specified value. The last
|
||||
matching term with a non-empty pattern selects the level specified. If
|
||||
no term matches, the log level is the default level. A filter with no
|
||||
terms matches every scope with a log level of 1.
|
||||
|
||||
Examples of debug filters:
|
||||
---------
|
||||
(empty string)
|
||||
|
||||
Matches any scope with level 1.
|
||||
---------
|
||||
:1
|
||||
|
||||
Matches any scope with level 1.
|
||||
---------
|
||||
*
|
||||
|
||||
Matches any scope with level 1.
|
||||
---------
|
||||
CodeGen,CodeInstall
|
||||
|
||||
Matches scopes containing "CodeGen" or "CodeInstall", both with level 1.
|
||||
---------
|
||||
CodeGen:2,CodeInstall:1
|
||||
|
||||
Matches scopes containing "CodeGen" with level 2, or "CodeInstall" with level 1.
|
||||
---------
|
||||
Outer:2,Inner:0}
|
||||
|
||||
Matches scopes containing "Outer" with log level 2, or "Inner" with log level 0. If the scope
|
||||
name contains both patterns then the log level will be 0. This is useful for silencing subscopes.
|
||||
---------
|
||||
:1,Dead:2
|
||||
|
||||
Matches scopes containing "Dead" with level 2, and all other scopes with level 1.
|
||||
---------
|
||||
Dead:0,:1
|
||||
|
||||
Matches all scopes with level 1, except those containing "Dead". Note that the location of
|
||||
the :1 doesn't matter since it's specifying the default log level so it's the same as
|
||||
specifying :1,Dead:0.
|
||||
---------
|
||||
Code*
|
||||
|
||||
Matches scopes starting with "Code" with level 1.
|
||||
---------
|
||||
Code,~Dead
|
||||
|
||||
Matches scopes containing "Code" but not "Dead", with level 1.
|
@ -0,0 +1,40 @@
|
||||
Pattern for filtering debug scope output based on method context.
|
||||
The syntax for a pattern is:
|
||||
|
||||
SourcePatterns = SourcePattern ["," SourcePatterns] .
|
||||
SourcePattern = [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] .
|
||||
Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" .
|
||||
Class = { package "." } class .
|
||||
|
||||
Glob pattern matching (*, ?) is allowed in all parts of the source pattern.
|
||||
|
||||
Examples of method filters:
|
||||
---------
|
||||
visit(Argument;BlockScope)
|
||||
|
||||
Matches all methods named "visit", with the first parameter of
|
||||
type "Argument", and the second parameter of type "BlockScope".
|
||||
The packages of the parameter types are irrelevant.
|
||||
---------
|
||||
arraycopy(Object;;;;)
|
||||
|
||||
Matches all methods named "arraycopy", with the first parameter
|
||||
of type "Object", and four more parameters of any type. The
|
||||
packages of the parameter types are irrelevant.
|
||||
---------
|
||||
org.graalvm.compiler.core.graph.PostOrderNodeIterator.*
|
||||
|
||||
Matches all methods in the class "org.graalvm.compiler.core.graph.PostOrderNodeIterator".
|
||||
---------
|
||||
*
|
||||
|
||||
Matches all methods in all classes
|
||||
---------
|
||||
org.graalvm.compiler.core.graph.*.visit
|
||||
|
||||
Matches all methods named "visit" in classes in the package
|
||||
"org.graalvm.compiler.core.graph".
|
||||
---------
|
||||
arraycopy,toString
|
||||
|
||||
Matches all methods named "arraycopy" or "toString", meaning that ',' acts as an or operator.
|
@ -0,0 +1,11 @@
|
||||
File to which metrics are dumped per compilation.
|
||||
A CSV format is used if the file ends with .csv otherwise a more
|
||||
human readable format is used. The fields in the CSV format are:
|
||||
compilable - method being compiled
|
||||
compilable_identity - identity hash code of compilable
|
||||
compilation_nr - where this compilation lies in the ordered
|
||||
sequence of all compilations identified by
|
||||
compilable_identity
|
||||
compilation_id - runtime issued identifier for the compilation
|
||||
metric_name - name of metric
|
||||
metric_value - value of metric
|
@ -931,7 +931,7 @@ public final class NodeClass<T> extends FieldIntrospection<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the node has no inputs and no successors
|
||||
* @return true if the node has no inputs and no successors
|
||||
*/
|
||||
public boolean isLeafNode() {
|
||||
return isLeafNode;
|
||||
|
@ -40,20 +40,12 @@ import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.graalvm.compiler.test.SubprocessUtil;
|
||||
import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests support for dumping graphs and other info useful for debugging a compiler crash.
|
||||
*/
|
||||
public class CompilationWrapperTest extends GraalCompilerTest {
|
||||
public CompilationWrapperTest() {
|
||||
try {
|
||||
Class.forName("java.lang.management.ManagementFactory");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Assume.assumeNoException("skip this test if there is no java.management JDK9 module around", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests compilation requested by the VM.
|
||||
|
@ -25,6 +25,7 @@ package org.graalvm.compiler.hotspot.test;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@ -84,4 +85,20 @@ public class ObjectCloneTest extends GraalCompilerTest {
|
||||
}
|
||||
test("cloneList", list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) {
|
||||
return super.editGraphBuilderConfiguration(conf.withNodeSourcePosition(true));
|
||||
}
|
||||
|
||||
static final int[] ARRAY = new int[]{1, 2, 4, 3};
|
||||
|
||||
public static int[] cloneConstantArray() {
|
||||
return ARRAY.clone();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloneConstantArray() {
|
||||
test("cloneConstantArray");
|
||||
}
|
||||
}
|
||||
|
@ -157,8 +157,8 @@ public abstract class CompilerConfigurationFactory implements Comparable<Compile
|
||||
/**
|
||||
* Selects and instantiates a {@link CompilerConfigurationFactory}. The selection algorithm is
|
||||
* as follows: if {@code name} is non-null, then select the factory with the same name else if
|
||||
* {@link Options#CompilerConfiguration}{@code .getValue()} is non-null then select the factory
|
||||
* whose name matches the value else select the factory with the highest
|
||||
* {@code Options.CompilerConfiguration.getValue()} is non-null then select the factory whose
|
||||
* name matches the value else select the factory with the highest
|
||||
* {@link #autoSelectionPriority} value.
|
||||
*
|
||||
* @param name the name of the compiler configuration to select (optional)
|
||||
|
@ -23,7 +23,6 @@
|
||||
package org.graalvm.compiler.hotspot;
|
||||
|
||||
import java.util.Formatter;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Mechanism for checking that the current Java runtime environment supports the minimum JVMCI API
|
||||
@ -40,10 +39,6 @@ class JVMCIVersionCheck {
|
||||
private static final int JVMCI8_MIN_MAJOR_VERSION = 0;
|
||||
private static final int JVMCI8_MIN_MINOR_VERSION = 29;
|
||||
|
||||
// MAX_VALUE indicates that no current EA version is compatible with Graal.
|
||||
// Note: Keep README.md in sync with the EA version support checked here.
|
||||
private static final int JVMCI9_MIN_EA_BUILD = 176;
|
||||
|
||||
private static void failVersionCheck(boolean exit, String reason, Object... args) {
|
||||
Formatter errorMessage = new Formatter().format(reason, args);
|
||||
String javaHome = System.getProperty("java.home");
|
||||
@ -55,7 +50,7 @@ class JVMCIVersionCheck {
|
||||
if (System.getProperty("java.specification.version").compareTo("1.9") < 0) {
|
||||
errorMessage.format("Download the latest JVMCI JDK 8 from http://www.oracle.com/technetwork/oracle-labs/program-languages/downloads/index.html");
|
||||
} else {
|
||||
errorMessage.format("Download the latest JDK 9 EA from https://jdk9.java.net/download/");
|
||||
errorMessage.format("Download the latest JDK 9 build from https://jdk9.java.net/download/");
|
||||
}
|
||||
String value = System.getenv("JVMCI_VERSION_CHECK");
|
||||
if ("warn".equals(value)) {
|
||||
@ -119,34 +114,11 @@ class JVMCIVersionCheck {
|
||||
// Allow local builds
|
||||
return;
|
||||
}
|
||||
// http://openjdk.java.net/jeps/223
|
||||
if (vmVersion.startsWith("9+")) {
|
||||
int start = "9+".length();
|
||||
int end = start;
|
||||
end = start;
|
||||
while (end < vmVersion.length() && Character.isDigit(vmVersion.charAt(end))) {
|
||||
end++;
|
||||
}
|
||||
int build;
|
||||
try {
|
||||
build = Integer.parseInt(vmVersion.substring(start, end));
|
||||
} catch (NumberFormatException e) {
|
||||
failVersionCheck(exitOnFailure, "The VM does not support the minimum JVMCI API version required by Graal.%n" +
|
||||
"Cannot read JDK9 EA build number from java.vm.version property: %s.%n", vmVersion);
|
||||
return;
|
||||
}
|
||||
if (build >= JVMCI9_MIN_EA_BUILD) {
|
||||
return;
|
||||
}
|
||||
if (Objects.equals(JVMCI9_MIN_EA_BUILD, Integer.MAX_VALUE)) {
|
||||
failVersionCheck(exitOnFailure, "This version of Graal is not compatible with any JDK 9 Early Access build.%n");
|
||||
} else {
|
||||
failVersionCheck(exitOnFailure, "The VM is an insufficiently recent EA JDK9 build for Graal: %d < %d.%n", build, JVMCI9_MIN_EA_BUILD);
|
||||
}
|
||||
if (vmVersion.startsWith("9-ea")) {
|
||||
failVersionCheck(exitOnFailure, "This version of Graal is not compatible with JDK 9 Early Access builds.%n");
|
||||
return;
|
||||
} else {
|
||||
// Graal will be compatible with all JDK versions as of 9 GA
|
||||
// until a JVMCI API change is made in a 9u or later release.
|
||||
// Graal is compatible with all JDK versions as of 9 GA.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ import org.graalvm.compiler.debug.CSVUtil;
|
||||
import org.graalvm.compiler.debug.GraalError;
|
||||
import org.graalvm.compiler.debug.TTY;
|
||||
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
|
||||
import org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions;
|
||||
import org.graalvm.compiler.nodes.debug.DynamicCounterNode;
|
||||
import org.graalvm.compiler.options.Option;
|
||||
import org.graalvm.compiler.options.OptionKey;
|
||||
@ -69,20 +68,8 @@ import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
|
||||
* Counters will be displayed as a rate (per second) if their group name starts with "~", otherwise
|
||||
* they will be displayed as a total number.
|
||||
*
|
||||
* <h1>Example</h1> In order to create statistics about allocations within the DaCapo pmd benchmark
|
||||
* the following steps are necessary:
|
||||
* <ul>
|
||||
* <li>Set {@code -XX:JVMCICounterSize=value}. The actual required value depends on the granularity
|
||||
* of the profiling, 10000 should be enough for most cases.</li>
|
||||
* <li>Also: {@code -XX:+/-JVMCICountersExcludeCompiler} specifies whether the numbers generated by
|
||||
* compiler threads should be excluded (default: true).</li>
|
||||
* <li>Start the DaCapo pmd benchmark with
|
||||
* {@code "-Dgraal.BenchmarkDynamicCounters=err, starting ====, PASSED in "} and
|
||||
* {@code -Dgraal.ProfileAllocations=true}.</li>
|
||||
* <li>The numbers will only include allocation from compiled code!</li>
|
||||
* <li>The counters can be further configured by modifying the
|
||||
* {@link HotspotSnippetsOptions#ProfileAllocationsContext} flag..</li>
|
||||
* </ul>
|
||||
* See <a href="BenchmarkDynamicCountersHelp.txt">here</a> for a detailed example of how to use
|
||||
* benchmark counters.
|
||||
*/
|
||||
public class BenchmarkCounters {
|
||||
|
||||
@ -94,11 +81,7 @@ public class BenchmarkCounters {
|
||||
@Option(help = "Turn on the benchmark counters, and displays the results every n milliseconds", type = OptionType.Debug)
|
||||
public static final OptionKey<Integer> TimedDynamicCounters = new OptionKey<>(-1);
|
||||
|
||||
@Option(help = "Turn on the benchmark counters, and listen for specific patterns on System.out/System.err:%n" +
|
||||
"Format: (err|out),start pattern,end pattern (~ matches multiple digits)%n" +
|
||||
"Examples:%n" +
|
||||
" dacapo = 'err, starting =====, PASSED in'%n" +
|
||||
" specjvm2008 = 'out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:'", type = OptionType.Debug)
|
||||
@Option(help = "file:doc-files/BenchmarkDynamicCountersHelp.txt", type = OptionType.Debug)
|
||||
public static final OptionKey<String> BenchmarkDynamicCounters = new OptionKey<>(null);
|
||||
@Option(help = "Use grouping separators for number printing", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> DynamicCountersPrintGroupSeparator = new OptionKey<>(true);
|
||||
|
@ -0,0 +1,24 @@
|
||||
Turn on the benchmark counters, and listen for specific patterns on System.out/System.err.
|
||||
The format of this option is:
|
||||
|
||||
(err|out),start pattern,end pattern
|
||||
|
||||
You can use "~" to match 1 or more digits.
|
||||
Examples:
|
||||
|
||||
err, starting =====, PASSED in
|
||||
out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:
|
||||
|
||||
The first pattern matches DaCapo output and the second matches SPECjvm2008 output.
|
||||
|
||||
As a more detailed example, here are the options to use for getting statistics
|
||||
about allocations within the DaCapo pmd benchmark:
|
||||
|
||||
-XX:JVMCICounterSize=<value> -XX:-JVMCICountersExcludeCompiler \
|
||||
-Dgraal.BenchmarkDynamicCounters="err, starting ====, PASSED in " \
|
||||
-Dgraal.ProfileAllocations=true
|
||||
|
||||
The JVMCICounterSize value depends on the granularity of the profiling -
|
||||
10000 should be sufficient. Omit JVMCICountersExcludeCompiler to exclude
|
||||
counting allocations on the compiler threads.
|
||||
The counters can be further configured by the ProfileAllocationsContext option.
|
@ -25,6 +25,7 @@ package org.graalvm.compiler.hotspot.meta;
|
||||
import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
|
||||
import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
|
||||
import static org.graalvm.compiler.core.common.GraalOptions.VerifyPhases;
|
||||
import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
@ -98,10 +99,12 @@ public class HotSpotSuitesProvider extends SuitesProviderBase {
|
||||
midTierLowering.add(new ReplaceConstantNodesPhase());
|
||||
|
||||
// Replace inlining policy
|
||||
ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(InliningPhase.class);
|
||||
InliningPhase inlining = (InliningPhase) iter.previous();
|
||||
CanonicalizerPhase canonicalizer = inlining.getCanonicalizer();
|
||||
iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer));
|
||||
if (Inline.getValue(options)) {
|
||||
ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(InliningPhase.class);
|
||||
InliningPhase inlining = (InliningPhase) iter.previous();
|
||||
CanonicalizerPhase canonicalizer = inlining.getCanonicalizer();
|
||||
iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class HotspotSnippetsOptions {
|
||||
@Option(help = "Enable profiling of allocation sites.", type = OptionType.Debug)
|
||||
public static final OptionKey<Boolean> ProfileAllocations = new OptionKey<>(false);
|
||||
|
||||
@Option(help = "Control the naming of the counters when using ProfileAllocations.", type = OptionType.Debug)
|
||||
@Option(help = "file:doc-files/ProfileAllocationsContextHelp.txt", type = OptionType.Debug)
|
||||
public static final EnumOptionKey<ProfileContext> ProfileAllocationsContext = new EnumOptionKey<>(ProfileContext.AllocatingMethod);
|
||||
|
||||
@Option(help = "Enable profiling of monitor operations.", type = OptionType.Debug)
|
||||
|
@ -0,0 +1,8 @@
|
||||
Control the naming and granularity of the counters when using ProfileAllocations.
|
||||
The accepted values are:
|
||||
AllocatingMethod - a counter per method
|
||||
InstanceOrArray - one counter for all instance allocations and
|
||||
one counter for all array allocations
|
||||
AllocatedType - one counter per allocated type
|
||||
AllocatedTypesInMethod - one counter per allocated type, per method
|
||||
|
@ -36,6 +36,8 @@ import org.graalvm.compiler.phases.Phase;
|
||||
import org.graalvm.compiler.phases.graph.ReentrantNodeIterator;
|
||||
import org.graalvm.util.EconomicMap;
|
||||
|
||||
import static org.graalvm.compiler.nodes.cfg.ControlFlowGraph.multiplyProbabilities;
|
||||
|
||||
public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure<Double> {
|
||||
|
||||
private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure();
|
||||
@ -75,31 +77,17 @@ public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.N
|
||||
for (double d : exitStates.getValues()) {
|
||||
exitProbability += d;
|
||||
}
|
||||
exitProbability = Math.min(1D, exitProbability);
|
||||
if (exitProbability < ControlFlowGraph.MIN_PROBABILITY) {
|
||||
exitProbability = ControlFlowGraph.MIN_PROBABILITY;
|
||||
}
|
||||
assert exitProbability <= 1D && exitProbability >= 0D;
|
||||
double loopFrequency = 1D / exitProbability;
|
||||
exitProbability = Math.min(1.0, exitProbability);
|
||||
exitProbability = Math.max(ControlFlowGraph.MIN_PROBABILITY, exitProbability);
|
||||
double loopFrequency = 1.0 / exitProbability;
|
||||
loop.setLoopFrequency(loopFrequency);
|
||||
|
||||
double adjustmentFactor = initialState * loopFrequency;
|
||||
exitStates.replaceAll((exitNode, probability) -> multiplySaturate(probability, adjustmentFactor));
|
||||
exitStates.replaceAll((exitNode, probability) -> multiplyProbabilities(probability, adjustmentFactor));
|
||||
|
||||
return exitStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies a and b and saturates the result to {@link ControlFlowGraph#MAX_PROBABILITY}.
|
||||
*/
|
||||
public static double multiplySaturate(double a, double b) {
|
||||
double r = a * b;
|
||||
if (r > ControlFlowGraph.MAX_PROBABILITY) {
|
||||
return ControlFlowGraph.MAX_PROBABILITY;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the frequencies of all loops in the given graph. This is done by performing a
|
||||
* reverse postorder iteration and computing the probability of all fixed nodes. The combined
|
||||
|
@ -68,6 +68,8 @@ public final class AArch64ArrayEqualsOp extends AArch64LIRInstruction {
|
||||
|
||||
public AArch64ArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, Value result, Value array1, Value array2, Value length) {
|
||||
super(TYPE);
|
||||
|
||||
assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported";
|
||||
this.kind = kind;
|
||||
|
||||
Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
|
||||
|
@ -33,6 +33,8 @@ import org.graalvm.compiler.asm.Label;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64Address;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp;
|
||||
import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
|
||||
import org.graalvm.compiler.core.common.LIRKind;
|
||||
import org.graalvm.compiler.lir.LIRInstructionClass;
|
||||
@ -69,6 +71,10 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
@Temp({REG}) protected Value temp2;
|
||||
@Temp({REG}) protected Value temp3;
|
||||
@Temp({REG}) protected Value temp4;
|
||||
|
||||
@Temp({REG, ILLEGAL}) protected Value temp5;
|
||||
@Temp({REG, ILLEGAL}) protected Value tempXMM;
|
||||
|
||||
@Temp({REG, ILLEGAL}) protected Value vectorTemp1;
|
||||
@Temp({REG, ILLEGAL}) protected Value vectorTemp2;
|
||||
|
||||
@ -91,6 +97,15 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
|
||||
this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
|
||||
|
||||
this.temp5 = kind.isNumericFloat() ? tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())) : Value.ILLEGAL;
|
||||
if (kind == JavaKind.Float) {
|
||||
this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.SINGLE));
|
||||
} else if (kind == JavaKind.Double) {
|
||||
this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
|
||||
} else {
|
||||
this.tempXMM = Value.ILLEGAL;
|
||||
}
|
||||
|
||||
// We only need the vector temporaries if we generate SSE code.
|
||||
if (supportsSSE41(tool.target())) {
|
||||
this.vectorTemp1 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
|
||||
@ -170,10 +185,14 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
Label loop = new Label();
|
||||
Label compareTail = new Label();
|
||||
|
||||
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||
Label loopCheck = new Label();
|
||||
Label nanCheck = new Label();
|
||||
|
||||
// Compare 16-byte vectors
|
||||
masm.andl(result, SSE4_1_VECTOR_SIZE - 1); // tail count (in bytes)
|
||||
masm.andl(length, ~(SSE4_1_VECTOR_SIZE - 1)); // vector count (in bytes)
|
||||
masm.jccb(ConditionFlag.Zero, compareTail);
|
||||
masm.jcc(ConditionFlag.Zero, compareTail);
|
||||
|
||||
masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||
masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
@ -186,13 +205,24 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
masm.pxor(vector1, vector2);
|
||||
masm.ptest(vector1, vector1);
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel);
|
||||
|
||||
masm.bind(loopCheck);
|
||||
masm.addq(length, SSE4_1_VECTOR_SIZE);
|
||||
masm.jcc(ConditionFlag.NotZero, loop);
|
||||
|
||||
masm.testl(result, result);
|
||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
||||
|
||||
if (requiresNaNCheck) {
|
||||
Label unalignedCheck = new Label();
|
||||
masm.jmpb(unalignedCheck);
|
||||
masm.bind(nanCheck);
|
||||
emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, SSE4_1_VECTOR_SIZE);
|
||||
masm.jmpb(loopCheck);
|
||||
masm.bind(unalignedCheck);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the remaining bytes with an unaligned memory load aligned to the end of the
|
||||
* array.
|
||||
@ -201,7 +231,12 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
|
||||
masm.pxor(vector1, vector2);
|
||||
masm.ptest(vector1, vector1);
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
if (requiresNaNCheck) {
|
||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
||||
emitFloatCompareWithinRange(crb, masm, array1, array2, result, -SSE4_1_VECTOR_SIZE, falseLabel, SSE4_1_VECTOR_SIZE);
|
||||
} else {
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
}
|
||||
masm.jmp(trueLabel);
|
||||
|
||||
masm.bind(compareTail);
|
||||
@ -233,10 +268,14 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
Label loop = new Label();
|
||||
Label compareTail = new Label();
|
||||
|
||||
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||
Label loopCheck = new Label();
|
||||
Label nanCheck = new Label();
|
||||
|
||||
// Compare 16-byte vectors
|
||||
masm.andl(result, AVX_VECTOR_SIZE - 1); // tail count (in bytes)
|
||||
masm.andl(length, ~(AVX_VECTOR_SIZE - 1)); // vector count (in bytes)
|
||||
masm.jccb(ConditionFlag.Zero, compareTail);
|
||||
masm.jcc(ConditionFlag.Zero, compareTail);
|
||||
|
||||
masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||
masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
@ -249,13 +288,24 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.vmovdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
masm.vpxor(vector1, vector1, vector2);
|
||||
masm.vptest(vector1, vector1);
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel);
|
||||
|
||||
masm.bind(loopCheck);
|
||||
masm.addq(length, AVX_VECTOR_SIZE);
|
||||
masm.jcc(ConditionFlag.NotZero, loop);
|
||||
|
||||
masm.testl(result, result);
|
||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
||||
|
||||
if (requiresNaNCheck) {
|
||||
Label unalignedCheck = new Label();
|
||||
masm.jmpb(unalignedCheck);
|
||||
masm.bind(nanCheck);
|
||||
emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, AVX_VECTOR_SIZE);
|
||||
masm.jmpb(loopCheck);
|
||||
masm.bind(unalignedCheck);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the remaining bytes with an unaligned memory load aligned to the end of the
|
||||
* array.
|
||||
@ -264,7 +314,12 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.vmovdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -AVX_VECTOR_SIZE));
|
||||
masm.vpxor(vector1, vector1, vector2);
|
||||
masm.vptest(vector1, vector1);
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
if (requiresNaNCheck) {
|
||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
||||
emitFloatCompareWithinRange(crb, masm, array1, array2, result, -AVX_VECTOR_SIZE, falseLabel, AVX_VECTOR_SIZE);
|
||||
} else {
|
||||
masm.jcc(ConditionFlag.NotZero, falseLabel);
|
||||
}
|
||||
masm.jmp(trueLabel);
|
||||
|
||||
masm.bind(compareTail);
|
||||
@ -283,11 +338,15 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
Label loop = new Label();
|
||||
Label compareTail = new Label();
|
||||
|
||||
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||
Label loopCheck = new Label();
|
||||
Label nanCheck = new Label();
|
||||
|
||||
Register temp = asRegister(temp4);
|
||||
|
||||
masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes)
|
||||
masm.andl(length, ~(VECTOR_SIZE - 1)); // vector count (in bytes)
|
||||
masm.jccb(ConditionFlag.Zero, compareTail);
|
||||
masm.jcc(ConditionFlag.Zero, compareTail);
|
||||
|
||||
masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||
masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
@ -298,12 +357,27 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.bind(loop);
|
||||
masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||
masm.cmpq(temp, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||
masm.jcc(ConditionFlag.NotEqual, requiresNaNCheck ? nanCheck : falseLabel);
|
||||
|
||||
masm.bind(loopCheck);
|
||||
masm.addq(length, VECTOR_SIZE);
|
||||
masm.jccb(ConditionFlag.NotZero, loop);
|
||||
|
||||
masm.testl(result, result);
|
||||
masm.jccb(ConditionFlag.Zero, trueLabel);
|
||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
||||
|
||||
if (requiresNaNCheck) {
|
||||
// NaN check is slow path and hence placed outside of the main loop.
|
||||
Label unalignedCheck = new Label();
|
||||
masm.jmpb(unalignedCheck);
|
||||
masm.bind(nanCheck);
|
||||
// At most two iterations, unroll in the emitted code.
|
||||
for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) {
|
||||
emitFloatCompare(masm, array1, array2, length, offset, falseLabel, kind.getByteCount() == VECTOR_SIZE);
|
||||
}
|
||||
masm.jmpb(loopCheck);
|
||||
masm.bind(unalignedCheck);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the remaining bytes with an unaligned memory load aligned to the end of the
|
||||
@ -311,7 +385,15 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
*/
|
||||
masm.movq(temp, new AMD64Address(array1, result, Scale.Times1, -VECTOR_SIZE));
|
||||
masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE));
|
||||
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||
if (requiresNaNCheck) {
|
||||
masm.jcc(ConditionFlag.Equal, trueLabel);
|
||||
// At most two iterations, unroll in the emitted code.
|
||||
for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) {
|
||||
emitFloatCompare(masm, array1, array2, result, -VECTOR_SIZE + offset, falseLabel, kind.getByteCount() == VECTOR_SIZE);
|
||||
}
|
||||
} else {
|
||||
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||
}
|
||||
masm.jmpb(trueLabel);
|
||||
|
||||
masm.bind(compareTail);
|
||||
@ -333,8 +415,13 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
masm.jccb(ConditionFlag.Zero, compare2Bytes);
|
||||
masm.movl(temp, new AMD64Address(array1, 0));
|
||||
masm.cmpl(temp, new AMD64Address(array2, 0));
|
||||
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||
|
||||
if (kind == JavaKind.Float) {
|
||||
masm.jccb(ConditionFlag.Equal, trueLabel);
|
||||
emitFloatCompare(masm, array1, array2, Register.None, 0, falseLabel, true);
|
||||
masm.jmpb(trueLabel);
|
||||
} else {
|
||||
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||
}
|
||||
if (kind.getByteCount() <= 2) {
|
||||
// Move array pointers forward.
|
||||
masm.leaq(array1, new AMD64Address(array1, 4));
|
||||
@ -372,6 +459,71 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits code to fall through if {@code src} is NaN, otherwise jump to {@code branchOrdered}.
|
||||
*/
|
||||
private void emitNaNCheck(AMD64MacroAssembler masm, AMD64Address src, Label branchIfNonNaN) {
|
||||
assert kind.isNumericFloat();
|
||||
Register tempXMMReg = asRegister(tempXMM);
|
||||
if (kind == JavaKind.Float) {
|
||||
masm.movflt(tempXMMReg, src);
|
||||
} else {
|
||||
masm.movdbl(tempXMMReg, src);
|
||||
}
|
||||
SSEOp.UCOMIS.emit(masm, kind == JavaKind.Float ? OperandSize.PS : OperandSize.PD, tempXMMReg, tempXMMReg);
|
||||
masm.jcc(ConditionFlag.NoParity, branchIfNonNaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits code to compare if two floats are bitwise equal or both NaN.
|
||||
*/
|
||||
private void emitFloatCompare(AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, boolean skipBitwiseCompare) {
|
||||
AMD64Address address1 = new AMD64Address(base1, index, Scale.Times1, offset);
|
||||
AMD64Address address2 = new AMD64Address(base2, index, Scale.Times1, offset);
|
||||
|
||||
Label bitwiseEqual = new Label();
|
||||
|
||||
if (!skipBitwiseCompare) {
|
||||
// Bitwise compare
|
||||
Register temp = asRegister(temp4);
|
||||
|
||||
if (kind == JavaKind.Float) {
|
||||
masm.movl(temp, address1);
|
||||
masm.cmpl(temp, address2);
|
||||
} else {
|
||||
masm.movq(temp, address1);
|
||||
masm.cmpq(temp, address2);
|
||||
}
|
||||
masm.jccb(ConditionFlag.Equal, bitwiseEqual);
|
||||
}
|
||||
|
||||
emitNaNCheck(masm, address1, falseLabel);
|
||||
emitNaNCheck(masm, address2, falseLabel);
|
||||
|
||||
masm.bind(bitwiseEqual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits code to compare float equality within a range.
|
||||
*/
|
||||
private void emitFloatCompareWithinRange(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, int range) {
|
||||
assert kind.isNumericFloat();
|
||||
Label loop = new Label();
|
||||
Register i = asRegister(temp5);
|
||||
|
||||
masm.movq(i, range);
|
||||
masm.negq(i);
|
||||
// Align the main loop
|
||||
masm.align(crb.target.wordSize * 2);
|
||||
masm.bind(loop);
|
||||
emitFloatCompare(masm, base1, base2, index, offset, falseLabel, kind.getByteCount() == range);
|
||||
masm.addq(index, kind.getByteCount());
|
||||
masm.addq(i, kind.getByteCount());
|
||||
masm.jccb(ConditionFlag.NotZero, loop);
|
||||
// Floats within the range are equal, revert change to the register index
|
||||
masm.subq(index, range);
|
||||
}
|
||||
|
||||
private static final Unsafe UNSAFE = initUnsafe();
|
||||
|
||||
private static Unsafe initUnsafe() {
|
||||
|
@ -78,6 +78,8 @@ public final class SPARCArrayEqualsOp extends SPARCLIRInstruction {
|
||||
|
||||
public SPARCArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, AllocatableValue result, AllocatableValue array1, AllocatableValue array2, AllocatableValue length) {
|
||||
super(TYPE, SIZE);
|
||||
|
||||
assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported";
|
||||
this.kind = kind;
|
||||
|
||||
Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
|
||||
|
@ -59,7 +59,7 @@ public abstract class ArithmeticLIRGenerator implements ArithmeticLIRGeneratorTo
|
||||
if (isNumericInteger(a.getPlatformKind())) {
|
||||
LIRKind aKind = a.getValueKind(LIRKind.class);
|
||||
LIRKind bKind = b.getValueKind(LIRKind.class);
|
||||
assert a.getPlatformKind() == b.getPlatformKind();
|
||||
assert a.getPlatformKind() == b.getPlatformKind() : a.getPlatformKind() + " vs. " + b.getPlatformKind();
|
||||
|
||||
if (aKind.isUnknownReference()) {
|
||||
resultKind = aKind;
|
||||
|
@ -51,6 +51,8 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||
try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
|
||||
LoopsData dataCounted = new LoopsData(graph);
|
||||
dataCounted.detectedCountedLoops();
|
||||
Graph.Mark mark = graph.getMark();
|
||||
boolean prePostInserted = false;
|
||||
for (LoopEx loop : dataCounted.countedLoops()) {
|
||||
if (!LoopTransformations.isUnrollableLoop(loop)) {
|
||||
continue;
|
||||
@ -59,9 +61,10 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||
if (loop.loopBegin().isSimpleLoop()) {
|
||||
// First perform the pre/post transformation and do the partial
|
||||
// unroll when we come around again.
|
||||
LoopTransformations.insertPrePostLoops(loop, graph);
|
||||
LoopTransformations.insertPrePostLoops(loop);
|
||||
prePostInserted = true;
|
||||
} else {
|
||||
LoopTransformations.partialUnroll(loop, graph);
|
||||
LoopTransformations.partialUnroll(loop);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
@ -72,11 +75,25 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||
canonicalizer.applyIncremental(graph, context, listener.getNodes());
|
||||
listener.getNodes().clear();
|
||||
}
|
||||
|
||||
assert !prePostInserted || checkCounted(graph, mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkCounted(StructuredGraph graph, Graph.Mark mark) {
|
||||
LoopsData dataCounted;
|
||||
dataCounted = new LoopsData(graph);
|
||||
dataCounted.detectedCountedLoops();
|
||||
for (LoopEx anyLoop : dataCounted.loops()) {
|
||||
if (graph.isNew(mark, anyLoop.loopBegin())) {
|
||||
assert anyLoop.isCounted() : "pre/post transformation loses counted loop " + anyLoop.loopBegin();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkContract() {
|
||||
return false;
|
||||
|
@ -31,6 +31,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.graalvm.compiler.core.common.RetryableBailoutException;
|
||||
import org.graalvm.compiler.core.common.calc.Condition;
|
||||
import org.graalvm.compiler.debug.DebugContext;
|
||||
import org.graalvm.compiler.debug.GraalError;
|
||||
import org.graalvm.compiler.graph.Graph.Mark;
|
||||
@ -145,9 +146,9 @@ public abstract class LoopTransformations {
|
||||
// TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms)
|
||||
}
|
||||
|
||||
public static void partialUnroll(LoopEx loop, StructuredGraph graph) {
|
||||
public static void partialUnroll(LoopEx loop) {
|
||||
assert loop.loopBegin().isMainLoop();
|
||||
graph.getDebug().log("LoopPartialUnroll %s", loop);
|
||||
loop.loopBegin().graph().getDebug().log("LoopPartialUnroll %s", loop);
|
||||
|
||||
LoopFragmentInside newSegment = loop.inside().duplicate();
|
||||
newSegment.insertWithinAfter(loop);
|
||||
@ -222,72 +223,73 @@ public abstract class LoopTransformations {
|
||||
// The pre loop is constrained to one iteration for now and will likely
|
||||
// be updated to produce vector alignment if applicable.
|
||||
|
||||
public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) {
|
||||
public static LoopBeginNode insertPrePostLoops(LoopEx loop) {
|
||||
StructuredGraph graph = loop.loopBegin().graph();
|
||||
graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
|
||||
LoopFragmentWhole preLoop = loop.whole();
|
||||
CountedLoopInfo preCounted = loop.counted();
|
||||
IfNode preLimit = preCounted.getLimitTest();
|
||||
if (preLimit != null) {
|
||||
LoopBeginNode preLoopBegin = loop.loopBegin();
|
||||
InductionVariable preIv = preCounted.getCounter();
|
||||
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
|
||||
FixedNode continuationNode = preLoopExitNode.next();
|
||||
assert preLimit != null;
|
||||
LoopBeginNode preLoopBegin = loop.loopBegin();
|
||||
InductionVariable preIv = preCounted.getCounter();
|
||||
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
|
||||
FixedNode continuationNode = preLoopExitNode.next();
|
||||
|
||||
// Each duplication is inserted after the original, ergo create the post loop first
|
||||
LoopFragmentWhole mainLoop = preLoop.duplicate();
|
||||
LoopFragmentWhole postLoop = preLoop.duplicate();
|
||||
preLoopBegin.incrementSplits();
|
||||
preLoopBegin.incrementSplits();
|
||||
preLoopBegin.setPreLoop();
|
||||
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication");
|
||||
LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin);
|
||||
mainLoopBegin.setMainLoop();
|
||||
LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin);
|
||||
postLoopBegin.setPostLoop();
|
||||
// Each duplication is inserted after the original, ergo create the post loop first
|
||||
LoopFragmentWhole mainLoop = preLoop.duplicate();
|
||||
LoopFragmentWhole postLoop = preLoop.duplicate();
|
||||
preLoopBegin.incrementSplits();
|
||||
preLoopBegin.incrementSplits();
|
||||
preLoopBegin.setPreLoop();
|
||||
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication");
|
||||
LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin);
|
||||
mainLoopBegin.setMainLoop();
|
||||
LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin);
|
||||
postLoopBegin.setPostLoop();
|
||||
|
||||
EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
|
||||
AbstractMergeNode postMergeNode = postEndNode.merge();
|
||||
LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit();
|
||||
EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
|
||||
AbstractMergeNode postMergeNode = postEndNode.merge();
|
||||
LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit();
|
||||
|
||||
// Update the main loop phi initialization to carry from the pre loop
|
||||
for (PhiNode prePhiNode : preLoopBegin.phis()) {
|
||||
PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
|
||||
mainPhiNode.setValueAt(0, prePhiNode);
|
||||
}
|
||||
// Update the main loop phi initialization to carry from the pre loop
|
||||
for (PhiNode prePhiNode : preLoopBegin.phis()) {
|
||||
PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
|
||||
mainPhiNode.setValueAt(0, prePhiNode);
|
||||
}
|
||||
|
||||
EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin);
|
||||
AbstractMergeNode mainMergeNode = mainEndNode.merge();
|
||||
AbstractEndNode postEntryNode = postLoopBegin.forwardEnd();
|
||||
EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin);
|
||||
AbstractMergeNode mainMergeNode = mainEndNode.merge();
|
||||
AbstractEndNode postEntryNode = postLoopBegin.forwardEnd();
|
||||
|
||||
// In the case of no Bounds tests, we just flow right into the main loop
|
||||
AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
|
||||
LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit();
|
||||
mainLoopExitNode.setNext(mainLandingNode);
|
||||
preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
|
||||
// In the case of no Bounds tests, we just flow right into the main loop
|
||||
AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
|
||||
LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit();
|
||||
mainLoopExitNode.setNext(mainLandingNode);
|
||||
preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
|
||||
|
||||
// Add and update any phi edges as per merge usage as needed and update usages
|
||||
processPreLoopPhis(loop, mainLoop, postLoop);
|
||||
continuationNode.predecessor().clearSuccessors();
|
||||
postLoopExitNode.setNext(continuationNode);
|
||||
cleanupMerge(postMergeNode, postLoopExitNode);
|
||||
cleanupMerge(mainMergeNode, mainLandingNode);
|
||||
// Add and update any phi edges as per merge usage as needed and update usages
|
||||
processPreLoopPhis(loop, mainLoop, postLoop);
|
||||
continuationNode.predecessor().clearSuccessors();
|
||||
postLoopExitNode.setNext(continuationNode);
|
||||
cleanupMerge(postMergeNode, postLoopExitNode);
|
||||
cleanupMerge(mainMergeNode, mainLandingNode);
|
||||
|
||||
// Change the preLoop to execute one iteration for now
|
||||
updateMainLoopLimit(preLimit, preIv, mainLoop);
|
||||
updatePreLoopLimit(preLimit, preIv, preCounted);
|
||||
preLoopBegin.setLoopFrequency(1);
|
||||
mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
|
||||
postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
|
||||
// Change the preLoop to execute one iteration for now
|
||||
updateMainLoopLimit(preLimit, preIv, mainLoop);
|
||||
updatePreLoopLimit(preLimit, preIv, preCounted);
|
||||
preLoopBegin.setLoopFrequency(1);
|
||||
mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
|
||||
postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
|
||||
|
||||
// The pre and post loops don't require safepoints at all
|
||||
for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) {
|
||||
graph.removeFixed(safepoint);
|
||||
}
|
||||
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
|
||||
graph.removeFixed(safepoint);
|
||||
}
|
||||
// The pre and post loops don't require safepoints at all
|
||||
for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) {
|
||||
graph.removeFixed(safepoint);
|
||||
}
|
||||
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
|
||||
graph.removeFixed(safepoint);
|
||||
}
|
||||
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
|
||||
return mainLoopBegin;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -373,7 +375,7 @@ public abstract class LoopTransformations {
|
||||
throw GraalError.shouldNotReachHere();
|
||||
}
|
||||
|
||||
// Preloop always performs at least once iteration, so remove that from the main loop.
|
||||
// Preloop always performs at least one iteration, so remove that from the main loop.
|
||||
ValueNode newLimit = sub(graph, ub, mainStride);
|
||||
|
||||
// Re-wire the condition with the new limit
|
||||
@ -445,6 +447,14 @@ public abstract class LoopTransformations {
|
||||
return false;
|
||||
}
|
||||
LoopBeginNode loopBegin = loop.loopBegin();
|
||||
LogicNode condition = loop.counted().getLimitTest().condition();
|
||||
if (!(condition instanceof CompareNode)) {
|
||||
return false;
|
||||
}
|
||||
if (((CompareNode) condition).condition() == Condition.EQ || ((CompareNode) condition).condition() == Condition.NE) {
|
||||
condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s condition unsupported %s ", loopBegin, ((CompareNode) condition).condition());
|
||||
return false;
|
||||
}
|
||||
if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) {
|
||||
// Flow-less loops to partial unroll for now. 3 blocks corresponds to an if that either
|
||||
// exits or continues the loop. There might be fixed and floating work within the loop
|
||||
@ -452,6 +462,7 @@ public abstract class LoopTransformations {
|
||||
if (loop.loop().getBlocks().size() < 3) {
|
||||
return true;
|
||||
}
|
||||
condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s too large to unroll %s ", loopBegin, loop.loop().getBlocks().size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -22,12 +22,41 @@
|
||||
*/
|
||||
package org.graalvm.compiler.loop.test;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.graalvm.compiler.core.common.CompilationIdentifier;
|
||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.graalvm.compiler.debug.DebugContext;
|
||||
import org.graalvm.compiler.graph.iterators.NodeIterable;
|
||||
import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure;
|
||||
import org.graalvm.compiler.loop.DefaultLoopPolicies;
|
||||
import org.graalvm.compiler.loop.LoopEx;
|
||||
import org.graalvm.compiler.loop.LoopFragmentInside;
|
||||
import org.graalvm.compiler.loop.LoopsData;
|
||||
import org.graalvm.compiler.loop.phases.LoopPartialUnrollPhase;
|
||||
import org.graalvm.compiler.nodes.LoopBeginNode;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph;
|
||||
import org.graalvm.compiler.nodes.spi.LoweringTool;
|
||||
import org.graalvm.compiler.options.OptionValues;
|
||||
import org.graalvm.compiler.phases.BasePhase;
|
||||
import org.graalvm.compiler.phases.OptimisticOptimizations;
|
||||
import org.graalvm.compiler.phases.PhaseSuite;
|
||||
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
|
||||
import org.graalvm.compiler.phases.common.ConditionalEliminationPhase;
|
||||
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
|
||||
import org.graalvm.compiler.phases.common.DeoptimizationGroupingPhase;
|
||||
import org.graalvm.compiler.phases.common.FloatingReadPhase;
|
||||
import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
|
||||
import org.graalvm.compiler.phases.common.GuardLoweringPhase;
|
||||
import org.graalvm.compiler.phases.common.LoweringPhase;
|
||||
import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
|
||||
import org.graalvm.compiler.phases.tiers.MidTierContext;
|
||||
import org.graalvm.compiler.phases.tiers.Suites;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
|
||||
public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||
|
||||
@Override
|
||||
@ -41,100 +70,72 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static long testMultiplySnippet(int arg) {
|
||||
long r = 1;
|
||||
for (int i = 0; branchProbability(0.99, i < arg); i++) {
|
||||
r += r * i;
|
||||
public static long sumWithEqualityLimit(int[] text) {
|
||||
long sum = 0;
|
||||
for (int i = 0; branchProbability(0.99, i != text.length); ++i) {
|
||||
sum += volatileInt;
|
||||
}
|
||||
return r;
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Ignore("equality limits aren't working properly")
|
||||
@Test
|
||||
public void testMultiply() {
|
||||
test("testMultiplySnippet", 9);
|
||||
}
|
||||
|
||||
public static int testNestedSumSnippet(int d) {
|
||||
int c = 0;
|
||||
for (int i = 0; i < d; i++) {
|
||||
for (int j = 0; branchProbability(0.99, j < i); j++) {
|
||||
c += c + j & 0x3;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedSumBy2() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
test("testNestedSumBy2Snippet", i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int testNestedSumBy2Snippet(int d) {
|
||||
int c = 0;
|
||||
for (int i = 0; i < d; i++) {
|
||||
for (int j = 0; branchProbability(0.99, j < i); j += 2) {
|
||||
c += c + j & 0x3;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedSum() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
test("testNestedSumSnippet", i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int testSumDownSnippet(int d) {
|
||||
int c = 0;
|
||||
for (int j = d; branchProbability(0.99, j > -4); j--) {
|
||||
c += c + j & 0x3;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSumDown() {
|
||||
test("testSumDownSnippet", 1);
|
||||
for (int i = 0; i < 160; i++) {
|
||||
test("testSumDownSnippet", i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int testSumDownBy2Snippet(int d) {
|
||||
int c = 0;
|
||||
for (int j = d; branchProbability(0.99, j > -4); j -= 2) {
|
||||
c += c + j & 0x3;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSumDownBy2() {
|
||||
test("testSumDownBy2Snippet", 1);
|
||||
for (int i = 0; i < 160; i++) {
|
||||
test("testSumDownBy2Snippet", i);
|
||||
public void testSumWithEqualityLimit() {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
int[] data = new int[i];
|
||||
test("sumWithEqualityLimit", data);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoopCarried() {
|
||||
test("testLoopCarriedSnippet", 1, 2);
|
||||
test("testLoopCarriedSnippet", 0, 4);
|
||||
test("testLoopCarriedSnippet", 4, 0);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
test("testLoopCarriedSnippet", i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int testLoopCarriedSnippet(int a, int b) {
|
||||
int c = a;
|
||||
int d = b;
|
||||
for (int j = 0; branchProbability(0.99, j < a); j++) {
|
||||
d = c;
|
||||
c += 1;
|
||||
@Test
|
||||
public void testLoopCarriedDuplication() {
|
||||
testDuplicateBody("testLoopCarriedReference", "testLoopCarriedSnippet");
|
||||
}
|
||||
|
||||
static volatile int volatileInt = 3;
|
||||
|
||||
public int testLoopCarriedSnippet(int iterations) {
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; branchProbability(0.99, i < iterations); i++) {
|
||||
int t1 = volatileInt;
|
||||
int t2 = a + b;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
return c + d;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public int testLoopCarriedReference(int iterations) {
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; branchProbability(0.99, i < iterations); i += 2) {
|
||||
int t1 = volatileInt;
|
||||
int t2 = a + b;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
t1 = volatileInt;
|
||||
t2 = a + b;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public static long init = Runtime.getRuntime().totalMemory();
|
||||
@ -181,4 +182,82 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||
public void testSignExtension() {
|
||||
test("testSignExtensionSnippet", 9L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Suites createSuites(OptionValues opts) {
|
||||
Suites suites = super.createSuites(opts).copy();
|
||||
PhaseSuite<MidTierContext> mid = suites.getMidTier();
|
||||
ListIterator<BasePhase<? super MidTierContext>> iter = mid.findPhase(LoopPartialUnrollPhase.class);
|
||||
BasePhase<? super MidTierContext> partialUnoll = iter.previous();
|
||||
if (iter.previous().getClass() != FrameStateAssignmentPhase.class) {
|
||||
// Ensure LoopPartialUnrollPhase runs immediately after FrameStateAssignment, so it gets
|
||||
// priority over other optimizations in these tests.
|
||||
mid.findPhase(LoopPartialUnrollPhase.class).remove();
|
||||
ListIterator<BasePhase<? super MidTierContext>> fsa = mid.findPhase(FrameStateAssignmentPhase.class);
|
||||
fsa.add(partialUnoll);
|
||||
}
|
||||
return suites;
|
||||
}
|
||||
|
||||
public void testGraph(String reference, String test) {
|
||||
StructuredGraph referenceGraph = buildGraph(reference, false);
|
||||
StructuredGraph testGraph = buildGraph(test, true);
|
||||
assertEquals(referenceGraph, testGraph, false, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("try")
|
||||
public StructuredGraph buildGraph(String name, boolean partialUnroll) {
|
||||
CompilationIdentifier id = new CompilationIdentifier() {
|
||||
@Override
|
||||
public String toString(Verbosity verbosity) {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
ResolvedJavaMethod method = getResolvedJavaMethod(name);
|
||||
OptionValues options = new OptionValues(getInitialOptions(), DefaultLoopPolicies.UnrollMaxIterations, 2);
|
||||
StructuredGraph graph = parse(builder(method, StructuredGraph.AllowAssumptions.YES, id, options), getEagerGraphBuilderSuite());
|
||||
try (DebugContext.Scope buildScope = graph.getDebug().scope(name, method, graph)) {
|
||||
MidTierContext context = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null);
|
||||
|
||||
CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
|
||||
canonicalizer.apply(graph, context);
|
||||
new RemoveValueProxyPhase().apply(graph);
|
||||
new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
|
||||
new FloatingReadPhase().apply(graph);
|
||||
new DeadCodeEliminationPhase().apply(graph);
|
||||
new ConditionalEliminationPhase(true).apply(graph, context);
|
||||
ComputeLoopFrequenciesClosure.compute(graph);
|
||||
new GuardLoweringPhase().apply(graph, context);
|
||||
new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
|
||||
new FrameStateAssignmentPhase().apply(graph);
|
||||
new DeoptimizationGroupingPhase().apply(graph, context);
|
||||
canonicalizer.apply(graph, context);
|
||||
new ConditionalEliminationPhase(true).apply(graph, context);
|
||||
if (partialUnroll) {
|
||||
LoopsData dataCounted = new LoopsData(graph);
|
||||
dataCounted.detectedCountedLoops();
|
||||
for (LoopEx loop : dataCounted.countedLoops()) {
|
||||
LoopFragmentInside newSegment = loop.inside().duplicate();
|
||||
newSegment.insertWithinAfter(loop, false);
|
||||
}
|
||||
canonicalizer.apply(graph, getDefaultMidTierContext());
|
||||
}
|
||||
new DeadCodeEliminationPhase().apply(graph);
|
||||
canonicalizer.apply(graph, context);
|
||||
graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "before compare");
|
||||
return graph;
|
||||
} catch (Throwable e) {
|
||||
throw getDebugContext().handle(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDuplicateBody(String reference, String test) {
|
||||
|
||||
StructuredGraph referenceGraph = buildGraph(reference, false);
|
||||
StructuredGraph testGraph = buildGraph(test, true);
|
||||
CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
|
||||
canonicalizer.apply(testGraph, getDefaultMidTierContext());
|
||||
canonicalizer.apply(referenceGraph, getDefaultMidTierContext());
|
||||
assertEquals(referenceGraph, testGraph);
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +100,12 @@ public class DefaultLoopPolicies implements LoopPolicies {
|
||||
|
||||
@Override
|
||||
public boolean shouldPartiallyUnroll(LoopEx loop) {
|
||||
LoopBeginNode loopBegin = loop.loopBegin();
|
||||
if (!loop.isCounted()) {
|
||||
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s isn't counted", loopBegin);
|
||||
return false;
|
||||
}
|
||||
OptionValues options = loop.entryPoint().getOptions();
|
||||
LoopBeginNode loopBegin = loop.loopBegin();
|
||||
int maxNodes = ExactPartialUnrollMaxNodes.getValue(options);
|
||||
maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount()));
|
||||
int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count());
|
||||
@ -112,6 +113,7 @@ public class DefaultLoopPolicies implements LoopPolicies {
|
||||
if (unrollFactor == 1) {
|
||||
double loopFrequency = loopBegin.loopFrequency();
|
||||
if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) {
|
||||
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s frequency too low %s ", loopBegin, loopFrequency);
|
||||
return false;
|
||||
}
|
||||
loopBegin.setLoopOrigFrequency(loopFrequency);
|
||||
@ -136,6 +138,7 @@ public class DefaultLoopPolicies implements LoopPolicies {
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s unrolled loop is too large %s ", loopBegin, size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
package org.graalvm.compiler.loop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@ -142,18 +143,49 @@ public class LoopFragmentInside extends LoopFragment {
|
||||
end.setNext(loop.entryPoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate the body within the loop after the current copy copy of the body, updating the
|
||||
* iteration limit to account for the duplication.
|
||||
*
|
||||
* @param loop
|
||||
*/
|
||||
public void insertWithinAfter(LoopEx loop) {
|
||||
insertWithinAfter(loop, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate the body within the loop after the current copy copy of the body.
|
||||
*
|
||||
* @param loop
|
||||
* @param updateLimit true if the iteration limit should be adjusted.
|
||||
*/
|
||||
public void insertWithinAfter(LoopEx loop, boolean updateLimit) {
|
||||
assert isDuplicate() && original().loop() == loop;
|
||||
|
||||
patchNodes(dataFixWithinAfter);
|
||||
|
||||
/*
|
||||
* Collect any new back edges values before updating them since they might reference each
|
||||
* other.
|
||||
*/
|
||||
LoopBeginNode mainLoopBegin = loop.loopBegin();
|
||||
ArrayList<ValueNode> backedgeValues = new ArrayList<>();
|
||||
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
|
||||
ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1));
|
||||
if (duplicatedNode == null) {
|
||||
if (mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1))) {
|
||||
duplicatedNode = ((PhiNode) (mainPhiNode.valueAt(1))).valueAt(1);
|
||||
} else {
|
||||
assert mainPhiNode.valueAt(1).isConstant() : mainPhiNode.valueAt(1);
|
||||
}
|
||||
}
|
||||
backedgeValues.add(duplicatedNode);
|
||||
}
|
||||
int index = 0;
|
||||
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
|
||||
ValueNode duplicatedNode = backedgeValues.get(index++);
|
||||
if (duplicatedNode != null) {
|
||||
mainPhiNode.setValueAt(1, duplicatedNode);
|
||||
} else {
|
||||
assert mainPhiNode.valueAt(1).isConstant() || mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1)) : mainPhiNode.valueAt(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,27 +198,29 @@ public class LoopFragmentInside extends LoopFragment {
|
||||
}
|
||||
|
||||
int unrollFactor = mainLoopBegin.getUnrollFactor();
|
||||
|
||||
// Now use the previous unrollFactor to update the exit condition to power of two
|
||||
StructuredGraph graph = mainLoopBegin.graph();
|
||||
InductionVariable iv = loop.counted().getCounter();
|
||||
CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
|
||||
ValueNode compareBound;
|
||||
if (compareNode.getX() == iv.valueNode()) {
|
||||
compareBound = compareNode.getY();
|
||||
} else if (compareNode.getY() == iv.valueNode()) {
|
||||
compareBound = compareNode.getX();
|
||||
} else {
|
||||
throw GraalError.shouldNotReachHere();
|
||||
}
|
||||
if (iv.direction() == InductionVariable.Direction.Up) {
|
||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * iv.constantStride()));
|
||||
ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
|
||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||
} else if (iv.direction() == InductionVariable.Direction.Down) {
|
||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -iv.constantStride()));
|
||||
ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
|
||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||
if (updateLimit) {
|
||||
// Now use the previous unrollFactor to update the exit condition to power of two
|
||||
InductionVariable iv = loop.counted().getCounter();
|
||||
CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
|
||||
ValueNode compareBound;
|
||||
if (compareNode.getX() == iv.valueNode()) {
|
||||
compareBound = compareNode.getY();
|
||||
} else if (compareNode.getY() == iv.valueNode()) {
|
||||
compareBound = compareNode.getX();
|
||||
} else {
|
||||
throw GraalError.shouldNotReachHere();
|
||||
}
|
||||
long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor;
|
||||
if (iv.direction() == InductionVariable.Direction.Up) {
|
||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * originalStride));
|
||||
ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
|
||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||
} else if (iv.direction() == InductionVariable.Direction.Down) {
|
||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -originalStride));
|
||||
ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
|
||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||
}
|
||||
}
|
||||
mainLoopBegin.setUnrollFactor(unrollFactor * 2);
|
||||
mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.graalvm.compiler.microbenchmarks.graal;
|
||||
|
||||
import org.graalvm.compiler.microbenchmarks.graal.util.GraalState;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
@Warmup(iterations = 1)
|
||||
@Measurement(iterations = 1)
|
||||
@Fork(1)
|
||||
/**
|
||||
* This dummy class is used to verify that the JMH microbenchmarking environment is set up properly.
|
||||
*/
|
||||
public class TestJMHWhitebox {
|
||||
|
||||
@Benchmark
|
||||
public void testJMH(@SuppressWarnings("unused") GraalState s) {
|
||||
// This method was intentionally left blank.
|
||||
}
|
||||
|
||||
}
|
@ -25,12 +25,10 @@ package org.graalvm.compiler.nodes.test;
|
||||
import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import jdk.vm.ci.meta.JavaConstant;
|
||||
import jdk.vm.ci.meta.JavaKind;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp;
|
||||
import org.graalvm.compiler.core.common.type.IntegerStamp;
|
||||
@ -42,6 +40,12 @@ import org.graalvm.compiler.nodes.ConstantNode;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
||||
import org.graalvm.compiler.options.OptionValues;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import jdk.vm.ci.meta.JavaConstant;
|
||||
import jdk.vm.ci.meta.JavaKind;
|
||||
|
||||
/**
|
||||
* This class tests that integer stamps are created correctly for constants.
|
||||
@ -365,10 +369,187 @@ public class IntegerStampTest extends GraphTest {
|
||||
assertEquals(IntegerStamp.create(32, 0, 0x1ff, 0, 0x1ff), shl.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(32, 0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, 0x1e0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, -4096, -4096, -4096, -4096), shl.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), shl.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), shl.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
|
||||
assertEquals(IntegerStamp.create(64, 0, 0x1ff, 0, 0x1ff), shl.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(64, 0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, 0x1e0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, -4096, -4096, -4096, -4096), shl.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), shl.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), shl.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnsignedShiftRight() {
|
||||
ShiftOp<?> ushr = IntegerStamp.OPS.getUShr();
|
||||
assertEquals(IntegerStamp.create(32, 0, 0xff, 0, 0xff), ushr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(32, 0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, 0x0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, 0xffffff, 0xffffff, 0xffffff, 0xffffff), ushr.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), ushr.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), ushr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
|
||||
assertEquals(IntegerStamp.create(64, 0, 0xff, 0, 0xff), ushr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(64, 0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, 0x0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, 0xffffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffL),
|
||||
ushr.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), ushr.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), ushr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShiftRight() {
|
||||
ShiftOp<?> shr = IntegerStamp.OPS.getShr();
|
||||
assertEquals(IntegerStamp.create(32, 0, 0xff, 0, 0xff), shr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(32, 0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, 0x0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(32, -1, -1, -1, -1), shr.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), shr.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Int), shr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
|
||||
assertEquals(IntegerStamp.create(64, 0, 0xff, 0, 0xff), shr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1)));
|
||||
assertEquals(IntegerStamp.create(64, 0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, 0x0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(IntegerStamp.create(64, -1, -1, -1, -1), shr.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), shr.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5)));
|
||||
assertEquals(StampFactory.empty(JavaKind.Long), shr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMulHigh() {
|
||||
testSomeMulHigh(IntegerStamp.OPS.getMulHigh());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUMulHigh() {
|
||||
testSomeMulHigh(IntegerStamp.OPS.getUMulHigh());
|
||||
}
|
||||
|
||||
private static void testSomeMulHigh(BinaryOp<?> someMulHigh) {
|
||||
// 32 bits
|
||||
testMulHigh(someMulHigh, 0, 0, 32);
|
||||
|
||||
testMulHigh(someMulHigh, 1, 1, 32);
|
||||
testMulHigh(someMulHigh, 1, 5, 32);
|
||||
testMulHigh(someMulHigh, 256, 256, 32);
|
||||
testMulHigh(someMulHigh, 0xFFFFFFF, 0xFFFFFFA, 32);
|
||||
testMulHigh(someMulHigh, Integer.MAX_VALUE, 2, 32);
|
||||
|
||||
testMulHigh(someMulHigh, -1, -1, 32);
|
||||
testMulHigh(someMulHigh, -1, -5, 32);
|
||||
testMulHigh(someMulHigh, -256, -256, 32);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFF, -0xFFFFFFA, 32);
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, -2, 32);
|
||||
|
||||
testMulHigh(someMulHigh, -1, 1, 32);
|
||||
testMulHigh(someMulHigh, -1, 5, 32);
|
||||
testMulHigh(someMulHigh, -256, 256, 32);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFF, 0xFFFFFFA, 32);
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, 2, 32);
|
||||
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, Integer.MIN_VALUE, 32);
|
||||
testMulHigh(someMulHigh, Integer.MAX_VALUE, Integer.MAX_VALUE, 32);
|
||||
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), StampFactory.forKind(JavaKind.Int).empty()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), StampFactory.forKind(JavaKind.Int).unrestricted()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, 0, 0)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, 1, 1)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, -1, -1)));
|
||||
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), StampFactory.forKind(JavaKind.Int).unrestricted()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, 0, 0)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, 1, 1)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, -1, -1)));
|
||||
|
||||
// 64 bits
|
||||
testMulHigh(someMulHigh, 0, 0, 64);
|
||||
|
||||
testMulHigh(someMulHigh, 1, 1, 64);
|
||||
testMulHigh(someMulHigh, 1, 5, 64);
|
||||
testMulHigh(someMulHigh, 256, 256, 64);
|
||||
testMulHigh(someMulHigh, 0xFFFFFFF, 0xFFFFFFA, 64);
|
||||
testMulHigh(someMulHigh, 0xFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFAL, 64);
|
||||
testMulHigh(someMulHigh, Integer.MAX_VALUE, 2, 64);
|
||||
testMulHigh(someMulHigh, Long.MAX_VALUE, 2, 64);
|
||||
|
||||
testMulHigh(someMulHigh, -1, -1, 64);
|
||||
testMulHigh(someMulHigh, -1, -5, 64);
|
||||
testMulHigh(someMulHigh, -256, -256, 64);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFF, -0xFFFFFFA, 64);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFFFFFFFFFL, -0xFFFFFFFFFFFFFAL, 64);
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, -2, 64);
|
||||
testMulHigh(someMulHigh, Long.MIN_VALUE, -2, 64);
|
||||
|
||||
testMulHigh(someMulHigh, -1, 1, 64);
|
||||
testMulHigh(someMulHigh, -1, 5, 64);
|
||||
testMulHigh(someMulHigh, -256, 256, 64);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFF, 0xFFFFFFA, 64);
|
||||
testMulHigh(someMulHigh, -0xFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFAL, 64);
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, 2, 64);
|
||||
testMulHigh(someMulHigh, Long.MIN_VALUE, 2, 64);
|
||||
|
||||
testMulHigh(someMulHigh, Integer.MIN_VALUE, Integer.MIN_VALUE, 64);
|
||||
testMulHigh(someMulHigh, Long.MIN_VALUE, Long.MIN_VALUE, 64);
|
||||
testMulHigh(someMulHigh, Integer.MAX_VALUE, Integer.MAX_VALUE, 64);
|
||||
testMulHigh(someMulHigh, Long.MAX_VALUE, Long.MAX_VALUE, 64);
|
||||
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), StampFactory.forKind(JavaKind.Long).empty()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), StampFactory.forKind(JavaKind.Long).unrestricted()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, 0, 0)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, 1, 1)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, -1, -1)));
|
||||
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), StampFactory.forKind(JavaKind.Long).unrestricted()));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, 0, 0)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, 1, 1)));
|
||||
assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, -1, -1)));
|
||||
}
|
||||
|
||||
private static void testMulHigh(BinaryOp<?> someMulHigh, long a, long b, int bits) {
|
||||
long expectedResult = getExpectedValue(someMulHigh, a, b, bits);
|
||||
assertEquals(IntegerStamp.create(bits, expectedResult, expectedResult), someMulHigh.foldStamp(IntegerStamp.create(bits, a, a), IntegerStamp.create(bits, b, b)));
|
||||
}
|
||||
|
||||
private static long getExpectedValue(BinaryOp<?> someMulHigh, long a, long b, int bits) {
|
||||
if (someMulHigh == IntegerStamp.OPS.getMulHigh()) {
|
||||
return mulHigh(a, b, bits);
|
||||
} else {
|
||||
assertEquals(IntegerStamp.OPS.getUMulHigh(), someMulHigh);
|
||||
return umulHigh(a, b, bits);
|
||||
}
|
||||
}
|
||||
|
||||
private static long mulHigh(long a, long b, int bits) {
|
||||
BigInteger valA = BigInteger.valueOf(a);
|
||||
BigInteger valB = BigInteger.valueOf(b);
|
||||
BigInteger result = valA.multiply(valB).shiftRight(bits);
|
||||
if (bits == 32) {
|
||||
return result.intValue();
|
||||
} else {
|
||||
assertEquals(64, bits);
|
||||
return result.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static long umulHigh(long a, long b, int bits) {
|
||||
Assert.assertTrue(bits == 32 || bits == 64);
|
||||
BigInteger valA = BigInteger.valueOf(a);
|
||||
if (valA.compareTo(BigInteger.valueOf(0)) < 0) {
|
||||
valA = valA.add(BigInteger.ONE.shiftLeft(bits));
|
||||
}
|
||||
BigInteger valB = BigInteger.valueOf(b);
|
||||
if (valB.compareTo(BigInteger.valueOf(0)) < 0) {
|
||||
valB = valB.add(BigInteger.ONE.shiftLeft(bits));
|
||||
}
|
||||
|
||||
BigInteger result = valA.multiply(valB).shiftRight(bits);
|
||||
if (bits == 32) {
|
||||
return result.intValue();
|
||||
} else {
|
||||
return result.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -466,6 +466,28 @@ public class GraphDecoder {
|
||||
AbstractMergeNode merge = (AbstractMergeNode) node;
|
||||
EndNode singleEnd = merge.forwardEndAt(0);
|
||||
|
||||
/*
|
||||
* In some corner cases, the MergeNode already has PhiNodes. Since there is a single
|
||||
* EndNode, each PhiNode can only have one input, and we can replace the PhiNode with
|
||||
* this single input.
|
||||
*/
|
||||
for (PhiNode phi : merge.phis()) {
|
||||
assert phi.inputs().count() == 1 : "input count must match end count";
|
||||
Node singlePhiInput = phi.inputs().first();
|
||||
|
||||
/*
|
||||
* We do not have the orderID of the PhiNode anymore, so we need to search through
|
||||
* the complete list of nodes to find a match.
|
||||
*/
|
||||
for (int i = 0; i < loopScope.createdNodes.length; i++) {
|
||||
if (loopScope.createdNodes[i] == phi) {
|
||||
loopScope.createdNodes[i] = singlePhiInput;
|
||||
}
|
||||
}
|
||||
|
||||
phi.replaceAndDelete(singlePhiInput);
|
||||
}
|
||||
|
||||
/* Nodes that would use this merge as the guard need to use the previous block. */
|
||||
registerNode(loopScope, nodeOrderId, AbstractBeginNode.prevBegin(singleEnd), true, false);
|
||||
|
||||
|
@ -25,6 +25,7 @@ package org.graalvm.compiler.nodes.calc;
|
||||
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
|
||||
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
||||
import org.graalvm.compiler.core.common.type.IntegerStamp;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul;
|
||||
import org.graalvm.compiler.core.common.type.Stamp;
|
||||
@ -108,6 +109,27 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit
|
||||
return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX);
|
||||
} else if (CodeUtil.isPowerOf2(i + 1)) {
|
||||
return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX);
|
||||
} else {
|
||||
int bitCount = Long.bitCount(i);
|
||||
long highestBitValue = Long.highestOneBit(i);
|
||||
if (bitCount == 2) {
|
||||
// e.g., 0b1000_0010
|
||||
long lowerBitValue = i - highestBitValue;
|
||||
assert highestBitValue > 0 && lowerBitValue > 0;
|
||||
ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue)));
|
||||
ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue)));
|
||||
return AddNode.create(left, right);
|
||||
} else {
|
||||
// e.g., 0b1111_1101
|
||||
int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1;
|
||||
long subValue = (1 << shiftToRoundUpToPowerOf2) - i;
|
||||
if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) {
|
||||
assert CodeUtil.log2(subValue) >= 1;
|
||||
ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2));
|
||||
ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue)));
|
||||
return SubNode.create(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (i < 0) {
|
||||
if (CodeUtil.isPowerOf2(-i)) {
|
||||
|
@ -562,7 +562,7 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||
if (pred.getSuccessorCount() > 1) {
|
||||
assert pred.getEndNode() instanceof ControlSplitNode;
|
||||
ControlSplitNode controlSplit = (ControlSplitNode) pred.getEndNode();
|
||||
probability *= controlSplit.probability(block.getBeginNode());
|
||||
probability = multiplyProbabilities(probability, controlSplit.probability(block.getBeginNode()));
|
||||
}
|
||||
} else {
|
||||
probability = predecessors[0].probability;
|
||||
@ -572,7 +572,7 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||
|
||||
if (block.getBeginNode() instanceof LoopBeginNode) {
|
||||
LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode();
|
||||
probability *= loopBegin.loopFrequency();
|
||||
probability = multiplyProbabilities(probability, loopBegin.loopFrequency());
|
||||
}
|
||||
}
|
||||
if (probability < MIN_PROBABILITY) {
|
||||
@ -755,4 +755,20 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||
public void setNodeToBlock(NodeMap<Block> nodeMap) {
|
||||
this.nodeToBlock = nodeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies a and b and clamps the between {@link ControlFlowGraph#MIN_PROBABILITY} and
|
||||
* {@link ControlFlowGraph#MAX_PROBABILITY}.
|
||||
*/
|
||||
public static double multiplyProbabilities(double a, double b) {
|
||||
assert !Double.isNaN(a) && !Double.isNaN(b) && Double.isFinite(a) && Double.isFinite(b) : a + " " + b;
|
||||
double r = a * b;
|
||||
if (r > MAX_PROBABILITY) {
|
||||
return MAX_PROBABILITY;
|
||||
}
|
||||
if (r < MIN_PROBABILITY) {
|
||||
return MIN_PROBABILITY;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,12 @@
|
||||
*/
|
||||
package org.graalvm.compiler.options.processor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -50,7 +53,9 @@ import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import org.graalvm.compiler.options.Option;
|
||||
import org.graalvm.compiler.options.OptionDescriptor;
|
||||
@ -117,22 +122,13 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
String help = annotation.help();
|
||||
if (help.length() != 0) {
|
||||
char firstChar = help.charAt(0);
|
||||
if (!Character.isUpperCase(firstChar)) {
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String optionName = annotation.name();
|
||||
if (optionName.equals("")) {
|
||||
optionName = fieldName;
|
||||
}
|
||||
|
||||
if (!Character.isUpperCase(optionName.charAt(0))) {
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Option name must start with capital letter", element);
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Option name must start with an upper case letter", element);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -154,6 +150,7 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
String separator = "";
|
||||
Set<Element> originatingElementsList = info.originatingElements;
|
||||
originatingElementsList.add(field);
|
||||
PackageElement enclosingPackage = null;
|
||||
while (enclosing != null) {
|
||||
if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
|
||||
if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
|
||||
@ -164,13 +161,64 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
originatingElementsList.add(enclosing);
|
||||
declaringClass = enclosing.getSimpleName() + separator + declaringClass;
|
||||
separator = ".";
|
||||
} else {
|
||||
assert enclosing.getKind() == ElementKind.PACKAGE;
|
||||
} else if (enclosing.getKind() == ElementKind.PACKAGE) {
|
||||
enclosingPackage = (PackageElement) enclosing;
|
||||
}
|
||||
enclosing = enclosing.getEnclosingElement();
|
||||
}
|
||||
if (enclosingPackage == null) {
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Option field cannot be declared in the unnamed package", element);
|
||||
return;
|
||||
}
|
||||
String[] helpValue = annotation.help();
|
||||
String help = "";
|
||||
String[] extraHelp = {};
|
||||
|
||||
info.options.add(new OptionInfo(optionName, help, optionType, declaringClass, field));
|
||||
if (helpValue.length == 1) {
|
||||
help = helpValue[0];
|
||||
if (help.startsWith("file:")) {
|
||||
String path = help.substring("file:".length());
|
||||
Filer filer = processingEnv.getFiler();
|
||||
try {
|
||||
FileObject file;
|
||||
try {
|
||||
file = filer.getResource(StandardLocation.SOURCE_PATH, enclosingPackage.getQualifiedName(), path);
|
||||
} catch (IllegalArgumentException | IOException e) {
|
||||
// Handle the case when a compiler doesn't support the SOURCE_PATH location
|
||||
file = filer.getResource(StandardLocation.CLASS_OUTPUT, enclosingPackage.getQualifiedName(), path);
|
||||
}
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(file.openInputStream()))) {
|
||||
help = br.readLine();
|
||||
if (help == null) {
|
||||
help = "";
|
||||
}
|
||||
String line = br.readLine();
|
||||
List<String> lines = new ArrayList<>();
|
||||
while (line != null) {
|
||||
lines.add(line);
|
||||
line = br.readLine();
|
||||
}
|
||||
extraHelp = lines.toArray(new String[lines.size()]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String msg = String.format("Error reading %s containing the help text for option field: %s", path, e);
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (helpValue.length > 1) {
|
||||
help = helpValue[0];
|
||||
extraHelp = Arrays.copyOfRange(helpValue, 1, helpValue.length);
|
||||
}
|
||||
if (help.length() != 0) {
|
||||
char firstChar = help.charAt(0);
|
||||
if (!Character.isUpperCase(firstChar)) {
|
||||
processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with an upper case letter", element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info.options.add(new OptionInfo(optionName, help, extraHelp, optionType, declaringClass, field));
|
||||
}
|
||||
|
||||
private void createFiles(OptionsInfo info) {
|
||||
@ -200,11 +248,11 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
|
||||
String desc = OptionDescriptor.class.getSimpleName();
|
||||
|
||||
int i = 0;
|
||||
Collections.sort(info.options);
|
||||
|
||||
out.println(" @Override");
|
||||
out.println(" public OptionDescriptor get(String value) {");
|
||||
out.println(" switch (value) {");
|
||||
out.println(" // CheckStyle: stop line length check");
|
||||
for (OptionInfo option : info.options) {
|
||||
String name = option.name;
|
||||
@ -214,41 +262,52 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
} else {
|
||||
optionField = option.declaringClass + "." + option.field.getSimpleName();
|
||||
}
|
||||
out.println(" case \"" + name + "\": {");
|
||||
String type = option.type;
|
||||
String help = option.help;
|
||||
String[] extraHelp = option.extraHelp;
|
||||
String declaringClass = option.declaringClass;
|
||||
Name fieldName = option.field.getSimpleName();
|
||||
out.println(" if (value.equals(\"" + name + "\")) {");
|
||||
out.printf(" return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionField);
|
||||
out.printf(" return " + desc + ".create(\n");
|
||||
out.printf(" /*name*/ \"%s\",\n", name);
|
||||
out.printf(" /*type*/ %s.class,\n", type);
|
||||
out.printf(" /*help*/ \"%s\",\n", help);
|
||||
if (extraHelp.length != 0) {
|
||||
out.printf(" /*extraHelp*/ new String[] {\n");
|
||||
for (String line : extraHelp) {
|
||||
out.printf(" \"%s\",\n", line.replace("\\", "\\\\").replace("\"", "\\\""));
|
||||
}
|
||||
out.printf(" },\n");
|
||||
}
|
||||
out.printf(" /*declaringClass*/ %s.class,\n", declaringClass);
|
||||
out.printf(" /*fieldName*/ \"%s\",\n", fieldName);
|
||||
out.printf(" /*option*/ %s);\n", optionField);
|
||||
out.println(" }");
|
||||
}
|
||||
out.println(" // CheckStyle: resume line length check");
|
||||
out.println(" }");
|
||||
out.println(" return null;");
|
||||
out.println(" }");
|
||||
out.println();
|
||||
out.println(" @Override");
|
||||
out.println(" public Iterator<" + desc + "> iterator() {");
|
||||
out.println(" // CheckStyle: stop line length check");
|
||||
out.println(" List<" + desc + "> options = Arrays.asList(");
|
||||
for (OptionInfo option : info.options) {
|
||||
String optionField;
|
||||
if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
|
||||
throw new InternalError();
|
||||
} else {
|
||||
optionField = option.declaringClass + "." + option.field.getSimpleName();
|
||||
}
|
||||
String name = option.name;
|
||||
String type = option.type;
|
||||
String help = option.help;
|
||||
String declaringClass = option.declaringClass;
|
||||
Name fieldName = option.field.getSimpleName();
|
||||
String comma = i == info.options.size() - 1 ? "" : ",";
|
||||
out.printf(" %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionField, comma);
|
||||
i++;
|
||||
out.println(" return new Iterator<OptionDescriptor>() {");
|
||||
out.println(" int i = 0;");
|
||||
out.println(" @Override");
|
||||
out.println(" public boolean hasNext() {");
|
||||
out.println(" return i < " + info.options.size() + ";");
|
||||
out.println(" }");
|
||||
out.println(" @Override");
|
||||
out.println(" public OptionDescriptor next() {");
|
||||
out.println(" switch (i++) {");
|
||||
for (int i = 0; i < info.options.size(); i++) {
|
||||
OptionInfo option = info.options.get(i);
|
||||
out.println(" case " + i + ": return get(\"" + option.name + "\");");
|
||||
}
|
||||
out.println(" );");
|
||||
out.println(" // CheckStyle: resume line length check");
|
||||
out.println(" return options.iterator();");
|
||||
out.println(" }");
|
||||
out.println(" throw new NoSuchElementException();");
|
||||
out.println(" }");
|
||||
out.println(" };");
|
||||
out.println(" }");
|
||||
out.println("}");
|
||||
}
|
||||
@ -274,13 +333,15 @@ public class OptionProcessor extends AbstractProcessor {
|
||||
|
||||
final String name;
|
||||
final String help;
|
||||
final String[] extraHelp;
|
||||
final String type;
|
||||
final String declaringClass;
|
||||
final VariableElement field;
|
||||
|
||||
OptionInfo(String name, String help, String type, String declaringClass, VariableElement field) {
|
||||
OptionInfo(String name, String help, String[] extraHelp, String type, String declaringClass, VariableElement field) {
|
||||
this.name = name;
|
||||
this.help = help;
|
||||
this.extraHelp = extraHelp;
|
||||
this.type = type;
|
||||
this.declaringClass = declaringClass;
|
||||
this.field = field;
|
||||
|
@ -28,27 +28,10 @@ import org.graalvm.util.EconomicMap;
|
||||
|
||||
public class EnumOptionKey<T extends Enum<T>> extends OptionKey<T> {
|
||||
final Class<T> enumClass;
|
||||
final ValueHelp<T> valueHelp;
|
||||
|
||||
/**
|
||||
* Provides help text for enum values.
|
||||
*/
|
||||
public interface ValueHelp<T extends Enum<T>> {
|
||||
/**
|
||||
* Gets help text for the enum {@code value} that includes the name of the value. If
|
||||
* {@code null} is returned, {@code value.toString()} is used.
|
||||
*/
|
||||
String getHelp(Object value);
|
||||
}
|
||||
|
||||
public EnumOptionKey(T value) {
|
||||
this(value, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EnumOptionKey(T value, ValueHelp<T> help) {
|
||||
public EnumOptionKey(T value) {
|
||||
super(value);
|
||||
this.valueHelp = help;
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Value must not be null");
|
||||
}
|
||||
@ -62,10 +45,6 @@ public class EnumOptionKey<T extends Enum<T>> extends OptionKey<T> {
|
||||
return EnumSet.allOf(enumClass);
|
||||
}
|
||||
|
||||
public ValueHelp<T> getValueHelp() {
|
||||
return valueHelp;
|
||||
}
|
||||
|
||||
Object valueOf(String name) {
|
||||
try {
|
||||
return Enum.valueOf(enumClass, name);
|
||||
|
@ -38,10 +38,20 @@ import java.lang.annotation.Target;
|
||||
public @interface Option {
|
||||
|
||||
/**
|
||||
* Gets a help message for the option. New lines can be embedded in the message with
|
||||
* {@code "%n"}.
|
||||
* Gets a help message for the option.
|
||||
* <p>
|
||||
* The first element of the array is the short help message. This part of the help message is
|
||||
* subject to line wrapping when printed.
|
||||
* <p>
|
||||
* The remaining elements contain a more detailed expansion of the help message and will be
|
||||
* printed as is in a left-aligned block (i.e. leading spaces will be preserved).
|
||||
* <p>
|
||||
* If there is only one element and it starts with {@code "file:"<path>}, then the help message
|
||||
* is located in a file located by resolving {@code <path>} against the location of the package
|
||||
* in which the option is declared. The first line in the file is the short help message as
|
||||
* described above. The remaining lines are the help message expansion.
|
||||
*/
|
||||
String help();
|
||||
String[] help();
|
||||
|
||||
/**
|
||||
* The name of the option. By default, the name of the annotated field should be used.
|
||||
|
@ -22,6 +22,10 @@
|
||||
*/
|
||||
package org.graalvm.compiler.options;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describes the attributes of a static field {@linkplain Option option} and provides access to its
|
||||
* {@linkplain OptionKey value}.
|
||||
@ -31,25 +35,34 @@ public final class OptionDescriptor {
|
||||
protected final String name;
|
||||
protected final Class<?> type;
|
||||
protected final String help;
|
||||
protected final List<String> extraHelp;
|
||||
protected final OptionKey<?> optionKey;
|
||||
protected final Class<?> declaringClass;
|
||||
protected final String fieldName;
|
||||
|
||||
private static final String[] NO_EXTRA_HELP = {};
|
||||
|
||||
public static OptionDescriptor create(String name, Class<?> type, String help, Class<?> declaringClass, String fieldName, OptionKey<?> option) {
|
||||
return create(name, type, help, NO_EXTRA_HELP, declaringClass, fieldName, option);
|
||||
}
|
||||
|
||||
public static OptionDescriptor create(String name, Class<?> type, String help, String[] extraHelp, Class<?> declaringClass, String fieldName, OptionKey<?> option) {
|
||||
assert option != null : declaringClass + "." + fieldName;
|
||||
OptionDescriptor result = option.getDescriptor();
|
||||
if (result == null) {
|
||||
result = new OptionDescriptor(name, type, help, declaringClass, fieldName, option);
|
||||
List<String> extraHelpList = extraHelp == null || extraHelp.length == 0 ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(extraHelp));
|
||||
result = new OptionDescriptor(name, type, help, extraHelpList, declaringClass, fieldName, option);
|
||||
option.setDescriptor(result);
|
||||
}
|
||||
assert result.name.equals(name) && result.type == type && result.declaringClass == declaringClass && result.fieldName.equals(fieldName) && result.optionKey == option;
|
||||
return result;
|
||||
}
|
||||
|
||||
private OptionDescriptor(String name, Class<?> type, String help, Class<?> declaringClass, String fieldName, OptionKey<?> optionKey) {
|
||||
private OptionDescriptor(String name, Class<?> type, String help, List<String> extraHelp, Class<?> declaringClass, String fieldName, OptionKey<?> optionKey) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.help = help;
|
||||
this.extraHelp = extraHelp;
|
||||
this.optionKey = optionKey;
|
||||
this.declaringClass = declaringClass;
|
||||
this.fieldName = fieldName;
|
||||
@ -65,12 +78,23 @@ public final class OptionDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a descriptive help message for the option.
|
||||
* Gets a descriptive help message for the option. This message should be self contained without
|
||||
* relying on {@link #getExtraHelp() extra help lines}.
|
||||
*
|
||||
* @see Option#help()
|
||||
*/
|
||||
public String getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extra lines of help text. These lines should not be subject to any line wrapping or
|
||||
* formatting apart from indentation.
|
||||
*/
|
||||
public List<String> getExtraHelp() {
|
||||
return extraHelp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the option. It's up to the client of this object how to use the name to get
|
||||
* a user specified value for the option from the environment.
|
||||
|
@ -24,15 +24,12 @@ package org.graalvm.compiler.options;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.graalvm.compiler.options.EnumOptionKey.ValueHelp;
|
||||
import org.graalvm.util.EconomicMap;
|
||||
import org.graalvm.util.Equivalence;
|
||||
import org.graalvm.util.UnmodifiableEconomicMap;
|
||||
@ -165,10 +162,9 @@ public class OptionValues {
|
||||
* @return {@code text} broken into lines
|
||||
*/
|
||||
private static List<String> wrap(String text, int width) {
|
||||
List<String> lines = Collections.singletonList(text);
|
||||
List<String> lines = new ArrayList<>();
|
||||
if (text.length() > width) {
|
||||
String[] chunks = text.split("\\s+");
|
||||
lines = new ArrayList<>();
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (String chunk : chunks) {
|
||||
if (line.length() + chunk.length() > width) {
|
||||
@ -178,22 +174,13 @@ public class OptionValues {
|
||||
if (line.length() != 0) {
|
||||
line.append(' ');
|
||||
}
|
||||
String[] embeddedLines = chunk.split("%n", -2);
|
||||
if (embeddedLines.length == 1) {
|
||||
line.append(chunk);
|
||||
} else {
|
||||
for (int i = 0; i < embeddedLines.length; i++) {
|
||||
line.append(embeddedLines[i]);
|
||||
if (i < embeddedLines.length - 1) {
|
||||
lines.add(line.toString());
|
||||
line.setLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
line.append(chunk);
|
||||
}
|
||||
if (line.length() != 0) {
|
||||
lines.add(line.toString());
|
||||
}
|
||||
} else {
|
||||
lines.add(text);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
@ -222,24 +209,7 @@ public class OptionValues {
|
||||
if (value instanceof String) {
|
||||
value = '"' + String.valueOf(value) + '"';
|
||||
}
|
||||
String help = desc.getHelp();
|
||||
if (desc.getOptionKey() instanceof EnumOptionKey) {
|
||||
EnumOptionKey<?> eoption = (EnumOptionKey<?>) desc.getOptionKey();
|
||||
EnumSet<?> evalues = eoption.getAllValues();
|
||||
String evaluesString = evalues.toString();
|
||||
ValueHelp<?> valueHelp = eoption.getValueHelp();
|
||||
if (help.length() > 0 && !help.endsWith(".")) {
|
||||
help += ".";
|
||||
}
|
||||
if (valueHelp == null) {
|
||||
help += " Valid values are: " + evaluesString.substring(1, evaluesString.length() - 1);
|
||||
} else {
|
||||
for (Object o : evalues) {
|
||||
String vhelp = valueHelp.getHelp(o);
|
||||
help += "%n" + (vhelp == null ? o : vhelp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String name = namePrefix + e.getKey();
|
||||
String assign = containsKey(desc.optionKey) ? ":=" : "=";
|
||||
String typeName = desc.getOptionKey() instanceof EnumOptionKey ? "String" : desc.getType().getSimpleName();
|
||||
@ -252,11 +222,16 @@ public class OptionValues {
|
||||
out.printf("%s[%s]%n", linePrefix, typeName);
|
||||
}
|
||||
|
||||
List<String> helpLines;
|
||||
String help = desc.getHelp();
|
||||
if (help.length() != 0) {
|
||||
List<String> helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT);
|
||||
for (int i = 0; i < helpLines.size(); i++) {
|
||||
out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", helpLines.get(i));
|
||||
}
|
||||
helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT);
|
||||
helpLines.addAll(desc.getExtraHelp());
|
||||
} else {
|
||||
helpLines = desc.getExtraHelp();
|
||||
}
|
||||
for (String line : helpLines) {
|
||||
out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.graalvm.compiler.api.replacements.MethodSubstitution;
|
||||
@ -351,7 +352,7 @@ public class InliningUtil extends ValueMergeUtil {
|
||||
}
|
||||
}
|
||||
|
||||
updateSourcePositions(invoke, inlineGraph, duplicates);
|
||||
updateSourcePositions(invoke, inlineGraph, duplicates, !Objects.equals(inlineGraph.method(), inlineeMethod));
|
||||
if (stateAfter != null) {
|
||||
processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
|
||||
int callerLockDepth = stateAfter.nestedLockDepth();
|
||||
@ -569,14 +570,14 @@ public class InliningUtil extends ValueMergeUtil {
|
||||
}
|
||||
|
||||
@SuppressWarnings("try")
|
||||
private static void updateSourcePositions(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap<Node, Node> duplicates) {
|
||||
private static void updateSourcePositions(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap<Node, Node> duplicates, boolean isSubstitution) {
|
||||
if (inlineGraph.mayHaveNodeSourcePosition() && invoke.stateAfter() != null) {
|
||||
if (invoke.asNode().getNodeSourcePosition() == null) {
|
||||
// Temporarily ignore the assert below.
|
||||
return;
|
||||
}
|
||||
|
||||
JavaConstant constantReceiver = invoke.getInvokeKind().hasReceiver() ? invoke.getReceiver().asJavaConstant() : null;
|
||||
JavaConstant constantReceiver = invoke.getInvokeKind().hasReceiver() && !isSubstitution ? invoke.getReceiver().asJavaConstant() : null;
|
||||
NodeSourcePosition invokePos = invoke.asNode().getNodeSourcePosition();
|
||||
assert invokePos != null : "missing source information";
|
||||
|
||||
|
@ -75,10 +75,11 @@ public abstract class AbstractInliningPolicy implements InliningPolicy {
|
||||
}
|
||||
|
||||
private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
|
||||
for (int i = 0; i < info.numberOfMethods(); i++) {
|
||||
if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i), info.invoke().bci())) {
|
||||
return false;
|
||||
}
|
||||
if (!onlyIntrinsics(replacements, info)) {
|
||||
return false;
|
||||
}
|
||||
if (!info.shouldInline()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ import org.graalvm.compiler.nodes.StartNode;
|
||||
import org.graalvm.util.EconomicMap;
|
||||
import org.graalvm.util.Equivalence;
|
||||
|
||||
import static org.graalvm.compiler.nodes.cfg.ControlFlowGraph.multiplyProbabilities;
|
||||
|
||||
/**
|
||||
* Compute probabilities for fixed nodes on the fly and cache them at {@link AbstractBeginNode}s.
|
||||
*/
|
||||
@ -106,7 +108,7 @@ public class FixedNodeProbabilityCache implements ToDoubleFunction<FixedNode> {
|
||||
}
|
||||
} else {
|
||||
ControlSplitNode split = (ControlSplitNode) current.predecessor();
|
||||
probability = split.probability((AbstractBeginNode) current) * applyAsDouble(split);
|
||||
probability = multiplyProbabilities(split.probability((AbstractBeginNode) current), applyAsDouble(split));
|
||||
}
|
||||
assert !Double.isNaN(probability) && !Double.isInfinite(probability) : current + " " + probability;
|
||||
cache.put(current, probability);
|
||||
@ -125,7 +127,7 @@ public class FixedNodeProbabilityCache implements ToDoubleFunction<FixedNode> {
|
||||
result += applyAsDouble(endNode);
|
||||
}
|
||||
if (current instanceof LoopBeginNode) {
|
||||
result *= ((LoopBeginNode) current).loopFrequency();
|
||||
result = multiplyProbabilities(result, ((LoopBeginNode) current).loopFrequency());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ public class BinaryGraphPrinter implements
|
||||
return ((Class<?>) obj).getName();
|
||||
}
|
||||
if (obj instanceof ResolvedJavaType) {
|
||||
return ((ResolvedJavaType) obj).getName();
|
||||
return ((ResolvedJavaType) obj).toJavaName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -403,7 +403,7 @@ public class BinaryGraphPrinter implements
|
||||
|
||||
@Override
|
||||
public String fieldTypeName(ResolvedJavaField field) {
|
||||
return field.getType().getName();
|
||||
return field.getType().toJavaName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,6 +31,8 @@ import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.Una
|
||||
import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN;
|
||||
import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.graalvm.compiler.bytecode.BytecodeProvider;
|
||||
import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode;
|
||||
import org.graalvm.compiler.nodes.ValueNode;
|
||||
@ -44,6 +46,7 @@ import org.graalvm.compiler.nodes.java.AtomicReadAndAddNode;
|
||||
import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode;
|
||||
import org.graalvm.compiler.nodes.memory.address.AddressNode;
|
||||
import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
|
||||
import org.graalvm.compiler.replacements.ArraysSubstitutions;
|
||||
import org.graalvm.compiler.replacements.IntegerSubstitutions;
|
||||
import org.graalvm.compiler.replacements.LongSubstitutions;
|
||||
import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeGetPlugin;
|
||||
@ -73,6 +76,7 @@ public class AMD64GraphBuilderPlugins {
|
||||
registerUnsafePlugins(invocationPlugins, replacementsBytecodeProvider);
|
||||
registerStringPlugins(invocationPlugins, arch, replacementsBytecodeProvider);
|
||||
registerMathPlugins(invocationPlugins, arch, arithmeticStubs, replacementsBytecodeProvider);
|
||||
registerArraysEqualsPlugins(invocationPlugins, replacementsBytecodeProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -229,4 +233,10 @@ public class AMD64GraphBuilderPlugins {
|
||||
r.registerOptional4("put" + kind.name() + "Unaligned", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, false));
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerArraysEqualsPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
|
||||
Registration r = new Registration(plugins, Arrays.class, bytecodeProvider);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
|
||||
}
|
||||
}
|
||||
|
@ -231,68 +231,6 @@ public class ArraysSubstitutionsTest extends MethodSubstitutionTest {
|
||||
return Arrays.equals(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsFloat() {
|
||||
Object[] args1 = new Object[N];
|
||||
Object[] args2 = new Object[N];
|
||||
int n = 0;
|
||||
|
||||
// equal arrays
|
||||
for (int i = 0; i < N / 2; i++, n++) {
|
||||
args1[n] = new float[i];
|
||||
args2[n] = new float[i];
|
||||
}
|
||||
|
||||
// non-equal arrays
|
||||
for (int i = 0; i < N / 2; i++, n++) {
|
||||
float[] a2 = new float[i];
|
||||
if (i > 0) {
|
||||
a2[i - 1] = 1;
|
||||
}
|
||||
args1[n] = new float[i];
|
||||
args2[n] = a2;
|
||||
}
|
||||
|
||||
Class<?>[] parameterTypes = new Class<?>[]{float[].class, float[].class};
|
||||
testSubstitution("arraysEqualsFloat", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static boolean arraysEqualsFloat(float[] a, float[] b) {
|
||||
return Arrays.equals(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsDouble() {
|
||||
Object[] args1 = new Object[N];
|
||||
Object[] args2 = new Object[N];
|
||||
int n = 0;
|
||||
|
||||
// equal arrays
|
||||
for (int i = 0; i < N / 2; i++, n++) {
|
||||
args1[n] = new double[i];
|
||||
args2[n] = new double[i];
|
||||
}
|
||||
|
||||
// non-equal arrays
|
||||
for (int i = 0; i < N / 2; i++, n++) {
|
||||
double[] a2 = new double[i];
|
||||
if (i > 0) {
|
||||
a2[i - 1] = 1;
|
||||
}
|
||||
args1[n] = new double[i];
|
||||
args2[n] = a2;
|
||||
}
|
||||
|
||||
Class<?>[] parameterTypes = new Class<?>[]{double[].class, double[].class};
|
||||
testSubstitution("arraysEqualsDouble", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static boolean arraysEqualsDouble(double[] a, double[] b) {
|
||||
return Arrays.equals(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsNodeGVN() {
|
||||
test("testEqualsNodeGVNSnippet", true);
|
||||
|
@ -24,16 +24,25 @@ package org.graalvm.compiler.replacements.test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.graalvm.compiler.api.directives.GraalDirectives;
|
||||
import org.graalvm.compiler.core.phases.HighTier;
|
||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.graalvm.compiler.nodes.ValueNode;
|
||||
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
|
||||
import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
|
||||
import org.graalvm.compiler.options.OptionValues;
|
||||
import org.graalvm.compiler.phases.common.AbstractInliningPhase;
|
||||
import org.graalvm.compiler.test.ExportingClassLoader;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import jdk.vm.ci.code.InstalledCode;
|
||||
import jdk.vm.ci.meta.DeoptimizationReason;
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
|
||||
/**
|
||||
@ -90,6 +99,61 @@ public class DeoptimizeOnExceptionTest extends GraalCompilerTest {
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3() {
|
||||
Assume.assumeTrue("Only works on jdk8 right now", Java8OrEarlier);
|
||||
ResolvedJavaMethod method = getResolvedJavaMethod("test3Snippet");
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Result actual;
|
||||
boolean expectedCompiledCode = (method.getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0);
|
||||
InstalledCode code = getCode(method, null, false, true, new OptionValues(getInitialOptions(), HighTier.Options.Inline, false));
|
||||
assertTrue(code.isValid());
|
||||
|
||||
try {
|
||||
actual = new Result(code.executeVarargs(false), null);
|
||||
} catch (Exception e) {
|
||||
actual = new Result(null, e);
|
||||
}
|
||||
|
||||
assertTrue(i > 0 == expectedCompiledCode, "expect compiled code to stay around after the first iteration");
|
||||
assertEquals(new Result(expectedCompiledCode, null), actual);
|
||||
assertTrue(expectedCompiledCode == code.isValid());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
|
||||
if (method.getName().equals("throwException")) {
|
||||
if (b.getMethod().getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0) {
|
||||
return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
|
||||
} else {
|
||||
return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return super.bytecodeParserShouldInlineInvoke(b, method, args);
|
||||
}
|
||||
|
||||
private static void throwException() throws Exception {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
static int footprint;
|
||||
|
||||
public static boolean test3Snippet(boolean rethrowException) throws Exception {
|
||||
try {
|
||||
footprint = 1;
|
||||
throwException();
|
||||
} catch (Exception e) {
|
||||
footprint = 2;
|
||||
if (rethrowException) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return GraalDirectives.inCompiledCode();
|
||||
}
|
||||
|
||||
public static class MyClassLoader extends ExportingClassLoader {
|
||||
@Override
|
||||
protected Class<?> findClass(String className) throws ClassNotFoundException {
|
||||
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.graalvm.compiler.replacements.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.graalvm.compiler.code.CompilationResult;
|
||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.graalvm.compiler.nodes.ConstantNode;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
||||
import org.junit.Test;
|
||||
|
||||
import jdk.vm.ci.code.InstalledCode;
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
|
||||
public class FloatArraysEqualsTest extends GraalCompilerTest {
|
||||
|
||||
public static boolean testFloatArraySnippet(float[] a, float[] b) {
|
||||
return Arrays.equals(a, b);
|
||||
}
|
||||
|
||||
private void testEqualInFloatArray(int arraySize, int index, float f1, float f2) {
|
||||
if (arraySize > 0 && index >= 0 && index < arraySize) {
|
||||
float[] a1 = new float[arraySize];
|
||||
float[] a2 = new float[arraySize];
|
||||
a1[index] = f1;
|
||||
a2[index] = f2;
|
||||
test("testFloatArraySnippet", a1, a2);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int FLOAT_TAIL = 1;
|
||||
public static final int FLOAT_8BYTE_ALIGNED = 2;
|
||||
public static final int FLOAT_8BYTE_UNALIGNED = 3;
|
||||
public static final int FLOAT_VECTOR_ALIGNED = 8;
|
||||
public static final int FLOAT_VECTOR_UNALIGNED = 9;
|
||||
|
||||
@Test
|
||||
public void testFloatArray() {
|
||||
for (int size : new int[]{FLOAT_TAIL, FLOAT_8BYTE_ALIGNED, FLOAT_8BYTE_UNALIGNED, FLOAT_VECTOR_ALIGNED, FLOAT_VECTOR_UNALIGNED}) {
|
||||
for (int index : new int[]{0, size - 1}) {
|
||||
testEqualInFloatArray(size, index, 1024, 1024);
|
||||
testEqualInFloatArray(size, index, 0.0f, -0.0f);
|
||||
testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), Float.intBitsToFloat(0x7fc00000));
|
||||
testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), Float.intBitsToFloat(0x7f800001));
|
||||
testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testDoubleArraySnippet(double[] a, double[] b) {
|
||||
return Arrays.equals(a, b);
|
||||
}
|
||||
|
||||
public static final int DOUBLE_8BYTE_ALIGNED = 1;
|
||||
public static final int DOUBLE_VECTOR_ALIGNED = 4;
|
||||
public static final int DOUBLE_VECTOR_UNALIGNED = 5;
|
||||
|
||||
private void testEqualInDoubleArray(int arraySize, int index, double d1, double d2) {
|
||||
if (arraySize > 0 && index >= 0 && index < arraySize) {
|
||||
double[] a1 = new double[arraySize];
|
||||
double[] a2 = new double[arraySize];
|
||||
a1[index] = d1;
|
||||
a2[index] = d2;
|
||||
test("testDoubleArraySnippet", a1, a2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleArrayOrdinary() {
|
||||
for (int size : new int[]{DOUBLE_8BYTE_ALIGNED, DOUBLE_VECTOR_ALIGNED, DOUBLE_VECTOR_UNALIGNED}) {
|
||||
for (int index : new int[]{0, size - 1}) {
|
||||
testEqualInDoubleArray(size, index, 1024, 1024);
|
||||
testEqualInDoubleArray(size, index, 0.0d, -0.0d);
|
||||
testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), Double.longBitsToDouble(0x7ff8000000000000L));
|
||||
testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), Double.longBitsToDouble(0x7ff0000000000001L));
|
||||
testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testFloatArrayWithPEASnippet0() {
|
||||
return Arrays.equals(new float[]{0.0f}, new float[]{-0.0f});
|
||||
}
|
||||
|
||||
public static boolean testFloatArrayWithPEASnippet1() {
|
||||
return Arrays.equals(new float[]{Float.intBitsToFloat(0x7fc00000)}, new float[]{Float.intBitsToFloat(0x7fc00000)});
|
||||
}
|
||||
|
||||
public static boolean testFloatArrayWithPEASnippet2() {
|
||||
return Arrays.equals(new float[]{Float.intBitsToFloat(0x7fc00000)}, new float[]{Float.intBitsToFloat(0x7f800001)});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatArrayWithPEA() {
|
||||
test("testFloatArrayWithPEASnippet0");
|
||||
test("testFloatArrayWithPEASnippet1");
|
||||
test("testFloatArrayWithPEASnippet2");
|
||||
}
|
||||
|
||||
public static boolean testDoubleArrayWithPEASnippet0() {
|
||||
return Arrays.equals(new double[]{0.0d}, new double[]{-0.0d});
|
||||
}
|
||||
|
||||
public static boolean testDoubleArrayWithPEASnippet1() {
|
||||
return Arrays.equals(new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}, new double[]{Double.longBitsToDouble(0x7ff8000000000000L)});
|
||||
}
|
||||
|
||||
public static boolean testDoubleArrayWithPEASnippet2() {
|
||||
return Arrays.equals(new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}, new double[]{Double.longBitsToDouble(0x7ff0000000000001L)});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleArrayWithPEA() {
|
||||
test("testDoubleArrayWithPEASnippet0");
|
||||
test("testDoubleArrayWithPEASnippet1");
|
||||
test("testDoubleArrayWithPEASnippet2");
|
||||
}
|
||||
|
||||
public static final float[] FLOAT_ARRAY1 = new float[]{0.0f};
|
||||
public static final float[] FLOAT_ARRAY2 = new float[]{-0.0f};
|
||||
public static final float[] FLOAT_ARRAY3 = new float[]{Float.intBitsToFloat(0x7fc00000)};
|
||||
public static final float[] FLOAT_ARRAY4 = new float[]{Float.intBitsToFloat(0x7f800001)};
|
||||
|
||||
public static final double[] DOUBLE_ARRAY1 = new double[]{0.0d};
|
||||
public static final double[] DOUBLE_ARRAY2 = new double[]{-0.0d};
|
||||
public static final double[] DOUBLE_ARRAY3 = new double[]{Double.longBitsToDouble(0x7ff8000000000000L)};
|
||||
public static final double[] DOUBLE_ARRAY4 = new double[]{Double.longBitsToDouble(0x7ff0000000000001L)};
|
||||
|
||||
public static boolean testStableFloatArraySnippet0() {
|
||||
return Arrays.equals(FLOAT_ARRAY1, FLOAT_ARRAY2);
|
||||
}
|
||||
|
||||
public static boolean testStableFloatArraySnippet1() {
|
||||
return Arrays.equals(FLOAT_ARRAY1, FLOAT_ARRAY2);
|
||||
}
|
||||
|
||||
public static boolean testStableDoubleArraySnippet0() {
|
||||
return Arrays.equals(DOUBLE_ARRAY1, DOUBLE_ARRAY2);
|
||||
}
|
||||
|
||||
public static boolean testStableDoubleArraySnippet1() {
|
||||
return Arrays.equals(DOUBLE_ARRAY3, DOUBLE_ARRAY4);
|
||||
}
|
||||
|
||||
public void testStableArray(String methodName) {
|
||||
ResolvedJavaMethod method = getResolvedJavaMethod(methodName);
|
||||
Result expected = executeExpected(method, null);
|
||||
|
||||
StructuredGraph graph = parseEager(method, AllowAssumptions.YES);
|
||||
|
||||
for (ConstantNode constantNode : graph.getNodes().filter(ConstantNode.class).snapshot()) {
|
||||
if (getConstantReflection().readArrayLength(constantNode.asJavaConstant()) != null) {
|
||||
ConstantNode newConstantNode = ConstantNode.forConstant(constantNode.asJavaConstant(), 1, true, getMetaAccess());
|
||||
newConstantNode = graph.unique(newConstantNode);
|
||||
constantNode.replaceAndDelete(newConstantNode);
|
||||
}
|
||||
}
|
||||
|
||||
CompilationResult result = compile(method, graph);
|
||||
InstalledCode code = addMethod(graph.getDebug(), method, result);
|
||||
|
||||
Result actual;
|
||||
|
||||
try {
|
||||
actual = new Result(code.executeVarargs(), null);
|
||||
} catch (Exception e) {
|
||||
actual = new Result(null, e);
|
||||
}
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStableArray() {
|
||||
testStableArray("testStableFloatArraySnippet0");
|
||||
testStableArray("testStableFloatArraySnippet1");
|
||||
testStableArray("testStableDoubleArraySnippet0");
|
||||
testStableArray("testStableDoubleArraySnippet1");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.graalvm.compiler.replacements.test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnsafeBooleanAccessTest extends GraalCompilerTest {
|
||||
|
||||
private static short onHeapMemory;
|
||||
|
||||
private static final Object onHeapMemoryBase;
|
||||
private static final long onHeapMemoryOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
Field staticField = UnsafeBooleanAccessTest.class.getDeclaredField("onHeapMemory");
|
||||
onHeapMemoryBase = UNSAFE.staticFieldBase(staticField);
|
||||
onHeapMemoryOffset = UNSAFE.staticFieldOffset(staticField);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testGetBooleanSnippet() {
|
||||
UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204);
|
||||
return UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBoolean() {
|
||||
test("testGetBooleanSnippet");
|
||||
}
|
||||
|
||||
public static short testPutBooleanSnippet() {
|
||||
UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204);
|
||||
boolean bool = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset);
|
||||
UNSAFE.putBoolean(onHeapMemoryBase, onHeapMemoryOffset, bool);
|
||||
return onHeapMemory;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutBoolean() {
|
||||
test("testPutBooleanSnippet");
|
||||
}
|
||||
|
||||
public static boolean testAndBooleanSnippet() {
|
||||
UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204);
|
||||
boolean bool0 = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset);
|
||||
boolean bool1 = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset + 1);
|
||||
return bool0 & bool1;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndBoolean() {
|
||||
test("testAndBooleanSnippet");
|
||||
}
|
||||
|
||||
}
|
@ -59,8 +59,10 @@ import org.graalvm.compiler.nodes.PiNode;
|
||||
import org.graalvm.compiler.nodes.StructuredGraph;
|
||||
import org.graalvm.compiler.nodes.ValueNode;
|
||||
import org.graalvm.compiler.nodes.calc.AddNode;
|
||||
import org.graalvm.compiler.nodes.calc.ConditionalNode;
|
||||
import org.graalvm.compiler.nodes.calc.IntegerBelowNode;
|
||||
import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
|
||||
import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
|
||||
import org.graalvm.compiler.nodes.calc.IsNullNode;
|
||||
import org.graalvm.compiler.nodes.calc.LeftShiftNode;
|
||||
import org.graalvm.compiler.nodes.calc.NarrowNode;
|
||||
@ -577,7 +579,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
|
||||
} else {
|
||||
memoryRead.setGuard(guard);
|
||||
}
|
||||
ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, compressible);
|
||||
ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, compressible), readKind);
|
||||
load.replaceAtUsages(readValue);
|
||||
return memoryRead;
|
||||
}
|
||||
@ -592,11 +594,20 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
|
||||
// An unsafe read must not float otherwise it may float above
|
||||
// a test guaranteeing the read is safe.
|
||||
memoryRead.setForceFixed(true);
|
||||
ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, false);
|
||||
ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, false), readKind);
|
||||
load.replaceAtUsages(readValue);
|
||||
graph.replaceFixedWithFixed(load, memoryRead);
|
||||
}
|
||||
|
||||
private static ValueNode performBooleanCoercionIfNecessary(ValueNode readValue, JavaKind readKind) {
|
||||
if (readKind == JavaKind.Boolean) {
|
||||
StructuredGraph graph = readValue.graph();
|
||||
IntegerEqualsNode eq = graph.addOrUnique(new IntegerEqualsNode(readValue, ConstantNode.forInt(0, graph)));
|
||||
return graph.addOrUnique(new ConditionalNode(eq, ConstantNode.forBoolean(false, graph), ConstantNode.forBoolean(true, graph)));
|
||||
}
|
||||
return readValue;
|
||||
}
|
||||
|
||||
protected void lowerUnsafeStoreNode(RawStoreNode store) {
|
||||
StructuredGraph graph = store.graph();
|
||||
boolean compressible = store.value().getStackKind() == JavaKind.Object;
|
||||
|
@ -189,9 +189,7 @@ public class StandardGraphBuilderPlugins {
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class);
|
||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
|
||||
}
|
||||
|
||||
private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
|
||||
|
@ -95,11 +95,16 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower
|
||||
return length;
|
||||
}
|
||||
|
||||
private static boolean isNaNFloat(JavaConstant constant) {
|
||||
JavaKind kind = constant.getJavaKind();
|
||||
return (kind == JavaKind.Float && Float.isNaN(constant.asFloat())) || (kind == JavaKind.Double && Double.isNaN(constant.asDouble()));
|
||||
}
|
||||
|
||||
private static boolean arrayEquals(ConstantReflectionProvider constantReflection, JavaConstant a, JavaConstant b, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
JavaConstant aElem = constantReflection.readArrayElement(a, i);
|
||||
JavaConstant bElem = constantReflection.readArrayElement(b, i);
|
||||
if (!constantReflection.constantEquals(aElem, bElem)) {
|
||||
if (!constantReflection.constantEquals(aElem, bElem) && !(isNaNFloat(aElem) && isNaNFloat(bElem))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -145,8 +150,28 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower
|
||||
ValueNode entry1 = tool.getEntry(virtual1, i);
|
||||
ValueNode entry2 = tool.getEntry(virtual2, i);
|
||||
if (entry1 != entry2) {
|
||||
// the contents might be different
|
||||
allEqual = false;
|
||||
if (entry1 instanceof ConstantNode && entry2 instanceof ConstantNode) {
|
||||
// Float NaN constants are different constant nodes but treated as
|
||||
// equal in Arrays.equals([F[F) or Arrays.equals([D[D).
|
||||
if (entry1.getStackKind() == JavaKind.Float && entry2.getStackKind() == JavaKind.Float) {
|
||||
float value1 = ((JavaConstant) ((ConstantNode) entry1).asConstant()).asFloat();
|
||||
float value2 = ((JavaConstant) ((ConstantNode) entry2).asConstant()).asFloat();
|
||||
if (Float.floatToIntBits(value1) != Float.floatToIntBits(value2)) {
|
||||
allEqual = false;
|
||||
}
|
||||
} else if (entry1.getStackKind() == JavaKind.Double && entry2.getStackKind() == JavaKind.Double) {
|
||||
double value1 = ((JavaConstant) ((ConstantNode) entry1).asConstant()).asDouble();
|
||||
double value2 = ((JavaConstant) ((ConstantNode) entry2).asConstant()).asDouble();
|
||||
if (Double.doubleToLongBits(value1) != Double.doubleToLongBits(value2)) {
|
||||
allEqual = false;
|
||||
}
|
||||
} else {
|
||||
allEqual = false;
|
||||
}
|
||||
} else {
|
||||
// the contents might be different
|
||||
allEqual = false;
|
||||
}
|
||||
}
|
||||
if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
|
||||
// the contents are different
|
||||
|
@ -57,7 +57,7 @@ public final class ExplodeLoopNode extends FixedWithNextNode {
|
||||
for (Node n : currentNext.cfgSuccessors()) {
|
||||
succs.add(n);
|
||||
}
|
||||
if (succs.size() == 1) {
|
||||
if (succs.size() == 1 && succs.get(0) != currentNext) {
|
||||
currentNext = succs.get(0);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -25,69 +25,28 @@ package org.graalvm.compiler.replacements.nodes.arithmetic;
|
||||
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
|
||||
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.graalvm.compiler.core.common.type.IntegerStamp;
|
||||
import org.graalvm.compiler.core.common.type.Stamp;
|
||||
import org.graalvm.compiler.core.common.type.StampFactory;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh;
|
||||
import org.graalvm.compiler.graph.NodeClass;
|
||||
import org.graalvm.compiler.graph.spi.Canonicalizable;
|
||||
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
||||
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
||||
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
||||
import org.graalvm.compiler.nodes.ConstantNode;
|
||||
import org.graalvm.compiler.nodes.ValueNode;
|
||||
import org.graalvm.compiler.nodes.calc.BinaryNode;
|
||||
import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
|
||||
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
|
||||
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
|
||||
|
||||
import jdk.vm.ci.meta.JavaKind;
|
||||
import jdk.vm.ci.meta.Constant;
|
||||
import jdk.vm.ci.meta.PrimitiveConstant;
|
||||
import jdk.vm.ci.meta.Value;
|
||||
|
||||
@NodeInfo(shortName = "*H", cycles = CYCLES_2, size = SIZE_2)
|
||||
public final class IntegerMulHighNode extends BinaryNode implements ArithmeticLIRLowerable {
|
||||
public final class IntegerMulHighNode extends BinaryArithmeticNode<MulHigh> implements Canonicalizable.BinaryCommutative<ValueNode> {
|
||||
public static final NodeClass<IntegerMulHighNode> TYPE = NodeClass.create(IntegerMulHighNode.class);
|
||||
|
||||
public IntegerMulHighNode(ValueNode x, ValueNode y) {
|
||||
this((IntegerStamp) x.stamp().unrestricted(), x, y);
|
||||
}
|
||||
|
||||
public IntegerMulHighNode(IntegerStamp stamp, ValueNode x, ValueNode y) {
|
||||
super(TYPE, stamp, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the minimum and maximum result of this node for the given inputs and returns the
|
||||
* result of the given BiFunction on the minimum and maximum values.
|
||||
*/
|
||||
private <T> T processExtremes(Stamp forX, Stamp forY, BiFunction<Long, Long, T> op) {
|
||||
IntegerStamp xStamp = (IntegerStamp) forX;
|
||||
IntegerStamp yStamp = (IntegerStamp) forY;
|
||||
|
||||
JavaKind kind = getStackKind();
|
||||
assert kind == JavaKind.Int || kind == JavaKind.Long;
|
||||
long[] xExtremes = {xStamp.lowerBound(), xStamp.upperBound()};
|
||||
long[] yExtremes = {yStamp.lowerBound(), yStamp.upperBound()};
|
||||
long min = Long.MAX_VALUE;
|
||||
long max = Long.MIN_VALUE;
|
||||
for (long a : xExtremes) {
|
||||
for (long b : yExtremes) {
|
||||
long result = kind == JavaKind.Int ? multiplyHigh((int) a, (int) b) : multiplyHigh(a, b);
|
||||
min = Math.min(min, result);
|
||||
max = Math.max(max, result);
|
||||
}
|
||||
}
|
||||
return op.apply(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stamp foldStamp(Stamp stampX, Stamp stampY) {
|
||||
return processExtremes(stampX, stampY, (min, max) -> StampFactory.forInteger(getStackKind(), min, max));
|
||||
}
|
||||
|
||||
@SuppressWarnings("cast")
|
||||
@Override
|
||||
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||
return processExtremes(forX.stamp(), forY.stamp(), (min, max) -> min == (long) max ? ConstantNode.forIntegerKind(getStackKind(), min) : this);
|
||||
super(TYPE, ArithmeticOpTable::getMulHigh, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -97,29 +56,35 @@ public final class IntegerMulHighNode extends BinaryNode implements ArithmeticLI
|
||||
nodeValueMap.setResult(this, gen.emitMulHigh(a, b));
|
||||
}
|
||||
|
||||
public static int multiplyHigh(int x, int y) {
|
||||
long r = (long) x * (long) y;
|
||||
return (int) (r >> 32);
|
||||
@Override
|
||||
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||
ValueNode ret = super.canonical(tool, forX, forY);
|
||||
if (ret != this) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (forX.isConstant() && !forY.isConstant()) {
|
||||
// we try to swap and canonicalize
|
||||
ValueNode improvement = canonical(tool, forY, forX);
|
||||
if (improvement != this) {
|
||||
return improvement;
|
||||
}
|
||||
// if this fails we only swap
|
||||
return new IntegerMulHighNode(forY, forX);
|
||||
}
|
||||
return canonical(this, forY);
|
||||
}
|
||||
|
||||
public static long multiplyHigh(long x, long y) {
|
||||
// Checkstyle: stop
|
||||
long x0, y0, z0;
|
||||
long x1, y1, z1, z2, t;
|
||||
// Checkstyle: resume
|
||||
|
||||
x0 = x & 0xFFFFFFFFL;
|
||||
x1 = x >> 32;
|
||||
|
||||
y0 = y & 0xFFFFFFFFL;
|
||||
y1 = y >> 32;
|
||||
|
||||
z0 = x0 * y0;
|
||||
t = x1 * y0 + (z0 >>> 32);
|
||||
z1 = t & 0xFFFFFFFFL;
|
||||
z2 = t >> 32;
|
||||
z1 += x0 * y1;
|
||||
|
||||
return x1 * y1 + z2 + (z1 >> 32);
|
||||
private static ValueNode canonical(IntegerMulHighNode self, ValueNode forY) {
|
||||
if (forY.isConstant()) {
|
||||
Constant c = forY.asConstant();
|
||||
if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
|
||||
long i = ((PrimitiveConstant) c).asLong();
|
||||
if (i == 0 || i == 1) {
|
||||
return ConstantNode.forIntegerStamp(self.stamp(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
@ -25,86 +25,28 @@ package org.graalvm.compiler.replacements.nodes.arithmetic;
|
||||
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
|
||||
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.graalvm.compiler.core.common.type.IntegerStamp;
|
||||
import org.graalvm.compiler.core.common.type.Stamp;
|
||||
import org.graalvm.compiler.core.common.type.StampFactory;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh;
|
||||
import org.graalvm.compiler.graph.NodeClass;
|
||||
import org.graalvm.compiler.graph.spi.Canonicalizable;
|
||||
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
||||
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
||||
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
||||
import org.graalvm.compiler.nodes.ConstantNode;
|
||||
import org.graalvm.compiler.nodes.ValueNode;
|
||||
import org.graalvm.compiler.nodes.calc.BinaryNode;
|
||||
import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
|
||||
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
|
||||
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
|
||||
|
||||
import jdk.vm.ci.meta.JavaKind;
|
||||
import jdk.vm.ci.meta.Constant;
|
||||
import jdk.vm.ci.meta.PrimitiveConstant;
|
||||
import jdk.vm.ci.meta.Value;
|
||||
|
||||
@NodeInfo(shortName = "|*H|", cycles = CYCLES_2, size = SIZE_2)
|
||||
public final class UnsignedMulHighNode extends BinaryNode implements ArithmeticLIRLowerable {
|
||||
|
||||
public final class UnsignedMulHighNode extends BinaryArithmeticNode<UMulHigh> implements Canonicalizable.BinaryCommutative<ValueNode> {
|
||||
public static final NodeClass<UnsignedMulHighNode> TYPE = NodeClass.create(UnsignedMulHighNode.class);
|
||||
|
||||
public UnsignedMulHighNode(ValueNode x, ValueNode y) {
|
||||
this((IntegerStamp) x.stamp().unrestricted(), x, y);
|
||||
}
|
||||
|
||||
public UnsignedMulHighNode(IntegerStamp stamp, ValueNode x, ValueNode y) {
|
||||
super(TYPE, stamp, x, y);
|
||||
}
|
||||
|
||||
private static long[] getUnsignedExtremes(IntegerStamp stamp) {
|
||||
if (stamp.lowerBound() < 0 && stamp.upperBound() >= 0) {
|
||||
/*
|
||||
* If -1 and 0 are both in the signed range, then we can't say anything about the
|
||||
* unsigned range, so we have to return [0, MAX_UNSIGNED].
|
||||
*/
|
||||
return new long[]{0, -1L};
|
||||
} else {
|
||||
return new long[]{stamp.lowerBound(), stamp.upperBound()};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the minimum and maximum result of this node for the given inputs and returns the
|
||||
* result of the given BiFunction on the minimum and maximum values. Note that the minima and
|
||||
* maxima are calculated using signed min/max functions, while the values themselves are
|
||||
* unsigned.
|
||||
*/
|
||||
private <T> T processExtremes(Stamp forX, Stamp forY, BiFunction<Long, Long, T> op) {
|
||||
IntegerStamp xStamp = (IntegerStamp) forX;
|
||||
IntegerStamp yStamp = (IntegerStamp) forY;
|
||||
|
||||
JavaKind kind = getStackKind();
|
||||
assert kind == JavaKind.Int || kind == JavaKind.Long;
|
||||
long[] xExtremes = getUnsignedExtremes(xStamp);
|
||||
long[] yExtremes = getUnsignedExtremes(yStamp);
|
||||
long min = Long.MAX_VALUE;
|
||||
long max = Long.MIN_VALUE;
|
||||
for (long a : xExtremes) {
|
||||
for (long b : yExtremes) {
|
||||
long result = kind == JavaKind.Int ? multiplyHighUnsigned((int) a, (int) b) : multiplyHighUnsigned(a, b);
|
||||
min = Math.min(min, result);
|
||||
max = Math.max(max, result);
|
||||
}
|
||||
}
|
||||
return op.apply(min, max);
|
||||
}
|
||||
|
||||
@SuppressWarnings("cast")
|
||||
@Override
|
||||
public Stamp foldStamp(Stamp stampX, Stamp stampY) {
|
||||
// if min is negative, then the value can reach into the unsigned range
|
||||
return processExtremes(stampX, stampY, (min, max) -> (min == (long) max || min >= 0) ? StampFactory.forInteger(getStackKind(), min, max) : StampFactory.forKind(getStackKind()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("cast")
|
||||
@Override
|
||||
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||
return processExtremes(forX.stamp(), forY.stamp(), (min, max) -> min == (long) max ? ConstantNode.forIntegerKind(getStackKind(), min) : this);
|
||||
super(TYPE, ArithmeticOpTable::getUMulHigh, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,31 +56,35 @@ public final class UnsignedMulHighNode extends BinaryNode implements ArithmeticL
|
||||
nodeValueMap.setResult(this, gen.emitUMulHigh(a, b));
|
||||
}
|
||||
|
||||
public static int multiplyHighUnsigned(int x, int y) {
|
||||
long xl = x & 0xFFFFFFFFL;
|
||||
long yl = y & 0xFFFFFFFFL;
|
||||
long r = xl * yl;
|
||||
return (int) (r >> 32);
|
||||
@Override
|
||||
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||
ValueNode ret = super.canonical(tool, forX, forY);
|
||||
if (ret != this) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (forX.isConstant() && !forY.isConstant()) {
|
||||
// we try to swap and canonicalize
|
||||
ValueNode improvement = canonical(tool, forY, forX);
|
||||
if (improvement != this) {
|
||||
return improvement;
|
||||
}
|
||||
// if this fails we only swap
|
||||
return new UnsignedMulHighNode(forY, forX);
|
||||
}
|
||||
return canonical(this, forY);
|
||||
}
|
||||
|
||||
public static long multiplyHighUnsigned(long x, long y) {
|
||||
// Checkstyle: stop
|
||||
long x0, y0, z0;
|
||||
long x1, y1, z1, z2, t;
|
||||
// Checkstyle: resume
|
||||
|
||||
x0 = x & 0xFFFFFFFFL;
|
||||
x1 = x >>> 32;
|
||||
|
||||
y0 = y & 0xFFFFFFFFL;
|
||||
y1 = y >>> 32;
|
||||
|
||||
z0 = x0 * y0;
|
||||
t = x1 * y0 + (z0 >>> 32);
|
||||
z1 = t & 0xFFFFFFFFL;
|
||||
z2 = t >>> 32;
|
||||
z1 += x0 * y1;
|
||||
|
||||
return x1 * y1 + z2 + (z1 >>> 32);
|
||||
private static ValueNode canonical(UnsignedMulHighNode self, ValueNode forY) {
|
||||
if (forY.isConstant()) {
|
||||
Constant c = forY.asConstant();
|
||||
if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
|
||||
long i = ((PrimitiveConstant) c).asLong();
|
||||
if (i == 0 || i == 1) {
|
||||
return ConstantNode.forIntegerStamp(self.stamp(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -20,7 +20,7 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.graalvm.compiler.microbenchmarks.graal;
|
||||
package micro.benchmarks;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@ -33,7 +33,7 @@ import org.openjdk.jmh.annotations.Warmup;
|
||||
/**
|
||||
* This dummy class is used to verify that the JMH microbenchmarking environment is set up properly.
|
||||
*/
|
||||
public class TestJMH {
|
||||
public class TestJMHBlackbox {
|
||||
|
||||
@Benchmark
|
||||
public void testJMH() {
|
@ -952,7 +952,7 @@ jlong os::javaTimeNanos() {
|
||||
if (now <= prev) {
|
||||
return prev; // same or retrograde time;
|
||||
}
|
||||
const uint64_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&Bsd::_max_abstime, prev);
|
||||
const uint64_t obsv = Atomic::cmpxchg(now, &Bsd::_max_abstime, prev);
|
||||
assert(obsv >= prev, "invariant"); // Monotonicity
|
||||
// If the CAS succeeded then we're done and return "now".
|
||||
// If the CAS failed and the observed value "obsv" is >= now then
|
||||
|
@ -1747,9 +1747,9 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
{EM_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"},
|
||||
{EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"},
|
||||
#if defined(VM_LITTLE_ENDIAN)
|
||||
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2LSB, (char*)"Power PC 64"},
|
||||
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2LSB, (char*)"Power PC 64 LE"},
|
||||
#else
|
||||
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64 LE"},
|
||||
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"},
|
||||
#endif
|
||||
{EM_ARM, EM_ARM, ELFCLASS32, ELFDATA2LSB, (char*)"ARM"},
|
||||
{EM_S390, EM_S390, ELFCLASSNONE, ELFDATA2MSB, (char*)"IBM System/390"},
|
||||
|
@ -1197,7 +1197,7 @@ inline hrtime_t getTimeNanos() {
|
||||
if (now <= prev) {
|
||||
return prev; // same or retrograde time;
|
||||
}
|
||||
const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev);
|
||||
const hrtime_t obsv = Atomic::cmpxchg(now, &max_hrtime, prev);
|
||||
assert(obsv >= prev, "invariant"); // Monotonicity
|
||||
// If the CAS succeeded then we're done and return "now".
|
||||
// If the CAS failed and the observed value "obsv" is >= now then
|
||||
|
@ -306,8 +306,13 @@ inline void cmpxchg_post_membar(cmpxchg_memory_order order) {
|
||||
}
|
||||
}
|
||||
|
||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
||||
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<1>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_CAST(1 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
@ -368,16 +373,22 @@ inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte c
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jbyte)(unsigned char)old_value;
|
||||
return PrimitiveConversions::cast<T>((unsigned char)old_value);
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_CAST(4 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
// specified otherwise (see atomic.hpp).
|
||||
|
||||
unsigned int old_value;
|
||||
T old_value;
|
||||
const uint64_t zero = 0;
|
||||
|
||||
cmpxchg_pre_membar(order);
|
||||
@ -412,16 +423,22 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jint) old_value;
|
||||
return old_value;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_CAST(8 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
// specified otherwise (see atomic.hpp).
|
||||
|
||||
long old_value;
|
||||
T old_value;
|
||||
const uint64_t zero = 0;
|
||||
|
||||
cmpxchg_pre_membar(order);
|
||||
@ -456,15 +473,7 @@ inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong c
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jlong) old_value;
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
#undef strasm_sync
|
||||
|
@ -25,8 +25,6 @@
|
||||
#ifndef OS_CPU_BSD_X86_VM_ATOMIC_BSD_X86_HPP
|
||||
#define OS_CPU_BSD_X86_VM_ATOMIC_BSD_X86_HPP
|
||||
|
||||
#include "runtime/os.hpp"
|
||||
|
||||
// Implementation of class atomic
|
||||
|
||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
||||
@ -81,8 +79,13 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* des
|
||||
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||
}
|
||||
|
||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
||||
inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<1>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(1 == sizeof(T));
|
||||
__asm__ volatile ( "lock cmpxchgb %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -90,7 +93,13 @@ inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte*
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
__asm__ volatile ( "lock cmpxchgl %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -137,7 +146,13 @@ inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* des
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
__asm__ __volatile__ ( "lock cmpxchgq %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -145,14 +160,6 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||
|
||||
#else // !AMD64
|
||||
@ -184,16 +191,14 @@ extern "C" {
|
||||
void _Atomic_move_long(const volatile jlong* src, volatile jlong* dst);
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
return _Atomic_cmpxchg_long(exchange_value, dest, compare_value, os::is_MP());
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
return cmpxchg_using_helper<jlong>(_Atomic_cmpxchg_long, exchange_value, dest, compare_value);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) {
|
||||
|
@ -57,9 +57,9 @@ static inline int __m68k_cmpxchg(int oldval, int newval, volatile int *ptr) {
|
||||
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||
`*PTR' before the operation.*/
|
||||
static inline int m68k_compare_and_swap(volatile int *ptr,
|
||||
int oldval,
|
||||
int newval) {
|
||||
static inline int m68k_compare_and_swap(int newval,
|
||||
volatile int *ptr,
|
||||
int oldval) {
|
||||
for (;;) {
|
||||
int prev = *ptr;
|
||||
if (prev != oldval)
|
||||
@ -118,9 +118,9 @@ typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
|
||||
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||
`*PTR' before the operation.*/
|
||||
static inline int arm_compare_and_swap(volatile int *ptr,
|
||||
int oldval,
|
||||
int newval) {
|
||||
static inline int arm_compare_and_swap(int newval,
|
||||
volatile int *ptr,
|
||||
int oldval) {
|
||||
for (;;) {
|
||||
int prev = *ptr;
|
||||
if (prev != oldval)
|
||||
@ -267,55 +267,38 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
|
||||
(volatile intptr_t*) dest);
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg(jint exchange_value,
|
||||
volatile jint* dest,
|
||||
jint compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_CAST(4 == sizeof(T));
|
||||
#ifdef ARM
|
||||
return arm_compare_and_swap(dest, compare_value, exchange_value);
|
||||
return cmpxchg_using_helper<int>(arm_compare_and_swap, exchange_value, dest, compare_value);
|
||||
#else
|
||||
#ifdef M68K
|
||||
return m68k_compare_and_swap(dest, compare_value, exchange_value);
|
||||
return cmpxchg_using_helper<int>(m68k_compare_and_swap, exchange_value, dest, compare_value);
|
||||
#else
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg(jlong exchange_value,
|
||||
volatile jlong* dest,
|
||||
jlong compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_CAST(8 == sizeof(T));
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value,
|
||||
volatile intptr_t* dest,
|
||||
intptr_t compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
#ifdef ARM
|
||||
return arm_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#else
|
||||
#ifdef M68K
|
||||
return m68k_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#else
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value,
|
||||
volatile void* dest,
|
||||
void* compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
|
||||
return (void *) cmpxchg_ptr((intptr_t) exchange_value,
|
||||
(volatile intptr_t*) dest,
|
||||
(intptr_t) compare_value,
|
||||
order);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) {
|
||||
volatile jlong dest;
|
||||
os::atomic_copy64(src, &dest);
|
||||
|
@ -85,9 +85,13 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest)
|
||||
(volatile intptr_t*) dest);
|
||||
}
|
||||
|
||||
template <typename T> T generic_cmpxchg(T exchange_value, volatile T* dest,
|
||||
T compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
template<size_t byte_size>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<byte_size>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(byte_size == sizeof(T));
|
||||
if (order == memory_order_relaxed) {
|
||||
T value = compare_value;
|
||||
__atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false,
|
||||
@ -98,17 +102,6 @@ template <typename T> T generic_cmpxchg(T exchange_value, volatile T* dest,
|
||||
}
|
||||
}
|
||||
|
||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
||||
inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
return generic_cmpxchg(exchange_value, dest, compare_value, order);
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
return generic_cmpxchg(exchange_value, dest, compare_value, order);
|
||||
}
|
||||
|
||||
inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; }
|
||||
inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; }
|
||||
|
||||
@ -139,24 +132,6 @@ inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* des
|
||||
return res;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
return generic_cmpxchg(exchange_value, dest, compare_value, order);
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
return generic_cmpxchg(exchange_value, dest, compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order)
|
||||
{
|
||||
return (void *) cmpxchg_ptr((intptr_t) exchange_value,
|
||||
(volatile intptr_t*) dest,
|
||||
(intptr_t) compare_value,
|
||||
order);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||
|
||||
#endif // OS_CPU_LINUX_AARCH64_VM_ATOMIC_LINUX_AARCH64_HPP
|
||||
|
@ -200,9 +200,38 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
|
||||
|
||||
// The memory_order parameter is ignored - we always provide the strongest/most-conservative ordering
|
||||
|
||||
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
#ifndef AARCH64
|
||||
|
||||
inline jint reorder_cmpxchg_func(jint exchange_value,
|
||||
jint volatile* dest,
|
||||
jint compare_value) {
|
||||
// Warning: Arguments are swapped to avoid moving them for kernel call
|
||||
return (*os::atomic_cmpxchg_func)(compare_value, exchange_value, dest);
|
||||
}
|
||||
|
||||
inline jlong reorder_cmpxchg_long_func(jlong exchange_value,
|
||||
jlong volatile* dest,
|
||||
jlong compare_value) {
|
||||
assert(VM_Version::supports_cx8(), "Atomic compare and exchange jlong not supported on this architecture!");
|
||||
// Warning: Arguments are swapped to avoid moving them for kernel call
|
||||
return (*os::atomic_cmpxchg_long_func)(compare_value, exchange_value, dest);
|
||||
}
|
||||
|
||||
#endif // !AARCH64
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
#ifdef AARCH64
|
||||
jint rv;
|
||||
T rv;
|
||||
int tmp;
|
||||
__asm__ volatile(
|
||||
"1:\n\t"
|
||||
@ -220,14 +249,19 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||
: "memory");
|
||||
return rv;
|
||||
#else
|
||||
// Warning: Arguments are swapped to avoid moving them for kernel call
|
||||
return (*os::atomic_cmpxchg_func)(compare_value, exchange_value, dest);
|
||||
return cmpxchg_using_helper<jint>(reorder_cmpxchg_func, exchange_value, dest, compare_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
#ifdef AARCH64
|
||||
jlong rv;
|
||||
T rv;
|
||||
int tmp;
|
||||
__asm__ volatile(
|
||||
"1:\n\t"
|
||||
@ -245,21 +279,8 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong
|
||||
: "memory");
|
||||
return rv;
|
||||
#else
|
||||
assert(VM_Version::supports_cx8(), "Atomic compare and exchange jlong not supported on this architecture!");
|
||||
return (*os::atomic_cmpxchg_long_func)(compare_value, exchange_value, dest);
|
||||
return cmpxchg_using_helper<jlong>(reorder_cmpxchg_long_func, exchange_value, dest, compare_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
#ifdef AARCH64
|
||||
return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
#else
|
||||
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value, order);
|
||||
}
|
||||
|
||||
#endif // OS_CPU_LINUX_ARM_VM_ATOMIC_LINUX_ARM_HPP
|
||||
|
@ -306,8 +306,13 @@ inline void cmpxchg_post_membar(cmpxchg_memory_order order) {
|
||||
}
|
||||
}
|
||||
|
||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
||||
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<1>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(1 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
@ -368,16 +373,22 @@ inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte c
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jbyte)(unsigned char)old_value;
|
||||
return PrimitiveConversions::cast<T>((unsigned char)old_value);
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
// specified otherwise (see atomic.hpp).
|
||||
|
||||
unsigned int old_value;
|
||||
T old_value;
|
||||
const uint64_t zero = 0;
|
||||
|
||||
cmpxchg_pre_membar(order);
|
||||
@ -412,16 +423,22 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jint) old_value;
|
||||
return old_value;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
|
||||
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||
// specified otherwise (see atomic.hpp).
|
||||
|
||||
long old_value;
|
||||
T old_value;
|
||||
const uint64_t zero = 0;
|
||||
|
||||
cmpxchg_pre_membar(order);
|
||||
@ -456,15 +473,7 @@ inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong c
|
||||
|
||||
cmpxchg_post_membar(order);
|
||||
|
||||
return (jlong) old_value;
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
#undef strasm_sync
|
||||
|
@ -478,8 +478,18 @@ inline void *Atomic::xchg_ptr(void *exchange_value, volatile void *dest) {
|
||||
// function is performed before the operand is fetched and again after the
|
||||
// operation is completed."
|
||||
|
||||
jint Atomic::cmpxchg(jint xchg_val, volatile jint* dest, jint cmp_val, cmpxchg_memory_order unused) {
|
||||
unsigned long old;
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T xchg_val,
|
||||
T volatile* dest,
|
||||
T cmp_val,
|
||||
cmpxchg_memory_order unused) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
T old;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
" CS %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.
|
||||
@ -493,11 +503,17 @@ jint Atomic::cmpxchg(jint xchg_val, volatile jint* dest, jint cmp_val, cmpxchg_m
|
||||
: "cc", "memory"
|
||||
);
|
||||
|
||||
return (jint)old;
|
||||
return old;
|
||||
}
|
||||
|
||||
jlong Atomic::cmpxchg(jlong xchg_val, volatile jlong* dest, jlong cmp_val, cmpxchg_memory_order unused) {
|
||||
unsigned long old;
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T xchg_val,
|
||||
T volatile* dest,
|
||||
T cmp_val,
|
||||
cmpxchg_memory_order unused) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
T old;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
" CSG %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.
|
||||
@ -511,15 +527,7 @@ jlong Atomic::cmpxchg(jlong xchg_val, volatile jlong* dest, jlong cmp_val, cmpxc
|
||||
: "cc", "memory"
|
||||
);
|
||||
|
||||
return (jlong)old;
|
||||
}
|
||||
|
||||
void* Atomic::cmpxchg_ptr(void *xchg_val, volatile void* dest, void* cmp_val, cmpxchg_memory_order unused) {
|
||||
return (void*)cmpxchg((jlong)xchg_val, (volatile jlong*)dest, (jlong)cmp_val, unused);
|
||||
}
|
||||
|
||||
intptr_t Atomic::cmpxchg_ptr(intptr_t xchg_val, volatile intptr_t* dest, intptr_t cmp_val, cmpxchg_memory_order unused) {
|
||||
return (intptr_t)cmpxchg((jlong)xchg_val, (volatile jlong*)dest, (jlong)cmp_val, unused);
|
||||
return old;
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||
|
@ -121,9 +121,18 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* des
|
||||
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||
}
|
||||
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
jint rv;
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
T rv;
|
||||
__asm__ volatile(
|
||||
" cas [%2], %3, %0"
|
||||
: "=r" (rv)
|
||||
@ -132,8 +141,14 @@ inline jint Atomic::cmpxchg (jint exchange_value, volatile jint*
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
jlong rv;
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
T rv;
|
||||
__asm__ volatile(
|
||||
" casx [%2], %3, %0"
|
||||
: "=r" (rv)
|
||||
@ -142,18 +157,4 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
intptr_t rv;
|
||||
__asm__ volatile(
|
||||
" casx [%2], %3, %0"
|
||||
: "=r" (rv)
|
||||
: "0" (exchange_value), "r" (dest), "r" (compare_value)
|
||||
: "memory");
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value, order);
|
||||
}
|
||||
|
||||
#endif // OS_CPU_LINUX_SPARC_VM_ATOMIC_LINUX_SPARC_INLINE_HPP
|
||||
|
@ -25,8 +25,6 @@
|
||||
#ifndef OS_CPU_LINUX_X86_VM_ATOMIC_LINUX_X86_HPP
|
||||
#define OS_CPU_LINUX_X86_VM_ATOMIC_LINUX_X86_HPP
|
||||
|
||||
#include "runtime/os.hpp"
|
||||
|
||||
// Implementation of class atomic
|
||||
|
||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
||||
@ -81,8 +79,13 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* des
|
||||
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||
}
|
||||
|
||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
||||
inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<1>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(1 == sizeof(T));
|
||||
__asm__ volatile ("lock cmpxchgb %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -90,7 +93,13 @@ inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte*
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
__asm__ volatile ("lock cmpxchgl %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -137,7 +146,13 @@ inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* des
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order /* order */) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
__asm__ __volatile__ ("lock cmpxchgq %1,(%3)"
|
||||
: "=a" (exchange_value)
|
||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||
@ -145,14 +160,6 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||
return exchange_value;
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value, order);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||
|
||||
#else // !AMD64
|
||||
@ -184,16 +191,14 @@ extern "C" {
|
||||
void _Atomic_move_long(const volatile jlong* src, volatile jlong* dst);
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
return _Atomic_cmpxchg_long(exchange_value, dest, compare_value);
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
return cmpxchg_using_helper<jlong>(_Atomic_cmpxchg_long, exchange_value, dest, compare_value);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) {
|
||||
|
@ -57,9 +57,9 @@ static inline int __m68k_cmpxchg(int oldval, int newval, volatile int *ptr) {
|
||||
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||
`*PTR' before the operation.*/
|
||||
static inline int m68k_compare_and_swap(volatile int *ptr,
|
||||
int oldval,
|
||||
int newval) {
|
||||
static inline int m68k_compare_and_swap(int newval,
|
||||
volatile int *ptr,
|
||||
int oldval) {
|
||||
for (;;) {
|
||||
int prev = *ptr;
|
||||
if (prev != oldval)
|
||||
@ -118,9 +118,9 @@ typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
|
||||
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||
`*PTR' before the operation.*/
|
||||
static inline int arm_compare_and_swap(volatile int *ptr,
|
||||
int oldval,
|
||||
int newval) {
|
||||
static inline int arm_compare_and_swap(int newval,
|
||||
volatile int *ptr,
|
||||
int oldval) {
|
||||
for (;;) {
|
||||
int prev = *ptr;
|
||||
if (prev != oldval)
|
||||
@ -261,55 +261,38 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
|
||||
(volatile intptr_t*) dest);
|
||||
}
|
||||
|
||||
inline jint Atomic::cmpxchg(jint exchange_value,
|
||||
volatile jint* dest,
|
||||
jint compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
#ifdef ARM
|
||||
return arm_compare_and_swap(dest, compare_value, exchange_value);
|
||||
return cmpxchg_using_helper<int>(arm_compare_and_swap, exchange_value, dest, compare_value);
|
||||
#else
|
||||
#ifdef M68K
|
||||
return m68k_compare_and_swap(dest, compare_value, exchange_value);
|
||||
return cmpxchg_using_helper<int>(m68k_compare_and_swap, exchange_value, dest, compare_value);
|
||||
#else
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg(jlong exchange_value,
|
||||
volatile jlong* dest,
|
||||
jlong compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value,
|
||||
volatile intptr_t* dest,
|
||||
intptr_t compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
#ifdef ARM
|
||||
return arm_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#else
|
||||
#ifdef M68K
|
||||
return m68k_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#else
|
||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||
#endif // M68K
|
||||
#endif // ARM
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value,
|
||||
volatile void* dest,
|
||||
void* compare_value,
|
||||
cmpxchg_memory_order order) {
|
||||
|
||||
return (void *) cmpxchg_ptr((intptr_t) exchange_value,
|
||||
(volatile intptr_t*) dest,
|
||||
(intptr_t) compare_value,
|
||||
order);
|
||||
}
|
||||
|
||||
inline jlong Atomic::load(const volatile jlong* src) {
|
||||
volatile jlong dest;
|
||||
os::atomic_copy64(src, &dest);
|
||||
|
@ -25,8 +25,6 @@
|
||||
#ifndef OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
||||
#define OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
||||
|
||||
#include "runtime/os.hpp"
|
||||
|
||||
// Implementation of class atomic
|
||||
|
||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
||||
@ -64,10 +62,6 @@ inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||
extern "C" jint _Atomic_swap32(jint exchange_value, volatile jint* dest);
|
||||
extern "C" intptr_t _Atomic_swap64(intptr_t exchange_value, volatile intptr_t* dest);
|
||||
|
||||
extern "C" jint _Atomic_cas32(jint exchange_value, volatile jint* dest, jint compare_value);
|
||||
extern "C" intptr_t _Atomic_cas64(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value);
|
||||
extern "C" jlong _Atomic_casl (jlong exchange_value, volatile jlong* dest, jlong compare_value);
|
||||
|
||||
extern "C" jint _Atomic_add32(jint inc, volatile jint* dest);
|
||||
extern "C" intptr_t _Atomic_add64(intptr_t add_value, volatile intptr_t* dest);
|
||||
|
||||
@ -97,22 +91,40 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* des
|
||||
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||
}
|
||||
|
||||
// No direct support for cmpxchg of bytes; emulate using int.
|
||||
template<>
|
||||
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||
|
||||
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
|
||||
return _Atomic_cas32(exchange_value, dest, compare_value);
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(4 == sizeof(T));
|
||||
T rv;
|
||||
__asm__ volatile(
|
||||
" cas [%2], %3, %0"
|
||||
: "=r" (rv)
|
||||
: "0" (exchange_value), "r" (dest), "r" (compare_value)
|
||||
: "memory");
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
||||
// Return 64 bit value in %o0
|
||||
return _Atomic_cas64((intptr_t)exchange_value, (intptr_t *)dest, (intptr_t)compare_value);
|
||||
}
|
||||
|
||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
||||
return _Atomic_cas64(exchange_value, dest, compare_value);
|
||||
}
|
||||
|
||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
||||
return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value, order);
|
||||
template<>
|
||||
template<typename T>
|
||||
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||
T volatile* dest,
|
||||
T compare_value,
|
||||
cmpxchg_memory_order order) const {
|
||||
STATIC_ASSERT(8 == sizeof(T));
|
||||
T rv;
|
||||
__asm__ volatile(
|
||||
" casx [%2], %3, %0"
|
||||
: "=r" (rv)
|
||||
: "0" (exchange_value), "r" (dest), "r" (compare_value)
|
||||
: "memory");
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif // OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user