mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-16 19:03:22 +00:00
Merge
This commit is contained in:
commit
98ac731a94
@ -248,7 +248,7 @@ jprt.build.targets=${jprt.build.targets.${jprt.tools.default.release}}
|
||||
|
||||
jprt.my.solaris.sparc.test.targets= \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jvm98, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-scimark, \
|
||||
${jprt.my.solaris.sparc}-product-{c1|c2}-runThese, \
|
||||
${jprt.my.solaris.sparc}-fastdebug-c1-runThese_Xshare, \
|
||||
@ -267,7 +267,7 @@ jprt.my.solaris.sparc.test.targets= \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_G1, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_ParOldGC, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_default, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_SerialGC, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_ParallelGC, \
|
||||
${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_CMS, \
|
||||
@ -276,7 +276,7 @@ jprt.my.solaris.sparc.test.targets= \
|
||||
|
||||
jprt.my.solaris.sparcv9.test.targets= \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jvm98, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-scimark, \
|
||||
${jprt.my.solaris.sparcv9}-product-c2-runThese, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-GCBasher_default, \
|
||||
@ -294,7 +294,7 @@ jprt.my.solaris.sparcv9.test.targets= \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-GCOld_G1, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-GCOld_ParOldGC, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_default, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_SerialGC, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_ParallelGC, \
|
||||
${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jbb_CMS, \
|
||||
@ -303,7 +303,7 @@ jprt.my.solaris.sparcv9.test.targets= \
|
||||
|
||||
jprt.my.solaris.x64.test.targets= \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jvm98, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-scimark, \
|
||||
${jprt.my.solaris.x64}-product-c2-runThese, \
|
||||
${jprt.my.solaris.x64}-product-c2-runThese_Xcomp, \
|
||||
@ -322,7 +322,7 @@ jprt.my.solaris.x64.test.targets= \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-GCOld_G1, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-GCOld_ParOldGC, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jbb_default, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jbb_SerialGC, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-jbb_ParallelGC, \
|
||||
${jprt.my.solaris.x64}-{product|fastdebug}-c2-GCOld_CMS, \
|
||||
@ -331,7 +331,7 @@ jprt.my.solaris.x64.test.targets= \
|
||||
|
||||
jprt.my.solaris.i586.test.targets= \
|
||||
${jprt.my.solaris.i586}-{product|fastdebug}-{c1|c2}-jvm98, \
|
||||
${jprt.my.solaris.i586}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.solaris.i586}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.solaris.i586}-{product|fastdebug}-{c1|c2}-scimark, \
|
||||
${jprt.my.solaris.i586}-product-{c1|c2}-runThese_Xcomp, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c1-runThese_Xcomp, \
|
||||
@ -358,7 +358,7 @@ jprt.my.solaris.i586.test.targets= \
|
||||
${jprt.my.solaris.i586}-product-c1-GCOld_G1, \
|
||||
${jprt.my.solaris.i586}-product-c1-GCOld_ParOldGC, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_default, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_default_tiered, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_default_nontiered, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_ParallelGC, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_CMS, \
|
||||
${jprt.my.solaris.i586}-fastdebug-c2-jbb_G1, \
|
||||
@ -366,7 +366,7 @@ jprt.my.solaris.i586.test.targets= \
|
||||
|
||||
jprt.my.linux.i586.test.targets = \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-{c1|c2}-jvm98, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-{c1|c2}-scimark, \
|
||||
${jprt.my.linux.i586}-product-c1-runThese_Xcomp, \
|
||||
${jprt.my.linux.i586}-fastdebug-c1-runThese_Xshare, \
|
||||
@ -386,7 +386,7 @@ jprt.my.linux.i586.test.targets = \
|
||||
${jprt.my.linux.i586}-product-{c1|c2}-GCOld_G1, \
|
||||
${jprt.my.linux.i586}-product-{c1|c2}-GCOld_ParOldGC, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c1-jbb_default, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c1-jbb_ParallelGC, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c1-jbb_CMS, \
|
||||
${jprt.my.linux.i586}-{product|fastdebug}-c1-jbb_G1, \
|
||||
@ -394,7 +394,7 @@ jprt.my.linux.i586.test.targets = \
|
||||
|
||||
jprt.my.linux.x64.test.targets = \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jvm98, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-scimark, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-GCBasher_default, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-GCBasher_SerialGC, \
|
||||
@ -411,14 +411,14 @@ jprt.my.linux.x64.test.targets = \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-GCOld_G1, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-GCOld_ParOldGC, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_default, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_ParallelGC, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_G1, \
|
||||
${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_ParOldGC
|
||||
|
||||
jprt.my.windows.i586.test.targets = \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-{c1|c2}-jvm98, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-{c1|c2}-scimark, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-runThese, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-runThese_Xcomp, \
|
||||
@ -438,7 +438,7 @@ jprt.my.windows.i586.test.targets = \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-GCOld_G1, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-GCOld_ParOldGC, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-{c1|c2}-jbb_default, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.windows.i586}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-jbb_ParallelGC, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-jbb_CMS, \
|
||||
${jprt.my.windows.i586}-product-{c1|c2}-jbb_G1, \
|
||||
@ -446,7 +446,7 @@ jprt.my.windows.i586.test.targets = \
|
||||
|
||||
jprt.my.windows.x64.test.targets = \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jvm98, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jvm98_tiered, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jvm98_nontiered, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-scimark, \
|
||||
${jprt.my.windows.x64}-product-c2-runThese, \
|
||||
${jprt.my.windows.x64}-product-c2-runThese_Xcomp, \
|
||||
@ -465,7 +465,7 @@ jprt.my.windows.x64.test.targets = \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-GCOld_G1, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-GCOld_ParOldGC, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jbb_default, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jbb_default_tiered, \
|
||||
${jprt.my.windows.x64}-{product|fastdebug}-c2-jbb_default_nontiered, \
|
||||
${jprt.my.windows.x64}-product-c2-jbb_CMS, \
|
||||
${jprt.my.windows.x64}-product-c2-jbb_ParallelGC, \
|
||||
${jprt.my.windows.x64}-product-c2-jbb_G1, \
|
||||
@ -473,9 +473,9 @@ jprt.my.windows.x64.test.targets = \
|
||||
|
||||
# Some basic "smoke" tests for OpenJDK builds
|
||||
jprt.test.targets.open = \
|
||||
${jprt.my.solaris.x64}-{productOpen|debugOpen|fastdebugOpen}-c2-jvm98_tiered, \
|
||||
${jprt.my.solaris.i586}-{productOpen|fastdebugOpen}-c2-jvm98_tiered, \
|
||||
${jprt.my.linux.x64}-{productOpen|fastdebugOpen}-c2-jvm98_tiered
|
||||
${jprt.my.solaris.x64}-{productOpen|debugOpen|fastdebugOpen}-c2-jvm98, \
|
||||
${jprt.my.solaris.i586}-{productOpen|fastdebugOpen}-c2-jvm98, \
|
||||
${jprt.my.linux.x64}-{productOpen|fastdebugOpen}-c2-jvm98
|
||||
|
||||
# Testing for actual embedded builds is different to standard
|
||||
jprt.my.linux.i586.test.targets.embedded = \
|
||||
|
||||
@ -855,12 +855,6 @@ class Assembler : public AbstractAssembler {
|
||||
Lookaside = 1 << 4
|
||||
};
|
||||
|
||||
// test if x is within signed immediate range for nbits
|
||||
static bool is_simm(intptr_t x, int nbits) { return -( intptr_t(1) << nbits-1 ) <= x && x < ( intptr_t(1) << nbits-1 ); }
|
||||
|
||||
// test if -4096 <= x <= 4095
|
||||
static bool is_simm13(intptr_t x) { return is_simm(x, 13); }
|
||||
|
||||
static bool is_in_wdisp_range(address a, address b, int nbits) {
|
||||
intptr_t d = intptr_t(b) - intptr_t(a);
|
||||
return is_simm(d, nbits + 2);
|
||||
@ -1203,7 +1197,7 @@ public:
|
||||
if (!UseCBCond || cbcond_before()) return false;
|
||||
intptr_t x = intptr_t(target_distance(L)) - intptr_t(pc());
|
||||
assert( (x & 3) == 0, "not word aligned");
|
||||
return is_simm(x, 12);
|
||||
return is_simm12(x);
|
||||
}
|
||||
|
||||
// Tells assembler you know that next instruction is delayed
|
||||
|
||||
@ -765,7 +765,7 @@ void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
|
||||
void LIR_Assembler::vtable_call(LIR_OpJavaCall* op) {
|
||||
add_debug_info_for_null_check_here(op->info());
|
||||
__ load_klass(O0, G3_scratch);
|
||||
if (__ is_simm13(op->vtable_offset())) {
|
||||
if (Assembler::is_simm13(op->vtable_offset())) {
|
||||
__ ld_ptr(G3_scratch, op->vtable_offset(), G5_method);
|
||||
} else {
|
||||
// This will generate 2 instructions
|
||||
|
||||
@ -42,7 +42,7 @@ define_pd_global(bool, ProfileInterpreter, false);
|
||||
#else
|
||||
define_pd_global(bool, ProfileInterpreter, true);
|
||||
#endif // CC_INTERP
|
||||
define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(bool, TieredCompilation, true);
|
||||
define_pd_global(intx, CompileThreshold, 10000);
|
||||
define_pd_global(intx, BackEdgeThreshold, 140000);
|
||||
|
||||
|
||||
@ -315,7 +315,7 @@ void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) {
|
||||
__ cmp_and_br_short(O7_temp, T_VOID, Assembler::equal, Assembler::pt, L_ok_4);
|
||||
extract_conversion_vminfo(_masm, L5_conversion, O5_temp);
|
||||
__ ld_ptr(L4_saved_args_base, __ argument_offset(O5_temp, O5_temp), O7_temp);
|
||||
assert(__ is_simm13(RETURN_VALUE_PLACEHOLDER), "must be simm13");
|
||||
assert(Assembler::is_simm13(RETURN_VALUE_PLACEHOLDER), "must be simm13");
|
||||
__ cmp_and_brx_short(O7_temp, (int32_t) RETURN_VALUE_PLACEHOLDER, Assembler::equal, Assembler::pt, L_ok_4);
|
||||
__ stop("damaged ricochet frame: RETURN_VALUE_PLACEHOLDER not found");
|
||||
__ BIND(L_ok_4);
|
||||
|
||||
@ -767,7 +767,7 @@ void AdapterGenerator::gen_c2i_adapter(
|
||||
// In the 64bit build because of wider slots and STACKBIAS we can run
|
||||
// out of bits in the displacement to do loads and stores. Use g3 as
|
||||
// temporary displacement.
|
||||
if (! __ is_simm13(extraspace)) {
|
||||
if (!Assembler::is_simm13(extraspace)) {
|
||||
__ set(extraspace, G3_scratch);
|
||||
__ sub(SP, G3_scratch, SP);
|
||||
} else {
|
||||
|
||||
@ -566,7 +566,7 @@ int MachCallDynamicJavaNode::ret_addr_offset() {
|
||||
} else {
|
||||
klass_load_size = 1*BytesPerInstWord;
|
||||
}
|
||||
if( Assembler::is_simm13(v_off) ) {
|
||||
if (Assembler::is_simm13(v_off)) {
|
||||
return klass_load_size +
|
||||
(2*BytesPerInstWord + // ld_ptr, ld_ptr
|
||||
NativeCall::instruction_size); // call; delay slot
|
||||
@ -1019,8 +1019,21 @@ void emit_hi(CodeBuffer &cbuf, int val) { }
|
||||
|
||||
|
||||
//=============================================================================
|
||||
const bool Matcher::constant_table_absolute_addressing = false;
|
||||
const RegMask& MachConstantBaseNode::_out_RegMask = PTR_REG_mask;
|
||||
const RegMask& MachConstantBaseNode::_out_RegMask = PTR_REG_mask();
|
||||
|
||||
int Compile::ConstantTable::calculate_table_base_offset() const {
|
||||
if (UseRDPCForConstantTableBase) {
|
||||
// The table base offset might be less but then it fits into
|
||||
// simm13 anyway and we are good (cf. MachConstantBaseNode::emit).
|
||||
return Assembler::min_simm13();
|
||||
} else {
|
||||
int offset = -(size() / 2);
|
||||
if (!Assembler::is_simm13(offset)) {
|
||||
offset = Assembler::min_simm13();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
Compile* C = ra_->C;
|
||||
@ -1028,8 +1041,9 @@ void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
MacroAssembler _masm(&cbuf);
|
||||
|
||||
Register r = as_Register(ra_->get_encode(this));
|
||||
CodeSection* cs = __ code()->consts();
|
||||
int consts_size = cs->align_at_start(cs->size());
|
||||
CodeSection* consts_section = __ code()->consts();
|
||||
int consts_size = consts_section->align_at_start(consts_section->size());
|
||||
assert(constant_table.size() == consts_size, err_msg("must be: %d == %d", constant_table.size(), consts_size));
|
||||
|
||||
if (UseRDPCForConstantTableBase) {
|
||||
// For the following RDPC logic to work correctly the consts
|
||||
@ -1037,30 +1051,37 @@ void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
// assert checks for that. The layout and the SECT_* constants
|
||||
// are defined in src/share/vm/asm/codeBuffer.hpp.
|
||||
assert(CodeBuffer::SECT_CONSTS + 1 == CodeBuffer::SECT_INSTS, "must be");
|
||||
int offset = __ offset();
|
||||
int insts_offset = __ offset();
|
||||
|
||||
// Layout:
|
||||
//
|
||||
// |----------- consts section ------------|----------- insts section -----------...
|
||||
// |------ constant table -----|- padding -|------------------x----
|
||||
// \ current PC (RDPC instruction)
|
||||
// |<------------- consts_size ----------->|<- insts_offset ->|
|
||||
// \ table base
|
||||
// The table base offset is later added to the load displacement
|
||||
// so it has to be negative.
|
||||
int table_base_offset = -(consts_size + insts_offset);
|
||||
int disp;
|
||||
|
||||
// If the displacement from the current PC to the constant table
|
||||
// base fits into simm13 we set the constant table base to the
|
||||
// current PC.
|
||||
if (__ is_simm13(-(consts_size + offset))) {
|
||||
constant_table.set_table_base_offset(-(consts_size + offset));
|
||||
if (Assembler::is_simm13(table_base_offset)) {
|
||||
constant_table.set_table_base_offset(table_base_offset);
|
||||
disp = 0;
|
||||
} else {
|
||||
// If the offset of the top constant (last entry in the table)
|
||||
// fits into simm13 we set the constant table base to the actual
|
||||
// table base.
|
||||
if (__ is_simm13(constant_table.top_offset())) {
|
||||
constant_table.set_table_base_offset(0);
|
||||
disp = consts_size + offset;
|
||||
} else {
|
||||
// Otherwise we set the constant table base in the middle of the
|
||||
// constant table.
|
||||
int half_consts_size = consts_size / 2;
|
||||
assert(half_consts_size * 2 == consts_size, "sanity");
|
||||
constant_table.set_table_base_offset(-half_consts_size); // table base offset gets added to the load displacement.
|
||||
disp = half_consts_size + offset;
|
||||
}
|
||||
// Otherwise we set the constant table base offset to the
|
||||
// maximum negative displacement of load instructions to keep
|
||||
// the disp as small as possible:
|
||||
//
|
||||
// |<------------- consts_size ----------->|<- insts_offset ->|
|
||||
// |<--------- min_simm13 --------->|<-------- disp --------->|
|
||||
// \ table base
|
||||
table_base_offset = Assembler::min_simm13();
|
||||
constant_table.set_table_base_offset(table_base_offset);
|
||||
disp = (consts_size + insts_offset) + table_base_offset;
|
||||
}
|
||||
|
||||
__ rdpc(r);
|
||||
@ -1072,8 +1093,7 @@ void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
}
|
||||
else {
|
||||
// Materialize the constant table base.
|
||||
assert(constant_table.size() == consts_size, err_msg("must be: %d == %d", constant_table.size(), consts_size));
|
||||
address baseaddr = cs->start() + -(constant_table.table_base_offset());
|
||||
address baseaddr = consts_section->start() + -(constant_table.table_base_offset());
|
||||
RelocationHolder rspec = internal_word_Relocation::spec(baseaddr);
|
||||
AddressLiteral base(baseaddr, rspec);
|
||||
__ set(base, r);
|
||||
@ -1169,6 +1189,13 @@ void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
||||
__ save(SP, G3, SP);
|
||||
}
|
||||
C->set_frame_complete( __ offset() );
|
||||
|
||||
if (!UseRDPCForConstantTableBase && C->has_mach_constant_base_node()) {
|
||||
// NOTE: We set the table base offset here because users might be
|
||||
// emitted before MachConstantBaseNode.
|
||||
Compile::ConstantTable& constant_table = C->constant_table();
|
||||
constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
|
||||
}
|
||||
}
|
||||
|
||||
uint MachPrologNode::size(PhaseRegAlloc *ra_) const {
|
||||
@ -1843,7 +1870,7 @@ const bool Matcher::convL2FSupported(void) {
|
||||
bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) {
|
||||
// The passed offset is relative to address of the branch.
|
||||
// Don't need to adjust the offset.
|
||||
return UseCBCond && Assembler::is_simm(offset, 12);
|
||||
return UseCBCond && Assembler::is_simm12(offset);
|
||||
}
|
||||
|
||||
const bool Matcher::isSimpleConstant64(jlong value) {
|
||||
@ -1997,7 +2024,7 @@ RegMask Matcher::modL_proj_mask() {
|
||||
}
|
||||
|
||||
const RegMask Matcher::method_handle_invoke_SP_save_mask() {
|
||||
return L7_REGP_mask;
|
||||
return L7_REGP_mask();
|
||||
}
|
||||
|
||||
%}
|
||||
@ -2072,8 +2099,8 @@ encode %{
|
||||
%}
|
||||
|
||||
enc_class form3_mem_reg_long_unaligned_marshal( memory mem, iRegL reg ) %{
|
||||
assert( Assembler::is_simm13($mem$$disp ), "need disp and disp+4" );
|
||||
assert( Assembler::is_simm13($mem$$disp+4), "need disp and disp+4" );
|
||||
assert(Assembler::is_simm13($mem$$disp ), "need disp and disp+4");
|
||||
assert(Assembler::is_simm13($mem$$disp+4), "need disp and disp+4");
|
||||
guarantee($mem$$index == R_G0_enc, "double index?");
|
||||
emit_form3_mem_reg(cbuf, this, $primary, -1, $mem$$base, $mem$$disp+4, R_G0_enc, R_O7_enc );
|
||||
emit_form3_mem_reg(cbuf, this, $primary, -1, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg );
|
||||
@ -2082,8 +2109,8 @@ encode %{
|
||||
%}
|
||||
|
||||
enc_class form3_mem_reg_double_unaligned( memory mem, RegD_low reg ) %{
|
||||
assert( Assembler::is_simm13($mem$$disp ), "need disp and disp+4" );
|
||||
assert( Assembler::is_simm13($mem$$disp+4), "need disp and disp+4" );
|
||||
assert(Assembler::is_simm13($mem$$disp ), "need disp and disp+4");
|
||||
assert(Assembler::is_simm13($mem$$disp+4), "need disp and disp+4");
|
||||
guarantee($mem$$index == R_G0_enc, "double index?");
|
||||
// Load long with 2 instructions
|
||||
emit_form3_mem_reg(cbuf, this, $primary, -1, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg+0 );
|
||||
@ -2563,7 +2590,7 @@ encode %{
|
||||
}
|
||||
int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size();
|
||||
int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes();
|
||||
if( __ is_simm13(v_off) ) {
|
||||
if (Assembler::is_simm13(v_off)) {
|
||||
__ ld_ptr(G3, v_off, G5_method);
|
||||
} else {
|
||||
// Generate 2 instructions
|
||||
@ -3336,7 +3363,7 @@ operand immI() %{
|
||||
|
||||
// Integer Immediate: 8-bit
|
||||
operand immI8() %{
|
||||
predicate(Assembler::is_simm(n->get_int(), 8));
|
||||
predicate(Assembler::is_simm8(n->get_int()));
|
||||
match(ConI);
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
@ -3365,7 +3392,7 @@ operand immI13m7() %{
|
||||
|
||||
// Integer Immediate: 16-bit
|
||||
operand immI16() %{
|
||||
predicate(Assembler::is_simm(n->get_int(), 16));
|
||||
predicate(Assembler::is_simm16(n->get_int()));
|
||||
match(ConI);
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
@ -3393,7 +3420,7 @@ operand immU6() %{
|
||||
|
||||
// Integer Immediate: 11-bit
|
||||
operand immI11() %{
|
||||
predicate(Assembler::is_simm(n->get_int(),11));
|
||||
predicate(Assembler::is_simm11(n->get_int()));
|
||||
match(ConI);
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
@ -3402,7 +3429,7 @@ operand immI11() %{
|
||||
|
||||
// Integer Immediate: 5-bit
|
||||
operand immI5() %{
|
||||
predicate(Assembler::is_simm(n->get_int(), 5));
|
||||
predicate(Assembler::is_simm5(n->get_int()));
|
||||
match(ConI);
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
@ -3634,7 +3661,7 @@ operand immL0() %{
|
||||
|
||||
// Integer Immediate: 5-bit
|
||||
operand immL5() %{
|
||||
predicate(n->get_long() == (int)n->get_long() && Assembler::is_simm((int)n->get_long(), 5));
|
||||
predicate(n->get_long() == (int)n->get_long() && Assembler::is_simm5((int)n->get_long()));
|
||||
match(ConL);
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
@ -9251,13 +9278,16 @@ instruct jumpXtnd(iRegX switch_val, o7RegI table) %{
|
||||
|
||||
format %{ "ADD $constanttablebase, $constantoffset, O7\n\t"
|
||||
"LD [O7 + $switch_val], O7\n\t"
|
||||
"JUMP O7"
|
||||
%}
|
||||
"JUMP O7" %}
|
||||
ins_encode %{
|
||||
// Calculate table address into a register.
|
||||
Register table_reg;
|
||||
Register label_reg = O7;
|
||||
if (constant_offset() == 0) {
|
||||
// If we are calculating the size of this instruction don't trust
|
||||
// zero offsets because they might change when
|
||||
// MachConstantBaseNode decides to optimize the constant table
|
||||
// base.
|
||||
if ((constant_offset() == 0) && !Compile::current()->in_scratch_emit_size()) {
|
||||
table_reg = $constanttablebase;
|
||||
} else {
|
||||
table_reg = O7;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2011, 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
|
||||
@ -83,7 +83,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
|
||||
}
|
||||
#endif
|
||||
int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes();
|
||||
if( __ is_simm13(v_off) ) {
|
||||
if (Assembler::is_simm13(v_off)) {
|
||||
__ ld_ptr(G3, v_off, G5_method);
|
||||
} else {
|
||||
__ set(v_off,G5);
|
||||
|
||||
@ -3535,7 +3535,8 @@ bool Assembler::reachable(AddressLiteral adr) {
|
||||
// addressing.
|
||||
bool Assembler::is_polling_page_far() {
|
||||
intptr_t addr = (intptr_t)os::get_polling_page();
|
||||
return !is_simm32(addr - (intptr_t)CodeCache::low_bound()) ||
|
||||
return ForceUnreachable ||
|
||||
!is_simm32(addr - (intptr_t)CodeCache::low_bound()) ||
|
||||
!is_simm32(addr - (intptr_t)CodeCache::high_bound());
|
||||
}
|
||||
|
||||
|
||||
@ -693,17 +693,6 @@ private:
|
||||
static address locate_next_instruction(address inst);
|
||||
|
||||
// Utilities
|
||||
|
||||
#ifdef _LP64
|
||||
static bool is_simm(int64_t x, int nbits) { return -(CONST64(1) << (nbits-1)) <= x &&
|
||||
x < (CONST64(1) << (nbits-1)); }
|
||||
static bool is_simm32(int64_t x) { return x == (int64_t)(int32_t)x; }
|
||||
#else
|
||||
static bool is_simm(int32_t x, int nbits) { return -(1 << (nbits-1)) <= x &&
|
||||
x < (1 << (nbits-1)); }
|
||||
static bool is_simm32(int32_t x) { return true; }
|
||||
#endif // _LP64
|
||||
|
||||
static bool is_polling_page_far() NOT_LP64({ return false;});
|
||||
|
||||
// Generic instructions
|
||||
|
||||
@ -44,7 +44,7 @@ define_pd_global(bool, ProfileInterpreter, false);
|
||||
#else
|
||||
define_pd_global(bool, ProfileInterpreter, true);
|
||||
#endif // CC_INTERP
|
||||
define_pd_global(bool, TieredCompilation, false);
|
||||
define_pd_global(bool, TieredCompilation, true);
|
||||
define_pd_global(intx, CompileThreshold, 10000);
|
||||
define_pd_global(intx, BackEdgeThreshold, 100000);
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
// Adapters
|
||||
enum /* platform_dependent_constants */ {
|
||||
adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 15000)) LP64_ONLY(32000 DEBUG_ONLY(+ 80000))
|
||||
adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 15000)) LP64_ONLY(32000 DEBUG_ONLY(+ 120000))
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@ -95,6 +95,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
#define inc_counter_np(counter) (0)
|
||||
#else
|
||||
void inc_counter_np_(int& counter) {
|
||||
// This can destroy rscratch1 if counter is far from the code cache
|
||||
__ incrementl(ExternalAddress((address)&counter));
|
||||
}
|
||||
#define inc_counter_np(counter) \
|
||||
@ -1268,7 +1269,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ subptr(end, start); // number of bytes to copy
|
||||
|
||||
intptr_t disp = (intptr_t) ct->byte_map_base;
|
||||
if (__ is_simm32(disp)) {
|
||||
if (Assembler::is_simm32(disp)) {
|
||||
Address cardtable(noreg, noreg, Address::no_scale, disp);
|
||||
__ lea(scratch, cardtable);
|
||||
} else {
|
||||
@ -1466,8 +1467,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ movb(Address(end_to, 8), rax);
|
||||
|
||||
__ BIND(L_exit);
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1555,8 +1556,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ decrement(qword_count);
|
||||
__ jcc(Assembler::notZero, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1564,8 +1565,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// Copy in 32-bytes chunks
|
||||
copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jbyte_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1658,8 +1659,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ movw(Address(end_to, 8), rax);
|
||||
|
||||
__ BIND(L_exit);
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1759,8 +1760,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ decrement(qword_count);
|
||||
__ jcc(Assembler::notZero, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1768,8 +1769,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// Copy in 32-bytes chunks
|
||||
copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jshort_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1859,8 +1860,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ leaq(end_to, Address(saved_to, dword_count, Address::times_4, -4));
|
||||
gen_write_ref_array_post_barrier(saved_to, end_to, rax);
|
||||
}
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1940,11 +1941,11 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ decrement(qword_count);
|
||||
__ jcc(Assembler::notZero, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr);
|
||||
if (is_oop) {
|
||||
__ jmp(L_exit);
|
||||
}
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1952,7 +1953,6 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// Copy in 32-bytes chunks
|
||||
copy_32_bytes_backward(from, to, qword_count, rax, L_copy_32_bytes, L_copy_8_bytes);
|
||||
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr);
|
||||
__ bind(L_exit);
|
||||
if (is_oop) {
|
||||
Register end_to = rdx;
|
||||
@ -1960,6 +1960,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
gen_write_ref_array_post_barrier(to, end_to, rax);
|
||||
}
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jint_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -2032,8 +2033,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
if (is_oop) {
|
||||
__ jmp(L_exit);
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -2045,11 +2046,13 @@ class StubGenerator: public StubCodeGenerator {
|
||||
if (is_oop) {
|
||||
__ BIND(L_exit);
|
||||
gen_write_ref_array_post_barrier(saved_to, end_to, rax);
|
||||
inc_counter_np(SharedRuntime::_oop_array_copy_ctr);
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr);
|
||||
}
|
||||
restore_arg_regs();
|
||||
if (is_oop) {
|
||||
inc_counter_np(SharedRuntime::_oop_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
}
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -2113,8 +2116,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
if (is_oop) {
|
||||
__ jmp(L_exit);
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -2127,11 +2130,13 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ BIND(L_exit);
|
||||
__ lea(rcx, Address(to, saved_count, Address::times_8, -8));
|
||||
gen_write_ref_array_post_barrier(to, rcx, rax);
|
||||
inc_counter_np(SharedRuntime::_oop_array_copy_ctr);
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr);
|
||||
}
|
||||
restore_arg_regs();
|
||||
if (is_oop) {
|
||||
inc_counter_np(SharedRuntime::_oop_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
} else {
|
||||
inc_counter_np(SharedRuntime::_jlong_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
}
|
||||
__ xorptr(rax, rax); // return 0
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -2331,8 +2336,8 @@ class StubGenerator: public StubCodeGenerator {
|
||||
__ BIND(L_done);
|
||||
__ movptr(r13, Address(rsp, saved_r13_offset * wordSize));
|
||||
__ movptr(r14, Address(rsp, saved_r14_offset * wordSize));
|
||||
inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr);
|
||||
restore_arg_regs();
|
||||
inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr); // Update counter after rscratch1 is free
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
|
||||
|
||||
@ -507,9 +507,12 @@ void encode_CopyXD( CodeBuffer &cbuf, int dst_encoding, int src_encoding ) {
|
||||
|
||||
|
||||
//=============================================================================
|
||||
const bool Matcher::constant_table_absolute_addressing = true;
|
||||
const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty;
|
||||
|
||||
int Compile::ConstantTable::calculate_table_base_offset() const {
|
||||
return 0; // absolute addressing, no offset
|
||||
}
|
||||
|
||||
void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
// Empty encoding
|
||||
}
|
||||
@ -639,6 +642,12 @@ void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (C->has_mach_constant_base_node()) {
|
||||
// NOTE: We set the table base offset here because users might be
|
||||
// emitted before MachConstantBaseNode.
|
||||
Compile::ConstantTable& constant_table = C->constant_table();
|
||||
constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
|
||||
}
|
||||
}
|
||||
|
||||
uint MachPrologNode::size(PhaseRegAlloc *ra_) const {
|
||||
@ -1515,12 +1524,12 @@ bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) {
|
||||
|
||||
// Register for DIVI projection of divmodI
|
||||
RegMask Matcher::divI_proj_mask() {
|
||||
return EAX_REG_mask;
|
||||
return EAX_REG_mask();
|
||||
}
|
||||
|
||||
// Register for MODI projection of divmodI
|
||||
RegMask Matcher::modI_proj_mask() {
|
||||
return EDX_REG_mask;
|
||||
return EDX_REG_mask();
|
||||
}
|
||||
|
||||
// Register for DIVL projection of divmodL
|
||||
@ -1536,7 +1545,7 @@ RegMask Matcher::modL_proj_mask() {
|
||||
}
|
||||
|
||||
const RegMask Matcher::method_handle_invoke_SP_save_mask() {
|
||||
return EBP_REG_mask;
|
||||
return EBP_REG_mask();
|
||||
}
|
||||
|
||||
// Returns true if the high 32 bits of the value is known to be zero.
|
||||
|
||||
@ -843,9 +843,12 @@ void emit_cmpfp_fixup(MacroAssembler& _masm) {
|
||||
|
||||
|
||||
//=============================================================================
|
||||
const bool Matcher::constant_table_absolute_addressing = true;
|
||||
const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty;
|
||||
|
||||
int Compile::ConstantTable::calculate_table_base_offset() const {
|
||||
return 0; // absolute addressing, no offset
|
||||
}
|
||||
|
||||
void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
||||
// Empty encoding
|
||||
}
|
||||
@ -977,6 +980,13 @@ void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const
|
||||
masm.bind(L);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (C->has_mach_constant_base_node()) {
|
||||
// NOTE: We set the table base offset here because users might be
|
||||
// emitted before MachConstantBaseNode.
|
||||
Compile::ConstantTable& constant_table = C->constant_table();
|
||||
constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
|
||||
}
|
||||
}
|
||||
|
||||
uint MachPrologNode::size(PhaseRegAlloc* ra_) const
|
||||
@ -2079,26 +2089,26 @@ bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) {
|
||||
|
||||
// Register for DIVI projection of divmodI
|
||||
RegMask Matcher::divI_proj_mask() {
|
||||
return INT_RAX_REG_mask;
|
||||
return INT_RAX_REG_mask();
|
||||
}
|
||||
|
||||
// Register for MODI projection of divmodI
|
||||
RegMask Matcher::modI_proj_mask() {
|
||||
return INT_RDX_REG_mask;
|
||||
return INT_RDX_REG_mask();
|
||||
}
|
||||
|
||||
// Register for DIVL projection of divmodL
|
||||
RegMask Matcher::divL_proj_mask() {
|
||||
return LONG_RAX_REG_mask;
|
||||
return LONG_RAX_REG_mask();
|
||||
}
|
||||
|
||||
// Register for MODL projection of divmodL
|
||||
RegMask Matcher::modL_proj_mask() {
|
||||
return LONG_RDX_REG_mask;
|
||||
return LONG_RDX_REG_mask();
|
||||
}
|
||||
|
||||
const RegMask Matcher::method_handle_invoke_SP_save_mask() {
|
||||
return PTR_RBP_REG_mask;
|
||||
return PTR_RBP_REG_mask();
|
||||
}
|
||||
|
||||
static Address build_address(int b, int i, int s, int d) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2011, 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
|
||||
@ -982,27 +982,9 @@ void ADLParser::frame_parse(void) {
|
||||
}
|
||||
if (strcmp(token,"interpreter_frame_pointer")==0) {
|
||||
interpreter_frame_pointer_parse(frame, false);
|
||||
// Add reg_class interpreter_frame_pointer_reg
|
||||
if( _AD._register != NULL ) {
|
||||
RegClass *reg_class = _AD._register->addRegClass("interpreter_frame_pointer_reg");
|
||||
char *interpreter_frame_pointer_reg = frame->_interpreter_frame_pointer_reg;
|
||||
if( interpreter_frame_pointer_reg != NULL ) {
|
||||
RegDef *regDef = _AD._register->getRegDef(interpreter_frame_pointer_reg);
|
||||
reg_class->addReg(regDef); // add regDef to regClass
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strcmp(token,"inline_cache_reg")==0) {
|
||||
inline_cache_parse(frame, false);
|
||||
// Add reg_class inline_cache_reg
|
||||
if( _AD._register != NULL ) {
|
||||
RegClass *reg_class = _AD._register->addRegClass("inline_cache_reg");
|
||||
char *inline_cache_reg = frame->_inline_cache_reg;
|
||||
if( inline_cache_reg != NULL ) {
|
||||
RegDef *regDef = _AD._register->getRegDef(inline_cache_reg);
|
||||
reg_class->addReg(regDef); // add regDef to regClass
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strcmp(token,"compiler_method_oop_reg")==0) {
|
||||
parse_err(WARN, "Using obsolete Token, compiler_method_oop_reg");
|
||||
@ -1010,15 +992,6 @@ void ADLParser::frame_parse(void) {
|
||||
}
|
||||
if (strcmp(token,"interpreter_method_oop_reg")==0) {
|
||||
interpreter_method_oop_parse(frame, false);
|
||||
// Add reg_class interpreter_method_oop_reg
|
||||
if( _AD._register != NULL ) {
|
||||
RegClass *reg_class = _AD._register->addRegClass("interpreter_method_oop_reg");
|
||||
char *method_oop_reg = frame->_interpreter_method_oop_reg;
|
||||
if( method_oop_reg != NULL ) {
|
||||
RegDef *regDef = _AD._register->getRegDef(method_oop_reg);
|
||||
reg_class->addReg(regDef); // add regDef to regClass
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strcmp(token,"cisc_spilling_operand_name")==0) {
|
||||
cisc_spilling_operand_name_parse(frame, false);
|
||||
@ -2363,6 +2336,14 @@ void ADLParser::reg_class_parse(void) {
|
||||
}
|
||||
}
|
||||
next_char(); // Skip closing ')'
|
||||
} else if (_curchar == '%') {
|
||||
char *code = find_cpp_block("reg class");
|
||||
if (code == NULL) {
|
||||
parse_err(SYNERR, "missing code declaration for reg class.\n");
|
||||
return;
|
||||
}
|
||||
reg_class->_user_defined = code;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for terminating ';'
|
||||
@ -3115,7 +3096,7 @@ void ADLParser::constant_parse_expression(EncClass* encoding, char* ec_name) {
|
||||
encoding->add_code(" _constant = C->constant_table().add");
|
||||
|
||||
// Parse everything in ( ) expression.
|
||||
encoding->add_code("(");
|
||||
encoding->add_code("(this, ");
|
||||
next_char(); // Skip '('
|
||||
int parens_depth = 1;
|
||||
|
||||
@ -3130,7 +3111,8 @@ void ADLParser::constant_parse_expression(EncClass* encoding, char* ec_name) {
|
||||
}
|
||||
else if (_curchar == ')') {
|
||||
parens_depth--;
|
||||
encoding->add_code(")");
|
||||
if (parens_depth > 0)
|
||||
encoding->add_code(")");
|
||||
next_char();
|
||||
}
|
||||
else {
|
||||
@ -3157,7 +3139,7 @@ void ADLParser::constant_parse_expression(EncClass* encoding, char* ec_name) {
|
||||
}
|
||||
|
||||
// Finish code line.
|
||||
encoding->add_code(";");
|
||||
encoding->add_code(");");
|
||||
|
||||
if (_AD._adlocation_debug) {
|
||||
encoding->add_code(end_line_marker());
|
||||
@ -3817,7 +3799,7 @@ void ADLParser::effect_parse(InstructForm *instr) {
|
||||
return;
|
||||
}
|
||||
// Get list of effect-operand pairs and insert into dictionary
|
||||
else get_effectlist(instr->_effects, instr->_localNames);
|
||||
else get_effectlist(instr->_effects, instr->_localNames, instr->_has_call);
|
||||
|
||||
// Debug Stuff
|
||||
if (_AD._adl_debug > 1) fprintf(stderr,"Effect description: %s\n", desc);
|
||||
@ -4595,7 +4577,7 @@ void ADLParser::get_oplist(NameList ¶meters, FormDict &operands) {
|
||||
// effect, and the second must be the name of an operand defined in the
|
||||
// operand list of this instruction. Stores the names with a pointer to the
|
||||
// effect form in a local effects table.
|
||||
void ADLParser::get_effectlist(FormDict &effects, FormDict &operands) {
|
||||
void ADLParser::get_effectlist(FormDict &effects, FormDict &operands, bool& has_call) {
|
||||
OperandForm *opForm;
|
||||
Effect *eForm;
|
||||
char *ident;
|
||||
@ -4628,26 +4610,31 @@ void ADLParser::get_effectlist(FormDict &effects, FormDict &operands) {
|
||||
// Debugging Stuff
|
||||
if (_AD._adl_debug > 1) fprintf(stderr, "\tEffect Type: %s\t", ident);
|
||||
skipws();
|
||||
// Get name of operand and check that it is in the local name table
|
||||
if( (ident = get_unique_ident(effects, "effect")) == NULL) {
|
||||
parse_err(SYNERR, "missing operand identifier in effect list\n");
|
||||
return;
|
||||
}
|
||||
const Form *form = operands[ident];
|
||||
opForm = form ? form->is_operand() : NULL;
|
||||
if( opForm == NULL ) {
|
||||
if( form && form->is_opclass() ) {
|
||||
const char* cname = form->is_opclass()->_ident;
|
||||
parse_err(SYNERR, "operand classes are illegal in effect lists (found %s %s)\n", cname, ident);
|
||||
} else {
|
||||
parse_err(SYNERR, "undefined operand %s in effect list\n", ident);
|
||||
if (eForm->is(Component::CALL)) {
|
||||
if (_AD._adl_debug > 1) fprintf(stderr, "\n");
|
||||
has_call = true;
|
||||
} else {
|
||||
// Get name of operand and check that it is in the local name table
|
||||
if( (ident = get_unique_ident(effects, "effect")) == NULL) {
|
||||
parse_err(SYNERR, "missing operand identifier in effect list\n");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
const Form *form = operands[ident];
|
||||
opForm = form ? form->is_operand() : NULL;
|
||||
if( opForm == NULL ) {
|
||||
if( form && form->is_opclass() ) {
|
||||
const char* cname = form->is_opclass()->_ident;
|
||||
parse_err(SYNERR, "operand classes are illegal in effect lists (found %s %s)\n", cname, ident);
|
||||
} else {
|
||||
parse_err(SYNERR, "undefined operand %s in effect list\n", ident);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Add the pair to the effects table
|
||||
effects.Insert(ident, eForm);
|
||||
// Debugging Stuff
|
||||
if (_AD._adl_debug > 1) fprintf(stderr, "\tOperand Name: %s\n", ident);
|
||||
}
|
||||
// Add the pair to the effects table
|
||||
effects.Insert(ident, eForm);
|
||||
// Debugging Stuff
|
||||
if (_AD._adl_debug > 1) fprintf(stderr, "\tOperand Name: %s\n", ident);
|
||||
skipws();
|
||||
} while(_curchar == ',');
|
||||
|
||||
|
||||
@ -232,7 +232,7 @@ protected:
|
||||
char *get_relation_dup(void);
|
||||
|
||||
void get_oplist(NameList ¶meters, FormDict &operands);// Parse type-operand pairs
|
||||
void get_effectlist(FormDict &effects, FormDict &operands); // Parse effect-operand pairs
|
||||
void get_effectlist(FormDict &effects, FormDict &operands, bool& has_call); // Parse effect-operand pairs
|
||||
// Return the contents of a parenthesized expression.
|
||||
// Requires initial '(' and consumes final ')', which is replaced by '\0'.
|
||||
char *get_paren_expr(const char *description, bool include_location = false);
|
||||
|
||||
@ -823,9 +823,9 @@ static const char *getRegMask(const char *reg_class_name) {
|
||||
} else {
|
||||
char *rc_name = toUpper(reg_class_name);
|
||||
const char *mask = "_mask";
|
||||
int length = (int)strlen(rc_name) + (int)strlen(mask) + 3;
|
||||
int length = (int)strlen(rc_name) + (int)strlen(mask) + 5;
|
||||
char *regMask = new char[length];
|
||||
sprintf(regMask,"%s%s", rc_name, mask);
|
||||
sprintf(regMask,"%s%s()", rc_name, mask);
|
||||
return regMask;
|
||||
}
|
||||
}
|
||||
@ -1018,6 +1018,9 @@ void ArchDesc::initBaseOpTypes() {
|
||||
ident = "TEMP";
|
||||
eForm = new Effect(ident);
|
||||
_globalNames.Insert(ident, eForm);
|
||||
ident = "CALL";
|
||||
eForm = new Effect(ident);
|
||||
_globalNames.Insert(ident, eForm);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ -219,7 +219,9 @@ void RegDef::output(FILE *fp) { // Write info to output files
|
||||
|
||||
//------------------------------RegClass---------------------------------------
|
||||
// Construct a register class into which registers will be inserted
|
||||
RegClass::RegClass(const char *classid) : _stack_or_reg(false), _classid(classid), _regDef(cmpstr,hashstr, Form::arena) {
|
||||
RegClass::RegClass(const char *classid) : _stack_or_reg(false), _classid(classid), _regDef(cmpstr,hashstr, Form::arena),
|
||||
_user_defined(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// record a register in this class
|
||||
|
||||
@ -161,6 +161,7 @@ public:
|
||||
NameList _regDefs; // List of registers in class
|
||||
Dict _regDef; // Dictionary of registers in class
|
||||
bool _stack_or_reg; // Allowed on any stack slot
|
||||
char* _user_defined;
|
||||
|
||||
// Public Methods
|
||||
RegClass(const char *classid);// Constructor
|
||||
|
||||
@ -31,7 +31,8 @@ InstructForm::InstructForm(const char *id, bool ideal_only)
|
||||
: _ident(id), _ideal_only(ideal_only),
|
||||
_localNames(cmpstr, hashstr, Form::arena),
|
||||
_effects(cmpstr, hashstr, Form::arena),
|
||||
_is_mach_constant(false)
|
||||
_is_mach_constant(false),
|
||||
_has_call(false)
|
||||
{
|
||||
_ftype = Form::INS;
|
||||
|
||||
@ -62,7 +63,8 @@ InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule)
|
||||
: _ident(id), _ideal_only(false),
|
||||
_localNames(instr->_localNames),
|
||||
_effects(instr->_effects),
|
||||
_is_mach_constant(false)
|
||||
_is_mach_constant(false),
|
||||
_has_call(false)
|
||||
{
|
||||
_ftype = Form::INS;
|
||||
|
||||
@ -1754,6 +1756,7 @@ static int effect_lookup(const char *name) {
|
||||
if(!strcmp(name, "USE_KILL")) return Component::USE_KILL;
|
||||
if(!strcmp(name, "TEMP")) return Component::TEMP;
|
||||
if(!strcmp(name, "INVALID")) return Component::INVALID;
|
||||
if(!strcmp(name, "CALL")) return Component::CALL;
|
||||
assert( false,"Invalid effect name specified\n");
|
||||
return Component::INVALID;
|
||||
}
|
||||
|
||||
@ -111,6 +111,8 @@ public:
|
||||
ComponentList _components; // List of Components matches MachNode's
|
||||
// operand structure
|
||||
|
||||
bool _has_call; // contain a call and caller save registers should be saved?
|
||||
|
||||
// Public Methods
|
||||
InstructForm(const char *id, bool ideal_only = false);
|
||||
InstructForm(const char *id, InstructForm *instr, MatchRule *rule);
|
||||
@ -895,7 +897,8 @@ public:
|
||||
DEF = 0x2, USE_DEF = 0x3,
|
||||
KILL = 0x4, USE_KILL = 0x5,
|
||||
SYNTHETIC = 0x8,
|
||||
TEMP = USE | SYNTHETIC
|
||||
TEMP = USE | SYNTHETIC,
|
||||
CALL = 0x10
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -162,11 +162,17 @@ void ArchDesc::declare_register_masks(FILE *fp_hpp) {
|
||||
RegClass *reg_class = _register->getRegClass(rc_name);
|
||||
assert( reg_class, "Using an undefined register class");
|
||||
|
||||
int len = RegisterForm::RegMask_Size();
|
||||
fprintf(fp_hpp, "extern const RegMask %s%s_mask;\n", prefix, toUpper( rc_name ) );
|
||||
if (reg_class->_user_defined == NULL) {
|
||||
fprintf(fp_hpp, "extern const RegMask _%s%s_mask;\n", prefix, toUpper( rc_name ) );
|
||||
fprintf(fp_hpp, "inline const RegMask &%s%s_mask() { return _%s%s_mask; }\n", prefix, toUpper( rc_name ), prefix, toUpper( rc_name ));
|
||||
} else {
|
||||
fprintf(fp_hpp, "inline const RegMask &%s%s_mask() { %s }\n", prefix, toUpper( rc_name ), reg_class->_user_defined);
|
||||
}
|
||||
|
||||
if( reg_class->_stack_or_reg ) {
|
||||
fprintf(fp_hpp, "extern const RegMask %sSTACK_OR_%s_mask;\n", prefix, toUpper( rc_name ) );
|
||||
assert(reg_class->_user_defined == NULL, "no user defined reg class here");
|
||||
fprintf(fp_hpp, "extern const RegMask _%sSTACK_OR_%s_mask;\n", prefix, toUpper( rc_name ) );
|
||||
fprintf(fp_hpp, "inline const RegMask &%sSTACK_OR_%s_mask() { return _%sSTACK_OR_%s_mask; }\n", prefix, toUpper( rc_name ), prefix, toUpper( rc_name ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,8 +194,10 @@ void ArchDesc::build_register_masks(FILE *fp_cpp) {
|
||||
RegClass *reg_class = _register->getRegClass(rc_name);
|
||||
assert( reg_class, "Using an undefined register class");
|
||||
|
||||
if (reg_class->_user_defined != NULL) continue;
|
||||
|
||||
int len = RegisterForm::RegMask_Size();
|
||||
fprintf(fp_cpp, "const RegMask %s%s_mask(", prefix, toUpper( rc_name ) );
|
||||
fprintf(fp_cpp, "const RegMask _%s%s_mask(", prefix, toUpper( rc_name ) );
|
||||
{ int i;
|
||||
for( i = 0; i < len-1; i++ )
|
||||
fprintf(fp_cpp," 0x%x,",reg_class->regs_in_word(i,false));
|
||||
@ -198,7 +206,7 @@ void ArchDesc::build_register_masks(FILE *fp_cpp) {
|
||||
|
||||
if( reg_class->_stack_or_reg ) {
|
||||
int i;
|
||||
fprintf(fp_cpp, "const RegMask %sSTACK_OR_%s_mask(", prefix, toUpper( rc_name ) );
|
||||
fprintf(fp_cpp, "const RegMask _%sSTACK_OR_%s_mask(", prefix, toUpper( rc_name ) );
|
||||
for( i = 0; i < len-1; i++ )
|
||||
fprintf(fp_cpp," 0x%x,",reg_class->regs_in_word(i,true));
|
||||
fprintf(fp_cpp," 0x%x );\n",reg_class->regs_in_word(i,true));
|
||||
@ -2585,9 +2593,9 @@ void ArchDesc::defineEvalConstant(FILE* fp, InstructForm& inst) {
|
||||
// Output instruction's emit prototype
|
||||
fprintf(fp, "void %sNode::eval_constant(Compile* C) {\n", inst._ident);
|
||||
|
||||
// For ideal jump nodes, allocate a jump table.
|
||||
// For ideal jump nodes, add a jump-table entry.
|
||||
if (inst.is_ideal_jump()) {
|
||||
fprintf(fp, " _constant = C->constant_table().allocate_jump_table(this);\n");
|
||||
fprintf(fp, " _constant = C->constant_table().add_jump_table(this);\n");
|
||||
}
|
||||
|
||||
// If user did not define an encode section,
|
||||
@ -2690,7 +2698,7 @@ static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) {
|
||||
if (strcmp(first_reg_class, "stack_slots") == 0) {
|
||||
fprintf(fp," return &(Compile::current()->FIRST_STACK_mask());\n");
|
||||
} else {
|
||||
fprintf(fp," return &%s_mask;\n", toUpper(first_reg_class));
|
||||
fprintf(fp," return &%s_mask();\n", toUpper(first_reg_class));
|
||||
}
|
||||
} else {
|
||||
// Build a switch statement to return the desired mask.
|
||||
@ -2702,7 +2710,7 @@ static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) {
|
||||
if( !strcmp(reg_class, "stack_slots") ) {
|
||||
fprintf(fp, " case %d: return &(Compile::current()->FIRST_STACK_mask());\n", index);
|
||||
} else {
|
||||
fprintf(fp, " case %d: return &%s_mask;\n", index, toUpper(reg_class));
|
||||
fprintf(fp, " case %d: return &%s_mask();\n", index, toUpper(reg_class));
|
||||
}
|
||||
}
|
||||
fprintf(fp," }\n");
|
||||
@ -4080,8 +4088,6 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) {
|
||||
fprintf(fp_cpp,"OptoReg::Name Matcher::inline_cache_reg() {");
|
||||
fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n",
|
||||
_frame->_inline_cache_reg);
|
||||
fprintf(fp_cpp,"const RegMask &Matcher::inline_cache_reg_mask() {");
|
||||
fprintf(fp_cpp," return INLINE_CACHE_REG_mask; }\n\n");
|
||||
fprintf(fp_cpp,"int Matcher::inline_cache_reg_encode() {");
|
||||
fprintf(fp_cpp," return _regEncode[inline_cache_reg()]; }\n\n");
|
||||
|
||||
@ -4089,8 +4095,6 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) {
|
||||
fprintf(fp_cpp,"OptoReg::Name Matcher::interpreter_method_oop_reg() {");
|
||||
fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n",
|
||||
_frame->_interpreter_method_oop_reg);
|
||||
fprintf(fp_cpp,"const RegMask &Matcher::interpreter_method_oop_reg_mask() {");
|
||||
fprintf(fp_cpp," return INTERPRETER_METHOD_OOP_REG_mask; }\n\n");
|
||||
fprintf(fp_cpp,"int Matcher::interpreter_method_oop_reg_encode() {");
|
||||
fprintf(fp_cpp," return _regEncode[interpreter_method_oop_reg()]; }\n\n");
|
||||
|
||||
@ -4101,11 +4105,6 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) {
|
||||
else
|
||||
fprintf(fp_cpp," return OptoReg::Name(%s_num); }\n\n",
|
||||
_frame->_interpreter_frame_pointer_reg);
|
||||
fprintf(fp_cpp,"const RegMask &Matcher::interpreter_frame_pointer_reg_mask() {");
|
||||
if (_frame->_interpreter_frame_pointer_reg == NULL)
|
||||
fprintf(fp_cpp," static RegMask dummy; return dummy; }\n\n");
|
||||
else
|
||||
fprintf(fp_cpp," return INTERPRETER_FRAME_POINTER_REG_mask; }\n\n");
|
||||
|
||||
// Frame Pointer definition
|
||||
/* CNC - I can not contemplate having a different frame pointer between
|
||||
|
||||
@ -1720,6 +1720,16 @@ void ArchDesc::declareClasses(FILE *fp) {
|
||||
}
|
||||
}
|
||||
|
||||
// flag: if this instruction is implemented with a call
|
||||
if ( instr->_has_call ) {
|
||||
if ( node_flags_set ) {
|
||||
fprintf(fp," | Flag_has_call");
|
||||
} else {
|
||||
fprintf(fp,"init_flags(Flag_has_call");
|
||||
node_flags_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( node_flags_set ) {
|
||||
fprintf(fp,"); ");
|
||||
}
|
||||
|
||||
@ -257,6 +257,29 @@ class AbstractAssembler : public ResourceObj {
|
||||
// ensure buf contains all code (call this before using/copying the code)
|
||||
void flush();
|
||||
|
||||
// min and max values for signed immediate ranges
|
||||
static int min_simm(int nbits) { return -(intptr_t(1) << (nbits - 1)) ; }
|
||||
static int max_simm(int nbits) { return (intptr_t(1) << (nbits - 1)) - 1; }
|
||||
|
||||
// Define some:
|
||||
static int min_simm10() { return min_simm(10); }
|
||||
static int min_simm13() { return min_simm(13); }
|
||||
static int min_simm16() { return min_simm(16); }
|
||||
|
||||
// Test if x is within signed immediate range for nbits
|
||||
static bool is_simm(intptr_t x, int nbits) { return min_simm(nbits) <= x && x <= max_simm(nbits); }
|
||||
|
||||
// Define some:
|
||||
static bool is_simm5( intptr_t x) { return is_simm(x, 5 ); }
|
||||
static bool is_simm8( intptr_t x) { return is_simm(x, 8 ); }
|
||||
static bool is_simm10(intptr_t x) { return is_simm(x, 10); }
|
||||
static bool is_simm11(intptr_t x) { return is_simm(x, 11); }
|
||||
static bool is_simm12(intptr_t x) { return is_simm(x, 12); }
|
||||
static bool is_simm13(intptr_t x) { return is_simm(x, 13); }
|
||||
static bool is_simm16(intptr_t x) { return is_simm(x, 16); }
|
||||
static bool is_simm26(intptr_t x) { return is_simm(x, 26); }
|
||||
static bool is_simm32(intptr_t x) { return is_simm(x, 32); }
|
||||
|
||||
// Accessors
|
||||
CodeBuffer* code() const; // _code_section->outer()
|
||||
CodeSection* code_section() const { return _code_section; }
|
||||
|
||||
@ -3495,9 +3495,6 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
|
||||
if (profile_calls()) {
|
||||
profile_call(recv, holder_known ? callee->holder() : NULL);
|
||||
}
|
||||
if (profile_inlined_calls()) {
|
||||
profile_invocation(callee, copy_state_before());
|
||||
}
|
||||
}
|
||||
|
||||
// Introduce a new callee continuation point - if the callee has
|
||||
@ -3571,6 +3568,10 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, BlockBeg
|
||||
append(new RuntimeCall(voidType, "dtrace_method_entry", CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), args));
|
||||
}
|
||||
|
||||
if (profile_inlined_calls()) {
|
||||
profile_invocation(callee, copy_state_before_with_bci(SynchronizationEntryBCI));
|
||||
}
|
||||
|
||||
BlockBegin* callee_start_block = block_at(0);
|
||||
if (callee_start_block != NULL) {
|
||||
assert(callee_start_block->is_set(BlockBegin::parser_loop_header_flag), "must be loop header");
|
||||
|
||||
@ -501,6 +501,7 @@ class Instruction: public CompilationResourceObj {
|
||||
virtual RoundFP* as_RoundFP() { return NULL; }
|
||||
virtual ExceptionObject* as_ExceptionObject() { return NULL; }
|
||||
virtual UnsafeOp* as_UnsafeOp() { return NULL; }
|
||||
virtual ProfileInvoke* as_ProfileInvoke() { return NULL; }
|
||||
|
||||
virtual void visit(InstructionVisitor* v) = 0;
|
||||
|
||||
|
||||
@ -429,7 +429,7 @@ CodeEmitInfo* LIRGenerator::state_for(Instruction* x, ValueStack* state, bool ig
|
||||
// all locals are dead on exit from the synthetic unlocker
|
||||
liveness.clear();
|
||||
} else {
|
||||
assert(x->as_MonitorEnter(), "only other case is MonitorEnter");
|
||||
assert(x->as_MonitorEnter() || x->as_ProfileInvoke(), "only other cases are MonitorEnter and ProfileInvoke");
|
||||
}
|
||||
}
|
||||
if (!liveness.is_valid()) {
|
||||
|
||||
@ -150,11 +150,23 @@ void BCEscapeAnalyzer::set_method_escape(ArgumentMap vars) {
|
||||
clear_bits(vars, _arg_local);
|
||||
}
|
||||
|
||||
void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars) {
|
||||
void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars, bool merge) {
|
||||
clear_bits(vars, _arg_local);
|
||||
clear_bits(vars, _arg_stack);
|
||||
if (vars.contains_allocated())
|
||||
_allocated_escapes = true;
|
||||
|
||||
if (merge && !vars.is_empty()) {
|
||||
// Merge new state into already processed block.
|
||||
// New state is not taken into account and
|
||||
// it may invalidate set_returned() result.
|
||||
if (vars.contains_unknown() || vars.contains_allocated()) {
|
||||
_return_local = false;
|
||||
}
|
||||
if (vars.contains_unknown() || vars.contains_vars()) {
|
||||
_return_allocated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BCEscapeAnalyzer::set_dirty(ArgumentMap vars) {
|
||||
@ -999,7 +1011,7 @@ void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest,
|
||||
t.set_difference(d_state->_stack[i]);
|
||||
extra_vars.set_union(t);
|
||||
}
|
||||
set_global_escape(extra_vars);
|
||||
set_global_escape(extra_vars, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ class BCEscapeAnalyzer : public ResourceObj {
|
||||
bool is_arg_stack(ArgumentMap vars);
|
||||
void clear_bits(ArgumentMap vars, VectorSet &bs);
|
||||
void set_method_escape(ArgumentMap vars);
|
||||
void set_global_escape(ArgumentMap vars);
|
||||
void set_global_escape(ArgumentMap vars, bool merge = false);
|
||||
void set_dirty(ArgumentMap vars);
|
||||
void set_modified(ArgumentMap vars, int offs, int size);
|
||||
|
||||
|
||||
@ -295,12 +295,6 @@ class ciMethod : public ciObject {
|
||||
// Print the name of this method in various incarnations.
|
||||
void print_name(outputStream* st = tty);
|
||||
void print_short_name(outputStream* st = tty);
|
||||
|
||||
methodOop get_method_handle_target() {
|
||||
KlassHandle receiver_limit; int flags = 0;
|
||||
methodHandle m = MethodHandles::decode_method(get_oop(), receiver_limit, flags);
|
||||
return m();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CI_CIMETHOD_HPP
|
||||
|
||||
@ -1748,7 +1748,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
|
||||
tty->print("%4d ", compile_id); // print compilation number
|
||||
tty->print("%s ", (is_osr ? "%" : " "));
|
||||
int code_size = (task->code() == NULL) ? 0 : task->code()->total_size();
|
||||
tty->print_cr("size: %d time: %d inlined: %d bytes", code_size, time.milliseconds(), task->num_inlined_bytecodes());
|
||||
tty->print_cr("size: %d time: %d inlined: %d bytes", code_size, (int)time.milliseconds(), task->num_inlined_bytecodes());
|
||||
}
|
||||
|
||||
if (compilable == ciEnv::MethodCompilable_never) {
|
||||
|
||||
@ -898,45 +898,41 @@ void PhaseCFG::dump_headers() {
|
||||
void PhaseCFG::verify( ) const {
|
||||
#ifdef ASSERT
|
||||
// Verify sane CFG
|
||||
for( uint i = 0; i < _num_blocks; i++ ) {
|
||||
for (uint i = 0; i < _num_blocks; i++) {
|
||||
Block *b = _blocks[i];
|
||||
uint cnt = b->_nodes.size();
|
||||
uint j;
|
||||
for( j = 0; j < cnt; j++ ) {
|
||||
for (j = 0; j < cnt; j++) {
|
||||
Node *n = b->_nodes[j];
|
||||
assert( _bbs[n->_idx] == b, "" );
|
||||
if( j >= 1 && n->is_Mach() &&
|
||||
n->as_Mach()->ideal_Opcode() == Op_CreateEx ) {
|
||||
assert( j == 1 || b->_nodes[j-1]->is_Phi(),
|
||||
"CreateEx must be first instruction in block" );
|
||||
if (j >= 1 && n->is_Mach() &&
|
||||
n->as_Mach()->ideal_Opcode() == Op_CreateEx) {
|
||||
assert(j == 1 || b->_nodes[j-1]->is_Phi(),
|
||||
"CreateEx must be first instruction in block");
|
||||
}
|
||||
for( uint k = 0; k < n->req(); k++ ) {
|
||||
for (uint k = 0; k < n->req(); k++) {
|
||||
Node *def = n->in(k);
|
||||
if( def && def != n ) {
|
||||
assert( _bbs[def->_idx] || def->is_Con(),
|
||||
"must have block; constants for debug info ok" );
|
||||
if (def && def != n) {
|
||||
assert(_bbs[def->_idx] || def->is_Con(),
|
||||
"must have block; constants for debug info ok");
|
||||
// Verify that instructions in the block is in correct order.
|
||||
// Uses must follow their definition if they are at the same block.
|
||||
// Mostly done to check that MachSpillCopy nodes are placed correctly
|
||||
// when CreateEx node is moved in build_ifg_physical().
|
||||
if( _bbs[def->_idx] == b &&
|
||||
if (_bbs[def->_idx] == b &&
|
||||
!(b->head()->is_Loop() && n->is_Phi()) &&
|
||||
// See (+++) comment in reg_split.cpp
|
||||
!(n->jvms() != NULL && n->jvms()->is_monitor_use(k)) ) {
|
||||
!(n->jvms() != NULL && n->jvms()->is_monitor_use(k))) {
|
||||
bool is_loop = false;
|
||||
if (n->is_Phi()) {
|
||||
for( uint l = 1; l < def->req(); l++ ) {
|
||||
for (uint l = 1; l < def->req(); l++) {
|
||||
if (n == def->in(l)) {
|
||||
is_loop = true;
|
||||
break; // Some kind of loop
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( is_loop || b->find_node(def) < j, "uses must follow definitions" );
|
||||
}
|
||||
if( def->is_SafePointScalarObject() ) {
|
||||
assert(_bbs[def->_idx] == b, "SafePointScalarObject Node should be at the same block as its SafePoint node");
|
||||
assert(_bbs[def->_idx] == _bbs[def->in(0)->_idx], "SafePointScalarObject Node should be at the same block as its control edge");
|
||||
assert(is_loop || b->find_node(def) < j, "uses must follow definitions");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -946,12 +942,11 @@ void PhaseCFG::verify( ) const {
|
||||
Node *bp = (Node*)b->_nodes[b->_nodes.size()-1]->is_block_proj();
|
||||
assert( bp, "last instruction must be a block proj" );
|
||||
assert( bp == b->_nodes[j], "wrong number of successors for this block" );
|
||||
if( bp->is_Catch() ) {
|
||||
while( b->_nodes[--j]->is_MachProj() ) ;
|
||||
assert( b->_nodes[j]->is_MachCall(), "CatchProj must follow call" );
|
||||
}
|
||||
else if( bp->is_Mach() && bp->as_Mach()->ideal_Opcode() == Op_If ) {
|
||||
assert( b->_num_succs == 2, "Conditional branch must have two targets");
|
||||
if (bp->is_Catch()) {
|
||||
while (b->_nodes[--j]->is_MachProj()) ;
|
||||
assert(b->_nodes[j]->is_MachCall(), "CatchProj must follow call");
|
||||
} else if (bp->is_Mach() && bp->as_Mach()->ideal_Opcode() == Op_If) {
|
||||
assert(b->_num_succs == 2, "Conditional branch must have two targets");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -281,6 +281,8 @@ class Block : public CFGElement {
|
||||
// Find and remove n from block list
|
||||
void find_remove( const Node *n );
|
||||
|
||||
// helper function that adds caller save registers to MachProjNode
|
||||
void add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_policy, bool exclude_soe);
|
||||
// Schedule a call next in the block
|
||||
uint sched_call(Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call);
|
||||
|
||||
|
||||
@ -456,6 +456,12 @@
|
||||
product(intx, EliminateAllocationArraySizeLimit, 64, \
|
||||
"Array size (number of elements) limit for scalar replacement") \
|
||||
\
|
||||
product(bool, OptimizePtrCompare, true, \
|
||||
"Use escape analysis to optimize pointers compare") \
|
||||
\
|
||||
notproduct(bool, PrintOptimizePtrCompare, false, \
|
||||
"Print information about optimized pointers compare") \
|
||||
\
|
||||
product(bool, UseOptoBiasInlining, true, \
|
||||
"Generate biased locking code in C2 ideal graph") \
|
||||
\
|
||||
|
||||
@ -318,17 +318,17 @@ CallGenerator* CallGenerator::for_direct_call(ciMethod* m, bool separate_io_proj
|
||||
return new DirectCallGenerator(m, separate_io_proj);
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_dynamic_call(ciMethod* m) {
|
||||
assert(m->is_method_handle_invoke() || m->is_method_handle_adapter(), "for_dynamic_call mismatch");
|
||||
return new DynamicCallGenerator(m);
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) {
|
||||
assert(!m->is_static(), "for_virtual_call mismatch");
|
||||
assert(!m->is_method_handle_invoke(), "should be a direct call");
|
||||
return new VirtualCallGenerator(m, vtable_index);
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_dynamic_call(ciMethod* m) {
|
||||
assert(m->is_method_handle_invoke() || m->is_method_handle_adapter(), "for_dynamic_call mismatch");
|
||||
return new DynamicCallGenerator(m);
|
||||
}
|
||||
|
||||
// Allow inlining decisions to be delayed
|
||||
class LateInlineCallGenerator : public DirectCallGenerator {
|
||||
CallGenerator* _inline_cg;
|
||||
@ -576,7 +576,9 @@ JVMState* PredictedCallGenerator::generate(JVMState* jvms) {
|
||||
kit.set_control(slow_ctl);
|
||||
if (!kit.stopped()) {
|
||||
slow_jvms = _if_missed->generate(kit.sync_jvms());
|
||||
assert(slow_jvms != NULL, "miss path must not fail to generate");
|
||||
if (kit.failing())
|
||||
return NULL; // might happen because of NodeCountInliningCutoff
|
||||
assert(slow_jvms != NULL, "must be");
|
||||
kit.add_exception_states_from(slow_jvms);
|
||||
kit.set_map(slow_jvms->map());
|
||||
if (!kit.stopped())
|
||||
@ -682,6 +684,15 @@ CallGenerator* CallGenerator::for_predicted_dynamic_call(ciMethodHandle* predict
|
||||
}
|
||||
|
||||
|
||||
CallGenerator* CallGenerator::for_method_handle_call(Node* method_handle, JVMState* jvms,
|
||||
ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
|
||||
assert(callee->is_method_handle_invoke() || callee->is_method_handle_adapter(), "for_method_handle_call mismatch");
|
||||
CallGenerator* cg = CallGenerator::for_method_handle_inline(method_handle, jvms, caller, callee, profile);
|
||||
if (cg != NULL)
|
||||
return cg;
|
||||
return CallGenerator::for_direct_call(callee);
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMState* jvms,
|
||||
ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
|
||||
if (method_handle->Opcode() == Op_ConP) {
|
||||
@ -721,8 +732,8 @@ CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMS
|
||||
// Generate a guard so that each can be inlined. We might want to
|
||||
// do more inputs at later point but this gets the most common
|
||||
// case.
|
||||
CallGenerator* cg1 = for_method_handle_inline(method_handle->in(1), jvms, caller, callee, profile.rescale(1.0 - prob));
|
||||
CallGenerator* cg2 = for_method_handle_inline(method_handle->in(2), jvms, caller, callee, profile.rescale(prob));
|
||||
CallGenerator* cg1 = for_method_handle_call(method_handle->in(1), jvms, caller, callee, profile.rescale(1.0 - prob));
|
||||
CallGenerator* cg2 = for_method_handle_call(method_handle->in(2), jvms, caller, callee, profile.rescale(prob));
|
||||
if (cg1 != NULL && cg2 != NULL) {
|
||||
const TypeOopPtr* oop_ptr = method_handle->in(1)->bottom_type()->is_oopptr();
|
||||
ciObject* const_oop = oop_ptr->const_oop();
|
||||
@ -733,6 +744,17 @@ CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMS
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_invokedynamic_call(JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
|
||||
assert(callee->is_method_handle_invoke() || callee->is_method_handle_adapter(), "for_invokedynamic_call mismatch");
|
||||
// Get the CallSite object.
|
||||
ciBytecodeStream str(caller);
|
||||
str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
|
||||
ciCallSite* call_site = str.get_call_site();
|
||||
CallGenerator* cg = CallGenerator::for_invokedynamic_inline(call_site, jvms, caller, callee, profile);
|
||||
if (cg != NULL)
|
||||
return cg;
|
||||
return CallGenerator::for_dynamic_call(callee);
|
||||
}
|
||||
|
||||
CallGenerator* CallGenerator::for_invokedynamic_inline(ciCallSite* call_site, JVMState* jvms,
|
||||
ciMethod* caller, ciMethod* callee, ciCallProfile profile) {
|
||||
@ -819,7 +841,9 @@ JVMState* PredictedDynamicCallGenerator::generate(JVMState* jvms) {
|
||||
kit.set_control(slow_ctl);
|
||||
if (!kit.stopped()) {
|
||||
slow_jvms = _if_missed->generate(kit.sync_jvms());
|
||||
assert(slow_jvms != NULL, "miss path must not fail to generate");
|
||||
if (kit.failing())
|
||||
return NULL; // might happen because of NodeCountInliningCutoff
|
||||
assert(slow_jvms != NULL, "must be");
|
||||
kit.add_exception_states_from(slow_jvms);
|
||||
kit.set_map(slow_jvms->map());
|
||||
if (!kit.stopped())
|
||||
|
||||
@ -108,8 +108,11 @@ class CallGenerator : public ResourceObj {
|
||||
|
||||
// How to generate vanilla out-of-line call sites:
|
||||
static CallGenerator* for_direct_call(ciMethod* m, bool separate_io_projs = false); // static, special
|
||||
static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic
|
||||
static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface
|
||||
static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic
|
||||
|
||||
static CallGenerator* for_method_handle_call(Node* method_handle, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
|
||||
static CallGenerator* for_invokedynamic_call( JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
|
||||
|
||||
static CallGenerator* for_method_handle_inline(Node* method_handle, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
|
||||
static CallGenerator* for_invokedynamic_inline(ciCallSite* call_site, JVMState* jvms, ciMethod* caller, ciMethod* callee, ciCallProfile profile);
|
||||
|
||||
@ -1071,8 +1071,11 @@ SafePointScalarObjectNode::SafePointScalarObjectNode(const TypeOopPtr* tp,
|
||||
init_class_id(Class_SafePointScalarObject);
|
||||
}
|
||||
|
||||
bool SafePointScalarObjectNode::pinned() const { return true; }
|
||||
bool SafePointScalarObjectNode::depends_only_on_test() const { return false; }
|
||||
// Do not allow value-numbering for SafePointScalarObject node.
|
||||
uint SafePointScalarObjectNode::hash() const { return NO_HASH; }
|
||||
uint SafePointScalarObjectNode::cmp( const Node &n ) const {
|
||||
return (&n == this); // Always fail except on self
|
||||
}
|
||||
|
||||
uint SafePointScalarObjectNode::ideal_reg() const {
|
||||
return 0; // No matching to machine instruction
|
||||
@ -1096,7 +1099,6 @@ SafePointScalarObjectNode::clone(int jvms_adj, Dict* sosn_map) const {
|
||||
if (cached != NULL) {
|
||||
return (SafePointScalarObjectNode*)cached;
|
||||
}
|
||||
Compile* C = Compile::current();
|
||||
SafePointScalarObjectNode* res = (SafePointScalarObjectNode*)Node::clone();
|
||||
res->_first_index += jvms_adj;
|
||||
sosn_map->Insert((void*)this, (void*)res);
|
||||
@ -1142,6 +1144,8 @@ uint AllocateArrayNode::size_of() const { return sizeof(*this); }
|
||||
|
||||
Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (remove_dead_region(phase, can_reshape)) return this;
|
||||
// Don't bother trying to transform a dead node
|
||||
if (in(0) && in(0)->is_top()) return NULL;
|
||||
|
||||
const Type* type = phase->type(Ideal_length());
|
||||
if (type->isa_int() && type->is_int()->_hi < 0) {
|
||||
@ -1522,13 +1526,16 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
// perform any generic optimizations first (returns 'this' or NULL)
|
||||
Node *result = SafePointNode::Ideal(phase, can_reshape);
|
||||
if (result != NULL) return result;
|
||||
// Don't bother trying to transform a dead node
|
||||
if (in(0) && in(0)->is_top()) return NULL;
|
||||
|
||||
// Now see if we can optimize away this lock. We don't actually
|
||||
// remove the locking here, we simply set the _eliminate flag which
|
||||
// prevents macro expansion from expanding the lock. Since we don't
|
||||
// modify the graph, the value returned from this function is the
|
||||
// one computed above.
|
||||
if (result == NULL && can_reshape && EliminateLocks && !is_eliminated()) {
|
||||
if (can_reshape && EliminateLocks && (!is_eliminated() || is_coarsened())) {
|
||||
//
|
||||
// If we are locking an unescaped object, the lock/unlock is unnecessary
|
||||
//
|
||||
@ -1537,8 +1544,16 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (cgr != NULL)
|
||||
es = cgr->escape_state(obj_node());
|
||||
if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) {
|
||||
// Mark it eliminated to update any counters
|
||||
this->set_eliminated();
|
||||
if (!is_eliminated()) {
|
||||
// Mark it eliminated to update any counters
|
||||
this->set_eliminated();
|
||||
} else {
|
||||
assert(is_coarsened(), "sanity");
|
||||
// The lock could be marked eliminated by lock coarsening
|
||||
// code during first IGVN before EA. Clear coarsened flag
|
||||
// to eliminate all associated locks/unlocks.
|
||||
this->clear_coarsened();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1546,7 +1561,7 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Try lock coarsening
|
||||
//
|
||||
PhaseIterGVN* iter = phase->is_IterGVN();
|
||||
if (iter != NULL) {
|
||||
if (iter != NULL && !is_eliminated()) {
|
||||
|
||||
GrowableArray<AbstractLockNode*> lock_ops;
|
||||
|
||||
@ -1602,7 +1617,7 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
lock->set_eliminated();
|
||||
lock->set_coarsened();
|
||||
}
|
||||
} else if (result != NULL && ctrl->is_Region() &&
|
||||
} else if (ctrl->is_Region() &&
|
||||
iter->_worklist.member(ctrl)) {
|
||||
// We weren't able to find any opportunities but the region this
|
||||
// lock is control dependent on hasn't been processed yet so put
|
||||
@ -1623,7 +1638,10 @@ uint UnlockNode::size_of() const { return sizeof(*this); }
|
||||
Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
// perform any generic optimizations first (returns 'this' or NULL)
|
||||
Node * result = SafePointNode::Ideal(phase, can_reshape);
|
||||
Node *result = SafePointNode::Ideal(phase, can_reshape);
|
||||
if (result != NULL) return result;
|
||||
// Don't bother trying to transform a dead node
|
||||
if (in(0) && in(0)->is_top()) return NULL;
|
||||
|
||||
// Now see if we can optimize away this unlock. We don't actually
|
||||
// remove the unlocking here, we simply set the _eliminate flag which
|
||||
@ -1631,7 +1649,7 @@ Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// modify the graph, the value returned from this function is the
|
||||
// one computed above.
|
||||
// Escape state is defined after Parse phase.
|
||||
if (result == NULL && can_reshape && EliminateLocks && !is_eliminated()) {
|
||||
if (can_reshape && EliminateLocks && (!is_eliminated() || is_coarsened())) {
|
||||
//
|
||||
// If we are unlocking an unescaped object, the lock/unlock is unnecessary.
|
||||
//
|
||||
@ -1640,8 +1658,16 @@ Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (cgr != NULL)
|
||||
es = cgr->escape_state(obj_node());
|
||||
if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) {
|
||||
// Mark it eliminated to update any counters
|
||||
this->set_eliminated();
|
||||
if (!is_eliminated()) {
|
||||
// Mark it eliminated to update any counters
|
||||
this->set_eliminated();
|
||||
} else {
|
||||
assert(is_coarsened(), "sanity");
|
||||
// The lock could be marked eliminated by lock coarsening
|
||||
// code during first IGVN before EA. Clear coarsened flag
|
||||
// to eliminate all associated locks/unlocks.
|
||||
this->clear_coarsened();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@ -440,6 +440,10 @@ class SafePointScalarObjectNode: public TypeNode {
|
||||
// states of the scalarized object fields are collected.
|
||||
uint _n_fields; // Number of non-static fields of the scalarized object.
|
||||
DEBUG_ONLY(AllocateNode* _alloc;)
|
||||
|
||||
virtual uint hash() const ; // { return NO_HASH; }
|
||||
virtual uint cmp( const Node &n ) const;
|
||||
|
||||
public:
|
||||
SafePointScalarObjectNode(const TypeOopPtr* tp,
|
||||
#ifdef ASSERT
|
||||
@ -454,15 +458,10 @@ public:
|
||||
|
||||
uint first_index() const { return _first_index; }
|
||||
uint n_fields() const { return _n_fields; }
|
||||
DEBUG_ONLY(AllocateNode* alloc() const { return _alloc; })
|
||||
|
||||
// SafePointScalarObject should be always pinned to the control edge
|
||||
// of the SafePoint node for which it was generated.
|
||||
virtual bool pinned() const; // { return true; }
|
||||
|
||||
// SafePointScalarObject depends on the SafePoint node
|
||||
// for which it was generated.
|
||||
virtual bool depends_only_on_test() const; // { return false; }
|
||||
#ifdef ASSERT
|
||||
AllocateNode* alloc() const { return _alloc; }
|
||||
#endif
|
||||
|
||||
virtual uint size_of() const { return sizeof(*this); }
|
||||
|
||||
@ -880,6 +879,7 @@ public:
|
||||
|
||||
bool is_coarsened() { return _coarsened; }
|
||||
void set_coarsened() { _coarsened = true; }
|
||||
void clear_coarsened() { _coarsened = false; }
|
||||
|
||||
// locking does not modify its arguments
|
||||
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase){ return false;}
|
||||
|
||||
@ -460,8 +460,11 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Is it dead loop?
|
||||
// If it is LoopNopde it had 2 (+1 itself) inputs and
|
||||
// one of them was cut. The loop is dead if it was EntryContol.
|
||||
assert(!this->is_Loop() || cnt_orig == 3, "Loop node should have 3 inputs");
|
||||
if (this->is_Loop() && del_it == LoopNode::EntryControl ||
|
||||
// Loop node may have only one input because entry path
|
||||
// is removed in PhaseIdealLoop::Dominators().
|
||||
assert(!this->is_Loop() || cnt_orig <= 3, "Loop node should have 3 or less inputs");
|
||||
if (this->is_Loop() && (del_it == LoopNode::EntryControl ||
|
||||
del_it == 0 && is_unreachable_region(phase)) ||
|
||||
!this->is_Loop() && has_phis && is_unreachable_region(phase)) {
|
||||
// Yes, the region will be removed during the next step below.
|
||||
// Cut the backedge input and remove phis since no data paths left.
|
||||
@ -1585,14 +1588,17 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Only one not-NULL unique input path is left.
|
||||
// Determine if this input is backedge of a loop.
|
||||
// (Skip new phis which have no uses and dead regions).
|
||||
if( outcnt() > 0 && r->in(0) != NULL ) {
|
||||
if (outcnt() > 0 && r->in(0) != NULL) {
|
||||
// First, take the short cut when we know it is a loop and
|
||||
// the EntryControl data path is dead.
|
||||
assert(!r->is_Loop() || r->req() == 3, "Loop node should have 3 inputs");
|
||||
// Loop node may have only one input because entry path
|
||||
// is removed in PhaseIdealLoop::Dominators().
|
||||
assert(!r->is_Loop() || r->req() <= 3, "Loop node should have 3 or less inputs");
|
||||
bool is_loop = (r->is_Loop() && r->req() == 3);
|
||||
// Then, check if there is a data loop when phi references itself directly
|
||||
// or through other data nodes.
|
||||
if( r->is_Loop() && !phase->eqv_uncast(uin, in(LoopNode::EntryControl)) ||
|
||||
!r->is_Loop() && is_unsafe_data_reference(uin) ) {
|
||||
if (is_loop && !phase->eqv_uncast(uin, in(LoopNode::EntryControl)) ||
|
||||
!is_loop && is_unsafe_data_reference(uin)) {
|
||||
// Break this data loop to avoid creation of a dead loop.
|
||||
if (can_reshape) {
|
||||
return top;
|
||||
|
||||
@ -1711,11 +1711,22 @@ void Compile::Optimize() {
|
||||
|
||||
if (failing()) return;
|
||||
|
||||
// Optimize out fields loads from scalar replaceable allocations.
|
||||
igvn.optimize();
|
||||
print_method("Iter GVN after EA", 2);
|
||||
|
||||
if (failing()) return;
|
||||
|
||||
if (congraph() != NULL && macro_count() > 0) {
|
||||
PhaseMacroExpand mexp(igvn);
|
||||
mexp.eliminate_macro_nodes();
|
||||
igvn.set_delay_transform(false);
|
||||
|
||||
igvn.optimize();
|
||||
print_method("Iter GVN after eliminating allocations and locks", 2);
|
||||
|
||||
if (failing()) return;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop transforms on the ideal graph. Range Check Elimination,
|
||||
@ -3052,24 +3063,13 @@ bool Compile::Constant::operator==(const Constant& other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emit constants grouped in the following order:
|
||||
static BasicType type_order[] = {
|
||||
T_FLOAT, // 32-bit
|
||||
T_OBJECT, // 32 or 64-bit
|
||||
T_ADDRESS, // 32 or 64-bit
|
||||
T_DOUBLE, // 64-bit
|
||||
T_LONG, // 64-bit
|
||||
T_VOID, // 32 or 64-bit (jump-tables are at the end of the constant table for code emission reasons)
|
||||
T_ILLEGAL
|
||||
};
|
||||
|
||||
static int type_to_size_in_bytes(BasicType t) {
|
||||
switch (t) {
|
||||
case T_LONG: return sizeof(jlong );
|
||||
case T_FLOAT: return sizeof(jfloat );
|
||||
case T_DOUBLE: return sizeof(jdouble);
|
||||
// We use T_VOID as marker for jump-table entries (labels) which
|
||||
// need an interal word relocation.
|
||||
// need an internal word relocation.
|
||||
case T_VOID:
|
||||
case T_ADDRESS:
|
||||
case T_OBJECT: return sizeof(jobject);
|
||||
@ -3079,87 +3079,92 @@ static int type_to_size_in_bytes(BasicType t) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Compile::ConstantTable::qsort_comparator(Constant* a, Constant* b) {
|
||||
// sort descending
|
||||
if (a->freq() > b->freq()) return -1;
|
||||
if (a->freq() < b->freq()) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Compile::ConstantTable::calculate_offsets_and_size() {
|
||||
int size = 0;
|
||||
for (int t = 0; type_order[t] != T_ILLEGAL; t++) {
|
||||
BasicType type = type_order[t];
|
||||
// First, sort the array by frequencies.
|
||||
_constants.sort(qsort_comparator);
|
||||
|
||||
for (int i = 0; i < _constants.length(); i++) {
|
||||
Constant con = _constants.at(i);
|
||||
if (con.type() != type) continue; // Skip other types.
|
||||
#ifdef ASSERT
|
||||
// Make sure all jump-table entries were sorted to the end of the
|
||||
// array (they have a negative frequency).
|
||||
bool found_void = false;
|
||||
for (int i = 0; i < _constants.length(); i++) {
|
||||
Constant con = _constants.at(i);
|
||||
if (con.type() == T_VOID)
|
||||
found_void = true; // jump-tables
|
||||
else
|
||||
assert(!found_void, "wrong sorting");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Align size for type.
|
||||
int typesize = type_to_size_in_bytes(con.type());
|
||||
size = align_size_up(size, typesize);
|
||||
int offset = 0;
|
||||
for (int i = 0; i < _constants.length(); i++) {
|
||||
Constant* con = _constants.adr_at(i);
|
||||
|
||||
// Set offset.
|
||||
con.set_offset(size);
|
||||
_constants.at_put(i, con);
|
||||
// Align offset for type.
|
||||
int typesize = type_to_size_in_bytes(con->type());
|
||||
offset = align_size_up(offset, typesize);
|
||||
con->set_offset(offset); // set constant's offset
|
||||
|
||||
// Add type size.
|
||||
size = size + typesize;
|
||||
if (con->type() == T_VOID) {
|
||||
MachConstantNode* n = (MachConstantNode*) con->get_jobject();
|
||||
offset = offset + typesize * n->outcnt(); // expand jump-table
|
||||
} else {
|
||||
offset = offset + typesize;
|
||||
}
|
||||
}
|
||||
|
||||
// Align size up to the next section start (which is insts; see
|
||||
// CodeBuffer::align_at_start).
|
||||
assert(_size == -1, "already set?");
|
||||
_size = align_size_up(size, CodeEntryAlignment);
|
||||
|
||||
if (Matcher::constant_table_absolute_addressing) {
|
||||
set_table_base_offset(0); // No table base offset required
|
||||
} else {
|
||||
if (UseRDPCForConstantTableBase) {
|
||||
// table base offset is set in MachConstantBaseNode::emit
|
||||
} else {
|
||||
// When RDPC is not used, the table base is set into the middle of
|
||||
// the constant table.
|
||||
int half_size = _size / 2;
|
||||
assert(half_size * 2 == _size, "sanity");
|
||||
set_table_base_offset(-half_size);
|
||||
}
|
||||
}
|
||||
_size = align_size_up(offset, CodeEntryAlignment);
|
||||
}
|
||||
|
||||
void Compile::ConstantTable::emit(CodeBuffer& cb) {
|
||||
MacroAssembler _masm(&cb);
|
||||
for (int t = 0; type_order[t] != T_ILLEGAL; t++) {
|
||||
BasicType type = type_order[t];
|
||||
|
||||
for (int i = 0; i < _constants.length(); i++) {
|
||||
Constant con = _constants.at(i);
|
||||
if (con.type() != type) continue; // Skip other types.
|
||||
|
||||
address constant_addr;
|
||||
switch (con.type()) {
|
||||
case T_LONG: constant_addr = _masm.long_constant( con.get_jlong() ); break;
|
||||
case T_FLOAT: constant_addr = _masm.float_constant( con.get_jfloat() ); break;
|
||||
case T_DOUBLE: constant_addr = _masm.double_constant(con.get_jdouble()); break;
|
||||
case T_OBJECT: {
|
||||
jobject obj = con.get_jobject();
|
||||
int oop_index = _masm.oop_recorder()->find_index(obj);
|
||||
constant_addr = _masm.address_constant((address) obj, oop_Relocation::spec(oop_index));
|
||||
break;
|
||||
}
|
||||
case T_ADDRESS: {
|
||||
address addr = (address) con.get_jobject();
|
||||
constant_addr = _masm.address_constant(addr);
|
||||
break;
|
||||
}
|
||||
// We use T_VOID as marker for jump-table entries (labels) which
|
||||
// need an interal word relocation.
|
||||
case T_VOID: {
|
||||
// Write a dummy word. The real value is filled in later
|
||||
// in fill_jump_table_in_constant_table.
|
||||
address addr = (address) con.get_jobject();
|
||||
constant_addr = _masm.address_constant(addr);
|
||||
break;
|
||||
}
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
assert(constant_addr != NULL, "consts section too small");
|
||||
assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), err_msg("must be: %d == %d", constant_addr - _masm.code()->consts()->start(), con.offset()));
|
||||
for (int i = 0; i < _constants.length(); i++) {
|
||||
Constant con = _constants.at(i);
|
||||
address constant_addr;
|
||||
switch (con.type()) {
|
||||
case T_LONG: constant_addr = _masm.long_constant( con.get_jlong() ); break;
|
||||
case T_FLOAT: constant_addr = _masm.float_constant( con.get_jfloat() ); break;
|
||||
case T_DOUBLE: constant_addr = _masm.double_constant(con.get_jdouble()); break;
|
||||
case T_OBJECT: {
|
||||
jobject obj = con.get_jobject();
|
||||
int oop_index = _masm.oop_recorder()->find_index(obj);
|
||||
constant_addr = _masm.address_constant((address) obj, oop_Relocation::spec(oop_index));
|
||||
break;
|
||||
}
|
||||
case T_ADDRESS: {
|
||||
address addr = (address) con.get_jobject();
|
||||
constant_addr = _masm.address_constant(addr);
|
||||
break;
|
||||
}
|
||||
// We use T_VOID as marker for jump-table entries (labels) which
|
||||
// need an internal word relocation.
|
||||
case T_VOID: {
|
||||
MachConstantNode* n = (MachConstantNode*) con.get_jobject();
|
||||
// Fill the jump-table with a dummy word. The real value is
|
||||
// filled in later in fill_jump_table.
|
||||
address dummy = (address) n;
|
||||
constant_addr = _masm.address_constant(dummy);
|
||||
// Expand jump-table
|
||||
for (uint i = 1; i < n->outcnt(); i++) {
|
||||
address temp_addr = _masm.address_constant(dummy + i);
|
||||
assert(temp_addr, "consts section too small");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
assert(constant_addr, "consts section too small");
|
||||
assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), err_msg("must be: %d == %d", constant_addr - _masm.code()->consts()->start(), con.offset()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3175,19 +3180,21 @@ void Compile::ConstantTable::add(Constant& con) {
|
||||
if (con.can_be_reused()) {
|
||||
int idx = _constants.find(con);
|
||||
if (idx != -1 && _constants.at(idx).can_be_reused()) {
|
||||
_constants.adr_at(idx)->inc_freq(con.freq()); // increase the frequency by the current value
|
||||
return;
|
||||
}
|
||||
}
|
||||
(void) _constants.append(con);
|
||||
}
|
||||
|
||||
Compile::Constant Compile::ConstantTable::add(BasicType type, jvalue value) {
|
||||
Constant con(type, value);
|
||||
Compile::Constant Compile::ConstantTable::add(MachConstantNode* n, BasicType type, jvalue value) {
|
||||
Block* b = Compile::current()->cfg()->_bbs[n->_idx];
|
||||
Constant con(type, value, b->_freq);
|
||||
add(con);
|
||||
return con;
|
||||
}
|
||||
|
||||
Compile::Constant Compile::ConstantTable::add(MachOper* oper) {
|
||||
Compile::Constant Compile::ConstantTable::add(MachConstantNode* n, MachOper* oper) {
|
||||
jvalue value;
|
||||
BasicType type = oper->type()->basic_type();
|
||||
switch (type) {
|
||||
@ -3198,20 +3205,18 @@ Compile::Constant Compile::ConstantTable::add(MachOper* oper) {
|
||||
case T_ADDRESS: value.l = (jobject) oper->constant(); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
return add(type, value);
|
||||
return add(n, type, value);
|
||||
}
|
||||
|
||||
Compile::Constant Compile::ConstantTable::allocate_jump_table(MachConstantNode* n) {
|
||||
Compile::Constant Compile::ConstantTable::add_jump_table(MachConstantNode* n) {
|
||||
jvalue value;
|
||||
// We can use the node pointer here to identify the right jump-table
|
||||
// as this method is called from Compile::Fill_buffer right before
|
||||
// the MachNodes are emitted and the jump-table is filled (means the
|
||||
// MachNode pointers do not change anymore).
|
||||
value.l = (jobject) n;
|
||||
Constant con(T_VOID, value, false); // Labels of a jump-table cannot be reused.
|
||||
for (uint i = 0; i < n->outcnt(); i++) {
|
||||
add(con);
|
||||
}
|
||||
Constant con(T_VOID, value, next_jump_table_freq(), false); // Labels of a jump-table cannot be reused.
|
||||
add(con);
|
||||
return con;
|
||||
}
|
||||
|
||||
@ -3230,9 +3235,9 @@ void Compile::ConstantTable::fill_jump_table(CodeBuffer& cb, MachConstantNode* n
|
||||
MacroAssembler _masm(&cb);
|
||||
address* jump_table_base = (address*) (_masm.code()->consts()->start() + offset);
|
||||
|
||||
for (int i = 0; i < labels.length(); i++) {
|
||||
for (uint i = 0; i < n->outcnt(); i++) {
|
||||
address* constant_addr = &jump_table_base[i];
|
||||
assert(*constant_addr == (address) n, "all jump-table entries must contain node pointer");
|
||||
assert(*constant_addr == (((address) n) + i), err_msg("all jump-table entries must contain adjusted node pointer: " INTPTR_FORMAT " == " INTPTR_FORMAT, *constant_addr, (((address) n) + i)));
|
||||
*constant_addr = cb.consts()->target(*labels.at(i), (address) constant_addr);
|
||||
cb.consts()->relocate((address) constant_addr, relocInfo::internal_word_type);
|
||||
}
|
||||
|
||||
@ -150,14 +150,16 @@ class Compile : public Phase {
|
||||
BasicType _type;
|
||||
jvalue _value;
|
||||
int _offset; // offset of this constant (in bytes) relative to the constant table base.
|
||||
float _freq;
|
||||
bool _can_be_reused; // true (default) if the value can be shared with other users.
|
||||
|
||||
public:
|
||||
Constant() : _type(T_ILLEGAL), _offset(-1), _can_be_reused(true) { _value.l = 0; }
|
||||
Constant(BasicType type, jvalue value, bool can_be_reused = true) :
|
||||
Constant() : _type(T_ILLEGAL), _offset(-1), _freq(0.0f), _can_be_reused(true) { _value.l = 0; }
|
||||
Constant(BasicType type, jvalue value, float freq = 0.0f, bool can_be_reused = true) :
|
||||
_type(type),
|
||||
_value(value),
|
||||
_offset(-1),
|
||||
_freq(freq),
|
||||
_can_be_reused(can_be_reused)
|
||||
{}
|
||||
|
||||
@ -173,6 +175,9 @@ class Compile : public Phase {
|
||||
int offset() const { return _offset; }
|
||||
void set_offset(int offset) { _offset = offset; }
|
||||
|
||||
float freq() const { return _freq; }
|
||||
void inc_freq(float freq) { _freq += freq; }
|
||||
|
||||
bool can_be_reused() const { return _can_be_reused; }
|
||||
};
|
||||
|
||||
@ -182,41 +187,51 @@ class Compile : public Phase {
|
||||
GrowableArray<Constant> _constants; // Constants of this table.
|
||||
int _size; // Size in bytes the emitted constant table takes (including padding).
|
||||
int _table_base_offset; // Offset of the table base that gets added to the constant offsets.
|
||||
int _nof_jump_tables; // Number of jump-tables in this constant table.
|
||||
|
||||
static int qsort_comparator(Constant* a, Constant* b);
|
||||
|
||||
// We use negative frequencies to keep the order of the
|
||||
// jump-tables in which they were added. Otherwise we get into
|
||||
// trouble with relocation.
|
||||
float next_jump_table_freq() { return -1.0f * (++_nof_jump_tables); }
|
||||
|
||||
public:
|
||||
ConstantTable() :
|
||||
_size(-1),
|
||||
_table_base_offset(-1) // We can use -1 here since the constant table is always bigger than 2 bytes (-(size / 2), see MachConstantBaseNode::emit).
|
||||
_table_base_offset(-1), // We can use -1 here since the constant table is always bigger than 2 bytes (-(size / 2), see MachConstantBaseNode::emit).
|
||||
_nof_jump_tables(0)
|
||||
{}
|
||||
|
||||
int size() const { assert(_size != -1, "size not yet calculated"); return _size; }
|
||||
int size() const { assert(_size != -1, "not calculated yet"); return _size; }
|
||||
|
||||
void set_table_base_offset(int x) { assert(_table_base_offset == -1, "set only once"); _table_base_offset = x; }
|
||||
int table_base_offset() const { assert(_table_base_offset != -1, "table base offset not yet set"); return _table_base_offset; }
|
||||
int calculate_table_base_offset() const; // AD specific
|
||||
void set_table_base_offset(int x) { assert(_table_base_offset == -1 || x == _table_base_offset, "can't change"); _table_base_offset = x; }
|
||||
int table_base_offset() const { assert(_table_base_offset != -1, "not set yet"); return _table_base_offset; }
|
||||
|
||||
void emit(CodeBuffer& cb);
|
||||
|
||||
// Returns the offset of the last entry (the top) of the constant table.
|
||||
int top_offset() const { assert(_constants.top().offset() != -1, "constant not yet bound"); return _constants.top().offset(); }
|
||||
int top_offset() const { assert(_constants.top().offset() != -1, "not bound yet"); return _constants.top().offset(); }
|
||||
|
||||
void calculate_offsets_and_size();
|
||||
int find_offset(Constant& con) const;
|
||||
|
||||
void add(Constant& con);
|
||||
Constant add(BasicType type, jvalue value);
|
||||
Constant add(MachOper* oper);
|
||||
Constant add(jfloat f) {
|
||||
Constant add(MachConstantNode* n, BasicType type, jvalue value);
|
||||
Constant add(MachConstantNode* n, MachOper* oper);
|
||||
Constant add(MachConstantNode* n, jfloat f) {
|
||||
jvalue value; value.f = f;
|
||||
return add(T_FLOAT, value);
|
||||
return add(n, T_FLOAT, value);
|
||||
}
|
||||
Constant add(jdouble d) {
|
||||
Constant add(MachConstantNode* n, jdouble d) {
|
||||
jvalue value; value.d = d;
|
||||
return add(T_DOUBLE, value);
|
||||
return add(n, T_DOUBLE, value);
|
||||
}
|
||||
|
||||
// Jump table
|
||||
Constant allocate_jump_table(MachConstantNode* n);
|
||||
void fill_jump_table(CodeBuffer& cb, MachConstantNode* n, GrowableArray<Label*> labels) const;
|
||||
// Jump-table
|
||||
Constant add_jump_table(MachConstantNode* n);
|
||||
void fill_jump_table(CodeBuffer& cb, MachConstantNode* n, GrowableArray<Label*> labels) const;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@ -62,7 +62,6 @@ void trace_type_profile(ciMethod *method, int depth, int bci, ciMethod *prof_met
|
||||
CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual,
|
||||
JVMState* jvms, bool allow_inline,
|
||||
float prof_factor) {
|
||||
CallGenerator* cg;
|
||||
ciMethod* caller = jvms->method();
|
||||
int bci = jvms->bci();
|
||||
Bytecodes::Code bytecode = caller->java_code_at_bci(bci);
|
||||
@ -110,7 +109,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||
// We do this before the strict f.p. check below because the
|
||||
// intrinsics handle strict f.p. correctly.
|
||||
if (allow_inline) {
|
||||
cg = find_intrinsic(call_method, call_is_virtual);
|
||||
CallGenerator* cg = find_intrinsic(call_method, call_is_virtual);
|
||||
if (cg != NULL) return cg;
|
||||
}
|
||||
|
||||
@ -121,33 +120,16 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||
if (call_method->is_method_handle_invoke()) {
|
||||
if (bytecode != Bytecodes::_invokedynamic) {
|
||||
GraphKit kit(jvms);
|
||||
Node* n = kit.argument(0);
|
||||
|
||||
CallGenerator* cg = CallGenerator::for_method_handle_inline(n, jvms, caller, call_method, profile);
|
||||
if (cg != NULL) {
|
||||
return cg;
|
||||
}
|
||||
return CallGenerator::for_direct_call(call_method);
|
||||
Node* method_handle = kit.argument(0);
|
||||
return CallGenerator::for_method_handle_call(method_handle, jvms, caller, call_method, profile);
|
||||
}
|
||||
else {
|
||||
// Get the CallSite object.
|
||||
ciMethod* caller_method = jvms->method();
|
||||
ciBytecodeStream str(caller_method);
|
||||
str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci.
|
||||
ciCallSite* call_site = str.get_call_site();
|
||||
|
||||
CallGenerator* cg = CallGenerator::for_invokedynamic_inline(call_site, jvms, caller, call_method, profile);
|
||||
if (cg != NULL) {
|
||||
return cg;
|
||||
}
|
||||
// If something failed, generate a normal dynamic call.
|
||||
return CallGenerator::for_dynamic_call(call_method);
|
||||
return CallGenerator::for_invokedynamic_call(jvms, caller, call_method, profile);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not inline strict fp into non-strict code, or the reverse
|
||||
bool caller_method_is_strict = jvms->method()->is_strict();
|
||||
if( caller_method_is_strict ^ call_method->is_strict() ) {
|
||||
if (caller->is_strict() ^ call_method->is_strict()) {
|
||||
allow_inline = false;
|
||||
}
|
||||
|
||||
@ -258,7 +240,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||
}
|
||||
if (miss_cg != NULL) {
|
||||
NOT_PRODUCT(trace_type_profile(jvms->method(), jvms->depth() - 1, jvms->bci(), receiver_method, profile.receiver(0), site_count, receiver_count));
|
||||
cg = CallGenerator::for_predicted_call(profile.receiver(0), miss_cg, hit_cg, profile.receiver_prob(0));
|
||||
CallGenerator* cg = CallGenerator::for_predicted_call(profile.receiver(0), miss_cg, hit_cg, profile.receiver_prob(0));
|
||||
if (cg != NULL) return cg;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +119,8 @@ ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) :
|
||||
} else {
|
||||
_noop_null = _oop_null; // Should be initialized
|
||||
}
|
||||
_pcmp_neq = NULL; // Should be initialized
|
||||
_pcmp_eq = NULL;
|
||||
}
|
||||
|
||||
void ConnectionGraph::add_pointsto_edge(uint from_i, uint to_i) {
|
||||
@ -128,6 +130,13 @@ void ConnectionGraph::add_pointsto_edge(uint from_i, uint to_i) {
|
||||
assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set");
|
||||
assert(f->node_type() == PointsToNode::LocalVar || f->node_type() == PointsToNode::Field, "invalid source of PointsTo edge");
|
||||
assert(t->node_type() == PointsToNode::JavaObject, "invalid destination of PointsTo edge");
|
||||
if (to_i == _phantom_object) { // Quick test for most common object
|
||||
if (f->has_unknown_ptr()) {
|
||||
return;
|
||||
} else {
|
||||
f->set_has_unknown_ptr();
|
||||
}
|
||||
}
|
||||
add_edge(f, to_i, PointsToNode::PointsToEdge);
|
||||
}
|
||||
|
||||
@ -163,6 +172,9 @@ int ConnectionGraph::address_offset(Node* adr, PhaseTransform *phase) {
|
||||
}
|
||||
|
||||
void ConnectionGraph::add_field_edge(uint from_i, uint to_i, int offset) {
|
||||
// Don't add fields to NULL pointer.
|
||||
if (is_null_ptr(from_i))
|
||||
return;
|
||||
PointsToNode *f = ptnode_adr(from_i);
|
||||
PointsToNode *t = ptnode_adr(to_i);
|
||||
|
||||
@ -177,7 +189,7 @@ void ConnectionGraph::add_field_edge(uint from_i, uint to_i, int offset) {
|
||||
|
||||
void ConnectionGraph::set_escape_state(uint ni, PointsToNode::EscapeState es) {
|
||||
// Don't change non-escaping state of NULL pointer.
|
||||
if (ni == _noop_null || ni == _oop_null)
|
||||
if (is_null_ptr(ni))
|
||||
return;
|
||||
PointsToNode *npt = ptnode_adr(ni);
|
||||
PointsToNode::EscapeState old_es = npt->escape_state();
|
||||
@ -309,6 +321,9 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* deferred_edg
|
||||
|
||||
visited->set(ni);
|
||||
PointsToNode *ptn = ptnode_adr(ni);
|
||||
assert(ptn->node_type() == PointsToNode::LocalVar ||
|
||||
ptn->node_type() == PointsToNode::Field, "sanity");
|
||||
assert(ptn->edge_count() != 0, "should have at least phantom_object");
|
||||
|
||||
// Mark current edges as visited and move deferred edges to separate array.
|
||||
for (uint i = 0; i < ptn->edge_count(); ) {
|
||||
@ -329,6 +344,7 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* deferred_edg
|
||||
uint t = deferred_edges->at(next);
|
||||
PointsToNode *ptt = ptnode_adr(t);
|
||||
uint e_cnt = ptt->edge_count();
|
||||
assert(e_cnt != 0, "should have at least phantom_object");
|
||||
for (uint e = 0; e < e_cnt; e++) {
|
||||
uint etgt = ptt->edge_target(e);
|
||||
if (visited->test_set(etgt))
|
||||
@ -337,10 +353,6 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* deferred_edg
|
||||
PointsToNode::EdgeType et = ptt->edge_type(e);
|
||||
if (et == PointsToNode::PointsToEdge) {
|
||||
add_pointsto_edge(ni, etgt);
|
||||
if(etgt == _phantom_object) {
|
||||
// Special case - field set outside (globally escaping).
|
||||
set_escape_state(ni, PointsToNode::GlobalEscape);
|
||||
}
|
||||
} else if (et == PointsToNode::DeferredEdge) {
|
||||
deferred_edges->append(etgt);
|
||||
} else {
|
||||
@ -348,6 +360,20 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* deferred_edg
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ptn->edge_count() == 0) {
|
||||
// No pointsto edges found after deferred edges are removed.
|
||||
// For example, in the next case where call is replaced
|
||||
// with uncommon trap and as result array's load references
|
||||
// itself through deferred edges:
|
||||
//
|
||||
// A a = b[i];
|
||||
// if (c!=null) a = c.foo();
|
||||
// b[i] = a;
|
||||
//
|
||||
// Assume the value was set outside this method and
|
||||
// add edge to phantom object.
|
||||
add_pointsto_edge(ni, _phantom_object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -356,13 +382,25 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* deferred_edg
|
||||
// a pointsto edge is added if it is a JavaObject
|
||||
|
||||
void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) {
|
||||
// No fields for NULL pointer.
|
||||
if (is_null_ptr(adr_i)) {
|
||||
return;
|
||||
}
|
||||
PointsToNode* an = ptnode_adr(adr_i);
|
||||
PointsToNode* to = ptnode_adr(to_i);
|
||||
bool deferred = (to->node_type() == PointsToNode::LocalVar);
|
||||
|
||||
bool escaped = (to_i == _phantom_object) && (offs == Type::OffsetTop);
|
||||
if (escaped) {
|
||||
// Values in fields escaped during call.
|
||||
assert(an->escape_state() >= PointsToNode::ArgEscape, "sanity");
|
||||
offs = Type::OffsetBot;
|
||||
}
|
||||
for (uint fe = 0; fe < an->edge_count(); fe++) {
|
||||
assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge");
|
||||
int fi = an->edge_target(fe);
|
||||
if (escaped) {
|
||||
set_escape_state(fi, PointsToNode::GlobalEscape);
|
||||
}
|
||||
PointsToNode* pf = ptnode_adr(fi);
|
||||
int po = pf->offset();
|
||||
if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) {
|
||||
@ -377,6 +415,15 @@ void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) {
|
||||
// Add a deferred edge from node given by "from_i" to any field of adr_i
|
||||
// whose offset matches "offset".
|
||||
void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) {
|
||||
// No fields for NULL pointer.
|
||||
if (is_null_ptr(adr_i)) {
|
||||
return;
|
||||
}
|
||||
if (adr_i == _phantom_object) {
|
||||
// Add only one edge for unknown object.
|
||||
add_pointsto_edge(from_i, _phantom_object);
|
||||
return;
|
||||
}
|
||||
PointsToNode* an = ptnode_adr(adr_i);
|
||||
bool is_alloc = an->_node->is_Allocate();
|
||||
for (uint fe = 0; fe < an->edge_count(); fe++) {
|
||||
@ -392,6 +439,13 @@ void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int o
|
||||
add_deferred_edge(from_i, fi);
|
||||
}
|
||||
}
|
||||
// Some fields references (AddP) may still be missing
|
||||
// until Connection Graph construction is complete.
|
||||
// For example, loads from RAW pointers with offset 0
|
||||
// which don't have AddP.
|
||||
// A reference to phantom_object will be added if
|
||||
// a field reference is still missing after completing
|
||||
// Connection Graph (see remove_deferred()).
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
@ -1540,8 +1594,8 @@ bool ConnectionGraph::compute_escape() {
|
||||
|
||||
GrowableArray<Node*> alloc_worklist;
|
||||
GrowableArray<Node*> addp_worklist;
|
||||
GrowableArray<Node*> ptr_cmp_worklist;
|
||||
PhaseGVN* igvn = _igvn;
|
||||
bool has_allocations = false;
|
||||
|
||||
// Push all useful nodes onto CG list and set their type.
|
||||
for( uint next = 0; next < worklist_init.size(); ++next ) {
|
||||
@ -1551,11 +1605,8 @@ bool ConnectionGraph::compute_escape() {
|
||||
// for an escape status. See process_call_result() below.
|
||||
if (n->is_Allocate() || n->is_CallStaticJava() &&
|
||||
ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) {
|
||||
has_allocations = true;
|
||||
if (n->is_Allocate())
|
||||
alloc_worklist.append(n);
|
||||
}
|
||||
if(n->is_AddP()) {
|
||||
alloc_worklist.append(n);
|
||||
} else if(n->is_AddP()) {
|
||||
// Collect address nodes. Use them during stage 3 below
|
||||
// to build initial connection graph field edges.
|
||||
addp_worklist.append(n);
|
||||
@ -1563,6 +1614,10 @@ bool ConnectionGraph::compute_escape() {
|
||||
// Collect all MergeMem nodes to add memory slices for
|
||||
// scalar replaceable objects in split_unique_types().
|
||||
_mergemem_worklist.append(n->as_MergeMem());
|
||||
} else if (OptimizePtrCompare && n->is_Cmp() &&
|
||||
(n->Opcode() == Op_CmpP || n->Opcode() == Op_CmpN)) {
|
||||
// Compare pointers nodes
|
||||
ptr_cmp_worklist.append(n);
|
||||
}
|
||||
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
|
||||
Node* m = n->fast_out(i); // Get user
|
||||
@ -1570,7 +1625,7 @@ bool ConnectionGraph::compute_escape() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_allocations) {
|
||||
if (alloc_worklist.length() == 0) {
|
||||
_collecting = false;
|
||||
return false; // Nothing to do.
|
||||
}
|
||||
@ -1588,7 +1643,7 @@ bool ConnectionGraph::compute_escape() {
|
||||
for( uint next = 0; next < addp_length; ++next ) {
|
||||
Node* n = addp_worklist.at(next);
|
||||
Node* base = get_addp_base(n);
|
||||
if (base->is_Proj())
|
||||
if (base->is_Proj() && base->in(0)->is_Call())
|
||||
base = base->in(0);
|
||||
PointsToNode::NodeType nt = ptnode_adr(base->_idx)->node_type();
|
||||
if (nt == PointsToNode::JavaObject) {
|
||||
@ -1653,39 +1708,7 @@ bool ConnectionGraph::compute_escape() {
|
||||
}
|
||||
#undef CG_BUILD_ITER_LIMIT
|
||||
|
||||
Arena* arena = Thread::current()->resource_area();
|
||||
VectorSet visited(arena);
|
||||
|
||||
// 5. Find fields initializing values for not escaped allocations
|
||||
uint alloc_length = alloc_worklist.length();
|
||||
for (uint next = 0; next < alloc_length; ++next) {
|
||||
Node* n = alloc_worklist.at(next);
|
||||
if (ptnode_adr(n->_idx)->escape_state() == PointsToNode::NoEscape) {
|
||||
find_init_values(n, &visited, igvn);
|
||||
}
|
||||
}
|
||||
|
||||
worklist.clear();
|
||||
|
||||
// 6. Remove deferred edges from the graph.
|
||||
uint cg_length = cg_worklist.length();
|
||||
for (uint next = 0; next < cg_length; ++next) {
|
||||
int ni = cg_worklist.at(next);
|
||||
PointsToNode* ptn = ptnode_adr(ni);
|
||||
PointsToNode::NodeType nt = ptn->node_type();
|
||||
if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
|
||||
remove_deferred(ni, &worklist, &visited);
|
||||
Node *n = ptn->_node;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Adjust escape state of nonescaping objects.
|
||||
for (uint next = 0; next < addp_length; ++next) {
|
||||
Node* n = addp_worklist.at(next);
|
||||
adjust_escape_state(n);
|
||||
}
|
||||
|
||||
// 8. Propagate escape states.
|
||||
// 5. Propagate escaped states.
|
||||
worklist.clear();
|
||||
|
||||
// mark all nodes reachable from GlobalEscape nodes
|
||||
@ -1694,20 +1717,72 @@ bool ConnectionGraph::compute_escape() {
|
||||
// mark all nodes reachable from ArgEscape nodes
|
||||
bool has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ArgEscape);
|
||||
|
||||
Arena* arena = Thread::current()->resource_area();
|
||||
VectorSet visited(arena);
|
||||
|
||||
// 6. Find fields initializing values for not escaped allocations
|
||||
uint alloc_length = alloc_worklist.length();
|
||||
for (uint next = 0; next < alloc_length; ++next) {
|
||||
Node* n = alloc_worklist.at(next);
|
||||
if (ptnode_adr(n->_idx)->escape_state() == PointsToNode::NoEscape) {
|
||||
has_non_escaping_obj = true;
|
||||
if (n->is_Allocate()) {
|
||||
find_init_values(n, &visited, igvn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint cg_length = cg_worklist.length();
|
||||
|
||||
// Skip the rest of code if all objects escaped.
|
||||
if (!has_non_escaping_obj) {
|
||||
cg_length = 0;
|
||||
addp_length = 0;
|
||||
}
|
||||
|
||||
for (uint next = 0; next < cg_length; ++next) {
|
||||
int ni = cg_worklist.at(next);
|
||||
PointsToNode* ptn = ptnode_adr(ni);
|
||||
PointsToNode::NodeType nt = ptn->node_type();
|
||||
if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
|
||||
if (ptn->edge_count() == 0) {
|
||||
// No values were found. Assume the value was set
|
||||
// outside this method - add edge to phantom object.
|
||||
add_pointsto_edge(ni, _phantom_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Remove deferred edges from the graph.
|
||||
for (uint next = 0; next < cg_length; ++next) {
|
||||
int ni = cg_worklist.at(next);
|
||||
PointsToNode* ptn = ptnode_adr(ni);
|
||||
PointsToNode::NodeType nt = ptn->node_type();
|
||||
if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
|
||||
remove_deferred(ni, &worklist, &visited);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Adjust escape state of nonescaping objects.
|
||||
for (uint next = 0; next < addp_length; ++next) {
|
||||
Node* n = addp_worklist.at(next);
|
||||
adjust_escape_state(n);
|
||||
}
|
||||
|
||||
// push all NoEscape nodes on the worklist
|
||||
worklist.clear();
|
||||
for( uint next = 0; next < cg_length; ++next ) {
|
||||
int nk = cg_worklist.at(next);
|
||||
if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape)
|
||||
if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape &&
|
||||
!is_null_ptr(nk))
|
||||
worklist.push(nk);
|
||||
}
|
||||
|
||||
alloc_worklist.clear();
|
||||
// mark all nodes reachable from NoEscape nodes
|
||||
// Propagate scalar_replaceable value.
|
||||
while(worklist.length() > 0) {
|
||||
uint nk = worklist.pop();
|
||||
PointsToNode* ptn = ptnode_adr(nk);
|
||||
if (ptn->node_type() == PointsToNode::JavaObject &&
|
||||
!(nk == _noop_null || nk == _oop_null))
|
||||
has_non_escaping_obj = true; // Non Escape
|
||||
Node* n = ptn->_node;
|
||||
bool scalar_replaceable = ptn->scalar_replaceable();
|
||||
if (n->is_Allocate() && scalar_replaceable) {
|
||||
@ -1719,6 +1794,8 @@ bool ConnectionGraph::compute_escape() {
|
||||
uint e_cnt = ptn->edge_count();
|
||||
for (uint ei = 0; ei < e_cnt; ei++) {
|
||||
uint npi = ptn->edge_target(ei);
|
||||
if (is_null_ptr(npi))
|
||||
continue;
|
||||
PointsToNode *np = ptnode_adr(npi);
|
||||
if (np->escape_state() < PointsToNode::NoEscape) {
|
||||
set_escape_state(npi, PointsToNode::NoEscape);
|
||||
@ -1727,7 +1804,6 @@ bool ConnectionGraph::compute_escape() {
|
||||
}
|
||||
worklist.push(npi);
|
||||
} else if (np->scalar_replaceable() && !scalar_replaceable) {
|
||||
// Propagate scalar_replaceable value.
|
||||
np->set_scalar_replaceable(false);
|
||||
worklist.push(npi);
|
||||
}
|
||||
@ -1737,9 +1813,11 @@ bool ConnectionGraph::compute_escape() {
|
||||
_collecting = false;
|
||||
assert(C->unique() == nodes_size(), "there should be no new ideal nodes during ConnectionGraph build");
|
||||
|
||||
assert(ptnode_adr(_oop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
|
||||
assert(ptnode_adr(_oop_null)->escape_state() == PointsToNode::NoEscape &&
|
||||
ptnode_adr(_oop_null)->edge_count() == 0, "sanity");
|
||||
if (UseCompressedOops) {
|
||||
assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
|
||||
assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape &&
|
||||
ptnode_adr(_noop_null)->edge_count() == 0, "sanity");
|
||||
}
|
||||
|
||||
if (EliminateLocks && has_non_escaping_obj) {
|
||||
@ -1749,18 +1827,53 @@ bool ConnectionGraph::compute_escape() {
|
||||
Node *n = C->macro_node(i);
|
||||
if (n->is_AbstractLock()) { // Lock and Unlock nodes
|
||||
AbstractLockNode* alock = n->as_AbstractLock();
|
||||
if (!alock->is_eliminated()) {
|
||||
if (!alock->is_eliminated() || alock->is_coarsened()) {
|
||||
PointsToNode::EscapeState es = escape_state(alock->obj_node());
|
||||
assert(es != PointsToNode::UnknownEscape, "should know");
|
||||
if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) {
|
||||
// Mark it eliminated
|
||||
alock->set_eliminated();
|
||||
if (!alock->is_eliminated()) {
|
||||
// Mark it eliminated to update any counters
|
||||
alock->set_eliminated();
|
||||
} else {
|
||||
// The lock could be marked eliminated by lock coarsening
|
||||
// code during first IGVN before EA. Clear coarsened flag
|
||||
// to eliminate all associated locks/unlocks and relock
|
||||
// during deoptimization.
|
||||
alock->clear_coarsened();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OptimizePtrCompare && has_non_escaping_obj) {
|
||||
// Add ConI(#CC_GT) and ConI(#CC_EQ).
|
||||
_pcmp_neq = igvn->makecon(TypeInt::CC_GT);
|
||||
_pcmp_eq = igvn->makecon(TypeInt::CC_EQ);
|
||||
// Optimize objects compare.
|
||||
while (ptr_cmp_worklist.length() != 0) {
|
||||
Node *n = ptr_cmp_worklist.pop();
|
||||
Node *res = optimize_ptr_compare(n);
|
||||
if (res != NULL) {
|
||||
#ifndef PRODUCT
|
||||
if (PrintOptimizePtrCompare) {
|
||||
tty->print_cr("++++ Replaced: %d %s(%d,%d) --> %s", n->_idx, (n->Opcode() == Op_CmpP ? "CmpP" : "CmpN"), n->in(1)->_idx, n->in(2)->_idx, (res == _pcmp_eq ? "EQ" : "NotEQ"));
|
||||
if (Verbose) {
|
||||
n->dump(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_igvn->replace_node(n, res);
|
||||
}
|
||||
}
|
||||
// cleanup
|
||||
if (_pcmp_neq->outcnt() == 0)
|
||||
igvn->hash_delete(_pcmp_neq);
|
||||
if (_pcmp_eq->outcnt() == 0)
|
||||
igvn->hash_delete(_pcmp_eq);
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (PrintEscapeAnalysis) {
|
||||
dump(); // Dump ConnectionGraph
|
||||
@ -1821,15 +1934,30 @@ void ConnectionGraph::find_init_values(Node* alloc, VectorSet* visited, PhaseTra
|
||||
// Connection Graph does not record a default initialization by NULL
|
||||
// captured by Initialize node.
|
||||
//
|
||||
uint null_idx = UseCompressedOops ? _noop_null : _oop_null;
|
||||
uint ae_cnt = pta->edge_count();
|
||||
bool visited_bottom_offset = false;
|
||||
for (uint ei = 0; ei < ae_cnt; ei++) {
|
||||
uint nidx = pta->edge_target(ei); // Field (AddP)
|
||||
PointsToNode* ptn = ptnode_adr(nidx);
|
||||
assert(ptn->_node->is_AddP(), "Should be AddP nodes only");
|
||||
int offset = ptn->offset();
|
||||
if (offset != Type::OffsetBot &&
|
||||
offset != oopDesc::klass_offset_in_bytes() &&
|
||||
!visited->test_set(offset)) {
|
||||
if (offset == Type::OffsetBot) {
|
||||
if (!visited_bottom_offset) {
|
||||
visited_bottom_offset = true;
|
||||
// Check only oop fields.
|
||||
const Type* adr_type = ptn->_node->as_AddP()->bottom_type();
|
||||
if (!adr_type->isa_aryptr() ||
|
||||
(adr_type->isa_aryptr()->klass() == NULL) ||
|
||||
adr_type->isa_aryptr()->klass()->is_obj_array_klass()) {
|
||||
// OffsetBot is used to reference array's element,
|
||||
// always add reference to NULL since we don't
|
||||
// known which element is referenced.
|
||||
add_edge_from_fields(alloc->_idx, null_idx, offset);
|
||||
}
|
||||
}
|
||||
} else if (offset != oopDesc::klass_offset_in_bytes() &&
|
||||
!visited->test_set(offset)) {
|
||||
|
||||
// Check only oop fields.
|
||||
const Type* adr_type = ptn->_node->as_AddP()->bottom_type();
|
||||
@ -1904,7 +2032,6 @@ void ConnectionGraph::find_init_values(Node* alloc, VectorSet* visited, PhaseTra
|
||||
}
|
||||
if (value == NULL || value != ptnode_adr(value->_idx)->_node) {
|
||||
// A field's initializing value was not recorded. Add NULL.
|
||||
uint null_idx = UseCompressedOops ? _noop_null : _oop_null;
|
||||
add_edge_from_fields(alloc->_idx, null_idx, offset);
|
||||
}
|
||||
}
|
||||
@ -1990,13 +2117,21 @@ bool ConnectionGraph::propagate_escape_state(GrowableArray<int>* cg_worklist,
|
||||
}
|
||||
// mark all reachable nodes
|
||||
while (worklist->length() > 0) {
|
||||
PointsToNode* ptn = ptnode_adr(worklist->pop());
|
||||
if (ptn->node_type() == PointsToNode::JavaObject) {
|
||||
int pt = worklist->pop();
|
||||
PointsToNode* ptn = ptnode_adr(pt);
|
||||
if (ptn->node_type() == PointsToNode::JavaObject &&
|
||||
!is_null_ptr(pt)) {
|
||||
has_java_obj = true;
|
||||
if (esc_state > PointsToNode::NoEscape) {
|
||||
// fields values are unknown if object escapes
|
||||
add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
|
||||
}
|
||||
}
|
||||
uint e_cnt = ptn->edge_count();
|
||||
for (uint ei = 0; ei < e_cnt; ei++) {
|
||||
uint npi = ptn->edge_target(ei);
|
||||
if (is_null_ptr(npi))
|
||||
continue;
|
||||
PointsToNode *np = ptnode_adr(npi);
|
||||
if (np->escape_state() < esc_state) {
|
||||
set_escape_state(npi, esc_state);
|
||||
@ -2008,8 +2143,100 @@ bool ConnectionGraph::propagate_escape_state(GrowableArray<int>* cg_worklist,
|
||||
return has_java_obj && (esc_state < PointsToNode::GlobalEscape);
|
||||
}
|
||||
|
||||
void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) {
|
||||
// Optimize objects compare.
|
||||
Node* ConnectionGraph::optimize_ptr_compare(Node* n) {
|
||||
assert(OptimizePtrCompare, "sanity");
|
||||
// Clone returned Set since PointsTo() returns pointer
|
||||
// to the same structure ConnectionGraph.pt_ptset.
|
||||
VectorSet ptset1 = *PointsTo(n->in(1));
|
||||
VectorSet ptset2 = *PointsTo(n->in(2));
|
||||
|
||||
// Check simple cases first.
|
||||
if (ptset1.Size() == 1) {
|
||||
uint pt1 = ptset1.getelem();
|
||||
PointsToNode* ptn1 = ptnode_adr(pt1);
|
||||
if (ptn1->escape_state() == PointsToNode::NoEscape) {
|
||||
if (ptset2.Size() == 1 && ptset2.getelem() == pt1) {
|
||||
// Comparing the same not escaping object.
|
||||
return _pcmp_eq;
|
||||
}
|
||||
Node* obj = ptn1->_node;
|
||||
// Comparing not escaping allocation.
|
||||
if ((obj->is_Allocate() || obj->is_CallStaticJava()) &&
|
||||
!ptset2.test(pt1)) {
|
||||
return _pcmp_neq; // This includes nullness check.
|
||||
}
|
||||
}
|
||||
} else if (ptset2.Size() == 1) {
|
||||
uint pt2 = ptset2.getelem();
|
||||
PointsToNode* ptn2 = ptnode_adr(pt2);
|
||||
if (ptn2->escape_state() == PointsToNode::NoEscape) {
|
||||
Node* obj = ptn2->_node;
|
||||
// Comparing not escaping allocation.
|
||||
if ((obj->is_Allocate() || obj->is_CallStaticJava()) &&
|
||||
!ptset1.test(pt2)) {
|
||||
return _pcmp_neq; // This includes nullness check.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptset1.disjoint(ptset2)) {
|
||||
return NULL; // Sets are not disjoint
|
||||
}
|
||||
|
||||
// Sets are disjoint.
|
||||
bool set1_has_unknown_ptr = ptset1.test(_phantom_object) != 0;
|
||||
bool set2_has_unknown_ptr = ptset2.test(_phantom_object) != 0;
|
||||
bool set1_has_null_ptr = (ptset1.test(_oop_null) | ptset1.test(_noop_null)) != 0;
|
||||
bool set2_has_null_ptr = (ptset2.test(_oop_null) | ptset2.test(_noop_null)) != 0;
|
||||
|
||||
if (set1_has_unknown_ptr && set2_has_null_ptr ||
|
||||
set2_has_unknown_ptr && set1_has_null_ptr) {
|
||||
// Check nullness of unknown object.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Disjointness by itself is not sufficient since
|
||||
// alias analysis is not complete for escaped objects.
|
||||
// Disjoint sets are definitely unrelated only when
|
||||
// at least one set has only not escaping objects.
|
||||
if (!set1_has_unknown_ptr && !set1_has_null_ptr) {
|
||||
bool has_only_non_escaping_alloc = true;
|
||||
for (VectorSetI i(&ptset1); i.test(); ++i) {
|
||||
uint pt = i.elem;
|
||||
PointsToNode* ptn = ptnode_adr(pt);
|
||||
Node* obj = ptn->_node;
|
||||
if (ptn->escape_state() != PointsToNode::NoEscape ||
|
||||
!(obj->is_Allocate() || obj->is_CallStaticJava())) {
|
||||
has_only_non_escaping_alloc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_only_non_escaping_alloc) {
|
||||
return _pcmp_neq;
|
||||
}
|
||||
}
|
||||
if (!set2_has_unknown_ptr && !set2_has_null_ptr) {
|
||||
bool has_only_non_escaping_alloc = true;
|
||||
for (VectorSetI i(&ptset2); i.test(); ++i) {
|
||||
uint pt = i.elem;
|
||||
PointsToNode* ptn = ptnode_adr(pt);
|
||||
Node* obj = ptn->_node;
|
||||
if (ptn->escape_state() != PointsToNode::NoEscape ||
|
||||
!(obj->is_Allocate() || obj->is_CallStaticJava())) {
|
||||
has_only_non_escaping_alloc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_only_non_escaping_alloc) {
|
||||
return _pcmp_neq;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) {
|
||||
bool is_arraycopy = false;
|
||||
switch (call->Opcode()) {
|
||||
#ifdef ASSERT
|
||||
case Op_Allocate:
|
||||
@ -2019,25 +2246,44 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
||||
assert(false, "should be done already");
|
||||
break;
|
||||
#endif
|
||||
case Op_CallLeaf:
|
||||
case Op_CallLeafNoFP:
|
||||
is_arraycopy = (call->as_CallLeaf()->_name != NULL &&
|
||||
strstr(call->as_CallLeaf()->_name, "arraycopy") != 0);
|
||||
// fall through
|
||||
case Op_CallLeaf:
|
||||
{
|
||||
// Stub calls, objects do not escape but they are not scale replaceable.
|
||||
// Adjust escape state for outgoing arguments.
|
||||
const TypeTuple * d = call->tf()->domain();
|
||||
bool src_has_oops = false;
|
||||
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
|
||||
const Type* at = d->field_at(i);
|
||||
Node *arg = call->in(i)->uncast();
|
||||
const Type *aat = phase->type(arg);
|
||||
PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state();
|
||||
if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() &&
|
||||
ptnode_adr(arg->_idx)->escape_state() < PointsToNode::ArgEscape) {
|
||||
(is_arraycopy || arg_esc < PointsToNode::ArgEscape)) {
|
||||
|
||||
assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||
|
||||
aat->isa_ptr() != NULL, "expecting an Ptr");
|
||||
bool arg_has_oops = aat->isa_oopptr() &&
|
||||
(aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() ||
|
||||
(aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass()));
|
||||
if (i == TypeFunc::Parms) {
|
||||
src_has_oops = arg_has_oops;
|
||||
}
|
||||
//
|
||||
// src or dst could be j.l.Object when other is basic type array:
|
||||
//
|
||||
// arraycopy(char[],0,Object*,0,size);
|
||||
// arraycopy(Object*,0,char[],0,size);
|
||||
//
|
||||
// Don't add edges from dst's fields in such cases.
|
||||
//
|
||||
bool arg_is_arraycopy_dest = src_has_oops && is_arraycopy &&
|
||||
arg_has_oops && (i > TypeFunc::Parms);
|
||||
#ifdef ASSERT
|
||||
if (!(call->Opcode() == Op_CallLeafNoFP &&
|
||||
call->as_CallLeaf()->_name != NULL &&
|
||||
(strstr(call->as_CallLeaf()->_name, "arraycopy") != 0) ||
|
||||
if (!(is_arraycopy ||
|
||||
call->as_CallLeaf()->_name != NULL &&
|
||||
(strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 ||
|
||||
strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 ))
|
||||
@ -2046,20 +2292,72 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
||||
assert(false, "EA: unexpected CallLeaf");
|
||||
}
|
||||
#endif
|
||||
// Always process arraycopy's destination object since
|
||||
// we need to add all possible edges to references in
|
||||
// source object.
|
||||
if (arg_esc >= PointsToNode::ArgEscape &&
|
||||
!arg_is_arraycopy_dest) {
|
||||
continue;
|
||||
}
|
||||
set_escape_state(arg->_idx, PointsToNode::ArgEscape);
|
||||
Node* arg_base = arg;
|
||||
if (arg->is_AddP()) {
|
||||
//
|
||||
// The inline_native_clone() case when the arraycopy stub is called
|
||||
// after the allocation before Initialize and CheckCastPP nodes.
|
||||
// Or normal arraycopy for object arrays case.
|
||||
//
|
||||
// Set AddP's base (Allocate) as not scalar replaceable since
|
||||
// pointer to the base (with offset) is passed as argument.
|
||||
//
|
||||
arg = get_addp_base(arg);
|
||||
arg_base = get_addp_base(arg);
|
||||
}
|
||||
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
|
||||
uint pt = j.elem;
|
||||
set_escape_state(pt, PointsToNode::ArgEscape);
|
||||
VectorSet argset = *PointsTo(arg_base); // Clone set
|
||||
for( VectorSetI j(&argset); j.test(); ++j ) {
|
||||
uint pd = j.elem; // Destination object
|
||||
set_escape_state(pd, PointsToNode::ArgEscape);
|
||||
|
||||
if (arg_is_arraycopy_dest) {
|
||||
PointsToNode* ptd = ptnode_adr(pd);
|
||||
// Conservatively reference an unknown object since
|
||||
// not all source's fields/elements may be known.
|
||||
add_edge_from_fields(pd, _phantom_object, Type::OffsetBot);
|
||||
|
||||
Node *src = call->in(TypeFunc::Parms)->uncast();
|
||||
Node* src_base = src;
|
||||
if (src->is_AddP()) {
|
||||
src_base = get_addp_base(src);
|
||||
}
|
||||
// Create edges from destination's fields to
|
||||
// everything known source's fields could point to.
|
||||
for( VectorSetI s(PointsTo(src_base)); s.test(); ++s ) {
|
||||
uint ps = s.elem;
|
||||
bool has_bottom_offset = false;
|
||||
for (uint fd = 0; fd < ptd->edge_count(); fd++) {
|
||||
assert(ptd->edge_type(fd) == PointsToNode::FieldEdge, "expecting a field edge");
|
||||
int fdi = ptd->edge_target(fd);
|
||||
PointsToNode* pfd = ptnode_adr(fdi);
|
||||
int offset = pfd->offset();
|
||||
if (offset == Type::OffsetBot)
|
||||
has_bottom_offset = true;
|
||||
assert(offset != -1, "offset should be set");
|
||||
add_deferred_edge_to_fields(fdi, ps, offset);
|
||||
}
|
||||
// Destination object may not have access (no field edge)
|
||||
// to fields which are accessed in source object.
|
||||
// As result no edges will be created to those source's
|
||||
// fields and escape state of destination object will
|
||||
// not be propagated to those fields.
|
||||
//
|
||||
// Mark source object as global escape except in
|
||||
// the case with Type::OffsetBot field (which is
|
||||
// common case for array elements access) when
|
||||
// edges are created to all source's fields.
|
||||
if (!has_bottom_offset) {
|
||||
set_escape_state(ps, PointsToNode::GlobalEscape);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2102,14 +2400,16 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
||||
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
|
||||
uint pt = j.elem;
|
||||
if (global_escapes) {
|
||||
//The argument global escapes, mark everything it could point to
|
||||
// The argument global escapes, mark everything it could point to
|
||||
set_escape_state(pt, PointsToNode::GlobalEscape);
|
||||
add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
|
||||
} else {
|
||||
if (fields_escapes) {
|
||||
// The argument itself doesn't escape, but any fields might
|
||||
add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
|
||||
}
|
||||
set_escape_state(pt, PointsToNode::ArgEscape);
|
||||
if (fields_escapes) {
|
||||
// The argument itself doesn't escape, but any fields might.
|
||||
// Use OffsetTop to indicate such case.
|
||||
add_edge_from_fields(pt, _phantom_object, Type::OffsetTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2135,6 +2435,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
||||
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
|
||||
uint pt = j.elem;
|
||||
set_escape_state(pt, PointsToNode::GlobalEscape);
|
||||
add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2235,15 +2536,16 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
||||
// it's fields will be marked as NoEscape at least.
|
||||
set_escape_state(call_idx, PointsToNode::NoEscape);
|
||||
ptnode_adr(call_idx)->set_scalar_replaceable(false);
|
||||
// Fields values are unknown
|
||||
add_edge_from_fields(call_idx, _phantom_object, Type::OffsetBot);
|
||||
add_pointsto_edge(resproj_idx, call_idx);
|
||||
copy_dependencies = true;
|
||||
} else if (call_analyzer->is_return_local()) {
|
||||
} else {
|
||||
// determine whether any arguments are returned
|
||||
set_escape_state(call_idx, PointsToNode::ArgEscape);
|
||||
bool ret_arg = false;
|
||||
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
|
||||
const Type* at = d->field_at(i);
|
||||
|
||||
if (at->isa_oopptr() != NULL) {
|
||||
Node *arg = call->in(i)->uncast();
|
||||
|
||||
@ -2259,17 +2561,14 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done && !ret_arg) {
|
||||
// Returns unknown object.
|
||||
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
||||
add_pointsto_edge(resproj_idx, _phantom_object);
|
||||
}
|
||||
if (done) {
|
||||
copy_dependencies = true;
|
||||
// is_return_local() is true when only arguments are returned.
|
||||
if (!ret_arg || !call_analyzer->is_return_local()) {
|
||||
// Returns unknown object.
|
||||
add_pointsto_edge(resproj_idx, _phantom_object);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
||||
add_pointsto_edge(resproj_idx, _phantom_object);
|
||||
}
|
||||
if (copy_dependencies)
|
||||
call_analyzer->copy_dependencies(_compile->dependencies());
|
||||
@ -2431,6 +2730,11 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase)
|
||||
add_node(n, PointsToNode::JavaObject, PointsToNode::GlobalEscape, false);
|
||||
break;
|
||||
}
|
||||
case Op_PartialSubtypeCheck:
|
||||
{ // Produces Null or notNull and is used in CmpP.
|
||||
add_node(n, PointsToNode::JavaObject, PointsToNode::ArgEscape, true);
|
||||
break;
|
||||
}
|
||||
case Op_Phi:
|
||||
{
|
||||
const Type *t = n->as_Phi()->type();
|
||||
@ -2589,10 +2893,11 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) {
|
||||
case Op_AddP:
|
||||
{
|
||||
Node *base = get_addp_base(n);
|
||||
int offset = address_offset(n, phase);
|
||||
// Create a field edge to this node from everything base could point to.
|
||||
for( VectorSetI i(PointsTo(base)); i.test(); ++i ) {
|
||||
uint pt = i.elem;
|
||||
add_field_edge(pt, n_idx, address_offset(n, phase));
|
||||
add_field_edge(pt, n_idx, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2659,6 +2964,10 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) {
|
||||
int offset = address_offset(adr, phase);
|
||||
for( VectorSetI i(PointsTo(adr_base)); i.test(); ++i ) {
|
||||
uint pt = i.elem;
|
||||
if (adr->is_AddP()) {
|
||||
// Add field edge if it is missing.
|
||||
add_field_edge(pt, adr->_idx, offset);
|
||||
}
|
||||
add_deferred_edge_to_fields(n_idx, pt, offset);
|
||||
}
|
||||
break;
|
||||
@ -2668,6 +2977,11 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) {
|
||||
assert(false, "Op_Parm");
|
||||
break;
|
||||
}
|
||||
case Op_PartialSubtypeCheck:
|
||||
{
|
||||
assert(false, "Op_PartialSubtypeCheck");
|
||||
break;
|
||||
}
|
||||
case Op_Phi:
|
||||
{
|
||||
#ifdef ASSERT
|
||||
@ -2745,11 +3059,14 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) {
|
||||
assert(adr->is_AddP(), "expecting an AddP");
|
||||
Node *adr_base = get_addp_base(adr);
|
||||
Node *val = n->in(MemNode::ValueIn)->uncast();
|
||||
int offset = address_offset(adr, phase);
|
||||
// For everything "adr_base" could point to, create a deferred edge
|
||||
// to "val" from each field with the same offset.
|
||||
for( VectorSetI i(PointsTo(adr_base)); i.test(); ++i ) {
|
||||
uint pt = i.elem;
|
||||
add_edge_from_fields(pt, val->_idx, address_offset(adr, phase));
|
||||
// Add field edge if it is missing.
|
||||
add_field_edge(pt, adr->_idx, offset);
|
||||
add_edge_from_fields(pt, val->_idx, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -160,6 +160,7 @@ private:
|
||||
Node* _node; // Ideal node corresponding to this PointsTo node.
|
||||
int _offset; // Object fields offsets.
|
||||
bool _scalar_replaceable; // Not escaped object could be replaced with scalar
|
||||
bool _has_unknown_ptr; // Has edge to phantom_object
|
||||
|
||||
public:
|
||||
PointsToNode():
|
||||
@ -168,6 +169,7 @@ public:
|
||||
_edges(NULL),
|
||||
_node(NULL),
|
||||
_offset(-1),
|
||||
_has_unknown_ptr(false),
|
||||
_scalar_replaceable(true) {}
|
||||
|
||||
|
||||
@ -175,6 +177,7 @@ public:
|
||||
NodeType node_type() const { return _type;}
|
||||
int offset() { return _offset;}
|
||||
bool scalar_replaceable() { return _scalar_replaceable;}
|
||||
bool has_unknown_ptr() { return _has_unknown_ptr;}
|
||||
|
||||
void set_offset(int offs) { _offset = offs;}
|
||||
void set_escape_state(EscapeState state) { _escape = state; }
|
||||
@ -183,6 +186,7 @@ public:
|
||||
_type = ntype;
|
||||
}
|
||||
void set_scalar_replaceable(bool v) { _scalar_replaceable = v; }
|
||||
void set_has_unknown_ptr() { _has_unknown_ptr = true; }
|
||||
|
||||
// count of outgoing edges
|
||||
uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); }
|
||||
@ -236,6 +240,8 @@ private:
|
||||
// are assumed to point to.
|
||||
uint _oop_null; // ConP(#NULL)->_idx
|
||||
uint _noop_null; // ConN(#NULL)->_idx
|
||||
Node* _pcmp_neq; // ConI(#CC_GT)
|
||||
Node* _pcmp_eq; // ConI(#CC_EQ)
|
||||
|
||||
Compile * _compile; // Compile object for current compilation
|
||||
PhaseIterGVN * _igvn; // Value numbering
|
||||
@ -248,6 +254,8 @@ private:
|
||||
}
|
||||
uint nodes_size() const { return _nodes.length(); }
|
||||
|
||||
bool is_null_ptr(uint idx) const { return (idx == _noop_null || idx == _oop_null); }
|
||||
|
||||
// Add node to ConnectionGraph.
|
||||
void add_node(Node *n, PointsToNode::NodeType nt, PointsToNode::EscapeState es, bool done);
|
||||
|
||||
@ -331,10 +339,9 @@ private:
|
||||
}
|
||||
|
||||
// Notify optimizer that a node has been modified
|
||||
// Node: This assumes that escape analysis is run before
|
||||
// PhaseIterGVN creation
|
||||
void record_for_optimizer(Node *n) {
|
||||
_igvn->_worklist.push(n);
|
||||
_igvn->add_users_to_worklist(n);
|
||||
}
|
||||
|
||||
// Set the escape state of a node
|
||||
@ -351,6 +358,9 @@ private:
|
||||
GrowableArray<uint>* worklist,
|
||||
PointsToNode::EscapeState esc_state);
|
||||
|
||||
// Optimize objects compare.
|
||||
Node* optimize_ptr_compare(Node* n);
|
||||
|
||||
// Compute the escape information
|
||||
bool compute_escape();
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ void PhaseCFG::replace_block_proj_ctrl( Node *n ) {
|
||||
assert(in0 != NULL, "Only control-dependent");
|
||||
const Node *p = in0->is_block_proj();
|
||||
if (p != NULL && p != n) { // Control from a block projection?
|
||||
assert(!n->pinned() || n->is_MachConstantBase() || n->is_SafePointScalarObject(), "only pinned MachConstantBase or SafePointScalarObject node is expected here");
|
||||
assert(!n->pinned() || n->is_MachConstantBase(), "only pinned MachConstantBase node is expected here");
|
||||
// Find trailing Region
|
||||
Block *pb = _bbs[in0->_idx]; // Block-projection already has basic block
|
||||
uint j = 0;
|
||||
|
||||
@ -447,6 +447,9 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) {
|
||||
if (flags & Node::Flag_may_be_short_branch) {
|
||||
print_prop("may_be_short_branch", "true");
|
||||
}
|
||||
if (flags & Node::Flag_has_call) {
|
||||
print_prop("has_call", "true");
|
||||
}
|
||||
|
||||
if (C->matcher() != NULL) {
|
||||
if (C->matcher()->is_shared(node)) {
|
||||
|
||||
@ -548,6 +548,22 @@ void Block::needed_for_next_call(Node *this_call, VectorSet &next_call, Block_Ar
|
||||
set_next_call(call, next_call, bbs);
|
||||
}
|
||||
|
||||
//------------------------------add_call_kills-------------------------------------
|
||||
void Block::add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_policy, bool exclude_soe) {
|
||||
// Fill in the kill mask for the call
|
||||
for( OptoReg::Name r = OptoReg::Name(0); r < _last_Mach_Reg; r=OptoReg::add(r,1) ) {
|
||||
if( !regs.Member(r) ) { // Not already defined by the call
|
||||
// Save-on-call register?
|
||||
if ((save_policy[r] == 'C') ||
|
||||
(save_policy[r] == 'A') ||
|
||||
((save_policy[r] == 'E') && exclude_soe)) {
|
||||
proj->_rout.Insert(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------sched_call-------------------------------------
|
||||
uint Block::sched_call( Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call ) {
|
||||
RegMask regs;
|
||||
@ -631,17 +647,7 @@ uint Block::sched_call( Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_
|
||||
proj->_rout.OR(Matcher::method_handle_invoke_SP_save_mask());
|
||||
}
|
||||
|
||||
// Fill in the kill mask for the call
|
||||
for( OptoReg::Name r = OptoReg::Name(0); r < _last_Mach_Reg; r=OptoReg::add(r,1) ) {
|
||||
if( !regs.Member(r) ) { // Not already defined by the call
|
||||
// Save-on-call register?
|
||||
if ((save_policy[r] == 'C') ||
|
||||
(save_policy[r] == 'A') ||
|
||||
((save_policy[r] == 'E') && exclude_soe)) {
|
||||
proj->_rout.Insert(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_call_kills(proj, regs, save_policy, exclude_soe);
|
||||
|
||||
return node_cnt;
|
||||
}
|
||||
@ -776,6 +782,7 @@ bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, int *ready_cnt, Vect
|
||||
}
|
||||
#endif
|
||||
|
||||
uint max_idx = matcher.C->unique();
|
||||
// Pull from worklist and schedule
|
||||
while( worklist.size() ) { // Worklist is not ready
|
||||
|
||||
@ -815,11 +822,28 @@ bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, int *ready_cnt, Vect
|
||||
phi_cnt = sched_call(matcher, cfg->_bbs, phi_cnt, worklist, ready_cnt, mcall, next_call);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n->is_Mach() && n->as_Mach()->has_call()) {
|
||||
RegMask regs;
|
||||
regs.Insert(matcher.c_frame_pointer());
|
||||
regs.OR(n->out_RegMask());
|
||||
|
||||
MachProjNode *proj = new (matcher.C, 1) MachProjNode( n, 1, RegMask::Empty, MachProjNode::fat_proj );
|
||||
cfg->_bbs.map(proj->_idx,this);
|
||||
_nodes.insert(phi_cnt++, proj);
|
||||
|
||||
add_call_kills(proj, regs, matcher._c_reg_save_policy, false);
|
||||
}
|
||||
|
||||
// Children are now all ready
|
||||
for (DUIterator_Fast i5max, i5 = n->fast_outs(i5max); i5 < i5max; i5++) {
|
||||
Node* m = n->fast_out(i5); // Get user
|
||||
if( cfg->_bbs[m->_idx] != this ) continue;
|
||||
if( m->is_Phi() ) continue;
|
||||
if (m->_idx > max_idx) { // new node, skip it
|
||||
assert(m->is_MachProj() && n->is_Mach() && n->as_Mach()->has_call(), "unexpected node types");
|
||||
continue;
|
||||
}
|
||||
if( !--ready_cnt[m->_idx] )
|
||||
worklist.push(m);
|
||||
}
|
||||
|
||||
@ -715,7 +715,6 @@ Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) {
|
||||
long limit_con = cl->limit()->get_int();
|
||||
julong trip_cnt = cl->trip_count();
|
||||
long final_con = init_con + trip_cnt*stride_con;
|
||||
final_con -= stride_con;
|
||||
int final_int = (int)final_con;
|
||||
// The final value should be in integer range since the loop
|
||||
// is counted and the limit was checked for overflow.
|
||||
@ -1947,7 +1946,7 @@ void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool skip_loop_opts)
|
||||
}
|
||||
|
||||
// Nothing to do, so get out
|
||||
if( !C->has_loops() && !do_split_ifs && !_verify_me && !_verify_only ) {
|
||||
if( !C->has_loops() && !skip_loop_opts && !do_split_ifs && !_verify_me && !_verify_only ) {
|
||||
_igvn.optimize(); // Cleanup NeverBranches
|
||||
return;
|
||||
}
|
||||
|
||||
@ -480,21 +480,20 @@ void MachTypeNode::dump_spec(outputStream *st) const {
|
||||
|
||||
//=============================================================================
|
||||
int MachConstantNode::constant_offset() {
|
||||
int offset = _constant.offset();
|
||||
// Bind the offset lazily.
|
||||
if (offset == -1) {
|
||||
if (_constant.offset() == -1) {
|
||||
Compile::ConstantTable& constant_table = Compile::current()->constant_table();
|
||||
// If called from Compile::scratch_emit_size assume the worst-case
|
||||
// for load offsets: half the constant table size.
|
||||
// NOTE: Don't return or calculate the actual offset (which might
|
||||
// be zero) because that leads to problems with e.g. jumpXtnd on
|
||||
// some architectures (cf. add-optimization in SPARC jumpXtnd).
|
||||
if (Compile::current()->in_scratch_emit_size())
|
||||
return constant_table.size() / 2;
|
||||
offset = constant_table.table_base_offset() + constant_table.find_offset(_constant);
|
||||
_constant.set_offset(offset);
|
||||
int offset = constant_table.find_offset(_constant);
|
||||
// If called from Compile::scratch_emit_size return the
|
||||
// pre-calculated offset.
|
||||
// NOTE: If the AD file does some table base offset optimizations
|
||||
// later the AD file needs to take care of this fact.
|
||||
if (Compile::current()->in_scratch_emit_size()) {
|
||||
return constant_table.calculate_table_base_offset() + offset;
|
||||
}
|
||||
_constant.set_offset(constant_table.table_base_offset() + offset);
|
||||
}
|
||||
return offset;
|
||||
return _constant.offset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -190,6 +190,9 @@ public:
|
||||
// Avoid back to back some instructions on some CPUs.
|
||||
bool avoid_back_to_back() const { return (flags() & Flag_avoid_back_to_back) != 0; }
|
||||
|
||||
// instruction implemented with a call
|
||||
bool has_call() const { return (flags() & Flag_has_call) != 0; }
|
||||
|
||||
// First index in _in[] corresponding to operand, or -1 if there is none
|
||||
int operand_index(uint operand) const;
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ void PhaseMacroExpand::copy_call_debug_info(CallNode *oldcall, CallNode * newcal
|
||||
uint old_unique = C->unique();
|
||||
Node* new_in = old_sosn->clone(jvms_adj, sosn_map);
|
||||
if (old_unique != C->unique()) {
|
||||
new_in->set_req(0, newcall->in(0)); // reset control edge
|
||||
new_in->set_req(0, C->root()); // reset control edge
|
||||
new_in = transform_later(new_in); // Register new node.
|
||||
}
|
||||
old_in = new_in;
|
||||
@ -565,7 +565,6 @@ bool PhaseMacroExpand::can_eliminate_allocation(AllocateNode *alloc, GrowableArr
|
||||
if (res == NULL) {
|
||||
// All users were eliminated.
|
||||
} else if (!res->is_CheckCastPP()) {
|
||||
alloc->_is_scalar_replaceable = false; // don't try again
|
||||
NOT_PRODUCT(fail_eliminate = "Allocation does not have unique CheckCastPP";)
|
||||
can_eliminate = false;
|
||||
} else {
|
||||
@ -719,7 +718,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <Sa
|
||||
alloc,
|
||||
#endif
|
||||
first_ind, nfields);
|
||||
sobj->init_req(0, sfpt->in(TypeFunc::Control));
|
||||
sobj->init_req(0, C->root());
|
||||
transform_later(sobj);
|
||||
|
||||
// Scan object's fields adding an input to the safepoint for each field.
|
||||
@ -762,10 +761,10 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <Sa
|
||||
|
||||
Node *field_val = value_from_mem(mem, basic_elem_type, field_type, field_addr_type, alloc);
|
||||
if (field_val == NULL) {
|
||||
// we weren't able to find a value for this field,
|
||||
// give up on eliminating this allocation
|
||||
alloc->_is_scalar_replaceable = false; // don't try again
|
||||
// remove any extra entries we added to the safepoint
|
||||
// We weren't able to find a value for this field,
|
||||
// give up on eliminating this allocation.
|
||||
|
||||
// Remove any extra entries we added to the safepoint.
|
||||
uint last = sfpt->req() - 1;
|
||||
for (int k = 0; k < j; k++) {
|
||||
sfpt->del_req(last--);
|
||||
@ -1804,9 +1803,9 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) {
|
||||
#ifndef PRODUCT
|
||||
if (PrintEliminateLocks) {
|
||||
if (alock->is_Lock()) {
|
||||
tty->print_cr("++++ Eliminating: %d Lock", alock->_idx);
|
||||
tty->print_cr("++++ Eliminated: %d Lock", alock->_idx);
|
||||
} else {
|
||||
tty->print_cr("++++ Eliminating: %d Unlock", alock->_idx);
|
||||
tty->print_cr("++++ Eliminated: %d Unlock", alock->_idx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2165,11 +2164,12 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
|
||||
_igvn.replace_node(_memproj_fallthrough, mem_phi);
|
||||
}
|
||||
|
||||
//------------------------------expand_macro_nodes----------------------
|
||||
// Returns true if a failure occurred.
|
||||
bool PhaseMacroExpand::expand_macro_nodes() {
|
||||
//---------------------------eliminate_macro_nodes----------------------
|
||||
// Eliminate scalar replaced allocations and associated locks.
|
||||
void PhaseMacroExpand::eliminate_macro_nodes() {
|
||||
if (C->macro_count() == 0)
|
||||
return false;
|
||||
return;
|
||||
|
||||
// First, attempt to eliminate locks
|
||||
int cnt = C->macro_count();
|
||||
for (int i=0; i < cnt; i++) {
|
||||
@ -2189,14 +2189,6 @@ bool PhaseMacroExpand::expand_macro_nodes() {
|
||||
debug_only(int old_macro_count = C->macro_count(););
|
||||
if (n->is_AbstractLock()) {
|
||||
success = eliminate_locking_node(n->as_AbstractLock());
|
||||
} else if (n->Opcode() == Op_LoopLimit) {
|
||||
// Remove it from macro list and put on IGVN worklist to optimize.
|
||||
C->remove_macro_node(n);
|
||||
_igvn._worklist.push(n);
|
||||
success = true;
|
||||
} else if (n->Opcode() == Op_Opaque1 || n->Opcode() == Op_Opaque2) {
|
||||
_igvn.replace_node(n, n->in(1));
|
||||
success = true;
|
||||
}
|
||||
assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
|
||||
progress = progress || success;
|
||||
@ -2220,18 +2212,50 @@ bool PhaseMacroExpand::expand_macro_nodes() {
|
||||
assert(!n->as_AbstractLock()->is_eliminated(), "sanity");
|
||||
break;
|
||||
default:
|
||||
assert(false, "unknown node type in macro list");
|
||||
assert(n->Opcode() == Op_LoopLimit ||
|
||||
n->Opcode() == Op_Opaque1 ||
|
||||
n->Opcode() == Op_Opaque2, "unknown node type in macro list");
|
||||
}
|
||||
assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
|
||||
progress = progress || success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------expand_macro_nodes----------------------
|
||||
// Returns true if a failure occurred.
|
||||
bool PhaseMacroExpand::expand_macro_nodes() {
|
||||
// Last attempt to eliminate macro nodes.
|
||||
eliminate_macro_nodes();
|
||||
|
||||
// Make sure expansion will not cause node limit to be exceeded.
|
||||
// Worst case is a macro node gets expanded into about 50 nodes.
|
||||
// Allow 50% more for optimization.
|
||||
if (C->check_node_count(C->macro_count() * 75, "out of nodes before macro expansion" ) )
|
||||
return true;
|
||||
|
||||
// Eliminate Opaque and LoopLimit nodes. Do it after all loop optimizations.
|
||||
bool progress = true;
|
||||
while (progress) {
|
||||
progress = false;
|
||||
for (int i = C->macro_count(); i > 0; i--) {
|
||||
Node * n = C->macro_node(i-1);
|
||||
bool success = false;
|
||||
debug_only(int old_macro_count = C->macro_count(););
|
||||
if (n->Opcode() == Op_LoopLimit) {
|
||||
// Remove it from macro list and put on IGVN worklist to optimize.
|
||||
C->remove_macro_node(n);
|
||||
_igvn._worklist.push(n);
|
||||
success = true;
|
||||
} else if (n->Opcode() == Op_Opaque1 || n->Opcode() == Op_Opaque2) {
|
||||
_igvn.replace_node(n, n->in(1));
|
||||
success = true;
|
||||
}
|
||||
assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
|
||||
progress = progress || success;
|
||||
}
|
||||
}
|
||||
|
||||
// expand "macro" nodes
|
||||
// nodes are removed from the macro list as they are processed
|
||||
while (C->macro_count() > 0) {
|
||||
@ -2265,5 +2289,6 @@ bool PhaseMacroExpand::expand_macro_nodes() {
|
||||
|
||||
_igvn.set_delay_transform(false);
|
||||
_igvn.optimize();
|
||||
if (C->failing()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -119,6 +119,7 @@ public:
|
||||
PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) {
|
||||
_igvn.set_delay_transform(true);
|
||||
}
|
||||
void eliminate_macro_nodes();
|
||||
bool expand_macro_nodes();
|
||||
|
||||
};
|
||||
|
||||
@ -294,7 +294,6 @@ public:
|
||||
RegMask _return_value_mask;
|
||||
// Inline Cache Register
|
||||
static OptoReg::Name inline_cache_reg();
|
||||
static const RegMask &inline_cache_reg_mask();
|
||||
static int inline_cache_reg_encode();
|
||||
|
||||
// Register for DIVI projection of divmodI
|
||||
@ -324,7 +323,6 @@ public:
|
||||
// and then expanded into the inline_cache_reg and a method_oop register
|
||||
|
||||
static OptoReg::Name interpreter_method_oop_reg();
|
||||
static const RegMask &interpreter_method_oop_reg_mask();
|
||||
static int interpreter_method_oop_reg_encode();
|
||||
|
||||
static OptoReg::Name compiler_method_oop_reg();
|
||||
@ -333,7 +331,6 @@ public:
|
||||
|
||||
// Interpreter's Frame Pointer Register
|
||||
static OptoReg::Name interpreter_frame_pointer_reg();
|
||||
static const RegMask &interpreter_frame_pointer_reg_mask();
|
||||
|
||||
// Java-Native calling convention
|
||||
// (what you use when intercalling between Java and C++ code)
|
||||
@ -371,10 +368,6 @@ public:
|
||||
// registers? True for Intel but false for most RISCs
|
||||
static const bool clone_shift_expressions;
|
||||
|
||||
// Should constant table entries be accessed with loads using
|
||||
// absolute addressing? True for x86 but false for most RISCs.
|
||||
static const bool constant_table_absolute_addressing;
|
||||
|
||||
static bool narrow_oop_use_complex_address();
|
||||
|
||||
// Generate implicit null check for narrow oops if it can fold
|
||||
|
||||
@ -265,6 +265,13 @@ Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) {
|
||||
if( phase->type( mem ) == Type::TOP ) return NodeSentinel; // caller will return NULL
|
||||
assert( mem != this, "dead loop in MemNode::Ideal" );
|
||||
|
||||
if (can_reshape && igvn != NULL && igvn->_worklist.member(mem)) {
|
||||
// This memory slice may be dead.
|
||||
// Delay this mem node transformation until the memory is processed.
|
||||
phase->is_IterGVN()->_worklist.push(this);
|
||||
return NodeSentinel; // caller will return NULL
|
||||
}
|
||||
|
||||
Node *address = in(MemNode::Address);
|
||||
const Type *t_adr = phase->type( address );
|
||||
if( t_adr == Type::TOP ) return NodeSentinel; // caller will return NULL
|
||||
@ -2661,6 +2668,8 @@ uint StrIntrinsicNode::match_edge(uint idx) const {
|
||||
// control copies
|
||||
Node *StrIntrinsicNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (remove_dead_region(phase, can_reshape)) return this;
|
||||
// Don't bother trying to transform a dead node
|
||||
if (in(0) && in(0)->is_top()) return NULL;
|
||||
|
||||
if (can_reshape) {
|
||||
Node* mem = phase->transform(in(MemNode::Memory));
|
||||
@ -2675,6 +2684,12 @@ Node *StrIntrinsicNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
const Type *StrIntrinsicNode::Value( PhaseTransform *phase ) const {
|
||||
if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP;
|
||||
return bottom_type();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
MemBarNode::MemBarNode(Compile* C, int alias_idx, Node* precedent)
|
||||
: MultiNode(TypeFunc::Parms + (precedent == NULL? 0: 1)),
|
||||
@ -2715,6 +2730,8 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) {
|
||||
// control copies
|
||||
Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (remove_dead_region(phase, can_reshape)) return this;
|
||||
// Don't bother trying to transform a dead node
|
||||
if (in(0) && in(0)->is_top()) return NULL;
|
||||
|
||||
// Eliminate volatile MemBars for scalar replaced objects.
|
||||
if (can_reshape && req() == (Precedent+1) &&
|
||||
|
||||
@ -800,6 +800,7 @@ public:
|
||||
virtual uint match_edge(uint idx) const;
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *Value(PhaseTransform *phase) const;
|
||||
};
|
||||
|
||||
//------------------------------StrComp-------------------------------------
|
||||
|
||||
@ -641,7 +641,8 @@ public:
|
||||
Flag_is_dead_loop_safe = Flag_is_cisc_alternate << 1,
|
||||
Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1,
|
||||
Flag_avoid_back_to_back = Flag_may_be_short_branch << 1,
|
||||
_max_flags = (Flag_avoid_back_to_back << 1) - 1 // allow flags combination
|
||||
Flag_has_call = Flag_avoid_back_to_back << 1,
|
||||
_max_flags = (Flag_has_call << 1) - 1 // allow flags combination
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@ -890,7 +890,7 @@ class CommandLineFlags {
|
||||
diagnostic(bool, TraceNMethodInstalls, false, \
|
||||
"Trace nmethod intallation") \
|
||||
\
|
||||
diagnostic(intx, ScavengeRootsInCode, 1, \
|
||||
diagnostic(intx, ScavengeRootsInCode, 2, \
|
||||
"0: do not allow scavengable oops in the code cache; " \
|
||||
"1: allow scavenging from the code cache; " \
|
||||
"2: emit as many constants as the compiler can see") \
|
||||
|
||||
@ -30,6 +30,27 @@
|
||||
#include "runtime/simpleThresholdPolicy.inline.hpp"
|
||||
#include "code/scopeDesc.hpp"
|
||||
|
||||
|
||||
void SimpleThresholdPolicy::print_counters(const char* prefix, methodHandle mh) {
|
||||
int invocation_count = mh->invocation_count();
|
||||
int backedge_count = mh->backedge_count();
|
||||
methodDataHandle mdh = mh->method_data();
|
||||
int mdo_invocations = 0, mdo_backedges = 0;
|
||||
int mdo_invocations_start = 0, mdo_backedges_start = 0;
|
||||
if (mdh() != NULL) {
|
||||
mdo_invocations = mdh->invocation_count();
|
||||
mdo_backedges = mdh->backedge_count();
|
||||
mdo_invocations_start = mdh->invocation_count_start();
|
||||
mdo_backedges_start = mdh->backedge_count_start();
|
||||
}
|
||||
tty->print(" %stotal: %d,%d %smdo: %d(%d),%d(%d)", prefix,
|
||||
invocation_count, backedge_count, prefix,
|
||||
mdo_invocations, mdo_invocations_start,
|
||||
mdo_backedges, mdo_backedges_start);
|
||||
tty->print(" %smax levels: %d,%d", prefix,
|
||||
mh->highest_comp_level(), mh->highest_osr_comp_level());
|
||||
}
|
||||
|
||||
// Print an event.
|
||||
void SimpleThresholdPolicy::print_event(EventType type, methodHandle mh, methodHandle imh,
|
||||
int bci, CompLevel level) {
|
||||
@ -38,8 +59,6 @@ void SimpleThresholdPolicy::print_event(EventType type, methodHandle mh, methodH
|
||||
ttyLocker tty_lock;
|
||||
tty->print("%lf: [", os::elapsedTime());
|
||||
|
||||
int invocation_count = mh->invocation_count();
|
||||
int backedge_count = mh->backedge_count();
|
||||
switch(type) {
|
||||
case CALL:
|
||||
tty->print("call");
|
||||
@ -82,23 +101,9 @@ void SimpleThresholdPolicy::print_event(EventType type, methodHandle mh, methodH
|
||||
print_specific(type, mh, imh, bci, level);
|
||||
|
||||
if (type != COMPILE) {
|
||||
methodDataHandle mdh = mh->method_data();
|
||||
int mdo_invocations = 0, mdo_backedges = 0;
|
||||
int mdo_invocations_start = 0, mdo_backedges_start = 0;
|
||||
if (mdh() != NULL) {
|
||||
mdo_invocations = mdh->invocation_count();
|
||||
mdo_backedges = mdh->backedge_count();
|
||||
mdo_invocations_start = mdh->invocation_count_start();
|
||||
mdo_backedges_start = mdh->backedge_count_start();
|
||||
}
|
||||
tty->print(" total: %d,%d mdo: %d(%d),%d(%d)",
|
||||
invocation_count, backedge_count,
|
||||
mdo_invocations, mdo_invocations_start,
|
||||
mdo_backedges, mdo_backedges_start);
|
||||
tty->print(" max levels: %d,%d",
|
||||
mh->highest_comp_level(), mh->highest_osr_comp_level());
|
||||
print_counters("", mh);
|
||||
if (inlinee_event) {
|
||||
tty->print(" inlinee max levels: %d,%d", imh->highest_comp_level(), imh->highest_osr_comp_level());
|
||||
print_counters("inlinee ", imh);
|
||||
}
|
||||
tty->print(" compilable: ");
|
||||
bool need_comma = false;
|
||||
|
||||
@ -55,7 +55,7 @@ class SimpleThresholdPolicy : public CompilationPolicy {
|
||||
// loop_event checks if a method should be OSR compiled at a different
|
||||
// level.
|
||||
CompLevel loop_event(methodOop method, CompLevel cur_level);
|
||||
|
||||
void print_counters(const char* prefix, methodHandle mh);
|
||||
protected:
|
||||
int c1_count() const { return _c1_count; }
|
||||
int c2_count() const { return _c2_count; }
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* @bug 6792161
|
||||
* @summary assert("No dead instructions after post-alloc")
|
||||
*
|
||||
* @run main/othervm -Xcomp -XX:MaxInlineSize=120 Test6792161
|
||||
* @run main/othervm/timeout=300 -Xcomp -XX:MaxInlineSize=120 Test6792161
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
132
hotspot/test/compiler/7110586/Test7110586.java
Normal file
132
hotspot/test/compiler/7110586/Test7110586.java
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7110586
|
||||
* @summary C2 generates icorrect results
|
||||
*
|
||||
* @run main/othervm -Xbatch Test7110586
|
||||
*/
|
||||
|
||||
public class Test7110586 {
|
||||
static int test1() {
|
||||
int i = 0;
|
||||
for ( ; i < 11; i+=1) {}
|
||||
return i;
|
||||
}
|
||||
static int test2() {
|
||||
int i = 0;
|
||||
for ( ; i < 11; i+=2) {}
|
||||
return i;
|
||||
}
|
||||
static int test3() {
|
||||
int i = 0;
|
||||
for ( ; i < 11; i+=3) {}
|
||||
return i;
|
||||
}
|
||||
static int test11() {
|
||||
int i = 0;
|
||||
for ( ; i < 11; i+=11) {}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int testm1() {
|
||||
int i = 0;
|
||||
for ( ; i > -11; i-=1) {}
|
||||
return i;
|
||||
}
|
||||
static int testm2() {
|
||||
int i = 0;
|
||||
for ( ; i > -11; i-=2) {}
|
||||
return i;
|
||||
}
|
||||
static int testm3() {
|
||||
int i = 0;
|
||||
for ( ; i > -11; i-=3) {}
|
||||
return i;
|
||||
}
|
||||
static int testm11() {
|
||||
int i = 0;
|
||||
for ( ; i > -11; i-=11) {}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
int x1 = 0;
|
||||
int x2 = 0;
|
||||
int x3 = 0;
|
||||
int x11 = 0;
|
||||
int m1 = 0;
|
||||
int m2 = 0;
|
||||
int m3 = 0;
|
||||
int m11 = 0;
|
||||
for (int i=0; i<10000; i++) {
|
||||
x1 = test1();
|
||||
x2 = test2();
|
||||
x3 = test3();
|
||||
x11 = test11();
|
||||
m1 = testm1();
|
||||
m2 = testm2();
|
||||
m3 = testm3();
|
||||
m11 = testm11();
|
||||
}
|
||||
boolean failed = false;
|
||||
if (x1 != 11) {
|
||||
System.out.println("ERROR (incr = +1): " + x1 + " != 11");
|
||||
failed = true;
|
||||
}
|
||||
if (x2 != 12) {
|
||||
System.out.println("ERROR (incr = +2): " + x2 + " != 12");
|
||||
failed = true;
|
||||
}
|
||||
if (x3 != 12) {
|
||||
System.out.println("ERROR (incr = +3): " + x3 + " != 12");
|
||||
failed = true;
|
||||
}
|
||||
if (x11 != 11) {
|
||||
System.out.println("ERROR (incr = +11): " + x11 + " != 11");
|
||||
failed = true;
|
||||
}
|
||||
if (m1 != -11) {
|
||||
System.out.println("ERROR (incr = -1): " + m1 + " != -11");
|
||||
failed = true;
|
||||
}
|
||||
if (m2 != -12) {
|
||||
System.out.println("ERROR (incr = -2): " + m2 + " != -12");
|
||||
failed = true;
|
||||
}
|
||||
if (m3 != -12) {
|
||||
System.out.println("ERROR (incr = -3): " + m3 + " != -12");
|
||||
failed = true;
|
||||
}
|
||||
if (m11 != -11) {
|
||||
System.out.println("ERROR (incr = -11): " + m11 + " != -11");
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
System.exit(97);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user