# HG changeset patch # User thartmann # Date 1490880513 -7200 # Node ID 6b322d41ce97bd1f4d250633494ceb8d9cfbeb70 # Parent 7f8e6ea1fd2d3e277bcd632f470e52d5a0877d7b 8173770: Image conversion improvements Reviewed-by: kvn, vlivanov, dlong, rhalade, mschoene, iignatyev diff -r 7f8e6ea1fd2d -r 6b322d41ce97 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Tue May 23 13:19:02 2017 +0300 +++ b/src/cpu/sparc/vm/sparc.ad Thu Mar 30 15:28:33 2017 +0200 @@ -3436,6 +3436,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); +%} + // Integer Immediate: 0-bit operand immI0() %{ predicate(n->get_int() == 0); @@ -4045,6 +4055,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)); @@ -9058,6 +9077,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 ); @@ -9144,6 +9174,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)); @@ -9510,6 +9551,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); @@ -9738,6 +9817,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); @@ -9972,6 +10087,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 7f8e6ea1fd2d -r 6b322d41ce97 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Tue May 23 13:19:02 2017 +0300 +++ b/src/cpu/x86/vm/x86_32.ad Thu Mar 30 15:28:33 2017 +0200 @@ -4505,6 +4505,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 ); @@ -5038,7 +5058,7 @@ %} %} -// Comparision Code used in long compares +// Comparison Code used in long compares operand cmpOp_commute() %{ match(Bool); @@ -5053,6 +5073,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 @@ -12820,6 +12855,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, eRegI 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))); @@ -12948,6 +13021,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, eRegI 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))); @@ -13081,6 +13189,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, eRegI 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, eRegI 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 7f8e6ea1fd2d -r 6b322d41ce97 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Tue May 23 13:19:02 2017 +0300 +++ b/src/cpu/x86/vm/x86_64.ad Thu Mar 30 15:28:33 2017 +0200 @@ -10986,6 +10986,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/adlc/archDesc.cpp --- a/src/share/vm/adlc/archDesc.cpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/adlc/archDesc.cpp Thu Mar 30 15:28:33 2017 +0200 @@ -1143,6 +1143,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/classes.hpp --- a/src/share/vm/opto/classes.hpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/classes.hpp Thu Mar 30 15:28:33 2017 +0200 @@ -79,6 +79,7 @@ macro(CmpLTMask) macro(CmpP) macro(CmpU) +macro(CmpUL) macro(CompareAndSwapI) macro(CompareAndSwapL) macro(CompareAndSwapP) diff -r 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/loopPredicate.cpp --- a/src/share/vm/opto/loopPredicate.cpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/loopPredicate.cpp Thu Mar 30 15:28:33 2017 +0200 @@ -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" @@ -646,50 +647,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); @@ -839,8 +930,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 @@ -858,16 +952,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/loopnode.hpp --- a/src/share/vm/opto/loopnode.hpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/loopnode.hpp Thu Mar 30 15:28:33 2017 +0200 @@ -893,8 +893,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/output.cpp Thu Mar 30 15:28:33 2017 +0200 @@ -2076,6 +2076,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/subnode.cpp Thu Mar 30 15:28:33 2017 +0200 @@ -616,6 +616,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/opto/subnode.hpp --- a/src/share/vm/opto/subnode.hpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/opto/subnode.hpp Thu Mar 30 15:28:33 2017 +0200 @@ -189,6 +189,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 7f8e6ea1fd2d -r 6b322d41ce97 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Tue May 23 13:19:02 2017 +0300 +++ b/src/share/vm/runtime/vmStructs.cpp Thu Mar 30 15:28:33 2017 +0200 @@ -1927,6 +1927,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) \