mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
Merge
This commit is contained in:
commit
c76b282388
133 changed files with 3255 additions and 1442 deletions
|
@ -3806,9 +3806,18 @@ void Compile::reshape_address(AddPNode* addp) {
|
||||||
// Any use that can't embed the address computation?
|
// Any use that can't embed the address computation?
|
||||||
for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) {
|
for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) {
|
||||||
Node* u = addp->fast_out(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;
|
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* off = addp->in(AddPNode::Offset);
|
||||||
|
|
|
@ -272,8 +272,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||||
// load pointer for resolved_references[] objArray
|
// load pointer for resolved_references[] objArray
|
||||||
ldr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
ldr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||||
ldr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
ldr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||||
// JNIHandles::resolve(obj);
|
resolve_oop_handle(result);
|
||||||
ldr(result, Address(result, 0));
|
|
||||||
// Add in the index
|
// Add in the index
|
||||||
add(result, result, tmp);
|
add(result, result, tmp);
|
||||||
load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
|
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) {
|
void MacroAssembler::load_mirror(Register dst, Register method) {
|
||||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||||
ldr(dst, Address(rmethod, Method::const_offset()));
|
ldr(dst, Address(rmethod, Method::const_offset()));
|
||||||
|
|
|
@ -790,6 +790,7 @@ public:
|
||||||
void store_klass(Register dst, Register src);
|
void store_klass(Register dst, Register src);
|
||||||
void cmp_klass(Register oop, Register trial_klass, Register tmp);
|
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_mirror(Register dst, Register method);
|
||||||
|
|
||||||
void load_heap_oop(Register dst, Address src);
|
void load_heap_oop(Register dst, Address src);
|
||||||
|
|
|
@ -56,6 +56,17 @@ public:
|
||||||
static void assert_is_initialized() {
|
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 {
|
enum Family {
|
||||||
CPU_ARM = 'A',
|
CPU_ARM = 'A',
|
||||||
CPU_BROADCOM = 'B',
|
CPU_BROADCOM = 'B',
|
||||||
|
|
|
@ -300,8 +300,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||||
// load pointer for resolved_references[] objArray
|
// load pointer for resolved_references[] objArray
|
||||||
ldr(cache, Address(result, ConstantPool::cache_offset_in_bytes()));
|
ldr(cache, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||||
ldr(cache, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
ldr(cache, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||||
// JNIHandles::resolve(result)
|
resolve_oop_handle(cache);
|
||||||
ldr(cache, Address(cache, 0));
|
|
||||||
// Add in the index
|
// Add in the index
|
||||||
// convert from field index to resolved_references() index and from
|
// 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
|
// 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();
|
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) {
|
void MacroAssembler::load_mirror(Register mirror, Register method, Register tmp) {
|
||||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
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));
|
ldr(mirror, Address(tmp, mirror_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Compressed pointers
|
// Compressed pointers
|
||||||
|
|
|
@ -687,6 +687,7 @@ public:
|
||||||
AbstractAssembler::emit_address((address)L.data());
|
AbstractAssembler::emit_address((address)L.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resolve_oop_handle(Register result);
|
||||||
void load_mirror(Register mirror, Register method, Register tmp);
|
void load_mirror(Register mirror, Register method, Register tmp);
|
||||||
|
|
||||||
// Porting layer between 32-bit ARM and AArch64
|
// 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.
|
// Load pointer for resolved_references[] objArray.
|
||||||
ld(result, ConstantPool::cache_offset_in_bytes(), result);
|
ld(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||||
ld(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
ld(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||||
// JNIHandles::resolve(result)
|
resolve_oop_handle(result);
|
||||||
ld(result, 0, result);
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
Label index_ok;
|
Label index_ok;
|
||||||
lwa(R0, arrayOopDesc::length_offset_in_bytes(), result);
|
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) {
|
void MacroAssembler::load_mirror_from_const_method(Register mirror, Register const_method) {
|
||||||
ld(mirror, in_bytes(ConstMethod::constants_offset()), const_method);
|
ld(mirror, in_bytes(ConstMethod::constants_offset()), const_method);
|
||||||
ld(mirror, ConstantPool::pool_holder_offset_in_bytes(), mirror);
|
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(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 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);
|
void load_mirror_from_const_method(Register mirror, Register const_method);
|
||||||
|
|
||||||
static int instr_size_for_decode_klass_not_null();
|
static int instr_size_for_decode_klass_not_null();
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
// if too small.
|
// if too small.
|
||||||
// Run with +PrintInterpreter to get the VM to print out the size.
|
// Run with +PrintInterpreter to get the VM to print out the size.
|
||||||
// Max size with JVMTI
|
// Max size with JVMTI
|
||||||
int TemplateInterpreter::InterpreterCodeSize = 230*K;
|
int TemplateInterpreter::InterpreterCodeSize = 256*K;
|
||||||
|
|
||||||
#ifdef PRODUCT
|
#ifdef PRODUCT
|
||||||
#define BLOCK_COMMENT(str) /* nothing */
|
#define BLOCK_COMMENT(str) /* nothing */
|
||||||
|
|
|
@ -364,8 +364,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result
|
||||||
// Load pointer for resolved_references[] objArray.
|
// Load pointer for resolved_references[] objArray.
|
||||||
z_lg(result, ConstantPool::cache_offset_in_bytes(), result);
|
z_lg(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||||
z_lg(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
z_lg(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||||
// JNIHandles::resolve(result)
|
resolve_oop_handle(result); // Load resolved references array itself.
|
||||||
z_lg(result, 0, result); // Load resolved references array itself.
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
NearLabel index_ok;
|
NearLabel index_ok;
|
||||||
z_lgf(Z_R0, Address(result, arrayOopDesc::length_offset_in_bytes()));
|
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) {
|
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||||
mem2reg_opt(mirror, Address(method, Method::const_offset()));
|
mem2reg_opt(mirror, Address(method, Method::const_offset()));
|
||||||
mem2reg_opt(mirror, Address(mirror, ConstMethod::constants_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,
|
void oop_decoder(Register Rdst, Register Rsrc, bool maybeNULL,
|
||||||
Register Rbase = Z_R1, int pow2_offset = -1);
|
Register Rbase = Z_R1, int pow2_offset = -1);
|
||||||
|
|
||||||
|
void resolve_oop_handle(Register result);
|
||||||
void load_mirror(Register mirror, Register method);
|
void load_mirror(Register mirror, Register method);
|
||||||
|
|
||||||
//--------------------------
|
//--------------------------
|
||||||
|
|
|
@ -730,8 +730,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||||
// load pointer for resolved_references[] objArray
|
// load pointer for resolved_references[] objArray
|
||||||
ld_ptr(result, ConstantPool::cache_offset_in_bytes(), result);
|
ld_ptr(result, ConstantPool::cache_offset_in_bytes(), result);
|
||||||
ld_ptr(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
ld_ptr(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
|
||||||
// JNIHandles::resolve(result)
|
resolve_oop_handle(result);
|
||||||
ld_ptr(result, 0, result);
|
|
||||||
// Add in the index
|
// Add in the index
|
||||||
add(result, tmp, result);
|
add(result, tmp, result);
|
||||||
load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), 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);
|
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) {
|
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||||
ld_ptr(method, in_bytes(Method::const_offset()), mirror);
|
ld_ptr(method, in_bytes(Method::const_offset()), mirror);
|
||||||
|
|
|
@ -995,6 +995,7 @@ public:
|
||||||
inline void ldbool(const Address& a, Register d);
|
inline void ldbool(const Address& a, Register d);
|
||||||
inline void movbool( bool boolconst, Register d);
|
inline void movbool( bool boolconst, Register d);
|
||||||
|
|
||||||
|
void resolve_oop_handle(Register result);
|
||||||
void load_mirror(Register mirror, Register method);
|
void load_mirror(Register mirror, Register method);
|
||||||
|
|
||||||
// klass oop manipulations if compressed
|
// klass oop manipulations if compressed
|
||||||
|
|
|
@ -511,8 +511,7 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||||
// load pointer for resolved_references[] objArray
|
// load pointer for resolved_references[] objArray
|
||||||
movptr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
movptr(result, Address(result, ConstantPool::cache_offset_in_bytes()));
|
||||||
movptr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
movptr(result, Address(result, ConstantPoolCache::resolved_references_offset_in_bytes()));
|
||||||
// JNIHandles::resolve(obj);
|
resolve_oop_handle(result);
|
||||||
movptr(result, Address(result, 0));
|
|
||||||
// Add in the index
|
// Add in the index
|
||||||
addptr(result, tmp);
|
addptr(result, tmp);
|
||||||
load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
|
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
|
#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) {
|
void MacroAssembler::load_mirror(Register mirror, Register method) {
|
||||||
// get mirror
|
// get mirror
|
||||||
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
const int mirror_offset = in_bytes(Klass::java_mirror_offset());
|
||||||
|
@ -7030,7 +7036,6 @@ void MacroAssembler::reinit_heapbase() {
|
||||||
|
|
||||||
#endif // _LP64
|
#endif // _LP64
|
||||||
|
|
||||||
|
|
||||||
// C2 compiled method's prolog code.
|
// C2 compiled method's prolog code.
|
||||||
void MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b) {
|
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 movbool(Address dst, Register src);
|
||||||
void testbool(Register dst);
|
void testbool(Register dst);
|
||||||
|
|
||||||
|
void resolve_oop_handle(Register result);
|
||||||
void load_mirror(Register mirror, Register method);
|
void load_mirror(Register mirror, Register method);
|
||||||
|
|
||||||
// oop manipulations
|
// oop manipulations
|
||||||
|
|
|
@ -158,7 +158,7 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw GraalError.shouldNotReachHere();
|
throw GraalError.shouldNotReachHere(input.getPlatformKind().toString());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -451,7 +451,7 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
|
||||||
protected Value emitZeroExtendMemory(AMD64Kind memoryKind, int resultBits, AMD64AddressValue address, LIRFrameState state) {
|
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
|
// Issue a zero extending load of the proper bit size and set the result to
|
||||||
// the proper kind.
|
// 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) {
|
switch (memoryKind) {
|
||||||
case BYTE:
|
case BYTE:
|
||||||
getLIRGen().append(new AMD64Unary.MemoryOp(MOVZXB, DWORD, result, address, state));
|
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;
|
import org.graalvm.compiler.debug.GraalError;
|
||||||
|
|
||||||
public enum FloatConvert {
|
public enum FloatConvert {
|
||||||
F2I,
|
F2I(FloatConvertCategory.FloatingPointToInteger),
|
||||||
D2I,
|
D2I(FloatConvertCategory.FloatingPointToInteger),
|
||||||
F2L,
|
F2L(FloatConvertCategory.FloatingPointToInteger),
|
||||||
D2L,
|
D2L(FloatConvertCategory.FloatingPointToInteger),
|
||||||
I2F,
|
I2F(FloatConvertCategory.IntegerToFloatingPoint),
|
||||||
L2F,
|
L2F(FloatConvertCategory.IntegerToFloatingPoint),
|
||||||
D2F,
|
D2F(FloatConvertCategory.FloatingPointToFloatingPoint),
|
||||||
I2D,
|
I2D(FloatConvertCategory.IntegerToFloatingPoint),
|
||||||
L2D,
|
L2D(FloatConvertCategory.IntegerToFloatingPoint),
|
||||||
F2D;
|
F2D(FloatConvertCategory.FloatingPointToFloatingPoint);
|
||||||
|
|
||||||
|
private FloatConvertCategory category;
|
||||||
|
|
||||||
|
FloatConvert(FloatConvertCategory category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FloatConvertCategory getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
public FloatConvert reverse() {
|
public FloatConvert reverse() {
|
||||||
switch (this) {
|
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.And;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div;
|
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.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.Or;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem;
|
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.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.BinaryOp.Xor;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow;
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend;
|
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<Sub> sub;
|
||||||
|
|
||||||
private final BinaryOp<Mul> mul;
|
private final BinaryOp<Mul> mul;
|
||||||
|
private final BinaryOp<MulHigh> mulHigh;
|
||||||
|
private final BinaryOp<UMulHigh> umulHigh;
|
||||||
private final BinaryOp<Div> div;
|
private final BinaryOp<Div> div;
|
||||||
private final BinaryOp<Rem> rem;
|
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 {
|
public interface ArithmeticOpWrapper {
|
||||||
|
|
||||||
|
@ -121,6 +125,8 @@ public final class ArithmeticOpTable {
|
||||||
BinaryOp<Sub> sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub());
|
BinaryOp<Sub> sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub());
|
||||||
|
|
||||||
BinaryOp<Mul> mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul());
|
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<Div> div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv());
|
||||||
BinaryOp<Rem> rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem());
|
BinaryOp<Rem> rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem());
|
||||||
|
|
||||||
|
@ -141,16 +147,18 @@ public final class ArithmeticOpTable {
|
||||||
IntegerConvertOp<Narrow> narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow());
|
IntegerConvertOp<Narrow> narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow());
|
||||||
|
|
||||||
FloatConvertOp[] floatConvert = CollectionsUtil.filterAndMapToArray(inner.floatConvert, Objects::nonNull, wrapper::wrapFloatConvertOp, FloatConvertOp[]::new);
|
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,
|
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,
|
||||||
BinaryOp<Xor> xor, ShiftOp<Shl> shl, ShiftOp<Shr> shr, ShiftOp<UShr> ushr, UnaryOp<Abs> abs, UnaryOp<Sqrt> sqrt, IntegerConvertOp<ZeroExtend> zeroExtend,
|
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<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) {
|
IntegerConvertOp<ZeroExtend> zeroExtend, IntegerConvertOp<SignExtend> signExtend, IntegerConvertOp<Narrow> narrow, FloatConvertOp... floatConvert) {
|
||||||
this.neg = neg;
|
this.neg = neg;
|
||||||
this.add = add;
|
this.add = add;
|
||||||
this.sub = sub;
|
this.sub = sub;
|
||||||
this.mul = mul;
|
this.mul = mul;
|
||||||
|
this.mulHigh = mulHigh;
|
||||||
|
this.umulHigh = umulHigh;
|
||||||
this.div = div;
|
this.div = div;
|
||||||
this.rem = rem;
|
this.rem = rem;
|
||||||
this.not = not;
|
this.not = not;
|
||||||
|
@ -206,6 +214,20 @@ public final class ArithmeticOpTable {
|
||||||
return mul;
|
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.
|
* Describes the division operation.
|
||||||
*/
|
*/
|
||||||
|
@ -321,6 +343,8 @@ public final class ArithmeticOpTable {
|
||||||
Objects.equals(add, that.add) &&
|
Objects.equals(add, that.add) &&
|
||||||
Objects.equals(sub, that.sub) &&
|
Objects.equals(sub, that.sub) &&
|
||||||
Objects.equals(mul, that.mul) &&
|
Objects.equals(mul, that.mul) &&
|
||||||
|
Objects.equals(mulHigh, that.mulHigh) &&
|
||||||
|
Objects.equals(umulHigh, that.umulHigh) &&
|
||||||
Objects.equals(div, that.div) &&
|
Objects.equals(div, that.div) &&
|
||||||
Objects.equals(rem, that.rem) &&
|
Objects.equals(rem, that.rem) &&
|
||||||
Objects.equals(not, that.not) &&
|
Objects.equals(not, that.not) &&
|
||||||
|
@ -360,8 +384,8 @@ public final class ArithmeticOpTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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[" +
|
return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) +
|
||||||
toString(floatConvert) + "]]";
|
",floatConvert[" + toString(floatConvert) + "]]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class Op {
|
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> {
|
public abstract static class Div extends BinaryOp<Div> {
|
||||||
|
|
||||||
protected Div(boolean associative, boolean commutative) {
|
protected Div(boolean associative, boolean commutative) {
|
||||||
|
|
|
@ -302,7 +302,7 @@ public class FloatStamp extends PrimitiveStamp {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ArithmeticOpTable OPS = new ArithmeticOpTable(
|
public static final ArithmeticOpTable OPS = new ArithmeticOpTable(
|
||||||
|
|
||||||
new UnaryOp.Neg() {
|
new UnaryOp.Neg() {
|
||||||
|
|
||||||
|
@ -437,6 +437,10 @@ public class FloatStamp extends PrimitiveStamp {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
null,
|
||||||
|
|
||||||
|
null,
|
||||||
|
|
||||||
new BinaryOp.Div(false, false) {
|
new BinaryOp.Div(false, false) {
|
||||||
|
|
||||||
@Override
|
@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) {
|
new BinaryOp.Div(true, false) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1046,10 +1204,14 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||||
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||||
IntegerStamp value = (IntegerStamp) stamp;
|
IntegerStamp value = (IntegerStamp) stamp;
|
||||||
int bits = value.getBits();
|
int bits = value.getBits();
|
||||||
long defaultMask = CodeUtil.mask(bits);
|
if (value.isEmpty()) {
|
||||||
if (value.upMask() == 0) {
|
return value;
|
||||||
|
} else if (shift.isEmpty()) {
|
||||||
|
return StampFactory.forInteger(bits).empty();
|
||||||
|
} else if (value.upMask() == 0) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shiftMask = getShiftAmountMask(stamp);
|
int shiftMask = getShiftAmountMask(stamp);
|
||||||
int shiftBits = Integer.bitCount(shiftMask);
|
int shiftBits = Integer.bitCount(shiftMask);
|
||||||
if (shift.lowerBound() == shift.upperBound()) {
|
if (shift.lowerBound() == shift.upperBound()) {
|
||||||
|
@ -1068,6 +1230,7 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((shift.lowerBound() >>> shiftBits) == (shift.upperBound() >>> shiftBits)) {
|
if ((shift.lowerBound() >>> shiftBits) == (shift.upperBound() >>> shiftBits)) {
|
||||||
|
long defaultMask = CodeUtil.mask(bits);
|
||||||
long downMask = defaultMask;
|
long downMask = defaultMask;
|
||||||
long upMask = 0;
|
long upMask = 0;
|
||||||
for (long i = shift.lowerBound(); i <= shift.upperBound(); i++) {
|
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) {
|
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||||
IntegerStamp value = (IntegerStamp) stamp;
|
IntegerStamp value = (IntegerStamp) stamp;
|
||||||
int bits = value.getBits();
|
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);
|
long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp);
|
||||||
if (shiftCount == 0) {
|
if (shiftCount == 0) {
|
||||||
return stamp;
|
return stamp;
|
||||||
|
@ -1153,6 +1320,12 @@ public final class IntegerStamp extends PrimitiveStamp {
|
||||||
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
public Stamp foldStamp(Stamp stamp, IntegerStamp shift) {
|
||||||
IntegerStamp value = (IntegerStamp) stamp;
|
IntegerStamp value = (IntegerStamp) stamp;
|
||||||
int bits = value.getBits();
|
int bits = value.getBits();
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
return value;
|
||||||
|
} else if (shift.isEmpty()) {
|
||||||
|
return StampFactory.forInteger(bits).empty();
|
||||||
|
}
|
||||||
|
|
||||||
if (shift.lowerBound() == shift.upperBound()) {
|
if (shift.lowerBound() == shift.upperBound()) {
|
||||||
long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp);
|
long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp);
|
||||||
if (shiftCount == 0) {
|
if (shiftCount == 0) {
|
||||||
|
|
|
@ -46,6 +46,16 @@ public class UnsafeReadEliminationTest extends GraalCompilerTest {
|
||||||
public static double SideEffectD;
|
public static double SideEffectD;
|
||||||
public static double SideEffectL;
|
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) {
|
public static long test1Snippet(double a) {
|
||||||
final Object m = Memory;
|
final Object m = Memory;
|
||||||
if (a > 0) {
|
if (a > 0) {
|
||||||
|
@ -130,4 +140,76 @@ public class UnsafeReadEliminationTest extends GraalCompilerTest {
|
||||||
Assert.assertEquals(writes, graph.getNodes().filter(WriteNode.class).count());
|
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 fieldOffset1;
|
||||||
private static final long fieldOffset2;
|
private static final long fieldOffset2;
|
||||||
|
|
||||||
private static final long byteArrayBaseOffset;
|
|
||||||
private static final long intArrayBaseOffset;
|
|
||||||
private static final long longArrayBaseOffset;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x"));
|
long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x"));
|
||||||
|
@ -55,9 +51,6 @@ public class UnsafeEATest extends EATestBase {
|
||||||
fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z"));
|
fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z"));
|
||||||
}
|
}
|
||||||
assert fieldOffset2 == fieldOffset1 + 4;
|
assert fieldOffset2 == fieldOffset1 + 4;
|
||||||
byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
|
|
||||||
intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class);
|
|
||||||
longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -203,76 +196,4 @@ public class UnsafeEATest extends EATestBase {
|
||||||
return x;
|
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) {
|
public static CompilationPrinter begin(OptionValues options, CompilationIdentifier id, JavaMethod method, int entryBCI) {
|
||||||
if (PrintCompilation.getValue(options) && !TTY.isSuppressed()) {
|
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 new CompilationPrinter(id, method, entryBCI);
|
||||||
}
|
}
|
||||||
return DISABLED;
|
return DISABLED;
|
||||||
|
|
|
@ -82,26 +82,6 @@ public abstract class CompilationWrapper<T> {
|
||||||
*/
|
*/
|
||||||
ExitVM;
|
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
|
* Gets the action that is one level less verbose than this action, bottoming out at the
|
||||||
* least verbose action.
|
* least verbose action.
|
||||||
|
|
|
@ -36,13 +36,15 @@ public class GraalCompilerOptions {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
@Option(help = "Print an informational line to the console for each completed compilation.", type = OptionType.Debug)
|
@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);
|
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. " +
|
@Option(help = "Pattern for method(s) that will trigger an exception when compiled. " +
|
||||||
"This option exists to test handling compilation crashes gracefully.", type = OptionType.Debug)
|
"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);
|
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)
|
@Option(help = "file:doc-files/CompilationBailoutActionHelp.txt", type = OptionType.User)
|
||||||
public static final EnumOptionKey<ExceptionAction> CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose, ExceptionAction.HELP);
|
public static final EnumOptionKey<ExceptionAction> CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent);
|
||||||
@Option(help = "The action to take when compilation fails with a bailout exception.", type = OptionType.User)
|
@Option(help = "Specifies the action to take when compilation fails with a bailout exception. " +
|
||||||
public static final EnumOptionKey<ExceptionAction> CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent, ExceptionAction.HELP);
|
"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 " +
|
@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)
|
"by CompilationFailureAction or CompilationBailoutAction before changing to a less verbose action.", type = OptionType.User)
|
||||||
public static final OptionKey<Integer> MaxCompilationProblemsPerAction = new OptionKey<>(5);
|
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) {
|
private Immutable(OptionValues options) {
|
||||||
this.options = 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.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false);
|
||||||
this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(Time.getValue(options)), true);
|
this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(timeValue), true);
|
||||||
this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(TrackMemUse.getValue(options)), 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) ||
|
this.scopesEnabled = DumpOnError.getValue(options) ||
|
||||||
Dump.getValue(options) != null ||
|
Dump.getValue(options) != null ||
|
||||||
|
|
|
@ -28,8 +28,11 @@ import java.util.regex.Pattern;
|
||||||
import org.graalvm.compiler.debug.DebugContext.Scope;
|
import org.graalvm.compiler.debug.DebugContext.Scope;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the filter specified by the {@link DebugOptions#Dump}, {@link DebugOptions#Log},
|
* Implements the filter specified by options such as {@link DebugOptions#Dump},
|
||||||
* {@link DebugOptions#Count} and {@link DebugOptions#Time} options.
|
* {@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>
|
* <p>
|
||||||
* These options enable the associated debug facility if their filter matches the
|
* These options enable the associated debug facility if their filter matches the
|
||||||
* {@linkplain Scope#getQualifiedName() name} of the current scope. For 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
|
* {@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.
|
* {@code level = 0} means disabled and a {@code level > 0} means enabled.
|
||||||
* <p>
|
* <p>
|
||||||
* A filter is a list of comma-separated terms of the form {@code <pattern>[:<level>]}. {@code
|
* The syntax for a filter is explained <a href="file:doc-files/DumpHelp.txt">here</a>.
|
||||||
* <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>
|
|
||||||
*/
|
*/
|
||||||
final class DebugFilter {
|
final class DebugFilter {
|
||||||
|
|
||||||
|
@ -148,13 +111,16 @@ final class DebugFilter {
|
||||||
if (terms == null) {
|
if (terms == null) {
|
||||||
return DebugContext.BASIC_LEVEL;
|
return DebugContext.BASIC_LEVEL;
|
||||||
} else {
|
} else {
|
||||||
int level = 0;
|
int defaultLevel = 0;
|
||||||
|
int level = -1;
|
||||||
for (Term t : terms) {
|
for (Term t : terms) {
|
||||||
if (t.matches(input)) {
|
if (t.isMatchAny()) {
|
||||||
|
defaultLevel = t.level;
|
||||||
|
} else if (t.matches(input)) {
|
||||||
level = t.level;
|
level = t.level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return level;
|
return level == -1 ? defaultLevel : level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +142,7 @@ final class DebugFilter {
|
||||||
|
|
||||||
Term(String filter, int level) {
|
Term(String filter, int level) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
if (filter.isEmpty()) {
|
if (filter.isEmpty() || filter.equals("*")) {
|
||||||
this.pattern = null;
|
this.pattern = null;
|
||||||
} else if (filter.contains("*") || filter.contains("?")) {
|
} else if (filter.contains("*") || filter.contains("?")) {
|
||||||
this.pattern = Pattern.compile(MethodFilter.createGlobString(filter));
|
this.pattern = Pattern.compile(MethodFilter.createGlobString(filter));
|
||||||
|
@ -192,6 +158,10 @@ final class DebugFilter {
|
||||||
return pattern == null || pattern.matcher(input).matches();
|
return pattern == null || pattern.matcher(input).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMatchAny() {
|
||||||
|
return pattern == null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return (pattern == null ? ".*" : pattern.toString()) + ":" + level;
|
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)
|
"An empty value enables all memory usage trackers unconditionally.", type = OptionType.Debug)
|
||||||
public static final OptionKey<String> MemUseTrackers = new OptionKey<>(null);
|
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)
|
"An empty value enables all counters unconditionally.", type = OptionType.Debug)
|
||||||
public static final OptionKey<String> Count = new OptionKey<>(null);
|
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)
|
"An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug)
|
||||||
public static final OptionKey<String> TrackMemUse = new OptionKey<>(null);
|
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)
|
"An empty value enables all timers unconditionally.", type = OptionType.Debug)
|
||||||
public static final OptionKey<String> Time = new OptionKey<>(null);
|
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);
|
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);
|
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);
|
public static final OptionKey<String> Log = new OptionKey<>(null);
|
||||||
|
@Option(help = "file:doc-files/MethodFilterHelp.txt")
|
||||||
@Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug)
|
|
||||||
public static final OptionKey<String> MethodFilter = new OptionKey<>(null);
|
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)
|
@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);
|
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)
|
"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);
|
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 " +
|
"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)
|
"will not include metrics for compiler code that is not executed.", type = OptionType.Debug)
|
||||||
public static final OptionKey<Boolean> ListMetrics = new OptionKey<>(false);
|
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 " +
|
@Option(help = "file:doc-files/MetricsFileHelp.txt", type = OptionType.Debug)
|
||||||
"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)
|
|
||||||
public static final OptionKey<String> MetricsFile = new OptionKey<>(null);
|
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 " +
|
@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)
|
"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)
|
@Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug)
|
||||||
public static final OptionKey<Boolean> PrintCanonicalGraphStrings = new OptionKey<>(false);
|
public static final OptionKey<Boolean> PrintCanonicalGraphStrings = new OptionKey<>(false);
|
||||||
@Option(help = "Choose format used when dumping canonical text for graphs: " +
|
@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)
|
"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);
|
public static final OptionKey<Integer> PrintCanonicalGraphStringFlavor = new OptionKey<>(0);
|
||||||
@Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug)
|
@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
|
* 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:
|
* parameters. The syntax for a filter is explained <a href="MethodFilterHelp.txt">here</a>.
|
||||||
*
|
|
||||||
* <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>
|
|
||||||
*/
|
*/
|
||||||
public class MethodFilter {
|
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() {
|
public boolean isLeafNode() {
|
||||||
return 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;
|
||||||
import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
|
import org.graalvm.compiler.test.SubprocessUtil.Subprocess;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests support for dumping graphs and other info useful for debugging a compiler crash.
|
* Tests support for dumping graphs and other info useful for debugging a compiler crash.
|
||||||
*/
|
*/
|
||||||
public class CompilationWrapperTest extends GraalCompilerTest {
|
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.
|
* Tests compilation requested by the VM.
|
||||||
|
|
|
@ -25,6 +25,7 @@ package org.graalvm.compiler.hotspot.test;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
import org.graalvm.compiler.core.test.GraalCompilerTest;
|
||||||
|
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,4 +85,20 @@ public class ObjectCloneTest extends GraalCompilerTest {
|
||||||
}
|
}
|
||||||
test("cloneList", list);
|
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
|
* 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
|
* 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
|
* {@code Options.CompilerConfiguration.getValue()} is non-null then select the factory whose
|
||||||
* whose name matches the value else select the factory with the highest
|
* name matches the value else select the factory with the highest
|
||||||
* {@link #autoSelectionPriority} value.
|
* {@link #autoSelectionPriority} value.
|
||||||
*
|
*
|
||||||
* @param name the name of the compiler configuration to select (optional)
|
* @param name the name of the compiler configuration to select (optional)
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
package org.graalvm.compiler.hotspot;
|
package org.graalvm.compiler.hotspot;
|
||||||
|
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mechanism for checking that the current Java runtime environment supports the minimum JVMCI API
|
* 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_MAJOR_VERSION = 0;
|
||||||
private static final int JVMCI8_MIN_MINOR_VERSION = 29;
|
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) {
|
private static void failVersionCheck(boolean exit, String reason, Object... args) {
|
||||||
Formatter errorMessage = new Formatter().format(reason, args);
|
Formatter errorMessage = new Formatter().format(reason, args);
|
||||||
String javaHome = System.getProperty("java.home");
|
String javaHome = System.getProperty("java.home");
|
||||||
|
@ -55,7 +50,7 @@ class JVMCIVersionCheck {
|
||||||
if (System.getProperty("java.specification.version").compareTo("1.9") < 0) {
|
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");
|
errorMessage.format("Download the latest JVMCI JDK 8 from http://www.oracle.com/technetwork/oracle-labs/program-languages/downloads/index.html");
|
||||||
} else {
|
} 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");
|
String value = System.getenv("JVMCI_VERSION_CHECK");
|
||||||
if ("warn".equals(value)) {
|
if ("warn".equals(value)) {
|
||||||
|
@ -119,34 +114,11 @@ class JVMCIVersionCheck {
|
||||||
// Allow local builds
|
// Allow local builds
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// http://openjdk.java.net/jeps/223
|
if (vmVersion.startsWith("9-ea")) {
|
||||||
if (vmVersion.startsWith("9+")) {
|
failVersionCheck(exitOnFailure, "This version of Graal is not compatible with JDK 9 Early Access builds.%n");
|
||||||
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);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Graal will be compatible with all JDK versions as of 9 GA
|
// Graal is compatible with all JDK versions as of 9 GA.
|
||||||
// until a JVMCI API change is made in a 9u or later release.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.graalvm.compiler.debug.CSVUtil;
|
||||||
import org.graalvm.compiler.debug.GraalError;
|
import org.graalvm.compiler.debug.GraalError;
|
||||||
import org.graalvm.compiler.debug.TTY;
|
import org.graalvm.compiler.debug.TTY;
|
||||||
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
|
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
|
||||||
import org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions;
|
|
||||||
import org.graalvm.compiler.nodes.debug.DynamicCounterNode;
|
import org.graalvm.compiler.nodes.debug.DynamicCounterNode;
|
||||||
import org.graalvm.compiler.options.Option;
|
import org.graalvm.compiler.options.Option;
|
||||||
import org.graalvm.compiler.options.OptionKey;
|
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
|
* Counters will be displayed as a rate (per second) if their group name starts with "~", otherwise
|
||||||
* they will be displayed as a total number.
|
* they will be displayed as a total number.
|
||||||
*
|
*
|
||||||
* <h1>Example</h1> In order to create statistics about allocations within the DaCapo pmd benchmark
|
* See <a href="BenchmarkDynamicCountersHelp.txt">here</a> for a detailed example of how to use
|
||||||
* the following steps are necessary:
|
* benchmark counters.
|
||||||
* <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>
|
|
||||||
*/
|
*/
|
||||||
public class BenchmarkCounters {
|
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)
|
@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);
|
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" +
|
@Option(help = "file:doc-files/BenchmarkDynamicCountersHelp.txt", type = OptionType.Debug)
|
||||||
"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)
|
|
||||||
public static final OptionKey<String> BenchmarkDynamicCounters = new OptionKey<>(null);
|
public static final OptionKey<String> BenchmarkDynamicCounters = new OptionKey<>(null);
|
||||||
@Option(help = "Use grouping separators for number printing", type = OptionType.Debug)
|
@Option(help = "Use grouping separators for number printing", type = OptionType.Debug)
|
||||||
public static final OptionKey<Boolean> DynamicCountersPrintGroupSeparator = new OptionKey<>(true);
|
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.GeneratePIC;
|
||||||
import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
|
import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
|
||||||
import static org.graalvm.compiler.core.common.GraalOptions.VerifyPhases;
|
import static org.graalvm.compiler.core.common.GraalOptions.VerifyPhases;
|
||||||
|
import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
|
||||||
|
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
@ -98,12 +99,14 @@ public class HotSpotSuitesProvider extends SuitesProviderBase {
|
||||||
midTierLowering.add(new ReplaceConstantNodesPhase());
|
midTierLowering.add(new ReplaceConstantNodesPhase());
|
||||||
|
|
||||||
// Replace inlining policy
|
// Replace inlining policy
|
||||||
|
if (Inline.getValue(options)) {
|
||||||
ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(InliningPhase.class);
|
ListIterator<BasePhase<? super HighTierContext>> iter = ret.getHighTier().findPhase(InliningPhase.class);
|
||||||
InliningPhase inlining = (InliningPhase) iter.previous();
|
InliningPhase inlining = (InliningPhase) iter.previous();
|
||||||
CanonicalizerPhase canonicalizer = inlining.getCanonicalizer();
|
CanonicalizerPhase canonicalizer = inlining.getCanonicalizer();
|
||||||
iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer));
|
iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret.getMidTier().appendPhase(new WriteBarrierAdditionPhase(config));
|
ret.getMidTier().appendPhase(new WriteBarrierAdditionPhase(config));
|
||||||
if (VerifyPhases.getValue(options)) {
|
if (VerifyPhases.getValue(options)) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class HotspotSnippetsOptions {
|
||||||
@Option(help = "Enable profiling of allocation sites.", type = OptionType.Debug)
|
@Option(help = "Enable profiling of allocation sites.", type = OptionType.Debug)
|
||||||
public static final OptionKey<Boolean> ProfileAllocations = new OptionKey<>(false);
|
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);
|
public static final EnumOptionKey<ProfileContext> ProfileAllocationsContext = new EnumOptionKey<>(ProfileContext.AllocatingMethod);
|
||||||
|
|
||||||
@Option(help = "Enable profiling of monitor operations.", type = OptionType.Debug)
|
@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.compiler.phases.graph.ReentrantNodeIterator;
|
||||||
import org.graalvm.util.EconomicMap;
|
import org.graalvm.util.EconomicMap;
|
||||||
|
|
||||||
|
import static org.graalvm.compiler.nodes.cfg.ControlFlowGraph.multiplyProbabilities;
|
||||||
|
|
||||||
public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure<Double> {
|
public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure<Double> {
|
||||||
|
|
||||||
private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure();
|
private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure();
|
||||||
|
@ -75,31 +77,17 @@ public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.N
|
||||||
for (double d : exitStates.getValues()) {
|
for (double d : exitStates.getValues()) {
|
||||||
exitProbability += d;
|
exitProbability += d;
|
||||||
}
|
}
|
||||||
exitProbability = Math.min(1D, exitProbability);
|
exitProbability = Math.min(1.0, exitProbability);
|
||||||
if (exitProbability < ControlFlowGraph.MIN_PROBABILITY) {
|
exitProbability = Math.max(ControlFlowGraph.MIN_PROBABILITY, exitProbability);
|
||||||
exitProbability = ControlFlowGraph.MIN_PROBABILITY;
|
double loopFrequency = 1.0 / exitProbability;
|
||||||
}
|
|
||||||
assert exitProbability <= 1D && exitProbability >= 0D;
|
|
||||||
double loopFrequency = 1D / exitProbability;
|
|
||||||
loop.setLoopFrequency(loopFrequency);
|
loop.setLoopFrequency(loopFrequency);
|
||||||
|
|
||||||
double adjustmentFactor = initialState * loopFrequency;
|
double adjustmentFactor = initialState * loopFrequency;
|
||||||
exitStates.replaceAll((exitNode, probability) -> multiplySaturate(probability, adjustmentFactor));
|
exitStates.replaceAll((exitNode, probability) -> multiplyProbabilities(probability, adjustmentFactor));
|
||||||
|
|
||||||
return exitStates;
|
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
|
* 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
|
* 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) {
|
public AArch64ArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, Value result, Value array1, Value array2, Value length) {
|
||||||
super(TYPE);
|
super(TYPE);
|
||||||
|
|
||||||
|
assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported";
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
|
||||||
Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
|
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;
|
||||||
import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
|
import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
|
||||||
import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag;
|
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.asm.amd64.AMD64MacroAssembler;
|
||||||
import org.graalvm.compiler.core.common.LIRKind;
|
import org.graalvm.compiler.core.common.LIRKind;
|
||||||
import org.graalvm.compiler.lir.LIRInstructionClass;
|
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 temp2;
|
||||||
@Temp({REG}) protected Value temp3;
|
@Temp({REG}) protected Value temp3;
|
||||||
@Temp({REG}) protected Value temp4;
|
@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 vectorTemp1;
|
||||||
@Temp({REG, ILLEGAL}) protected Value vectorTemp2;
|
@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.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
|
||||||
this.temp4 = 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.
|
// We only need the vector temporaries if we generate SSE code.
|
||||||
if (supportsSSE41(tool.target())) {
|
if (supportsSSE41(tool.target())) {
|
||||||
this.vectorTemp1 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
|
this.vectorTemp1 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
|
||||||
|
@ -170,10 +185,14 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||||
Label loop = new Label();
|
Label loop = new Label();
|
||||||
Label compareTail = new Label();
|
Label compareTail = new Label();
|
||||||
|
|
||||||
|
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||||
|
Label loopCheck = new Label();
|
||||||
|
Label nanCheck = new Label();
|
||||||
|
|
||||||
// Compare 16-byte vectors
|
// Compare 16-byte vectors
|
||||||
masm.andl(result, SSE4_1_VECTOR_SIZE - 1); // tail count (in bytes)
|
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.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(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||||
masm.leaq(array2, new AMD64Address(array2, 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.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||||
masm.pxor(vector1, vector2);
|
masm.pxor(vector1, vector2);
|
||||||
masm.ptest(vector1, vector1);
|
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.addq(length, SSE4_1_VECTOR_SIZE);
|
||||||
masm.jcc(ConditionFlag.NotZero, loop);
|
masm.jcc(ConditionFlag.NotZero, loop);
|
||||||
|
|
||||||
masm.testl(result, result);
|
masm.testl(result, result);
|
||||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
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
|
* Compare the remaining bytes with an unaligned memory load aligned to the end of the
|
||||||
* array.
|
* 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.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
|
||||||
masm.pxor(vector1, vector2);
|
masm.pxor(vector1, vector2);
|
||||||
masm.ptest(vector1, vector1);
|
masm.ptest(vector1, vector1);
|
||||||
|
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.jcc(ConditionFlag.NotZero, falseLabel);
|
||||||
|
}
|
||||||
masm.jmp(trueLabel);
|
masm.jmp(trueLabel);
|
||||||
|
|
||||||
masm.bind(compareTail);
|
masm.bind(compareTail);
|
||||||
|
@ -233,10 +268,14 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||||
Label loop = new Label();
|
Label loop = new Label();
|
||||||
Label compareTail = new Label();
|
Label compareTail = new Label();
|
||||||
|
|
||||||
|
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||||
|
Label loopCheck = new Label();
|
||||||
|
Label nanCheck = new Label();
|
||||||
|
|
||||||
// Compare 16-byte vectors
|
// Compare 16-byte vectors
|
||||||
masm.andl(result, AVX_VECTOR_SIZE - 1); // tail count (in bytes)
|
masm.andl(result, AVX_VECTOR_SIZE - 1); // tail count (in bytes)
|
||||||
masm.andl(length, ~(AVX_VECTOR_SIZE - 1)); // vector 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(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||||
masm.leaq(array2, new AMD64Address(array2, 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.vmovdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
|
||||||
masm.vpxor(vector1, vector1, vector2);
|
masm.vpxor(vector1, vector1, vector2);
|
||||||
masm.vptest(vector1, vector1);
|
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.addq(length, AVX_VECTOR_SIZE);
|
||||||
masm.jcc(ConditionFlag.NotZero, loop);
|
masm.jcc(ConditionFlag.NotZero, loop);
|
||||||
|
|
||||||
masm.testl(result, result);
|
masm.testl(result, result);
|
||||||
masm.jcc(ConditionFlag.Zero, trueLabel);
|
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
|
* Compare the remaining bytes with an unaligned memory load aligned to the end of the
|
||||||
* array.
|
* array.
|
||||||
|
@ -264,7 +314,12 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||||
masm.vmovdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -AVX_VECTOR_SIZE));
|
masm.vmovdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -AVX_VECTOR_SIZE));
|
||||||
masm.vpxor(vector1, vector1, vector2);
|
masm.vpxor(vector1, vector1, vector2);
|
||||||
masm.vptest(vector1, vector1);
|
masm.vptest(vector1, vector1);
|
||||||
|
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.jcc(ConditionFlag.NotZero, falseLabel);
|
||||||
|
}
|
||||||
masm.jmp(trueLabel);
|
masm.jmp(trueLabel);
|
||||||
|
|
||||||
masm.bind(compareTail);
|
masm.bind(compareTail);
|
||||||
|
@ -283,11 +338,15 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||||
Label loop = new Label();
|
Label loop = new Label();
|
||||||
Label compareTail = new Label();
|
Label compareTail = new Label();
|
||||||
|
|
||||||
|
boolean requiresNaNCheck = kind.isNumericFloat();
|
||||||
|
Label loopCheck = new Label();
|
||||||
|
Label nanCheck = new Label();
|
||||||
|
|
||||||
Register temp = asRegister(temp4);
|
Register temp = asRegister(temp4);
|
||||||
|
|
||||||
masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes)
|
masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes)
|
||||||
masm.andl(length, ~(VECTOR_SIZE - 1)); // vector 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(array1, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||||
masm.leaq(array2, new AMD64Address(array2, 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.bind(loop);
|
||||||
masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0));
|
masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0));
|
||||||
masm.cmpq(temp, new AMD64Address(array2, 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.addq(length, VECTOR_SIZE);
|
||||||
masm.jccb(ConditionFlag.NotZero, loop);
|
masm.jccb(ConditionFlag.NotZero, loop);
|
||||||
|
|
||||||
masm.testl(result, result);
|
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
|
* 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.movq(temp, new AMD64Address(array1, result, Scale.Times1, -VECTOR_SIZE));
|
||||||
masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE));
|
masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE));
|
||||||
|
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.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||||
|
}
|
||||||
masm.jmpb(trueLabel);
|
masm.jmpb(trueLabel);
|
||||||
|
|
||||||
masm.bind(compareTail);
|
masm.bind(compareTail);
|
||||||
|
@ -333,8 +415,13 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
|
||||||
masm.jccb(ConditionFlag.Zero, compare2Bytes);
|
masm.jccb(ConditionFlag.Zero, compare2Bytes);
|
||||||
masm.movl(temp, new AMD64Address(array1, 0));
|
masm.movl(temp, new AMD64Address(array1, 0));
|
||||||
masm.cmpl(temp, new AMD64Address(array2, 0));
|
masm.cmpl(temp, new AMD64Address(array2, 0));
|
||||||
|
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);
|
masm.jccb(ConditionFlag.NotEqual, falseLabel);
|
||||||
|
}
|
||||||
if (kind.getByteCount() <= 2) {
|
if (kind.getByteCount() <= 2) {
|
||||||
// Move array pointers forward.
|
// Move array pointers forward.
|
||||||
masm.leaq(array1, new AMD64Address(array1, 4));
|
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 final Unsafe UNSAFE = initUnsafe();
|
||||||
|
|
||||||
private static 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) {
|
public SPARCArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, AllocatableValue result, AllocatableValue array1, AllocatableValue array2, AllocatableValue length) {
|
||||||
super(TYPE, SIZE);
|
super(TYPE, SIZE);
|
||||||
|
|
||||||
|
assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported";
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
|
||||||
Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
|
Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
|
||||||
|
|
|
@ -59,7 +59,7 @@ public abstract class ArithmeticLIRGenerator implements ArithmeticLIRGeneratorTo
|
||||||
if (isNumericInteger(a.getPlatformKind())) {
|
if (isNumericInteger(a.getPlatformKind())) {
|
||||||
LIRKind aKind = a.getValueKind(LIRKind.class);
|
LIRKind aKind = a.getValueKind(LIRKind.class);
|
||||||
LIRKind bKind = b.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()) {
|
if (aKind.isUnknownReference()) {
|
||||||
resultKind = aKind;
|
resultKind = aKind;
|
||||||
|
|
|
@ -51,6 +51,8 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||||
try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
|
try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
|
||||||
LoopsData dataCounted = new LoopsData(graph);
|
LoopsData dataCounted = new LoopsData(graph);
|
||||||
dataCounted.detectedCountedLoops();
|
dataCounted.detectedCountedLoops();
|
||||||
|
Graph.Mark mark = graph.getMark();
|
||||||
|
boolean prePostInserted = false;
|
||||||
for (LoopEx loop : dataCounted.countedLoops()) {
|
for (LoopEx loop : dataCounted.countedLoops()) {
|
||||||
if (!LoopTransformations.isUnrollableLoop(loop)) {
|
if (!LoopTransformations.isUnrollableLoop(loop)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -59,9 +61,10 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||||
if (loop.loopBegin().isSimpleLoop()) {
|
if (loop.loopBegin().isSimpleLoop()) {
|
||||||
// First perform the pre/post transformation and do the partial
|
// First perform the pre/post transformation and do the partial
|
||||||
// unroll when we come around again.
|
// unroll when we come around again.
|
||||||
LoopTransformations.insertPrePostLoops(loop, graph);
|
LoopTransformations.insertPrePostLoops(loop);
|
||||||
|
prePostInserted = true;
|
||||||
} else {
|
} else {
|
||||||
LoopTransformations.partialUnroll(loop, graph);
|
LoopTransformations.partialUnroll(loop);
|
||||||
}
|
}
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
@ -72,11 +75,25 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
|
||||||
canonicalizer.applyIncremental(graph, context, listener.getNodes());
|
canonicalizer.applyIncremental(graph, context, listener.getNodes());
|
||||||
listener.getNodes().clear();
|
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
|
@Override
|
||||||
public boolean checkContract() {
|
public boolean checkContract() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.graalvm.compiler.core.common.RetryableBailoutException;
|
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.DebugContext;
|
||||||
import org.graalvm.compiler.debug.GraalError;
|
import org.graalvm.compiler.debug.GraalError;
|
||||||
import org.graalvm.compiler.graph.Graph.Mark;
|
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)
|
// 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();
|
assert loop.loopBegin().isMainLoop();
|
||||||
graph.getDebug().log("LoopPartialUnroll %s", loop);
|
loop.loopBegin().graph().getDebug().log("LoopPartialUnroll %s", loop);
|
||||||
|
|
||||||
LoopFragmentInside newSegment = loop.inside().duplicate();
|
LoopFragmentInside newSegment = loop.inside().duplicate();
|
||||||
newSegment.insertWithinAfter(loop);
|
newSegment.insertWithinAfter(loop);
|
||||||
|
@ -222,12 +223,13 @@ public abstract class LoopTransformations {
|
||||||
// The pre loop is constrained to one iteration for now and will likely
|
// The pre loop is constrained to one iteration for now and will likely
|
||||||
// be updated to produce vector alignment if applicable.
|
// 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);
|
graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
|
||||||
LoopFragmentWhole preLoop = loop.whole();
|
LoopFragmentWhole preLoop = loop.whole();
|
||||||
CountedLoopInfo preCounted = loop.counted();
|
CountedLoopInfo preCounted = loop.counted();
|
||||||
IfNode preLimit = preCounted.getLimitTest();
|
IfNode preLimit = preCounted.getLimitTest();
|
||||||
if (preLimit != null) {
|
assert preLimit != null;
|
||||||
LoopBeginNode preLoopBegin = loop.loopBegin();
|
LoopBeginNode preLoopBegin = loop.loopBegin();
|
||||||
InductionVariable preIv = preCounted.getCounter();
|
InductionVariable preIv = preCounted.getCounter();
|
||||||
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
|
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
|
||||||
|
@ -286,8 +288,8 @@ public abstract class LoopTransformations {
|
||||||
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
|
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
|
||||||
graph.removeFixed(safepoint);
|
graph.removeFixed(safepoint);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
|
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
|
||||||
|
return mainLoopBegin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -373,7 +375,7 @@ public abstract class LoopTransformations {
|
||||||
throw GraalError.shouldNotReachHere();
|
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);
|
ValueNode newLimit = sub(graph, ub, mainStride);
|
||||||
|
|
||||||
// Re-wire the condition with the new limit
|
// Re-wire the condition with the new limit
|
||||||
|
@ -445,6 +447,14 @@ public abstract class LoopTransformations {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LoopBeginNode loopBegin = loop.loopBegin();
|
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()) {
|
if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) {
|
||||||
// Flow-less loops to partial unroll for now. 3 blocks corresponds to an if that either
|
// 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
|
// 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) {
|
if (loop.loop().getBlocks().size() < 3) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s too large to unroll %s ", loopBegin, loop.loop().getBlocks().size());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,41 @@
|
||||||
*/
|
*/
|
||||||
package org.graalvm.compiler.loop.test;
|
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.core.test.GraalCompilerTest;
|
||||||
|
import org.graalvm.compiler.debug.DebugContext;
|
||||||
import org.graalvm.compiler.graph.iterators.NodeIterable;
|
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.LoopBeginNode;
|
||||||
import org.graalvm.compiler.nodes.StructuredGraph;
|
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 org.junit.Test;
|
||||||
|
|
||||||
|
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||||
|
|
||||||
public class LoopPartialUnrollTest extends GraalCompilerTest {
|
public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,100 +70,72 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long testMultiplySnippet(int arg) {
|
public static long sumWithEqualityLimit(int[] text) {
|
||||||
long r = 1;
|
long sum = 0;
|
||||||
for (int i = 0; branchProbability(0.99, i < arg); i++) {
|
for (int i = 0; branchProbability(0.99, i != text.length); ++i) {
|
||||||
r += r * i;
|
sum += volatileInt;
|
||||||
}
|
}
|
||||||
return r;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("equality limits aren't working properly")
|
||||||
@Test
|
@Test
|
||||||
public void testMultiply() {
|
public void testSumWithEqualityLimit() {
|
||||||
test("testMultiplySnippet", 9);
|
for (int i = 0; i < 128; i++) {
|
||||||
}
|
int[] data = new int[i];
|
||||||
|
test("sumWithEqualityLimit", data);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoopCarried() {
|
public void testLoopCarried() {
|
||||||
test("testLoopCarriedSnippet", 1, 2);
|
for (int i = 0; i < 64; i++) {
|
||||||
test("testLoopCarriedSnippet", 0, 4);
|
test("testLoopCarriedSnippet", i);
|
||||||
test("testLoopCarriedSnippet", 4, 0);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int testLoopCarriedSnippet(int a, int b) {
|
@Test
|
||||||
int c = a;
|
public void testLoopCarriedDuplication() {
|
||||||
int d = b;
|
testDuplicateBody("testLoopCarriedReference", "testLoopCarriedSnippet");
|
||||||
for (int j = 0; branchProbability(0.99, j < a); j++) {
|
|
||||||
d = c;
|
|
||||||
c += 1;
|
|
||||||
}
|
}
|
||||||
return c + d;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
public static long init = Runtime.getRuntime().totalMemory();
|
||||||
|
@ -181,4 +182,82 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
|
||||||
public void testSignExtension() {
|
public void testSignExtension() {
|
||||||
test("testSignExtensionSnippet", 9L);
|
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
|
@Override
|
||||||
public boolean shouldPartiallyUnroll(LoopEx loop) {
|
public boolean shouldPartiallyUnroll(LoopEx loop) {
|
||||||
|
LoopBeginNode loopBegin = loop.loopBegin();
|
||||||
if (!loop.isCounted()) {
|
if (!loop.isCounted()) {
|
||||||
|
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s isn't counted", loopBegin);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
OptionValues options = loop.entryPoint().getOptions();
|
OptionValues options = loop.entryPoint().getOptions();
|
||||||
LoopBeginNode loopBegin = loop.loopBegin();
|
|
||||||
int maxNodes = ExactPartialUnrollMaxNodes.getValue(options);
|
int maxNodes = ExactPartialUnrollMaxNodes.getValue(options);
|
||||||
maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount()));
|
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());
|
int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count());
|
||||||
|
@ -112,6 +113,7 @@ public class DefaultLoopPolicies implements LoopPolicies {
|
||||||
if (unrollFactor == 1) {
|
if (unrollFactor == 1) {
|
||||||
double loopFrequency = loopBegin.loopFrequency();
|
double loopFrequency = loopBegin.loopFrequency();
|
||||||
if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) {
|
if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) {
|
||||||
|
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s frequency too low %s ", loopBegin, loopFrequency);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
loopBegin.setLoopOrigFrequency(loopFrequency);
|
loopBegin.setLoopOrigFrequency(loopFrequency);
|
||||||
|
@ -136,6 +138,7 @@ public class DefaultLoopPolicies implements LoopPolicies {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s unrolled loop is too large %s ", loopBegin, size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
*/
|
*/
|
||||||
package org.graalvm.compiler.loop;
|
package org.graalvm.compiler.loop;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -142,18 +143,49 @@ public class LoopFragmentInside extends LoopFragment {
|
||||||
end.setNext(loop.entryPoint());
|
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) {
|
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;
|
assert isDuplicate() && original().loop() == loop;
|
||||||
|
|
||||||
patchNodes(dataFixWithinAfter);
|
patchNodes(dataFixWithinAfter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Collect any new back edges values before updating them since they might reference each
|
||||||
|
* other.
|
||||||
|
*/
|
||||||
LoopBeginNode mainLoopBegin = loop.loopBegin();
|
LoopBeginNode mainLoopBegin = loop.loopBegin();
|
||||||
|
ArrayList<ValueNode> backedgeValues = new ArrayList<>();
|
||||||
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
|
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
|
||||||
ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1));
|
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) {
|
if (duplicatedNode != null) {
|
||||||
mainPhiNode.setValueAt(1, duplicatedNode);
|
mainPhiNode.setValueAt(1, duplicatedNode);
|
||||||
} else {
|
|
||||||
assert mainPhiNode.valueAt(1).isConstant() || mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1)) : mainPhiNode.valueAt(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,9 +198,9 @@ public class LoopFragmentInside extends LoopFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
int unrollFactor = mainLoopBegin.getUnrollFactor();
|
int unrollFactor = mainLoopBegin.getUnrollFactor();
|
||||||
|
|
||||||
// Now use the previous unrollFactor to update the exit condition to power of two
|
|
||||||
StructuredGraph graph = mainLoopBegin.graph();
|
StructuredGraph graph = mainLoopBegin.graph();
|
||||||
|
if (updateLimit) {
|
||||||
|
// Now use the previous unrollFactor to update the exit condition to power of two
|
||||||
InductionVariable iv = loop.counted().getCounter();
|
InductionVariable iv = loop.counted().getCounter();
|
||||||
CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
|
CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
|
||||||
ValueNode compareBound;
|
ValueNode compareBound;
|
||||||
|
@ -179,15 +211,17 @@ public class LoopFragmentInside extends LoopFragment {
|
||||||
} else {
|
} else {
|
||||||
throw GraalError.shouldNotReachHere();
|
throw GraalError.shouldNotReachHere();
|
||||||
}
|
}
|
||||||
|
long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor;
|
||||||
if (iv.direction() == InductionVariable.Direction.Up) {
|
if (iv.direction() == InductionVariable.Direction.Up) {
|
||||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * iv.constantStride()));
|
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * originalStride));
|
||||||
ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
|
ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
|
||||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||||
} else if (iv.direction() == InductionVariable.Direction.Down) {
|
} else if (iv.direction() == InductionVariable.Direction.Down) {
|
||||||
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -iv.constantStride()));
|
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -originalStride));
|
||||||
ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
|
ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
|
||||||
compareNode.replaceFirstInput(compareBound, newLimit);
|
compareNode.replaceFirstInput(compareBound, newLimit);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
mainLoopBegin.setUnrollFactor(unrollFactor * 2);
|
mainLoopBegin.setUnrollFactor(unrollFactor * 2);
|
||||||
mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
|
mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
|
||||||
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
|
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
|
||||||
|
|
|
@ -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.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import jdk.vm.ci.meta.JavaConstant;
|
|
||||||
import jdk.vm.ci.meta.JavaKind;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import java.math.BigInteger;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
|
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.IntegerConvertOp;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp;
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp;
|
||||||
import org.graalvm.compiler.core.common.type.IntegerStamp;
|
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;
|
||||||
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
|
||||||
import org.graalvm.compiler.options.OptionValues;
|
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.
|
* 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, 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, 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, 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, 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, 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, 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;
|
AbstractMergeNode merge = (AbstractMergeNode) node;
|
||||||
EndNode singleEnd = merge.forwardEndAt(0);
|
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. */
|
/* Nodes that would use this merge as the guard need to use the previous block. */
|
||||||
registerNode(loopScope, nodeOrderId, AbstractBeginNode.prevBegin(singleEnd), true, false);
|
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 static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
|
||||||
|
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
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;
|
||||||
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul;
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul;
|
||||||
import org.graalvm.compiler.core.common.type.Stamp;
|
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);
|
return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX);
|
||||||
} else if (CodeUtil.isPowerOf2(i + 1)) {
|
} else if (CodeUtil.isPowerOf2(i + 1)) {
|
||||||
return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX);
|
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) {
|
} else if (i < 0) {
|
||||||
if (CodeUtil.isPowerOf2(-i)) {
|
if (CodeUtil.isPowerOf2(-i)) {
|
||||||
|
|
|
@ -562,7 +562,7 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||||
if (pred.getSuccessorCount() > 1) {
|
if (pred.getSuccessorCount() > 1) {
|
||||||
assert pred.getEndNode() instanceof ControlSplitNode;
|
assert pred.getEndNode() instanceof ControlSplitNode;
|
||||||
ControlSplitNode controlSplit = (ControlSplitNode) pred.getEndNode();
|
ControlSplitNode controlSplit = (ControlSplitNode) pred.getEndNode();
|
||||||
probability *= controlSplit.probability(block.getBeginNode());
|
probability = multiplyProbabilities(probability, controlSplit.probability(block.getBeginNode()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
probability = predecessors[0].probability;
|
probability = predecessors[0].probability;
|
||||||
|
@ -572,7 +572,7 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||||
|
|
||||||
if (block.getBeginNode() instanceof LoopBeginNode) {
|
if (block.getBeginNode() instanceof LoopBeginNode) {
|
||||||
LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode();
|
LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode();
|
||||||
probability *= loopBegin.loopFrequency();
|
probability = multiplyProbabilities(probability, loopBegin.loopFrequency());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (probability < MIN_PROBABILITY) {
|
if (probability < MIN_PROBABILITY) {
|
||||||
|
@ -755,4 +755,20 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph<Block> {
|
||||||
public void setNodeToBlock(NodeMap<Block> nodeMap) {
|
public void setNodeToBlock(NodeMap<Block> nodeMap) {
|
||||||
this.nodeToBlock = 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;
|
package org.graalvm.compiler.options.processor;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
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.Elements;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
import javax.tools.FileObject;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
|
||||||
import org.graalvm.compiler.options.Option;
|
import org.graalvm.compiler.options.Option;
|
||||||
import org.graalvm.compiler.options.OptionDescriptor;
|
import org.graalvm.compiler.options.OptionDescriptor;
|
||||||
|
@ -117,22 +122,13 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
return;
|
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();
|
String optionName = annotation.name();
|
||||||
if (optionName.equals("")) {
|
if (optionName.equals("")) {
|
||||||
optionName = fieldName;
|
optionName = fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Character.isUpperCase(optionName.charAt(0))) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +150,7 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
String separator = "";
|
String separator = "";
|
||||||
Set<Element> originatingElementsList = info.originatingElements;
|
Set<Element> originatingElementsList = info.originatingElements;
|
||||||
originatingElementsList.add(field);
|
originatingElementsList.add(field);
|
||||||
|
PackageElement enclosingPackage = null;
|
||||||
while (enclosing != null) {
|
while (enclosing != null) {
|
||||||
if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
|
if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
|
||||||
if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
|
if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
|
||||||
|
@ -164,13 +161,64 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
originatingElementsList.add(enclosing);
|
originatingElementsList.add(enclosing);
|
||||||
declaringClass = enclosing.getSimpleName() + separator + declaringClass;
|
declaringClass = enclosing.getSimpleName() + separator + declaringClass;
|
||||||
separator = ".";
|
separator = ".";
|
||||||
} else {
|
} else if (enclosing.getKind() == ElementKind.PACKAGE) {
|
||||||
assert enclosing.getKind() == ElementKind.PACKAGE;
|
enclosingPackage = (PackageElement) enclosing;
|
||||||
}
|
}
|
||||||
enclosing = enclosing.getEnclosingElement();
|
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) {
|
private void createFiles(OptionsInfo info) {
|
||||||
|
@ -200,11 +248,11 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
String desc = OptionDescriptor.class.getSimpleName();
|
String desc = OptionDescriptor.class.getSimpleName();
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
Collections.sort(info.options);
|
Collections.sort(info.options);
|
||||||
|
|
||||||
out.println(" @Override");
|
out.println(" @Override");
|
||||||
out.println(" public OptionDescriptor get(String value) {");
|
out.println(" public OptionDescriptor get(String value) {");
|
||||||
|
out.println(" switch (value) {");
|
||||||
out.println(" // CheckStyle: stop line length check");
|
out.println(" // CheckStyle: stop line length check");
|
||||||
for (OptionInfo option : info.options) {
|
for (OptionInfo option : info.options) {
|
||||||
String name = option.name;
|
String name = option.name;
|
||||||
|
@ -214,41 +262,52 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
} else {
|
} else {
|
||||||
optionField = option.declaringClass + "." + option.field.getSimpleName();
|
optionField = option.declaringClass + "." + option.field.getSimpleName();
|
||||||
}
|
}
|
||||||
|
out.println(" case \"" + name + "\": {");
|
||||||
String type = option.type;
|
String type = option.type;
|
||||||
String help = option.help;
|
String help = option.help;
|
||||||
|
String[] extraHelp = option.extraHelp;
|
||||||
String declaringClass = option.declaringClass;
|
String declaringClass = option.declaringClass;
|
||||||
Name fieldName = option.field.getSimpleName();
|
Name fieldName = option.field.getSimpleName();
|
||||||
out.println(" if (value.equals(\"" + name + "\")) {");
|
out.printf(" return " + desc + ".create(\n");
|
||||||
out.printf(" return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionField);
|
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(" }");
|
||||||
}
|
}
|
||||||
out.println(" // CheckStyle: resume line length check");
|
out.println(" // CheckStyle: resume line length check");
|
||||||
|
out.println(" }");
|
||||||
out.println(" return null;");
|
out.println(" return null;");
|
||||||
out.println(" }");
|
out.println(" }");
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" @Override");
|
out.println(" @Override");
|
||||||
out.println(" public Iterator<" + desc + "> iterator() {");
|
out.println(" public Iterator<" + desc + "> iterator() {");
|
||||||
out.println(" // CheckStyle: stop line length check");
|
out.println(" return new Iterator<OptionDescriptor>() {");
|
||||||
out.println(" List<" + desc + "> options = Arrays.asList(");
|
out.println(" int i = 0;");
|
||||||
for (OptionInfo option : info.options) {
|
out.println(" @Override");
|
||||||
String optionField;
|
out.println(" public boolean hasNext() {");
|
||||||
if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
|
out.println(" return i < " + info.options.size() + ";");
|
||||||
throw new InternalError();
|
out.println(" }");
|
||||||
} else {
|
out.println(" @Override");
|
||||||
optionField = option.declaringClass + "." + option.field.getSimpleName();
|
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 + "\");");
|
||||||
}
|
}
|
||||||
String name = option.name;
|
out.println(" }");
|
||||||
String type = option.type;
|
out.println(" throw new NoSuchElementException();");
|
||||||
String help = option.help;
|
out.println(" }");
|
||||||
String declaringClass = option.declaringClass;
|
out.println(" };");
|
||||||
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(" );");
|
|
||||||
out.println(" // CheckStyle: resume line length check");
|
|
||||||
out.println(" return options.iterator();");
|
|
||||||
out.println(" }");
|
out.println(" }");
|
||||||
out.println("}");
|
out.println("}");
|
||||||
}
|
}
|
||||||
|
@ -274,13 +333,15 @@ public class OptionProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final String help;
|
final String help;
|
||||||
|
final String[] extraHelp;
|
||||||
final String type;
|
final String type;
|
||||||
final String declaringClass;
|
final String declaringClass;
|
||||||
final VariableElement field;
|
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.name = name;
|
||||||
this.help = help;
|
this.help = help;
|
||||||
|
this.extraHelp = extraHelp;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.declaringClass = declaringClass;
|
this.declaringClass = declaringClass;
|
||||||
this.field = field;
|
this.field = field;
|
||||||
|
|
|
@ -28,27 +28,10 @@ import org.graalvm.util.EconomicMap;
|
||||||
|
|
||||||
public class EnumOptionKey<T extends Enum<T>> extends OptionKey<T> {
|
public class EnumOptionKey<T extends Enum<T>> extends OptionKey<T> {
|
||||||
final Class<T> enumClass;
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public EnumOptionKey(T value, ValueHelp<T> help) {
|
public EnumOptionKey(T value) {
|
||||||
super(value);
|
super(value);
|
||||||
this.valueHelp = help;
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new IllegalArgumentException("Value must not be 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);
|
return EnumSet.allOf(enumClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueHelp<T> getValueHelp() {
|
|
||||||
return valueHelp;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object valueOf(String name) {
|
Object valueOf(String name) {
|
||||||
try {
|
try {
|
||||||
return Enum.valueOf(enumClass, name);
|
return Enum.valueOf(enumClass, name);
|
||||||
|
|
|
@ -38,10 +38,20 @@ import java.lang.annotation.Target;
|
||||||
public @interface Option {
|
public @interface Option {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a help message for the option. New lines can be embedded in the message with
|
* Gets a help message for the option.
|
||||||
* {@code "%n"}.
|
* <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.
|
* 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;
|
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
|
* Describes the attributes of a static field {@linkplain Option option} and provides access to its
|
||||||
* {@linkplain OptionKey value}.
|
* {@linkplain OptionKey value}.
|
||||||
|
@ -31,25 +35,34 @@ public final class OptionDescriptor {
|
||||||
protected final String name;
|
protected final String name;
|
||||||
protected final Class<?> type;
|
protected final Class<?> type;
|
||||||
protected final String help;
|
protected final String help;
|
||||||
|
protected final List<String> extraHelp;
|
||||||
protected final OptionKey<?> optionKey;
|
protected final OptionKey<?> optionKey;
|
||||||
protected final Class<?> declaringClass;
|
protected final Class<?> declaringClass;
|
||||||
protected final String fieldName;
|
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) {
|
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;
|
assert option != null : declaringClass + "." + fieldName;
|
||||||
OptionDescriptor result = option.getDescriptor();
|
OptionDescriptor result = option.getDescriptor();
|
||||||
if (result == null) {
|
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);
|
option.setDescriptor(result);
|
||||||
}
|
}
|
||||||
assert result.name.equals(name) && result.type == type && result.declaringClass == declaringClass && result.fieldName.equals(fieldName) && result.optionKey == option;
|
assert result.name.equals(name) && result.type == type && result.declaringClass == declaringClass && result.fieldName.equals(fieldName) && result.optionKey == option;
|
||||||
return result;
|
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.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.help = help;
|
this.help = help;
|
||||||
|
this.extraHelp = extraHelp;
|
||||||
this.optionKey = optionKey;
|
this.optionKey = optionKey;
|
||||||
this.declaringClass = declaringClass;
|
this.declaringClass = declaringClass;
|
||||||
this.fieldName = fieldName;
|
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() {
|
public String getHelp() {
|
||||||
return help;
|
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
|
* 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.
|
* 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.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.graalvm.compiler.options.EnumOptionKey.ValueHelp;
|
|
||||||
import org.graalvm.util.EconomicMap;
|
import org.graalvm.util.EconomicMap;
|
||||||
import org.graalvm.util.Equivalence;
|
import org.graalvm.util.Equivalence;
|
||||||
import org.graalvm.util.UnmodifiableEconomicMap;
|
import org.graalvm.util.UnmodifiableEconomicMap;
|
||||||
|
@ -165,10 +162,9 @@ public class OptionValues {
|
||||||
* @return {@code text} broken into lines
|
* @return {@code text} broken into lines
|
||||||
*/
|
*/
|
||||||
private static List<String> wrap(String text, int width) {
|
private static List<String> wrap(String text, int width) {
|
||||||
List<String> lines = Collections.singletonList(text);
|
List<String> lines = new ArrayList<>();
|
||||||
if (text.length() > width) {
|
if (text.length() > width) {
|
||||||
String[] chunks = text.split("\\s+");
|
String[] chunks = text.split("\\s+");
|
||||||
lines = new ArrayList<>();
|
|
||||||
StringBuilder line = new StringBuilder();
|
StringBuilder line = new StringBuilder();
|
||||||
for (String chunk : chunks) {
|
for (String chunk : chunks) {
|
||||||
if (line.length() + chunk.length() > width) {
|
if (line.length() + chunk.length() > width) {
|
||||||
|
@ -178,22 +174,13 @@ public class OptionValues {
|
||||||
if (line.length() != 0) {
|
if (line.length() != 0) {
|
||||||
line.append(' ');
|
line.append(' ');
|
||||||
}
|
}
|
||||||
String[] embeddedLines = chunk.split("%n", -2);
|
|
||||||
if (embeddedLines.length == 1) {
|
|
||||||
line.append(chunk);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (line.length() != 0) {
|
if (line.length() != 0) {
|
||||||
lines.add(line.toString());
|
lines.add(line.toString());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
lines.add(text);
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
@ -222,24 +209,7 @@ public class OptionValues {
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
value = '"' + String.valueOf(value) + '"';
|
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 name = namePrefix + e.getKey();
|
||||||
String assign = containsKey(desc.optionKey) ? ":=" : "=";
|
String assign = containsKey(desc.optionKey) ? ":=" : "=";
|
||||||
String typeName = desc.getOptionKey() instanceof EnumOptionKey ? "String" : desc.getType().getSimpleName();
|
String typeName = desc.getOptionKey() instanceof EnumOptionKey ? "String" : desc.getType().getSimpleName();
|
||||||
|
@ -252,11 +222,16 @@ public class OptionValues {
|
||||||
out.printf("%s[%s]%n", linePrefix, typeName);
|
out.printf("%s[%s]%n", linePrefix, typeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> helpLines;
|
||||||
|
String help = desc.getHelp();
|
||||||
if (help.length() != 0) {
|
if (help.length() != 0) {
|
||||||
List<String> helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT);
|
helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT);
|
||||||
for (int i = 0; i < helpLines.size(); i++) {
|
helpLines.addAll(desc.getExtraHelp());
|
||||||
out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", helpLines.get(i));
|
} 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.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.graalvm.compiler.api.replacements.MethodSubstitution;
|
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) {
|
if (stateAfter != null) {
|
||||||
processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
|
processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
|
||||||
int callerLockDepth = stateAfter.nestedLockDepth();
|
int callerLockDepth = stateAfter.nestedLockDepth();
|
||||||
|
@ -569,14 +570,14 @@ public class InliningUtil extends ValueMergeUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("try")
|
@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 (inlineGraph.mayHaveNodeSourcePosition() && invoke.stateAfter() != null) {
|
||||||
if (invoke.asNode().getNodeSourcePosition() == null) {
|
if (invoke.asNode().getNodeSourcePosition() == null) {
|
||||||
// Temporarily ignore the assert below.
|
// Temporarily ignore the assert below.
|
||||||
return;
|
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();
|
NodeSourcePosition invokePos = invoke.asNode().getNodeSourcePosition();
|
||||||
assert invokePos != null : "missing source information";
|
assert invokePos != null : "missing source information";
|
||||||
|
|
||||||
|
|
|
@ -75,10 +75,11 @@ public abstract class AbstractInliningPolicy implements InliningPolicy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
|
private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) {
|
||||||
for (int i = 0; i < info.numberOfMethods(); i++) {
|
if (!onlyIntrinsics(replacements, info)) {
|
||||||
if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i), info.invoke().bci())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!info.shouldInline()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ import org.graalvm.compiler.nodes.StartNode;
|
||||||
import org.graalvm.util.EconomicMap;
|
import org.graalvm.util.EconomicMap;
|
||||||
import org.graalvm.util.Equivalence;
|
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.
|
* 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 {
|
} else {
|
||||||
ControlSplitNode split = (ControlSplitNode) current.predecessor();
|
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;
|
assert !Double.isNaN(probability) && !Double.isInfinite(probability) : current + " " + probability;
|
||||||
cache.put(current, probability);
|
cache.put(current, probability);
|
||||||
|
@ -125,7 +127,7 @@ public class FixedNodeProbabilityCache implements ToDoubleFunction<FixedNode> {
|
||||||
result += applyAsDouble(endNode);
|
result += applyAsDouble(endNode);
|
||||||
}
|
}
|
||||||
if (current instanceof LoopBeginNode) {
|
if (current instanceof LoopBeginNode) {
|
||||||
result *= ((LoopBeginNode) current).loopFrequency();
|
result = multiplyProbabilities(result, ((LoopBeginNode) current).loopFrequency());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,7 +366,7 @@ public class BinaryGraphPrinter implements
|
||||||
return ((Class<?>) obj).getName();
|
return ((Class<?>) obj).getName();
|
||||||
}
|
}
|
||||||
if (obj instanceof ResolvedJavaType) {
|
if (obj instanceof ResolvedJavaType) {
|
||||||
return ((ResolvedJavaType) obj).getName();
|
return ((ResolvedJavaType) obj).toJavaName();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -403,7 +403,7 @@ public class BinaryGraphPrinter implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String fieldTypeName(ResolvedJavaField field) {
|
public String fieldTypeName(ResolvedJavaField field) {
|
||||||
return field.getType().getName();
|
return field.getType().toJavaName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN;
|
||||||
import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
|
import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.graalvm.compiler.bytecode.BytecodeProvider;
|
import org.graalvm.compiler.bytecode.BytecodeProvider;
|
||||||
import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode;
|
import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode;
|
||||||
import org.graalvm.compiler.nodes.ValueNode;
|
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.java.AtomicReadAndWriteNode;
|
||||||
import org.graalvm.compiler.nodes.memory.address.AddressNode;
|
import org.graalvm.compiler.nodes.memory.address.AddressNode;
|
||||||
import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
|
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.IntegerSubstitutions;
|
||||||
import org.graalvm.compiler.replacements.LongSubstitutions;
|
import org.graalvm.compiler.replacements.LongSubstitutions;
|
||||||
import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeGetPlugin;
|
import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeGetPlugin;
|
||||||
|
@ -73,6 +76,7 @@ public class AMD64GraphBuilderPlugins {
|
||||||
registerUnsafePlugins(invocationPlugins, replacementsBytecodeProvider);
|
registerUnsafePlugins(invocationPlugins, replacementsBytecodeProvider);
|
||||||
registerStringPlugins(invocationPlugins, arch, replacementsBytecodeProvider);
|
registerStringPlugins(invocationPlugins, arch, replacementsBytecodeProvider);
|
||||||
registerMathPlugins(invocationPlugins, arch, arithmeticStubs, 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));
|
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);
|
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
|
@Test
|
||||||
public void testEqualsNodeGVN() {
|
public void testEqualsNodeGVN() {
|
||||||
test("testEqualsNodeGVNSnippet", true);
|
test("testEqualsNodeGVNSnippet", true);
|
||||||
|
|
|
@ -24,16 +24,25 @@ package org.graalvm.compiler.replacements.test;
|
||||||
|
|
||||||
import java.util.Random;
|
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.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.phases.common.AbstractInliningPhase;
|
||||||
import org.graalvm.compiler.test.ExportingClassLoader;
|
import org.graalvm.compiler.test.ExportingClassLoader;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import jdk.vm.ci.code.InstalledCode;
|
||||||
|
import jdk.vm.ci.meta.DeoptimizationReason;
|
||||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,6 +99,61 @@ public class DeoptimizeOnExceptionTest extends GraalCompilerTest {
|
||||||
return "SUCCESS";
|
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 {
|
public static class MyClassLoader extends ExportingClassLoader {
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> findClass(String className) throws ClassNotFoundException {
|
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.StructuredGraph;
|
||||||
import org.graalvm.compiler.nodes.ValueNode;
|
import org.graalvm.compiler.nodes.ValueNode;
|
||||||
import org.graalvm.compiler.nodes.calc.AddNode;
|
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.IntegerBelowNode;
|
||||||
import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
|
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.IsNullNode;
|
||||||
import org.graalvm.compiler.nodes.calc.LeftShiftNode;
|
import org.graalvm.compiler.nodes.calc.LeftShiftNode;
|
||||||
import org.graalvm.compiler.nodes.calc.NarrowNode;
|
import org.graalvm.compiler.nodes.calc.NarrowNode;
|
||||||
|
@ -577,7 +579,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
|
||||||
} else {
|
} else {
|
||||||
memoryRead.setGuard(guard);
|
memoryRead.setGuard(guard);
|
||||||
}
|
}
|
||||||
ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, compressible);
|
ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, compressible), readKind);
|
||||||
load.replaceAtUsages(readValue);
|
load.replaceAtUsages(readValue);
|
||||||
return memoryRead;
|
return memoryRead;
|
||||||
}
|
}
|
||||||
|
@ -592,11 +594,20 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
|
||||||
// An unsafe read must not float otherwise it may float above
|
// An unsafe read must not float otherwise it may float above
|
||||||
// a test guaranteeing the read is safe.
|
// a test guaranteeing the read is safe.
|
||||||
memoryRead.setForceFixed(true);
|
memoryRead.setForceFixed(true);
|
||||||
ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, false);
|
ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, false), readKind);
|
||||||
load.replaceAtUsages(readValue);
|
load.replaceAtUsages(readValue);
|
||||||
graph.replaceFixedWithFixed(load, memoryRead);
|
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) {
|
protected void lowerUnsafeStoreNode(RawStoreNode store) {
|
||||||
StructuredGraph graph = store.graph();
|
StructuredGraph graph = store.graph();
|
||||||
boolean compressible = store.value().getStackKind() == JavaKind.Object;
|
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", short[].class, short[].class);
|
||||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
|
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
|
||||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].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", long[].class, long[].class);
|
||||||
r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
|
private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
|
||||||
|
|
|
@ -95,11 +95,16 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower
|
||||||
return length;
|
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) {
|
private static boolean arrayEquals(ConstantReflectionProvider constantReflection, JavaConstant a, JavaConstant b, int len) {
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
JavaConstant aElem = constantReflection.readArrayElement(a, i);
|
JavaConstant aElem = constantReflection.readArrayElement(a, i);
|
||||||
JavaConstant bElem = constantReflection.readArrayElement(b, i);
|
JavaConstant bElem = constantReflection.readArrayElement(b, i);
|
||||||
if (!constantReflection.constantEquals(aElem, bElem)) {
|
if (!constantReflection.constantEquals(aElem, bElem) && !(isNaNFloat(aElem) && isNaNFloat(bElem))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,9 +150,29 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower
|
||||||
ValueNode entry1 = tool.getEntry(virtual1, i);
|
ValueNode entry1 = tool.getEntry(virtual1, i);
|
||||||
ValueNode entry2 = tool.getEntry(virtual2, i);
|
ValueNode entry2 = tool.getEntry(virtual2, i);
|
||||||
if (entry1 != entry2) {
|
if (entry1 != entry2) {
|
||||||
|
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
|
// the contents might be different
|
||||||
allEqual = false;
|
allEqual = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
|
if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
|
||||||
// the contents are different
|
// the contents are different
|
||||||
tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
|
tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
|
||||||
|
|
|
@ -57,7 +57,7 @@ public final class ExplodeLoopNode extends FixedWithNextNode {
|
||||||
for (Node n : currentNext.cfgSuccessors()) {
|
for (Node n : currentNext.cfgSuccessors()) {
|
||||||
succs.add(n);
|
succs.add(n);
|
||||||
}
|
}
|
||||||
if (succs.size() == 1) {
|
if (succs.size() == 1 && succs.get(0) != currentNext) {
|
||||||
currentNext = succs.get(0);
|
currentNext = succs.get(0);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
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.NodeCycles.CYCLES_2;
|
||||||
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
||||||
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh;
|
||||||
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.graph.NodeClass;
|
import org.graalvm.compiler.graph.NodeClass;
|
||||||
|
import org.graalvm.compiler.graph.spi.Canonicalizable;
|
||||||
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
||||||
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
||||||
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
||||||
import org.graalvm.compiler.nodes.ConstantNode;
|
import org.graalvm.compiler.nodes.ConstantNode;
|
||||||
import org.graalvm.compiler.nodes.ValueNode;
|
import org.graalvm.compiler.nodes.ValueNode;
|
||||||
import org.graalvm.compiler.nodes.calc.BinaryNode;
|
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
|
||||||
import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
|
|
||||||
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
|
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;
|
import jdk.vm.ci.meta.Value;
|
||||||
|
|
||||||
@NodeInfo(shortName = "*H", cycles = CYCLES_2, size = SIZE_2)
|
@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 static final NodeClass<IntegerMulHighNode> TYPE = NodeClass.create(IntegerMulHighNode.class);
|
||||||
|
|
||||||
public IntegerMulHighNode(ValueNode x, ValueNode y) {
|
public IntegerMulHighNode(ValueNode x, ValueNode y) {
|
||||||
this((IntegerStamp) x.stamp().unrestricted(), x, y);
|
super(TYPE, ArithmeticOpTable::getMulHigh, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -97,29 +56,35 @@ public final class IntegerMulHighNode extends BinaryNode implements ArithmeticLI
|
||||||
nodeValueMap.setResult(this, gen.emitMulHigh(a, b));
|
nodeValueMap.setResult(this, gen.emitMulHigh(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int multiplyHigh(int x, int y) {
|
@Override
|
||||||
long r = (long) x * (long) y;
|
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||||
return (int) (r >> 32);
|
ValueNode ret = super.canonical(tool, forX, forY);
|
||||||
|
if (ret != this) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long multiplyHigh(long x, long y) {
|
if (forX.isConstant() && !forY.isConstant()) {
|
||||||
// Checkstyle: stop
|
// we try to swap and canonicalize
|
||||||
long x0, y0, z0;
|
ValueNode improvement = canonical(tool, forY, forX);
|
||||||
long x1, y1, z1, z2, t;
|
if (improvement != this) {
|
||||||
// Checkstyle: resume
|
return improvement;
|
||||||
|
}
|
||||||
|
// if this fails we only swap
|
||||||
|
return new IntegerMulHighNode(forY, forX);
|
||||||
|
}
|
||||||
|
return canonical(this, forY);
|
||||||
|
}
|
||||||
|
|
||||||
x0 = x & 0xFFFFFFFFL;
|
private static ValueNode canonical(IntegerMulHighNode self, ValueNode forY) {
|
||||||
x1 = x >> 32;
|
if (forY.isConstant()) {
|
||||||
|
Constant c = forY.asConstant();
|
||||||
y0 = y & 0xFFFFFFFFL;
|
if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
|
||||||
y1 = y >> 32;
|
long i = ((PrimitiveConstant) c).asLong();
|
||||||
|
if (i == 0 || i == 1) {
|
||||||
z0 = x0 * y0;
|
return ConstantNode.forIntegerStamp(self.stamp(), 0);
|
||||||
t = x1 * y0 + (z0 >>> 32);
|
}
|
||||||
z1 = t & 0xFFFFFFFFL;
|
}
|
||||||
z2 = t >> 32;
|
}
|
||||||
z1 += x0 * y1;
|
return self;
|
||||||
|
|
||||||
return x1 * y1 + z2 + (z1 >> 32);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.NodeCycles.CYCLES_2;
|
||||||
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
|
||||||
|
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh;
|
||||||
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.graph.NodeClass;
|
import org.graalvm.compiler.graph.NodeClass;
|
||||||
|
import org.graalvm.compiler.graph.spi.Canonicalizable;
|
||||||
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
|
||||||
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
|
||||||
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
import org.graalvm.compiler.nodeinfo.NodeInfo;
|
||||||
import org.graalvm.compiler.nodes.ConstantNode;
|
import org.graalvm.compiler.nodes.ConstantNode;
|
||||||
import org.graalvm.compiler.nodes.ValueNode;
|
import org.graalvm.compiler.nodes.ValueNode;
|
||||||
import org.graalvm.compiler.nodes.calc.BinaryNode;
|
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
|
||||||
import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
|
|
||||||
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
|
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;
|
import jdk.vm.ci.meta.Value;
|
||||||
|
|
||||||
@NodeInfo(shortName = "|*H|", cycles = CYCLES_2, size = SIZE_2)
|
@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 static final NodeClass<UnsignedMulHighNode> TYPE = NodeClass.create(UnsignedMulHighNode.class);
|
||||||
|
|
||||||
public UnsignedMulHighNode(ValueNode x, ValueNode y) {
|
public UnsignedMulHighNode(ValueNode x, ValueNode y) {
|
||||||
this((IntegerStamp) x.stamp().unrestricted(), x, y);
|
super(TYPE, ArithmeticOpTable::getUMulHigh, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,31 +56,35 @@ public final class UnsignedMulHighNode extends BinaryNode implements ArithmeticL
|
||||||
nodeValueMap.setResult(this, gen.emitUMulHigh(a, b));
|
nodeValueMap.setResult(this, gen.emitUMulHigh(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int multiplyHighUnsigned(int x, int y) {
|
@Override
|
||||||
long xl = x & 0xFFFFFFFFL;
|
public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
|
||||||
long yl = y & 0xFFFFFFFFL;
|
ValueNode ret = super.canonical(tool, forX, forY);
|
||||||
long r = xl * yl;
|
if (ret != this) {
|
||||||
return (int) (r >> 32);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long multiplyHighUnsigned(long x, long y) {
|
if (forX.isConstant() && !forY.isConstant()) {
|
||||||
// Checkstyle: stop
|
// we try to swap and canonicalize
|
||||||
long x0, y0, z0;
|
ValueNode improvement = canonical(tool, forY, forX);
|
||||||
long x1, y1, z1, z2, t;
|
if (improvement != this) {
|
||||||
// Checkstyle: resume
|
return improvement;
|
||||||
|
}
|
||||||
|
// if this fails we only swap
|
||||||
|
return new UnsignedMulHighNode(forY, forX);
|
||||||
|
}
|
||||||
|
return canonical(this, forY);
|
||||||
|
}
|
||||||
|
|
||||||
x0 = x & 0xFFFFFFFFL;
|
private static ValueNode canonical(UnsignedMulHighNode self, ValueNode forY) {
|
||||||
x1 = x >>> 32;
|
if (forY.isConstant()) {
|
||||||
|
Constant c = forY.asConstant();
|
||||||
y0 = y & 0xFFFFFFFFL;
|
if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
|
||||||
y1 = y >>> 32;
|
long i = ((PrimitiveConstant) c).asLong();
|
||||||
|
if (i == 0 || i == 1) {
|
||||||
z0 = x0 * y0;
|
return ConstantNode.forIntegerStamp(self.stamp(), 0);
|
||||||
t = x1 * y0 + (z0 >>> 32);
|
}
|
||||||
z1 = t & 0xFFFFFFFFL;
|
}
|
||||||
z2 = t >>> 32;
|
}
|
||||||
z1 += x0 * y1;
|
return self;
|
||||||
|
|
||||||
return x1 * y1 + z2 + (z1 >>> 32);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
package org.graalvm.compiler.microbenchmarks.graal;
|
package micro.benchmarks;
|
||||||
|
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
import org.openjdk.jmh.annotations.Fork;
|
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.
|
* This dummy class is used to verify that the JMH microbenchmarking environment is set up properly.
|
||||||
*/
|
*/
|
||||||
public class TestJMH {
|
public class TestJMHBlackbox {
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public void testJMH() {
|
public void testJMH() {
|
|
@ -952,7 +952,7 @@ jlong os::javaTimeNanos() {
|
||||||
if (now <= prev) {
|
if (now <= prev) {
|
||||||
return prev; // same or retrograde time;
|
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
|
assert(obsv >= prev, "invariant"); // Monotonicity
|
||||||
// If the CAS succeeded then we're done and return "now".
|
// If the CAS succeeded then we're done and return "now".
|
||||||
// If the CAS failed and the observed value "obsv" is >= now then
|
// 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_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"},
|
||||||
{EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"},
|
{EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"},
|
||||||
#if defined(VM_LITTLE_ENDIAN)
|
#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
|
#else
|
||||||
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64 LE"},
|
{EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"},
|
||||||
#endif
|
#endif
|
||||||
{EM_ARM, EM_ARM, ELFCLASS32, ELFDATA2LSB, (char*)"ARM"},
|
{EM_ARM, EM_ARM, ELFCLASS32, ELFDATA2LSB, (char*)"ARM"},
|
||||||
{EM_S390, EM_S390, ELFCLASSNONE, ELFDATA2MSB, (char*)"IBM System/390"},
|
{EM_S390, EM_S390, ELFCLASSNONE, ELFDATA2MSB, (char*)"IBM System/390"},
|
||||||
|
|
|
@ -1197,7 +1197,7 @@ inline hrtime_t getTimeNanos() {
|
||||||
if (now <= prev) {
|
if (now <= prev) {
|
||||||
return prev; // same or retrograde time;
|
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
|
assert(obsv >= prev, "invariant"); // Monotonicity
|
||||||
// If the CAS succeeded then we're done and return "now".
|
// If the CAS succeeded then we're done and return "now".
|
||||||
// If the CAS failed and the observed value "obsv" is >= now then
|
// 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
|
template<>
|
||||||
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// 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);
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||||
// specified otherwise (see atomic.hpp).
|
// specified otherwise (see atomic.hpp).
|
||||||
|
|
||||||
unsigned int old_value;
|
T old_value;
|
||||||
const uint64_t zero = 0;
|
const uint64_t zero = 0;
|
||||||
|
|
||||||
cmpxchg_pre_membar(order);
|
cmpxchg_pre_membar(order);
|
||||||
|
@ -412,16 +423,22 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||||
|
|
||||||
cmpxchg_post_membar(order);
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||||
// specified otherwise (see atomic.hpp).
|
// specified otherwise (see atomic.hpp).
|
||||||
|
|
||||||
long old_value;
|
T old_value;
|
||||||
const uint64_t zero = 0;
|
const uint64_t zero = 0;
|
||||||
|
|
||||||
cmpxchg_pre_membar(order);
|
cmpxchg_pre_membar(order);
|
||||||
|
@ -456,15 +473,7 @@ inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong c
|
||||||
|
|
||||||
cmpxchg_post_membar(order);
|
cmpxchg_post_membar(order);
|
||||||
|
|
||||||
return (jlong) old_value;
|
return 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef strasm_sync
|
#undef strasm_sync
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
#ifndef OS_CPU_BSD_X86_VM_ATOMIC_BSD_X86_HPP
|
#ifndef OS_CPU_BSD_X86_VM_ATOMIC_BSD_X86_HPP
|
||||||
#define 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
|
// Implementation of class atomic
|
||||||
|
|
||||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
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);
|
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
template<>
|
||||||
inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
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)"
|
__asm__ volatile ( "lock cmpxchgb %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
||||||
|
@ -90,7 +93,13 @@ inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte*
|
||||||
return exchange_value;
|
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)"
|
__asm__ volatile ( "lock cmpxchgl %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
: "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;
|
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)"
|
__asm__ __volatile__ ( "lock cmpxchgq %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||||
|
@ -145,14 +160,6 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||||
return exchange_value;
|
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; }
|
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||||
|
|
||||||
#else // !AMD64
|
#else // !AMD64
|
||||||
|
@ -184,16 +191,14 @@ extern "C" {
|
||||||
void _Atomic_move_long(const volatile jlong* src, volatile jlong* dst);
|
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) {
|
template<>
|
||||||
return _Atomic_cmpxchg_long(exchange_value, dest, compare_value, os::is_MP());
|
template<typename T>
|
||||||
}
|
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||||
|
T volatile* dest,
|
||||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
T compare_value,
|
||||||
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
cmpxchg_memory_order order) const {
|
||||||
}
|
STATIC_ASSERT(8 == sizeof(T));
|
||||||
|
return cmpxchg_using_helper<jlong>(_Atomic_cmpxchg_long, 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((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::load(const volatile jlong* src) {
|
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'
|
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||||
`*PTR' before the operation.*/
|
`*PTR' before the operation.*/
|
||||||
static inline int m68k_compare_and_swap(volatile int *ptr,
|
static inline int m68k_compare_and_swap(int newval,
|
||||||
int oldval,
|
volatile int *ptr,
|
||||||
int newval) {
|
int oldval) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int prev = *ptr;
|
int prev = *ptr;
|
||||||
if (prev != oldval)
|
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'
|
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||||
`*PTR' before the operation.*/
|
`*PTR' before the operation.*/
|
||||||
static inline int arm_compare_and_swap(volatile int *ptr,
|
static inline int arm_compare_and_swap(int newval,
|
||||||
int oldval,
|
volatile int *ptr,
|
||||||
int newval) {
|
int oldval) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int prev = *ptr;
|
int prev = *ptr;
|
||||||
if (prev != oldval)
|
if (prev != oldval)
|
||||||
|
@ -267,55 +267,38 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
|
||||||
(volatile intptr_t*) dest);
|
(volatile intptr_t*) dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jint Atomic::cmpxchg(jint exchange_value,
|
// No direct support for cmpxchg of bytes; emulate using int.
|
||||||
volatile jint* dest,
|
template<>
|
||||||
jint compare_value,
|
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||||
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));
|
||||||
#ifdef ARM
|
#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
|
#else
|
||||||
#ifdef M68K
|
#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
|
#else
|
||||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||||
#endif // M68K
|
#endif // M68K
|
||||||
#endif // ARM
|
#endif // ARM
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::cmpxchg(jlong exchange_value,
|
template<>
|
||||||
volatile jlong* dest,
|
template<typename T>
|
||||||
jlong compare_value,
|
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||||
cmpxchg_memory_order order) {
|
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);
|
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) {
|
inline jlong Atomic::load(const volatile jlong* src) {
|
||||||
volatile jlong dest;
|
volatile jlong dest;
|
||||||
os::atomic_copy64(src, &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);
|
(volatile intptr_t*) dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> T generic_cmpxchg(T exchange_value, volatile T* dest,
|
template<size_t byte_size>
|
||||||
T compare_value, cmpxchg_memory_order order)
|
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) {
|
if (order == memory_order_relaxed) {
|
||||||
T value = compare_value;
|
T value = compare_value;
|
||||||
__atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false,
|
__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, jlong* dest) { *dest = store_value; }
|
||||||
inline void Atomic::store (jlong store_value, volatile 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;
|
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; }
|
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||||
|
|
||||||
#endif // OS_CPU_LINUX_AARCH64_VM_ATOMIC_LINUX_AARCH64_HPP
|
#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
|
// 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
|
#ifdef AARCH64
|
||||||
jint rv;
|
T rv;
|
||||||
int tmp;
|
int tmp;
|
||||||
__asm__ volatile(
|
__asm__ volatile(
|
||||||
"1:\n\t"
|
"1:\n\t"
|
||||||
|
@ -220,14 +249,19 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||||
: "memory");
|
: "memory");
|
||||||
return rv;
|
return rv;
|
||||||
#else
|
#else
|
||||||
// Warning: Arguments are swapped to avoid moving them for kernel call
|
return cmpxchg_using_helper<jint>(reorder_cmpxchg_func, exchange_value, dest, compare_value);
|
||||||
return (*os::atomic_cmpxchg_func)(compare_value, exchange_value, dest);
|
|
||||||
#endif
|
#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
|
#ifdef AARCH64
|
||||||
jlong rv;
|
T rv;
|
||||||
int tmp;
|
int tmp;
|
||||||
__asm__ volatile(
|
__asm__ volatile(
|
||||||
"1:\n\t"
|
"1:\n\t"
|
||||||
|
@ -245,21 +279,8 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong
|
||||||
: "memory");
|
: "memory");
|
||||||
return rv;
|
return rv;
|
||||||
#else
|
#else
|
||||||
assert(VM_Version::supports_cx8(), "Atomic compare and exchange jlong not supported on this architecture!");
|
return cmpxchg_using_helper<jlong>(reorder_cmpxchg_long_func, exchange_value, dest, compare_value);
|
||||||
return (*os::atomic_cmpxchg_long_func)(compare_value, exchange_value, dest);
|
|
||||||
#endif
|
#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
|
#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
|
template<>
|
||||||
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// 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);
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||||
// specified otherwise (see atomic.hpp).
|
// specified otherwise (see atomic.hpp).
|
||||||
|
|
||||||
unsigned int old_value;
|
T old_value;
|
||||||
const uint64_t zero = 0;
|
const uint64_t zero = 0;
|
||||||
|
|
||||||
cmpxchg_pre_membar(order);
|
cmpxchg_pre_membar(order);
|
||||||
|
@ -412,16 +423,22 @@ inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compa
|
||||||
|
|
||||||
cmpxchg_post_membar(order);
|
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
|
// Note that cmpxchg guarantees a two-way memory barrier across
|
||||||
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
// the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
|
||||||
// specified otherwise (see atomic.hpp).
|
// specified otherwise (see atomic.hpp).
|
||||||
|
|
||||||
long old_value;
|
T old_value;
|
||||||
const uint64_t zero = 0;
|
const uint64_t zero = 0;
|
||||||
|
|
||||||
cmpxchg_pre_membar(order);
|
cmpxchg_pre_membar(order);
|
||||||
|
@ -456,15 +473,7 @@ inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong c
|
||||||
|
|
||||||
cmpxchg_post_membar(order);
|
cmpxchg_post_membar(order);
|
||||||
|
|
||||||
return (jlong) old_value;
|
return 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef strasm_sync
|
#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
|
// function is performed before the operand is fetched and again after the
|
||||||
// operation is completed."
|
// operation is completed."
|
||||||
|
|
||||||
jint Atomic::cmpxchg(jint xchg_val, volatile jint* dest, jint cmp_val, cmpxchg_memory_order unused) {
|
// No direct support for cmpxchg of bytes; emulate using int.
|
||||||
unsigned long old;
|
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__ (
|
__asm__ __volatile__ (
|
||||||
" CS %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.
|
" 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"
|
: "cc", "memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
return (jint)old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
jlong Atomic::cmpxchg(jlong xchg_val, volatile jlong* dest, jlong cmp_val, cmpxchg_memory_order unused) {
|
template<>
|
||||||
unsigned long old;
|
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__ (
|
__asm__ __volatile__ (
|
||||||
" CSG %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.
|
" 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"
|
: "cc", "memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
return (jlong)old;
|
return 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
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);
|
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) {
|
template<>
|
||||||
jint rv;
|
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(
|
__asm__ volatile(
|
||||||
" cas [%2], %3, %0"
|
" cas [%2], %3, %0"
|
||||||
: "=r" (rv)
|
: "=r" (rv)
|
||||||
|
@ -132,8 +141,14 @@ inline jint Atomic::cmpxchg (jint exchange_value, volatile jint*
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order) {
|
template<>
|
||||||
jlong rv;
|
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(
|
__asm__ volatile(
|
||||||
" casx [%2], %3, %0"
|
" casx [%2], %3, %0"
|
||||||
: "=r" (rv)
|
: "=r" (rv)
|
||||||
|
@ -142,18 +157,4 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||||
return rv;
|
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
|
#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
|
#ifndef OS_CPU_LINUX_X86_VM_ATOMIC_LINUX_X86_HPP
|
||||||
#define 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
|
// Implementation of class atomic
|
||||||
|
|
||||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
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);
|
return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
|
template<>
|
||||||
inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order) {
|
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)"
|
__asm__ volatile ("lock cmpxchgb %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
: "q" (exchange_value), "a" (compare_value), "r" (dest)
|
||||||
|
@ -90,7 +93,13 @@ inline jbyte Atomic::cmpxchg (jbyte exchange_value, volatile jbyte*
|
||||||
return exchange_value;
|
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)"
|
__asm__ volatile ("lock cmpxchgl %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
: "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;
|
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)"
|
__asm__ __volatile__ ("lock cmpxchgq %1,(%3)"
|
||||||
: "=a" (exchange_value)
|
: "=a" (exchange_value)
|
||||||
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
: "r" (exchange_value), "a" (compare_value), "r" (dest)
|
||||||
|
@ -145,14 +160,6 @@ inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong*
|
||||||
return exchange_value;
|
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; }
|
inline jlong Atomic::load(const volatile jlong* src) { return *src; }
|
||||||
|
|
||||||
#else // !AMD64
|
#else // !AMD64
|
||||||
|
@ -184,16 +191,14 @@ extern "C" {
|
||||||
void _Atomic_move_long(const volatile jlong* src, volatile jlong* dst);
|
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) {
|
template<>
|
||||||
return _Atomic_cmpxchg_long(exchange_value, dest, compare_value);
|
template<typename T>
|
||||||
}
|
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||||
|
T volatile* dest,
|
||||||
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order) {
|
T compare_value,
|
||||||
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
cmpxchg_memory_order order) const {
|
||||||
}
|
STATIC_ASSERT(8 == sizeof(T));
|
||||||
|
return cmpxchg_using_helper<jlong>(_Atomic_cmpxchg_long, 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((jint)exchange_value, (volatile jint*)dest, (jint)compare_value, order);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::load(const volatile jlong* src) {
|
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'
|
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||||
`*PTR' before the operation.*/
|
`*PTR' before the operation.*/
|
||||||
static inline int m68k_compare_and_swap(volatile int *ptr,
|
static inline int m68k_compare_and_swap(int newval,
|
||||||
int oldval,
|
volatile int *ptr,
|
||||||
int newval) {
|
int oldval) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int prev = *ptr;
|
int prev = *ptr;
|
||||||
if (prev != oldval)
|
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'
|
/* Perform an atomic compare and swap: if the current value of `*PTR'
|
||||||
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of
|
||||||
`*PTR' before the operation.*/
|
`*PTR' before the operation.*/
|
||||||
static inline int arm_compare_and_swap(volatile int *ptr,
|
static inline int arm_compare_and_swap(int newval,
|
||||||
int oldval,
|
volatile int *ptr,
|
||||||
int newval) {
|
int oldval) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int prev = *ptr;
|
int prev = *ptr;
|
||||||
if (prev != oldval)
|
if (prev != oldval)
|
||||||
|
@ -261,55 +261,38 @@ inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
|
||||||
(volatile intptr_t*) dest);
|
(volatile intptr_t*) dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jint Atomic::cmpxchg(jint exchange_value,
|
// No direct support for cmpxchg of bytes; emulate using int.
|
||||||
volatile jint* dest,
|
template<>
|
||||||
jint compare_value,
|
struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};
|
||||||
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));
|
||||||
#ifdef ARM
|
#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
|
#else
|
||||||
#ifdef M68K
|
#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
|
#else
|
||||||
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
|
||||||
#endif // M68K
|
#endif // M68K
|
||||||
#endif // ARM
|
#endif // ARM
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jlong Atomic::cmpxchg(jlong exchange_value,
|
template<>
|
||||||
volatile jlong* dest,
|
template<typename T>
|
||||||
jlong compare_value,
|
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||||
cmpxchg_memory_order order) {
|
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);
|
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) {
|
inline jlong Atomic::load(const volatile jlong* src) {
|
||||||
volatile jlong dest;
|
volatile jlong dest;
|
||||||
os::atomic_copy64(src, &dest);
|
os::atomic_copy64(src, &dest);
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
#ifndef OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
#ifndef OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
||||||
#define 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
|
// Implementation of class atomic
|
||||||
|
|
||||||
inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; }
|
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" 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" 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" jint _Atomic_add32(jint inc, volatile jint* dest);
|
||||||
extern "C" intptr_t _Atomic_add64(intptr_t add_value, volatile intptr_t* 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);
|
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) {
|
template<>
|
||||||
return _Atomic_cas32(exchange_value, dest, compare_value);
|
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) {
|
template<>
|
||||||
// Return 64 bit value in %o0
|
template<typename T>
|
||||||
return _Atomic_cas64((intptr_t)exchange_value, (intptr_t *)dest, (intptr_t)compare_value);
|
inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
|
||||||
}
|
T volatile* dest,
|
||||||
|
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) {
|
cmpxchg_memory_order order) const {
|
||||||
return _Atomic_cas64(exchange_value, dest, compare_value);
|
STATIC_ASSERT(8 == sizeof(T));
|
||||||
}
|
T rv;
|
||||||
|
__asm__ volatile(
|
||||||
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order) {
|
" casx [%2], %3, %0"
|
||||||
return (void*)cmpxchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest, (intptr_t)compare_value, order);
|
: "=r" (rv)
|
||||||
|
: "0" (exchange_value), "r" (dest), "r" (compare_value)
|
||||||
|
: "memory");
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // OS_CPU_SOLARIS_SPARC_VM_ATOMIC_SOLARIS_SPARC_HPP
|
#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…
Add table
Add a link
Reference in a new issue