# HG changeset patch # User Greg Lewis # Date 1504852635 25200 # Node ID 660752df7b733d0b913b788132be8d60bf0fe48c # Parent 0c571e44ae7cc98b235cdfae411e96db19105dcb# Parent 1396a255c595b120fdec206d60c7bda32aa1844f Merge from main OpenJDK repository diff -r 0c571e44ae7c -r 660752df7b73 .hgtags --- a/.hgtags Sun Jun 04 11:11:49 2017 -0700 +++ b/.hgtags Thu Sep 07 23:37:15 2017 -0700 @@ -846,3 +846,5 @@ be8da42894af5f7d11b4bad83e166186f01ce1b4 jdk7u141-b00 22c5a6ca09e35b63baf51bad4cb3d8f0cf326705 jdk7u141-b01 56ad25be7d88c2c2da562fe1e8879c8723d01da1 jdk7u141-b02 +75662a7ec1719b3133636d09bd078968579a55ab jdk7u151-b00 +d0c7cea0660f7a8188a7b8c1f6d1a6c8d6388fb0 jdk7u151-b01 diff -r 0c571e44ae7c -r 660752df7b73 make/linux/makefiles/gcc.make --- a/make/linux/makefiles/gcc.make Sun Jun 04 11:11:49 2017 -0700 +++ b/make/linux/makefiles/gcc.make Thu Sep 07 23:37:15 2017 -0700 @@ -149,6 +149,7 @@ else ACCEPTABLE_WARNINGS = -Wpointer-arith -Wconversion -Wsign-compare endif +ACCEPTABLE_WARNINGS += -Wno-deprecated-declarations CFLAGS_WARN/DEFAULT = $(WARNINGS_ARE_ERRORS) $(ACCEPTABLE_WARNINGS) # Special cases diff -r 0c571e44ae7c -r 660752df7b73 src/cpu/ppc/vm/ppc.ad --- a/src/cpu/ppc/vm/ppc.ad Sun Jun 04 11:11:49 2017 -0700 +++ b/src/cpu/ppc/vm/ppc.ad Thu Sep 07 23:37:15 2017 -0700 @@ -3498,6 +3498,14 @@ __ cmplwi(Rcrx, Rsrc1, Isrc2); %} + enc_class enc_cmpldi(flagsReg crx, iRegIsrc src1, uimmL16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpli); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpldi(Rcrx, Rsrc1, $src2$$constant); + %} + enc_class enc_btst_reg(iRegIsrc src1, iRegIsrc src2) %{ // TODO: PPC port $archOpcode(ppc64Opcode_and_); @@ -11356,6 +11364,23 @@ ins_pipe(pipe_class_compare); %} +// Added CmpUL for LoopPredicate. +instruct cmpUL_reg_reg(flagsReg crx, iRegLsrc src1, iRegLsrc src2) %{ + match(Set crx (CmpUL src1 src2)); + format %{ "CMPLD $crx, $src1, $src2" %} + size(4); + ins_encode( enc_cmpld(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpUL_reg_imm16(flagsReg crx, iRegLsrc src1, uimmL16 src2) %{ + match(Set crx (CmpUL src1 src2)); + format %{ "CMPLDI $crx, $src1, $src2" %} + size(4); + ins_encode( enc_cmpldi(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + instruct testL_reg_reg(flagsRegCR0 cr0, iRegLsrc src1, iRegLsrc src2, immL_0 zero) %{ match(Set cr0 (CmpL (AndL src1 src2) zero)); // r0 is killed diff -r 0c571e44ae7c -r 660752df7b73 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Sun Jun 04 11:11:49 2017 -0700 +++ b/src/cpu/sparc/vm/sparc.ad Thu Sep 07 23:37:15 2017 -0700 @@ -3448,6 +3448,16 @@ interface(CONST_INTER); %} +// Unsigned Long Immediate: 12-bit (non-negative that fits in simm13) +operand immUL12() %{ + predicate((0 <= n->get_long()) && (n->get_long() == (int)n->get_long()) && Assembler::is_simm13((int)n->get_long())); + match(ConL); + op_cost(0); + + format %{ %} + interface(CONST_INTER); +%} + // Int Immediate non-negative operand immU31() %{ @@ -4068,6 +4078,15 @@ interface(REG_INTER); %} +// Condition Code Register, unsigned long comparisons. +operand flagsRegUL() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "xcc_UL" %} + interface(REG_INTER); +%} + // Condition Code Register, floating comparisons, unordered same as "less". operand flagsRegF() %{ constraint(ALLOC_IN_RC(float_flags)); @@ -8864,6 +8883,17 @@ ins_pipe(ialu_cconly_reg_reg); %} +instruct compUL_iReg(flagsRegUL xcc, iRegL op1, iRegL op2) %{ + match(Set xcc (CmpUL op1 op2)); + effect(DEF xcc, USE op1, USE op2); + + size(4); + format %{ "CMP $op1,$op2\t! unsigned long" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode(form3_rs1_rs2_rd(op1, op2, R_G0)); + ins_pipe(ialu_cconly_reg_reg); +%} + instruct compI_iReg_imm13(flagsReg icc, iRegI op1, immI13 op2) %{ match(Set icc (CmpI op1 op2)); effect( DEF icc, USE op1 ); @@ -8950,6 +8980,17 @@ ins_pipe(ialu_cconly_reg_imm); %} +instruct compUL_iReg_imm13(flagsRegUL xcc, iRegL op1, immUL12 op2) %{ + match(Set xcc (CmpUL op1 op2)); + effect(DEF xcc, USE op1, USE op2); + + size(4); + format %{ "CMP $op1,$op2\t! unsigned long" %} + opcode(Assembler::subcc_op3, Assembler::arith_op); + ins_encode(form3_rs1_simm13_rd(op1, op2, R_G0)); + ins_pipe(ialu_cconly_reg_imm); +%} + // Compare Pointers instruct compP_iRegP(flagsRegP pcc, iRegP op1, iRegP op2 ) %{ match(Set pcc (CmpP op1 op2)); @@ -9316,6 +9357,44 @@ ins_pipe(cmp_br_reg_imm); %} +instruct cmpUL_reg_branch(cmpOpU cmp, iRegL op1, iRegL op2, label labl, flagsRegUL xcc) %{ + match(If cmp (CmpUL op1 op2)); + effect(USE labl, KILL xcc); + + size(12); + ins_cost(BRANCH_COST); + format %{ "CMP $op1,$op2\t! unsigned long\n\t" + "BP$cmp $labl" %} + ins_encode %{ + Label* L = $labl$$label; + Assembler::Predict predict_taken = + cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn; + __ cmp($op1$$Register, $op2$$Register); + __ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L); + __ delayed()->nop(); + %} + ins_pipe(cmp_br_reg_reg); +%} + +instruct cmpUL_imm_branch(cmpOpU cmp, iRegL op1, immL5 op2, label labl, flagsRegUL xcc) %{ + match(If cmp (CmpUL op1 op2)); + effect(USE labl, KILL xcc); + + size(12); + ins_cost(BRANCH_COST); + format %{ "CMP $op1,$op2\t! unsigned long\n\t" + "BP$cmp $labl" %} + ins_encode %{ + Label* L = $labl$$label; + Assembler::Predict predict_taken = + cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn; + __ cmp($op1$$Register, $op2$$constant); + __ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L); + __ delayed()->nop(); + %} + ins_pipe(cmp_br_reg_imm); +%} + instruct cmpL_reg_branch(cmpOp cmp, iRegL op1, iRegL op2, label labl, flagsRegL xcc) %{ match(If cmp (CmpL op1 op2)); effect(USE labl, KILL xcc); @@ -9544,6 +9623,42 @@ ins_pipe(cbcond_reg_imm); %} +instruct cmpUL_reg_branch_short(cmpOpU cmp, iRegL op1, iRegL op2, label labl, flagsRegUL xcc) %{ + match(If cmp (CmpUL op1 op2)); + predicate(UseCBCond); + effect(USE labl, KILL xcc); + + size(4); + ins_cost(BRANCH_COST); + format %{ "CXB$cmp $op1,$op2,$labl\t! unsigned long" %} + ins_encode %{ + Label* L = $labl$$label; + assert(__ use_cbcond(*L), "back to back cbcond"); + __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$Register, *L); + %} + ins_short_branch(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); + ins_pipe(cbcond_reg_reg); +%} + +instruct cmpUL_imm_branch_short(cmpOpU cmp, iRegL op1, immL5 op2, label labl, flagsRegUL xcc) %{ + match(If cmp (CmpUL op1 op2)); + predicate(UseCBCond); + effect(USE labl, KILL xcc); + + size(4); + ins_cost(BRANCH_COST); + format %{ "CXB$cmp $op1,$op2,$labl\t! unsigned long" %} + ins_encode %{ + Label* L = $labl$$label; + assert(__ use_cbcond(*L), "back to back cbcond"); + __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$constant, *L); + %} + ins_short_branch(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); + ins_pipe(cbcond_reg_imm); +%} + instruct cmpL_reg_branch_short(cmpOp cmp, iRegL op1, iRegL op2, label labl, flagsRegL xcc) %{ match(If cmp (CmpL op1 op2)); predicate(UseCBCond); @@ -9778,6 +9893,25 @@ ins_pipe(br_cc); %} +instruct branchConU_long(cmpOpU cmp, flagsRegUL xcc, label labl) %{ + match(If cmp xcc); + effect(USE labl); + + size(8); + ins_cost(BRANCH_COST); + format %{ "BP$cmp $xcc,$labl" %} + ins_encode %{ + Label* L = $labl$$label; + Assembler::Predict predict_taken = + cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn; + + __ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L); + __ delayed()->nop(); + %} + ins_avoid_back_to_back(AVOID_BEFORE); + ins_pipe(br_cc); +%} + // Manifest a CmpL3 result in an integer register. Very painful. // This is the test to avoid. instruct cmpL3_reg_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg ccr ) %{ diff -r 0c571e44ae7c -r 660752df7b73 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Sun Jun 04 11:11:49 2017 -0700 +++ b/src/cpu/x86/vm/x86_32.ad Thu Sep 07 23:37:15 2017 -0700 @@ -4545,6 +4545,26 @@ interface(REG_INTER); %} +// Condition Code Register used by unsigned long compare +operand flagsReg_ulong_LTGE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_U_LTGE" %} + interface(REG_INTER); +%} +operand flagsReg_ulong_EQNE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_U_EQNE" %} + interface(REG_INTER); +%} +operand flagsReg_ulong_LEGT() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ "FLAGS_U_LEGT" %} + interface(REG_INTER); +%} + // Float register operands operand regDPR() %{ predicate( UseSSE < 2 ); @@ -5058,7 +5078,7 @@ %} %} -// Comparision Code used in long compares +// Comparison Code used in long compares operand cmpOp_commute() %{ match(Bool); @@ -5073,6 +5093,21 @@ %} %} +// Comparison Code used in unsigned long compares +operand cmpOpU_commute() %{ + match(Bool); + + format %{ "" %} + interface(COND_INTER) %{ + equal(0x4, "e"); + not_equal(0x5, "ne"); + less(0x7, "nbe"); + greater_equal(0x6, "be"); + less_equal(0x3, "nb"); + greater(0x2, "b"); + %} +%} + //----------OPERAND CLASSES---------------------------------------------------- // Operand Classes are groups of operands that are used as to simplify // instruction definitions by not requiring the AD writer to specify separate @@ -12502,6 +12537,44 @@ %} %} +//====== +// Manifest a CmpUL result in the normal flags. Only good for LT or GE +// compares. Can be used for LE or GT compares by reversing arguments. +// NOT GOOD FOR EQ/NE tests. +instruct cmpUL_zero_flags_LTGE(flagsReg_ulong_LTGE flags, eRegL src, immL0 zero) %{ + match(Set flags (CmpUL src zero)); + ins_cost(100); + format %{ "TEST $src.hi,$src.hi" %} + opcode(0x85); + ins_encode(OpcP, RegReg_Hi2(src, src)); + ins_pipe(ialu_cr_reg_reg); +%} + +// Manifest a CmpUL result in the normal flags. Only good for LT or GE +// compares. Can be used for LE or GT compares by reversing arguments. +// NOT GOOD FOR EQ/NE tests. +instruct cmpUL_reg_flags_LTGE(flagsReg_ulong_LTGE flags, eRegL src1, eRegL src2, rRegI tmp) %{ + match(Set flags (CmpUL src1 src2)); + effect(TEMP tmp); + ins_cost(300); + format %{ "CMP $src1.lo,$src2.lo\t! Unsigned long compare; set flags for low bits\n\t" + "MOV $tmp,$src1.hi\n\t" + "SBB $tmp,$src2.hi\t! Compute flags for unsigned long compare" %} + ins_encode(long_cmp_flags2(src1, src2, tmp)); + ins_pipe(ialu_cr_reg_reg); +%} + +// Unsigned long compares reg < zero/req OR reg >= zero/req. +// Just a wrapper for a normal branch, plus the predicate test. +instruct cmpUL_LTGE(cmpOpU cmp, flagsReg_ulong_LTGE flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge); + expand %{ + jmpCon(cmp, flags, labl); // JLT or JGE... + %} +%} + // Compare 2 longs and CMOVE longs. instruct cmovLL_reg_LTGE(cmpOp cmp, flagsReg_long_LTGE flags, eRegL dst, eRegL src) %{ match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); @@ -12630,6 +12703,41 @@ %} %} +//====== +// Manifest a CmpUL result in the normal flags. Only good for EQ/NE compares. +instruct cmpUL_zero_flags_EQNE(flagsReg_ulong_EQNE flags, eRegL src, immL0 zero, rRegI tmp) %{ + match(Set flags (CmpUL src zero)); + effect(TEMP tmp); + ins_cost(200); + format %{ "MOV $tmp,$src.lo\n\t" + "OR $tmp,$src.hi\t! Unsigned long is EQ/NE 0?" %} + ins_encode(long_cmp_flags0(src, tmp)); + ins_pipe(ialu_reg_reg_long); +%} + +// Manifest a CmpUL result in the normal flags. Only good for EQ/NE compares. +instruct cmpUL_reg_flags_EQNE(flagsReg_ulong_EQNE flags, eRegL src1, eRegL src2) %{ + match(Set flags (CmpUL src1 src2)); + ins_cost(200+300); + format %{ "CMP $src1.lo,$src2.lo\t! Unsigned long compare; set flags for low bits\n\t" + "JNE,s skip\n\t" + "CMP $src1.hi,$src2.hi\n\t" + "skip:\t" %} + ins_encode(long_cmp_flags1(src1, src2)); + ins_pipe(ialu_cr_reg_reg); +%} + +// Unsigned long compare reg == zero/reg OR reg != zero/reg +// Just a wrapper for a normal branch, plus the predicate test. +instruct cmpUL_EQNE(cmpOpU cmp, flagsReg_ulong_EQNE flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne); + expand %{ + jmpCon(cmp, flags, labl); // JEQ or JNE... + %} +%} + // Compare 2 longs and CMOVE longs. instruct cmovLL_reg_EQNE(cmpOp cmp, flagsReg_long_EQNE flags, eRegL dst, eRegL src) %{ match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); @@ -12763,6 +12871,46 @@ %} %} +//====== +// Manifest a CmpUL result in the normal flags. Only good for LE or GT compares. +// Same as cmpUL_reg_flags_LEGT except must negate src +instruct cmpUL_zero_flags_LEGT(flagsReg_ulong_LEGT flags, eRegL src, immL0 zero, rRegI tmp) %{ + match(Set flags (CmpUL src zero)); + effect(TEMP tmp); + ins_cost(300); + format %{ "XOR $tmp,$tmp\t# Unsigned long compare for -$src < 0, use commuted test\n\t" + "CMP $tmp,$src.lo\n\t" + "SBB $tmp,$src.hi\n\t" %} + ins_encode(long_cmp_flags3(src, tmp)); + ins_pipe(ialu_reg_reg_long); +%} + +// Manifest a CmpUL result in the normal flags. Only good for LE or GT compares. +// Same as cmpUL_reg_flags_LTGE except operands swapped. Swapping operands +// requires a commuted test to get the same result. +instruct cmpUL_reg_flags_LEGT(flagsReg_ulong_LEGT flags, eRegL src1, eRegL src2, rRegI tmp) %{ + match(Set flags (CmpUL src1 src2)); + effect(TEMP tmp); + ins_cost(300); + format %{ "CMP $src2.lo,$src1.lo\t! Unsigned long compare, swapped operands, use with commuted test\n\t" + "MOV $tmp,$src2.hi\n\t" + "SBB $tmp,$src1.hi\t! Compute flags for unsigned long compare" %} + ins_encode(long_cmp_flags2( src2, src1, tmp)); + ins_pipe(ialu_cr_reg_reg); +%} + +// Unsigned long compares reg < zero/req OR reg >= zero/req. +// Just a wrapper for a normal branch, plus the predicate test +instruct cmpUL_LEGT(cmpOpU_commute cmp, flagsReg_ulong_LEGT flags, label labl) %{ + match(If cmp flags); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le); + ins_cost(300); + expand %{ + jmpCon(cmp, flags, labl); // JGT or JLE... + %} +%} + // Compare 2 longs and CMOVE longs. instruct cmovLL_reg_LEGT(cmpOp_commute cmp, flagsReg_long_LEGT flags, eRegL dst, eRegL src) %{ match(Set dst (CMoveL (Binary cmp flags) (Binary dst src))); diff -r 0c571e44ae7c -r 660752df7b73 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Sun Jun 04 11:11:49 2017 -0700 +++ b/src/cpu/x86/vm/x86_64.ad Thu Sep 07 23:37:15 2017 -0700 @@ -10738,6 +10738,48 @@ ins_pipe(pipe_slow); %} +// Unsigned long compare Instructions; really, same as signed long except they +// produce an rFlagsRegU instead of rFlagsReg. +instruct compUL_rReg(rFlagsRegU cr, rRegL op1, rRegL op2) +%{ + match(Set cr (CmpUL op1 op2)); + + format %{ "cmpq $op1, $op2\t# unsigned" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_reg_wide(op1, op2), OpcP, reg_reg(op1, op2)); + ins_pipe(ialu_cr_reg_reg); +%} + +instruct compUL_rReg_imm(rFlagsRegU cr, rRegL op1, immL32 op2) +%{ + match(Set cr (CmpUL op1 op2)); + + format %{ "cmpq $op1, $op2\t# unsigned" %} + opcode(0x81, 0x07); /* Opcode 81 /7 */ + ins_encode(OpcSErm_wide(op1, op2), Con8or32(op2)); + ins_pipe(ialu_cr_reg_imm); +%} + +instruct compUL_rReg_mem(rFlagsRegU cr, rRegL op1, memory op2) +%{ + match(Set cr (CmpUL op1 (LoadL op2))); + + format %{ "cmpq $op1, $op2\t# unsigned" %} + opcode(0x3B); /* Opcode 3B /r */ + ins_encode(REX_reg_mem_wide(op1, op2), OpcP, reg_mem(op1, op2)); + ins_pipe(ialu_cr_reg_mem); +%} + +instruct testUL_reg(rFlagsRegU cr, rRegL src, immL0 zero) +%{ + match(Set cr (CmpUL src zero)); + + format %{ "testq $src, $src\t# unsigned" %} + opcode(0x85); + ins_encode(REX_reg_reg_wide(src, src), OpcP, reg_reg(src, src)); + ins_pipe(ialu_cr_reg_imm); +%} + //----------Max and Min-------------------------------------------------------- // Min Instructions diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/adlc/archDesc.cpp --- a/src/share/vm/adlc/archDesc.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/adlc/archDesc.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -1187,6 +1187,7 @@ || strcmp(idealName,"CmpP") == 0 || strcmp(idealName,"CmpN") == 0 || strcmp(idealName,"CmpL") == 0 + || strcmp(idealName,"CmpUL") == 0 || strcmp(idealName,"CmpD") == 0 || strcmp(idealName,"CmpF") == 0 || strcmp(idealName,"FastLock") == 0 diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/c2_globals.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -637,7 +637,7 @@ \ diagnostic(bool, OptimizeExpensiveOps, true, \ "Find best control for expensive operations") \ - + \ C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG) diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/callGenerator.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -395,7 +395,7 @@ C->env()->notice_inlined_method(_inline_cg->method()); C->set_inlining_progress(true); - kit.replace_call(call, result); + kit.replace_call(call, result, true); } @@ -614,6 +614,10 @@ return kit.transfer_exceptions_into_jvms(); } + // Make a copy of the replaced nodes in case we need to restore them + ReplacedNodes replaced_nodes = kit.map()->replaced_nodes(); + replaced_nodes.clone(); + Node* exact_receiver = receiver; // will get updated in place... Node* slow_ctl = kit.type_check_receiver(receiver, _predicted_receiver, _hit_prob, @@ -667,6 +671,11 @@ return kit.transfer_exceptions_into_jvms(); } + // There are 2 branches and the replaced nodes are only valid on + // one: restore the replaced nodes to what they were before the + // branch. + kit.map()->set_replaced_nodes(replaced_nodes); + // Finish the diamond. kit.C->set_has_split_ifs(true); // Has chance for split-if optimization RegionNode* region = new (kit.C) RegionNode(3); diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/callGenerator.hpp diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/callnode.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -996,6 +996,7 @@ #ifndef PRODUCT void SafePointNode::dump_spec(outputStream *st) const { st->print(" SafePoint "); + _replaced_nodes.dump(st); } #endif diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/callnode.hpp --- a/src/share/vm/opto/callnode.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/callnode.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -30,6 +30,7 @@ #include "opto/multnode.hpp" #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" +#include "opto/replacednodes.hpp" #include "opto/type.hpp" // Portions of code courtesy of Clifford Click @@ -332,6 +333,7 @@ OopMap* _oop_map; // Array of OopMap info (8-bit char) for GC JVMState* const _jvms; // Pointer to list of JVM State objects const TypePtr* _adr_type; // What type of memory does this node produce? + ReplacedNodes _replaced_nodes; // During parsing: list of pair of nodes from calls to GraphKit::replace_in_map() // Many calls take *all* of memory as input, // but some produce a limited subset of that memory as output. @@ -423,6 +425,37 @@ void set_next_exception(SafePointNode* n); bool has_exceptions() const { return next_exception() != NULL; } + // Helper methods to operate on replaced nodes + ReplacedNodes replaced_nodes() const { + return _replaced_nodes; + } + + void set_replaced_nodes(ReplacedNodes replaced_nodes) { + _replaced_nodes = replaced_nodes; + } + + void clone_replaced_nodes() { + _replaced_nodes.clone(); + } + void record_replaced_node(Node* initial, Node* improved) { + _replaced_nodes.record(initial, improved); + } + void transfer_replaced_nodes_from(SafePointNode* sfpt, uint idx = 0) { + _replaced_nodes.transfer_from(sfpt->_replaced_nodes, idx); + } + void delete_replaced_nodes() { + _replaced_nodes.reset(); + } + void apply_replaced_nodes(uint idx) { + _replaced_nodes.apply(this, idx); + } + void merge_replaced_nodes_with(SafePointNode* sfpt) { + _replaced_nodes.merge_with(sfpt->_replaced_nodes); + } + bool has_replaced_nodes() const { + return !_replaced_nodes.is_empty(); + } + // Standard Node stuff virtual int Opcode() const; virtual bool pinned() const { return true; } diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/classes.hpp --- a/src/share/vm/opto/classes.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/classes.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -79,6 +79,7 @@ macro(CmpLTMask) macro(CmpP) macro(CmpU) +macro(CmpUL) macro(CompareAndSwapI) macro(CompareAndSwapL) macro(CompareAndSwapP) diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/compile.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -399,6 +399,11 @@ uint next = 0; while (next < useful.size()) { Node *n = useful.at(next++); + if (n->is_SafePoint()) { + // We're done with a parsing phase. Replaced nodes are not valid + // beyond that point. + n->as_SafePoint()->delete_replaced_nodes(); + } // Use raw traversal of out edges since this code removes out edges int max = n->outcnt(); for (int j = 0; j < max; ++j) { @@ -1895,8 +1900,8 @@ if (live_nodes() > (uint)LiveNodeCountInliningCutoff) { if (low_live_nodes < (uint)LiveNodeCountInliningCutoff * 8 / 10) { // PhaseIdealLoop is expensive so we only try it once we are - // out of loop and we only try it again if the previous helped - // got the number of nodes down significantly + // out of live nodes and we only try it again if the previous + // helped got the number of nodes down significantly PhaseIdealLoop ideal_loop( igvn, false, true ); if (failing()) return; low_live_nodes = live_nodes(); diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/compile.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -789,7 +789,9 @@ // Decide how to build a call. // The profile factor is a discount to apply to this site's interp. profile. - CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_does_dispatch, JVMState* jvms, bool allow_inline, float profile_factor, bool allow_intrinsics = true, bool delayed_forbidden = false); + CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_does_dispatch, + JVMState* jvms, bool allow_inline, float profile_factor, bool allow_intrinsics = true, + bool delayed_forbidden = false); bool should_delay_inlining(ciMethod* call_method, JVMState* jvms); // Helper functions to identify inlining potential at call-site diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/doCall.cpp diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/graphKit.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -427,6 +427,7 @@ } } } + phi_map->merge_replaced_nodes_with(ex_map); } //--------------------------use_exception_state-------------------------------- @@ -1384,17 +1385,27 @@ //--------------------------replace_in_map------------------------------------- void GraphKit::replace_in_map(Node* old, Node* neww) { - this->map()->replace_edge(old, neww); + if (old == neww) { + return; + } + + map()->replace_edge(old, neww); // Note: This operation potentially replaces any edge // on the map. This includes locals, stack, and monitors // of the current (innermost) JVM state. - // We can consider replacing in caller maps. - // The idea would be that an inlined function's null checks - // can be shared with the entire inlining tree. - // The expense of doing this is that the PreserveJVMState class - // would have to preserve caller states too, with a deep copy. + // don't let inconsistent types from profiling escape this + // method + + const Type* told = _gvn.type(old); + const Type* tnew = _gvn.type(neww); + + if (!tnew->higher_equal(told)) { + return; + } + + map()->record_replaced_node(old, neww); } @@ -1774,12 +1785,16 @@ // Replace the call with the current state of the kit. -void GraphKit::replace_call(CallNode* call, Node* result) { +void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes) { JVMState* ejvms = NULL; if (has_exceptions()) { ejvms = transfer_exceptions_into_jvms(); } + ReplacedNodes replaced_nodes = map()->replaced_nodes(); + ReplacedNodes replaced_nodes_exception; + Node* ex_ctl = top(); + SafePointNode* final_state = stop(); // Find all the needed outputs of this call @@ -1796,6 +1811,10 @@ C->gvn_replace_by(callprojs.fallthrough_catchproj, final_ctl); } if (callprojs.fallthrough_memproj != NULL) { + if (final_mem->is_MergeMem()) { + // Parser's exits MergeMem was not transformed but may be optimized + final_mem = _gvn.transform(final_mem); + } C->gvn_replace_by(callprojs.fallthrough_memproj, final_mem); } if (callprojs.fallthrough_ioproj != NULL) { @@ -1827,10 +1846,13 @@ // Load my combined exception state into the kit, with all phis transformed: SafePointNode* ex_map = ekit.combine_and_pop_all_exception_states(); + replaced_nodes_exception = ex_map->replaced_nodes(); Node* ex_oop = ekit.use_exception_state(ex_map); + if (callprojs.catchall_catchproj != NULL) { C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control()); + ex_ctl = ekit.control(); } if (callprojs.catchall_memproj != NULL) { C->gvn_replace_by(callprojs.catchall_memproj, ekit.reset_memory()); @@ -1863,6 +1885,13 @@ _gvn.transform(wl.pop()); } } + + if (callprojs.fallthrough_catchproj != NULL && !final_ctl->is_top() && do_replaced_nodes) { + replaced_nodes.apply(C, final_ctl); + } + if (!ex_ctl->is_top() && do_replaced_nodes) { + replaced_nodes_exception.apply(C, ex_ctl); + } } diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/graphKit.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -660,7 +660,7 @@ // Replace the call with the current state of the kit. Requires // that the call was generated with separate io_projs so that // exceptional control flow can be handled properly. - void replace_call(CallNode* call, Node* result); + void replace_call(CallNode* call, Node* result, bool do_replaced_nodes = false); // helper functions for statistics void increment_counter(address counter_addr); // increment a debug counter diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/ifnode.cpp --- a/src/share/vm/opto/ifnode.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/ifnode.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -1080,7 +1080,7 @@ // be skipped. For example, range check predicate has two checks // for lower and upper bounds. ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj(); - if (PhaseIdealLoop::is_uncommon_trap_proj(unc_proj, Deoptimization::Reason_predicate)) + if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate)) prev_dom = idom; // Now walk the current IfNode's projections. diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/library_call.cpp diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/loopPredicate.cpp --- a/src/share/vm/opto/loopPredicate.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/loopPredicate.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -28,6 +28,7 @@ #include "opto/callnode.hpp" #include "opto/connode.hpp" #include "opto/loopnode.hpp" +#include "opto/matcher.hpp" #include "opto/mulnode.hpp" #include "opto/rootnode.hpp" #include "opto/subnode.hpp" @@ -41,63 +42,6 @@ * checks (such as null checks). */ -//-------------------------------is_uncommon_trap_proj---------------------------- -// Return true if proj is the form of "proj->[region->..]call_uct" -bool PhaseIdealLoop::is_uncommon_trap_proj(ProjNode* proj, Deoptimization::DeoptReason reason) { - int path_limit = 10; - assert(proj, "invalid argument"); - Node* out = proj; - for (int ct = 0; ct < path_limit; ct++) { - out = out->unique_ctrl_out(); - if (out == NULL) - return false; - if (out->is_CallStaticJava()) { - int req = out->as_CallStaticJava()->uncommon_trap_request(); - if (req != 0) { - Deoptimization::DeoptReason trap_reason = Deoptimization::trap_request_reason(req); - if (trap_reason == reason || reason == Deoptimization::Reason_none) { - return true; - } - } - return false; // don't do further after call - } - if (out->Opcode() != Op_Region) - return false; - } - return false; -} - -//-------------------------------is_uncommon_trap_if_pattern------------------------- -// Return true for "if(test)-> proj -> ... -// | -// V -// other_proj->[region->..]call_uct" -// -// "must_reason_predicate" means the uct reason must be Reason_predicate -bool PhaseIdealLoop::is_uncommon_trap_if_pattern(ProjNode *proj, Deoptimization::DeoptReason reason) { - Node *in0 = proj->in(0); - if (!in0->is_If()) return false; - // Variation of a dead If node. - if (in0->outcnt() < 2) return false; - IfNode* iff = in0->as_If(); - - // we need "If(Conv2B(Opaque1(...)))" pattern for reason_predicate - if (reason != Deoptimization::Reason_none) { - if (iff->in(1)->Opcode() != Op_Conv2B || - iff->in(1)->in(1)->Opcode() != Op_Opaque1) { - return false; - } - } - - ProjNode* other_proj = iff->proj_out(1-proj->_con)->as_Proj(); - if (is_uncommon_trap_proj(other_proj, reason)) { - assert(reason == Deoptimization::Reason_none || - Compile::current()->is_predicate_opaq(iff->in(1)->in(1)), "should be on the list"); - return true; - } - return false; -} - //-------------------------------register_control------------------------- void PhaseIdealLoop::register_control(Node* n, IdealLoopTree *loop, Node* pred) { assert(n->is_CFG(), "must be control node"); @@ -147,7 +91,7 @@ // This code is also used to clone predicates to clonned loops. ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, Deoptimization::DeoptReason reason) { - assert(is_uncommon_trap_if_pattern(cont_proj, reason), "must be a uct if pattern!"); + assert(cont_proj->is_uncommon_trap_if_pattern(reason), "must be a uct if pattern!"); IfNode* iff = cont_proj->in(0)->as_If(); ProjNode *uncommon_proj = iff->proj_out(1 - cont_proj->_con); @@ -235,7 +179,7 @@ ProjNode* PhaseIterGVN::create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, Deoptimization::DeoptReason reason) { assert(new_entry != 0, "only used for clone predicate"); - assert(PhaseIdealLoop::is_uncommon_trap_if_pattern(cont_proj, reason), "must be a uct if pattern!"); + assert(cont_proj->is_uncommon_trap_if_pattern(reason), "must be a uct if pattern!"); IfNode* iff = cont_proj->in(0)->as_If(); ProjNode *uncommon_proj = iff->proj_out(1 - cont_proj->_con); @@ -422,7 +366,7 @@ ProjNode* PhaseIdealLoop::find_predicate_insertion_point(Node* start_c, Deoptimization::DeoptReason reason) { if (start_c == NULL || !start_c->is_Proj()) return NULL; - if (is_uncommon_trap_if_pattern(start_c->as_Proj(), reason)) { + if (start_c->as_Proj()->is_uncommon_trap_if_pattern(reason)) { return start_c->as_Proj(); } return NULL; @@ -644,50 +588,140 @@ // max(scale*i + offset) = scale*init + offset BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, - Node* init, Node* limit, Node* stride, - Node* range, bool upper) { + Node* init, Node* limit, jint stride, + Node* range, bool upper, bool &overflow) { + jint con_limit = limit->is_Con() ? limit->get_int() : 0; + jint con_init = init->is_Con() ? init->get_int() : 0; + jint con_offset = offset->is_Con() ? offset->get_int() : 0; + stringStream* predString = NULL; if (TraceLoopPredicate) { predString = new stringStream(); predString->print("rc_predicate "); } - Node* max_idx_expr = init; - int stride_con = stride->get_int(); - if ((stride_con > 0) == (scale > 0) == upper) { - if (LoopLimitCheck) { - // With LoopLimitCheck limit is not exact. - // Calculate exact limit here. - // Note, counted loop's test is '<' or '>'. - limit = exact_limit(loop); - max_idx_expr = new (C) SubINode(limit, stride); - register_new_node(max_idx_expr, ctrl); - if (TraceLoopPredicate) predString->print("(limit - stride) "); + overflow = false; + Node* max_idx_expr = NULL; + const TypeInt* idx_type = TypeInt::INT; + if ((stride > 0) == (scale > 0) == upper) { + if (TraceLoopPredicate) { + predString->print(limit->is_Con() ? "(%d " : "(limit ", con_limit); + predString->print("- %d) ", stride); + } + // Check if (limit - stride) may overflow + const TypeInt* limit_type = _igvn.type(limit)->isa_int(); + jint limit_lo = limit_type->_lo; + jint limit_hi = limit_type->_hi; + jint res_lo = limit_lo - stride; + jint res_hi = limit_hi - stride; + if ((stride > 0 && (res_lo < limit_lo)) || + (stride < 0 && (res_hi > limit_hi))) { + // No overflow possible + ConINode* con_stride = _igvn.intcon(stride); + set_ctrl(con_stride, C->root()); + max_idx_expr = new (C) SubINode(limit, con_stride); + idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen); } else { - max_idx_expr = new (C) SubINode(limit, stride); - register_new_node(max_idx_expr, ctrl); - if (TraceLoopPredicate) predString->print("(limit - stride) "); + // May overflow + overflow = true; + limit = new (C) ConvI2LNode(limit); + register_new_node(limit, ctrl); + ConLNode* con_stride = _igvn.longcon(stride); + set_ctrl(con_stride, C->root()); + max_idx_expr = new (C) SubLNode(limit, con_stride); } + register_new_node(max_idx_expr, ctrl); } else { - if (TraceLoopPredicate) predString->print("init "); + if (TraceLoopPredicate) { + predString->print(init->is_Con() ? "%d " : "init ", con_init); + } + idx_type = _igvn.type(init)->isa_int(); + max_idx_expr = init; } if (scale != 1) { ConNode* con_scale = _igvn.intcon(scale); - max_idx_expr = new (C) MulINode(max_idx_expr, con_scale); + set_ctrl(con_scale, C->root()); + if (TraceLoopPredicate) { + predString->print("* %d ", scale); + } + // Check if (scale * max_idx_expr) may overflow + const TypeInt* scale_type = TypeInt::make(scale); + MulINode* mul = new (C) MulINode(max_idx_expr, con_scale); + idx_type = (TypeInt*)mul->mul_ring(idx_type, scale_type); + if (overflow || TypeInt::INT->higher_equal(idx_type)) { + // May overflow + mul->destruct(); + if (!overflow) { + max_idx_expr = new (C) ConvI2LNode(max_idx_expr); + register_new_node(max_idx_expr, ctrl); + } + overflow = true; + con_scale = _igvn.longcon(scale); + set_ctrl(con_scale, C->root()); + max_idx_expr = new (C) MulLNode(max_idx_expr, con_scale); + } else { + // No overflow possible + max_idx_expr = mul; + } register_new_node(max_idx_expr, ctrl); - if (TraceLoopPredicate) predString->print("* %d ", scale); } - if (offset && (!offset->is_Con() || offset->get_int() != 0)){ - max_idx_expr = new (C) AddINode(max_idx_expr, offset); + if (offset && (!offset->is_Con() || con_offset != 0)){ + if (TraceLoopPredicate) { + predString->print(offset->is_Con() ? "+ %d " : "+ offset", con_offset); + } + // Check if (max_idx_expr + offset) may overflow + const TypeInt* offset_type = _igvn.type(offset)->isa_int(); + jint lo = idx_type->_lo + offset_type->_lo; + jint hi = idx_type->_hi + offset_type->_hi; + if (overflow || (lo > hi) || + ((idx_type->_lo & offset_type->_lo) < 0 && lo >= 0) || + ((~(idx_type->_hi | offset_type->_hi)) < 0 && hi < 0)) { + // May overflow + if (!overflow) { + max_idx_expr = new (C) ConvI2LNode(max_idx_expr); + register_new_node(max_idx_expr, ctrl); + } + overflow = true; + offset = new (C) ConvI2LNode(offset); + register_new_node(offset, ctrl); + max_idx_expr = new (C) AddLNode(max_idx_expr, offset); + } else { + // No overflow possible + max_idx_expr = new (C) AddINode(max_idx_expr, offset); + } register_new_node(max_idx_expr, ctrl); - if (TraceLoopPredicate) - if (offset->is_Con()) predString->print("+ %d ", offset->get_int()); - else predString->print("+ offset "); } - CmpUNode* cmp = new (C) CmpUNode(max_idx_expr, range); + CmpNode* cmp = NULL; + if (overflow) { + // Integer expressions may overflow, do long comparison + range = new (C) ConvI2LNode(range); + register_new_node(range, ctrl); + if (!Matcher::has_match_rule(Op_CmpUL)) { + // We don't support unsigned long comparisons. Set 'max_idx_expr' + // to max_julong if < 0 to make the signed comparison fail. + ConINode* sign_pos = _igvn.intcon(BitsPerLong - 1); + set_ctrl(sign_pos, C->root()); + Node* sign_bit_mask = new (C) RShiftLNode(max_idx_expr, sign_pos); + register_new_node(sign_bit_mask, ctrl); + // OR with sign bit to set all bits to 1 if negative (otherwise no change) + max_idx_expr = new (C) OrLNode(max_idx_expr, sign_bit_mask); + register_new_node(max_idx_expr, ctrl); + // AND with 0x7ff... to unset the sign bit + ConLNode* remove_sign_mask = _igvn.longcon(max_jlong); + set_ctrl(remove_sign_mask, C->root()); + max_idx_expr = new (C) AndLNode(max_idx_expr, remove_sign_mask); + register_new_node(max_idx_expr, ctrl); + + cmp = new (C) CmpLNode(max_idx_expr, range); + } else { + cmp = new (C) CmpULNode(max_idx_expr, range); + } + } else { + cmp = new (C) CmpUNode(max_idx_expr, range); + } register_new_node(cmp, ctrl); BoolNode* bol = new (C) BoolNode(cmp, BoolTest::lt); register_new_node(bol, ctrl); @@ -773,7 +807,7 @@ ProjNode* proj = if_proj_list.pop()->as_Proj(); IfNode* iff = proj->in(0)->as_If(); - if (!is_uncommon_trap_if_pattern(proj, Deoptimization::Reason_none)) { + if (!proj->is_uncommon_trap_if_pattern(Deoptimization::Reason_none)) { if (loop->is_loop_exit(iff)) { // stop processing the remaining projs in the list because the execution of them // depends on the condition of "iff" (iff->in(1)). @@ -837,8 +871,11 @@ assert(ok, "must be index expression"); Node* init = cl->init_trip(); - Node* limit = cl->limit(); - Node* stride = cl->stride(); + // Limit is not exact. + // Calculate exact limit here. + // Note, counted loop's test is '<' or '>'. + Node* limit = exact_limit(loop); + int stride = cl->stride()->get_int(); // Build if's for the upper and lower bound tests. The // lower_bound test will dominate the upper bound test and all @@ -856,16 +893,18 @@ assert(invar.is_invariant(offset), "offset must be loop invariant"); offset = invar.clone(offset, ctrl); } + // If predicate expressions may overflow in the integer range, longs are used. + bool overflow = false; // Test the lower bound - Node* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false); + Node* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow); IfNode* lower_bound_iff = lower_bound_proj->in(0)->as_If(); _igvn.hash_delete(lower_bound_iff); lower_bound_iff->set_req(1, lower_bound_bol); if (TraceLoopPredicate) tty->print_cr("lower bound check if: %d", lower_bound_iff->_idx); // Test the upper bound - Node* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true); + Node* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow); IfNode* upper_bound_iff = upper_bound_proj->in(0)->as_If(); _igvn.hash_delete(upper_bound_iff); upper_bound_iff->set_req(1, upper_bound_bol); diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/loopnode.cpp --- a/src/share/vm/opto/loopnode.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/loopnode.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -167,7 +167,7 @@ // expensive nodes will notice the loop and skip over it to try to // move the node further up. if (ctl->is_CountedLoop() && ctl->in(1) != NULL && ctl->in(1)->in(0) != NULL && ctl->in(1)->in(0)->is_If()) { - if (!is_uncommon_trap_if_pattern(ctl->in(1)->as_Proj(), Deoptimization::Reason_none)) { + if (!ctl->in(1)->as_Proj()->is_uncommon_trap_if_pattern(Deoptimization::Reason_none)) { break; } next = idom(ctl->in(1)->in(0)); @@ -181,7 +181,7 @@ } else if (parent_ctl->is_CountedLoopEnd() && parent_ctl->as_CountedLoopEnd()->loopnode() != NULL) { next = parent_ctl->as_CountedLoopEnd()->loopnode()->init_control(); } else if (parent_ctl->is_If()) { - if (!is_uncommon_trap_if_pattern(ctl->as_Proj(), Deoptimization::Reason_none)) { + if (!ctl->as_Proj()->is_uncommon_trap_if_pattern(Deoptimization::Reason_none)) { break; } assert(idom(ctl) == parent_ctl, "strange"); diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/loopnode.hpp --- a/src/share/vm/opto/loopnode.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/loopnode.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -877,13 +877,6 @@ // Return true if exp is a scaled induction var plus (or minus) constant bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth = 0); - // Return true if proj is for "proj->[region->..]call_uct" - static bool is_uncommon_trap_proj(ProjNode* proj, Deoptimization::DeoptReason reason); - // Return true for "if(test)-> proj -> ... - // | - // V - // other_proj->[region->..]call_uct" - static bool is_uncommon_trap_if_pattern(ProjNode* proj, Deoptimization::DeoptReason reason); // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, Deoptimization::DeoptReason reason); @@ -910,8 +903,8 @@ // Construct a range check for a predicate if BoolNode* rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, - Node* init, Node* limit, Node* stride, - Node* range, bool upper); + Node* init, Node* limit, jint stride, + Node* range, bool upper, bool &overflow); // Implementation of the loop predication to promote checks outside the loop bool loop_predication_impl(IdealLoopTree *loop); diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/loopopts.cpp --- a/src/share/vm/opto/loopopts.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/loopopts.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -234,8 +234,8 @@ // for lower and upper bounds. ProjNode* unc_proj = iff->as_If()->proj_out(1 - dp->as_Proj()->_con)->as_Proj(); if (exclude_loop_predicate && - (is_uncommon_trap_proj(unc_proj, Deoptimization::Reason_predicate) || - is_uncommon_trap_proj(unc_proj, Deoptimization::Reason_range_check))) { + (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) || + unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_range_check))) { // If this is a range check (IfNode::is_range_check), do not // reorder because Compile::allow_range_check_smearing might have // changed the check. diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/multnode.cpp --- a/src/share/vm/opto/multnode.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/multnode.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -23,6 +23,8 @@ */ #include "precompiled.hpp" +#include "opto/callnode.hpp" +#include "opto/cfgnode.hpp" #include "opto/matcher.hpp" #include "opto/multnode.hpp" #include "opto/opcodes.hpp" @@ -132,3 +134,59 @@ uint ProjNode::ideal_reg() const { return Matcher::base2reg[bottom_type()->base()]; } + +//-------------------------------is_uncommon_trap_proj---------------------------- +// Return true if proj is the form of "proj->[region->..]call_uct" +bool ProjNode::is_uncommon_trap_proj(Deoptimization::DeoptReason reason) { + int path_limit = 10; + Node* out = this; + for (int ct = 0; ct < path_limit; ct++) { + out = out->unique_ctrl_out(); + if (out == NULL) + return false; + if (out->is_CallStaticJava()) { + int req = out->as_CallStaticJava()->uncommon_trap_request(); + if (req != 0) { + Deoptimization::DeoptReason trap_reason = Deoptimization::trap_request_reason(req); + if (trap_reason == reason || reason == Deoptimization::Reason_none) { + return true; + } + } + return false; // don't do further after call + } + if (out->Opcode() != Op_Region) + return false; + } + return false; +} + +//-------------------------------is_uncommon_trap_if_pattern------------------------- +// Return true for "if(test)-> proj -> ... +// | +// V +// other_proj->[region->..]call_uct" +// +// "must_reason_predicate" means the uct reason must be Reason_predicate +bool ProjNode::is_uncommon_trap_if_pattern(Deoptimization::DeoptReason reason) { + Node *in0 = in(0); + if (!in0->is_If()) return false; + // Variation of a dead If node. + if (in0->outcnt() < 2) return false; + IfNode* iff = in0->as_If(); + + // we need "If(Conv2B(Opaque1(...)))" pattern for reason_predicate + if (reason != Deoptimization::Reason_none) { + if (iff->in(1)->Opcode() != Op_Conv2B || + iff->in(1)->in(1)->Opcode() != Op_Opaque1) { + return false; + } + } + + ProjNode* other_proj = iff->proj_out(1-_con)->as_Proj(); + if (other_proj->is_uncommon_trap_proj(reason)) { + assert(reason == Deoptimization::Reason_none || + Compile::current()->is_predicate_opaq(iff->in(1)->in(1)), "should be on the list"); + return true; + } + return false; +} diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/multnode.hpp --- a/src/share/vm/opto/multnode.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/multnode.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -86,6 +86,14 @@ #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif + + // Return true if proj is for "proj->[region->..]call_uct" + bool is_uncommon_trap_proj(Deoptimization::DeoptReason reason); + // Return true for "if(test)-> proj -> ... + // | + // V + // other_proj->[region->..]call_uct" + bool is_uncommon_trap_if_pattern(Deoptimization::DeoptReason reason); }; #endif // SHARE_VM_OPTO_MULTNODE_HPP diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/node.cpp --- a/src/share/vm/opto/node.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/node.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -527,6 +527,9 @@ CallNode *call = n->as_Call(); call->clone_jvms(); } + if (n->is_SafePoint()) { + n->as_SafePoint()->clone_replaced_nodes(); + } return n; // Return the clone } @@ -622,6 +625,9 @@ if (is_expensive()) { compile->remove_expensive_node(this); } + if (is_SafePoint()) { + as_SafePoint()->delete_replaced_nodes(); + } #ifdef ASSERT // We will not actually delete the storage, but we'll make the node unusable. *(address*)this = badAddress; // smash the C++ vtbl, probably diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/output.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -2108,6 +2108,7 @@ if( last->is_MachIf() && last->in(1) == n && ( op == Op_CmpI || op == Op_CmpU || + op == Op_CmpUL || op == Op_CmpP || op == Op_CmpF || op == Op_CmpD || diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/parse.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -348,13 +348,16 @@ int _est_switch_depth; // Debugging SwitchRanges. #endif + bool _first_return; // true if return is the first to be parsed + bool _replaced_nodes_for_exceptions; // needs processing of replaced nodes in exception paths? + uint _new_idx; // any node with _idx above were new during this parsing. Used to trim the replaced nodes list. + public: // Constructor Parse(JVMState* caller, ciMethod* parse_method, float expected_uses); virtual Parse* is_Parse() const { return (Parse*)this; } - public: // Accessors. JVMState* caller() const { return _caller; } float expected_uses() const { return _expected_uses; } diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/parse1.cpp --- a/src/share/vm/opto/parse1.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/parse1.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -395,6 +395,9 @@ _entry_bci = InvocationEntryBci; _tf = NULL; _block = NULL; + _first_return = true; + _replaced_nodes_for_exceptions = false; + _new_idx = C->unique(); debug_only(_block_count = -1); debug_only(_blocks = (Block*)-1); #ifndef PRODUCT @@ -918,6 +921,10 @@ for (uint i = 0; i < TypeFunc::Parms; i++) { caller.map()->set_req(i, ex_map->in(i)); } + if (ex_map->has_replaced_nodes()) { + _replaced_nodes_for_exceptions = true; + } + caller.map()->transfer_replaced_nodes_from(ex_map, _new_idx); // ...and the exception: Node* ex_oop = saved_ex_oop(ex_map); SafePointNode* caller_ex_map = caller.make_exception_state(ex_oop); @@ -987,7 +994,7 @@ bool do_synch = method()->is_synchronized() && GenerateSynchronizationCode; // record exit from a method if compiled while Dtrace is turned on. - if (do_synch || C->env()->dtrace_method_probes()) { + if (do_synch || C->env()->dtrace_method_probes() || _replaced_nodes_for_exceptions) { // First move the exception list out of _exits: GraphKit kit(_exits.transfer_exceptions_into_jvms()); SafePointNode* normal_map = kit.map(); // keep this guy safe @@ -1012,6 +1019,9 @@ if (C->env()->dtrace_method_probes()) { kit.make_dtrace_method_exit(method()); } + if (_replaced_nodes_for_exceptions) { + kit.map()->apply_replaced_nodes(_new_idx); + } // Done with exception-path processing. ex_map = kit.make_exception_state(ex_oop); assert(ex_jvms->same_calls_as(ex_map->jvms()), "sanity"); @@ -1031,6 +1041,7 @@ _exits.add_exception_state(ex_map); } } + _exits.map()->apply_replaced_nodes(_new_idx); } //-----------------------------create_entry_map------------------------------- @@ -1045,6 +1056,9 @@ return NULL; } + // clear current replaced nodes that are of no use from here on (map was cloned in build_exits). + _caller->map()->delete_replaced_nodes(); + // If this is an inlined method, we may have to do a receiver null check. if (_caller->has_method() && is_normal_parse() && !method()->is_static()) { GraphKit kit(_caller); @@ -1068,6 +1082,8 @@ SafePointNode* inmap = _caller->map(); assert(inmap != NULL, "must have inmap"); + // In case of null check on receiver above + map()->transfer_replaced_nodes_from(inmap, _new_idx); uint i; @@ -1693,6 +1709,8 @@ set_control(r->nonnull_req()); } + map()->merge_replaced_nodes_with(newin); + // newin has been subsumed into the lazy merge, and is now dead. set_block(save_block); @@ -2061,6 +2079,13 @@ phi->add_req(value); } + if (_first_return) { + _exits.map()->transfer_replaced_nodes_from(map(), _new_idx); + _first_return = false; + } else { + _exits.map()->merge_replaced_nodes_with(map()); + } + stop_and_kill_map(); // This CFG path dies here } diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/replacednodes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/opto/replacednodes.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#include "precompiled.hpp" +#include "opto/cfgnode.hpp" +#include "opto/phaseX.hpp" +#include "opto/replacednodes.hpp" + +void ReplacedNodes::allocate_if_necessary() { + if (_replaced_nodes == NULL) { + _replaced_nodes = new GrowableArray(); + } +} + +bool ReplacedNodes::is_empty() const { + return _replaced_nodes == NULL || _replaced_nodes->length() == 0; +} + +bool ReplacedNodes::has_node(const ReplacedNode& r) const { + return _replaced_nodes->find(r) != -1; +} + +bool ReplacedNodes::has_target_node(Node* n) const { + for (int i = 0; i < _replaced_nodes->length(); i++) { + if (_replaced_nodes->at(i).improved() == n) { + return true; + } + } + return false; +} + +// Record replaced node if not seen before +void ReplacedNodes::record(Node* initial, Node* improved) { + allocate_if_necessary(); + ReplacedNode r(initial, improved); + if (!has_node(r)) { + _replaced_nodes->push(r); + } +} + +// Copy replaced nodes from one map to another. idx is used to +// identify nodes that are too new to be of interest in the target +// node list. +void ReplacedNodes::transfer_from(const ReplacedNodes& other, uint idx) { + if (other.is_empty()) { + return; + } + allocate_if_necessary(); + for (int i = 0; i < other._replaced_nodes->length(); i++) { + ReplacedNode replaced = other._replaced_nodes->at(i); + // Only transfer the nodes that can actually be useful + if (!has_node(replaced) && (replaced.initial()->_idx < idx || has_target_node(replaced.initial()))) { + _replaced_nodes->push(replaced); + } + } +} + +void ReplacedNodes::clone() { + if (_replaced_nodes != NULL) { + GrowableArray* replaced_nodes_clone = new GrowableArray(); + replaced_nodes_clone->appendAll(_replaced_nodes); + _replaced_nodes = replaced_nodes_clone; + } +} + +void ReplacedNodes::reset() { + if (_replaced_nodes != NULL) { + _replaced_nodes->clear(); + } +} + +// Perfom node replacement (used when returning to caller) +void ReplacedNodes::apply(Node* n, uint idx) { + if (is_empty()) { + return; + } + for (int i = 0; i < _replaced_nodes->length(); i++) { + ReplacedNode replaced = _replaced_nodes->at(i); + // Only apply if improved node was created in a callee to avoid + // issues with irreducible loops in the caller + if (replaced.improved()->_idx >= idx) { + n->replace_edge(replaced.initial(), replaced.improved()); + } + } +} + +static void enqueue_use(Node* n, Node* use, Unique_Node_List& work) { + if (use->is_Phi()) { + Node* r = use->in(0); + assert(r->is_Region(), "Phi should have Region"); + for (uint i = 1; i < use->req(); i++) { + if (use->in(i) == n) { + work.push(r->in(i)); + } + } + } else { + work.push(use); + } +} + +// Perfom node replacement following late inlining +void ReplacedNodes::apply(Compile* C, Node* ctl) { + // ctl is the control on exit of the method that was late inlined + if (is_empty()) { + return; + } + for (int i = 0; i < _replaced_nodes->length(); i++) { + ReplacedNode replaced = _replaced_nodes->at(i); + Node* initial = replaced.initial(); + Node* improved = replaced.improved(); + assert (ctl != NULL && !ctl->is_top(), "replaced node should have actual control"); + + ResourceMark rm; + Unique_Node_List work; + // Go over all the uses of the node that is considered for replacement... + for (DUIterator j = initial->outs(); initial->has_out(j); j++) { + Node* use = initial->out(j); + + if (use == improved || use->outcnt() == 0) { + continue; + } + work.clear(); + enqueue_use(initial, use, work); + bool replace = true; + // Check that this use is dominated by ctl. Go ahead with the + // replacement if it is. + while (work.size() != 0 && replace) { + Node* n = work.pop(); + if (use->outcnt() == 0) { + continue; + } + if (n->is_CFG() || (n->in(0) != NULL && !n->in(0)->is_top())) { + int depth = 0; + Node *m = n; + if (!n->is_CFG()) { + n = n->in(0); + } + assert(n->is_CFG(), "should be CFG now"); + while(n != ctl) { + n = IfNode::up_one_dom(n); + depth++; + // limit search depth + if (depth >= 100 || n == NULL) { + replace = false; + break; + } + } + } else { + for (DUIterator k = n->outs(); n->has_out(k); k++) { + enqueue_use(n, n->out(k), work); + } + } + } + if (replace) { + bool is_in_table = C->initial_gvn()->hash_delete(use); + int replaced = use->replace_edge(initial, improved); + if (is_in_table) { + C->initial_gvn()->hash_find_insert(use); + } + C->record_for_igvn(use); + + assert(replaced > 0, "inconsistent"); + --j; + } + } + } +} + +void ReplacedNodes::dump(outputStream *st) const { + if (!is_empty()) { + st->print("replaced nodes: "); + for (int i = 0; i < _replaced_nodes->length(); i++) { + st->print("%d->%d", _replaced_nodes->at(i).initial()->_idx, _replaced_nodes->at(i).improved()->_idx); + if (i < _replaced_nodes->length()-1) { + st->print(","); + } + } + } +} + +// Merge 2 list of replaced node at a point where control flow paths merge +void ReplacedNodes::merge_with(const ReplacedNodes& other) { + if (is_empty()) { + return; + } + if (other.is_empty()) { + reset(); + return; + } + int shift = 0; + int len = _replaced_nodes->length(); + for (int i = 0; i < len; i++) { + if (!other.has_node(_replaced_nodes->at(i))) { + shift++; + } else if (shift > 0) { + _replaced_nodes->at_put(i-shift, _replaced_nodes->at(i)); + } + } + if (shift > 0) { + _replaced_nodes->trunc_to(len - shift); + } +} diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/replacednodes.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/opto/replacednodes.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +#ifndef SHARE_VM_OPTO_REPLACEDNODES_HPP +#define SHARE_VM_OPTO_REPLACEDNODES_HPP + +#include "opto/connode.hpp" + +// During parsing, when a node is "improved", +// GraphKit::replace_in_map() is called to update the current map so +// that the improved node is used from that point +// on. GraphKit::replace_in_map() doesn't operate on the callers maps +// and so some optimization opportunities may be lost. The +// ReplacedNodes class addresses that problem. +// +// A ReplacedNodes object is a list of pair of nodes. Every +// SafePointNode carries a ReplacedNodes object. Every time +// GraphKit::replace_in_map() is called, a new pair of nodes is pushed +// on the list of replaced nodes. When control flow paths merge, their +// replaced nodes are also merged. When parsing exits a method to +// return to a caller, the replaced nodes on the exit path are used to +// update the caller's map. +class ReplacedNodes VALUE_OBJ_CLASS_SPEC { + private: + class ReplacedNode VALUE_OBJ_CLASS_SPEC { + private: + Node* _initial; + Node* _improved; + public: + ReplacedNode() : _initial(NULL), _improved(NULL) {} + ReplacedNode(Node* initial, Node* improved) : _initial(initial), _improved(improved) {} + Node* initial() const { return _initial; } + Node* improved() const { return _improved; } + + bool operator==(const ReplacedNode& other) { + return _initial == other._initial && _improved == other._improved; + } + }; + GrowableArray* _replaced_nodes; + + void allocate_if_necessary(); + bool has_node(const ReplacedNode& r) const; + bool has_target_node(Node* n) const; + + public: + ReplacedNodes() + : _replaced_nodes(NULL) {} + + void clone(); + void record(Node* initial, Node* improved); + void transfer_from(const ReplacedNodes& other, uint idx); + void reset(); + void apply(Node* n, uint idx); + void merge_with(const ReplacedNodes& other); + bool is_empty() const; + void dump(outputStream *st) const; + void apply(Compile* C, Node* ctl); +}; + +#endif // SHARE_VM_OPTO_REPLACEDNODES_HPP diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/subnode.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -618,6 +618,60 @@ return TypeInt::CC; // else use worst case results } + +// Simplify a CmpUL (compare 2 unsigned longs) node, based on local information. +// If both inputs are constants, compare them. +const Type* CmpULNode::sub(const Type* t1, const Type* t2) const { + assert(!t1->isa_ptr(), "obsolete usage of CmpUL"); + + // comparing two unsigned longs + const TypeLong* r0 = t1->is_long(); // Handy access + const TypeLong* r1 = t2->is_long(); + + // Current installed version + // Compare ranges for non-overlap + julong lo0 = r0->_lo; + julong hi0 = r0->_hi; + julong lo1 = r1->_lo; + julong hi1 = r1->_hi; + + // If either one has both negative and positive values, + // it therefore contains both 0 and -1, and since [0..-1] is the + // full unsigned range, the type must act as an unsigned bottom. + bool bot0 = ((jlong)(lo0 ^ hi0) < 0); + bool bot1 = ((jlong)(lo1 ^ hi1) < 0); + + if (bot0 || bot1) { + // All unsigned values are LE -1 and GE 0. + if (lo0 == 0 && hi0 == 0) { + return TypeInt::CC_LE; // 0 <= bot + } else if ((jlong)lo0 == -1 && (jlong)hi0 == -1) { + return TypeInt::CC_GE; // -1 >= bot + } else if (lo1 == 0 && hi1 == 0) { + return TypeInt::CC_GE; // bot >= 0 + } else if ((jlong)lo1 == -1 && (jlong)hi1 == -1) { + return TypeInt::CC_LE; // bot <= -1 + } + } else { + // We can use ranges of the form [lo..hi] if signs are the same. + assert(lo0 <= hi0 && lo1 <= hi1, "unsigned ranges are valid"); + // results are reversed, '-' > '+' for unsigned compare + if (hi0 < lo1) { + return TypeInt::CC_LT; // smaller + } else if (lo0 > hi1) { + return TypeInt::CC_GT; // greater + } else if (hi0 == lo1 && lo0 == hi1) { + return TypeInt::CC_EQ; // Equal results + } else if (lo0 >= hi1) { + return TypeInt::CC_GE; + } else if (hi0 <= lo1) { + return TypeInt::CC_LE; + } + } + + return TypeInt::CC; // else use worst case results +} + //============================================================================= //------------------------------sub-------------------------------------------- // Simplify an CmpP (compare 2 pointers) node, based on local information. diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/opto/subnode.hpp --- a/src/share/vm/opto/subnode.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/opto/subnode.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -190,6 +190,15 @@ virtual const Type *sub( const Type *, const Type * ) const; }; +//------------------------------CmpULNode--------------------------------------- +// Compare 2 unsigned long values, returning condition codes (-1, 0 or 1). +class CmpULNode : public CmpNode { +public: + CmpULNode(Node* in1, Node* in2) : CmpNode(in1, in2) { } + virtual int Opcode() const; + virtual const Type* sub(const Type*, const Type*) const; +}; + //------------------------------CmpL3Node-------------------------------------- // Compare 2 long values, returning integer value (-1, 0 or 1). class CmpL3Node : public CmpLNode { diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/runtime/vmStructs.cpp Thu Sep 07 23:37:15 2017 -0700 @@ -1930,6 +1930,7 @@ declare_c2_type(CmpPNode, CmpNode) \ declare_c2_type(CmpNNode, CmpNode) \ declare_c2_type(CmpLNode, CmpNode) \ + declare_c2_type(CmpULNode, CmpNode) \ declare_c2_type(CmpL3Node, CmpLNode) \ declare_c2_type(CmpFNode, CmpNode) \ declare_c2_type(CmpF3Node, CmpFNode) \ diff -r 0c571e44ae7c -r 660752df7b73 src/share/vm/utilities/growableArray.hpp --- a/src/share/vm/utilities/growableArray.hpp Sun Jun 04 11:11:49 2017 -0700 +++ b/src/share/vm/utilities/growableArray.hpp Thu Sep 07 23:37:15 2017 -0700 @@ -333,6 +333,7 @@ // inserts the given element before the element at index i void insert_before(const int idx, const E& elem) { + assert(0 <= idx && idx <= _len, "illegal index"); check_nesting(); if (_len == _max) grow(_len); for (int j = _len - 1; j >= idx; j--) { @@ -344,7 +345,7 @@ void appendAll(const GrowableArray* l) { for (int i = 0; i < l->_len; i++) { - raw_at_put_grow(_len, l->_data[i], 0); + raw_at_put_grow(_len, l->_data[i], E()); } } diff -r 0c571e44ae7c -r 660752df7b73 test/compiler/c2/TestReplacedNodesOSR.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/c2/TestReplacedNodesOSR.java Thu Sep 07 23:37:15 2017 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. 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 8174164 + * @summary SafePointNode::_replaced_nodes breaks with irreducible loops + * @run main/othervm -XX:-BackgroundCompilation TestReplacedNodesOSR + * + */ + +public class TestReplacedNodesOSR { + + static Object dummy; + + static interface I { + } + + static class A implements I { + } + + static final class MyException extends Exception { + } + + static final A obj = new A(); + static I static_field() { return obj; } + + // When OSR compiled, this method has an irreducible loop + static void test(int v, MyException e) { + int i = 0; + for (;;) { + if (i == 1000) { + break; + } + try { + if ((i%2) == 0) { + int j = 0; + for (;;) { + j++; + if (i+j != v) { + if (j == 1000) { + break; + } + } else { + A a = (A)static_field(); + // replaced node recorded here + throw e; + } + } + } + } catch(MyException ex) { + } + i++; + // replaced node applied on return of the method + // replaced node used here + dummy = static_field(); + } + } + + + static public void main(String[] args) { + for (int i = 0; i < 1000; i++) { + test(1100, new MyException()); + } + } +}