# HG changeset patch # User asaha # Date 1500006775 25200 # Node ID d2661aa42bff322badbe6c1337fc638d2e0f5730 # Parent 791a8c45a354b2ef4bcb1aa612b943082ee76f78# Parent b3f7bb673523e388a84caab98e4b58c02c6d0be3 Merge diff -r 791a8c45a354 -r d2661aa42bff src/cpu/arm/vm/arm.ad --- a/src/cpu/arm/vm/arm.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/arm/vm/arm.ad Thu Jul 13 21:32:55 2017 -0700 @@ -2695,6 +2695,30 @@ format %{ "apsr_L_LEGT" %} interface(REG_INTER); %} + +operand flagsRegUL_LTGE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "apsr_UL_LTGE" %} + interface(REG_INTER); +%} + +operand flagsRegUL_EQNE() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "apsr_UL_EQNE" %} + interface(REG_INTER); +%} + +operand flagsRegUL_LEGT() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + + format %{ "apsr_UL_LEGT" %} + interface(REG_INTER); +%} #endif // Condition Code Register, floating comparisons, unordered same as "less". @@ -3249,6 +3273,39 @@ %} %} +operand cmpOpUL() %{ + match(Bool); + + format %{ "UL" %} + interface(COND_INTER) %{ + equal(0x0); + not_equal(0x1); + less(0x3); + greater_equal(0x2); + less_equal(0x9); + greater(0x8); + overflow(0x0); // unsupported/unimplemented + no_overflow(0x0); // unsupported/unimplemented + %} +%} + +operand cmpOpUL_commute() %{ + match(Bool); + + format %{ "UL" %} + interface(COND_INTER) %{ + equal(0x0); + not_equal(0x1); + less(0x8); + greater_equal(0x9); + less_equal(0x2); + greater(0x3); + overflow(0x0); // unsupported/unimplemented + no_overflow(0x0); // unsupported/unimplemented + %} +%} + + //----------OPERAND CLASSES---------------------------------------------------- // Operand Classes are groups of operands that are used to simplify // instruction definitions by not requiring the AD writer to specify separate @@ -10467,6 +10524,17 @@ %} ins_pipe(ialu_cconly_reg_reg); %} + +instruct compUL_iReg(flagsRegU xcc, iRegL op1, iRegL op2) %{ + match(Set xcc (CmpUL op1 op2)); + + size(4); + format %{ "CMP $op1,$op2\t! unsigned long" %} + ins_encode %{ + __ cmp($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_cconly_reg_reg); +%} #else instruct compL_reg_reg_LTGE(flagsRegL_LTGE xcc, iRegL op1, iRegL op2, iRegL tmp) %{ match(Set xcc (CmpL op1 op2)); @@ -10481,6 +10549,20 @@ %} ins_pipe(ialu_cconly_reg_reg); %} + +instruct compUL_reg_reg_LTGE(flagsRegUL_LTGE xcc, iRegL op1, iRegL op2, iRegL tmp) %{ + match(Set xcc (CmpUL op1 op2)); + effect(DEF xcc, USE op1, USE op2, TEMP tmp); + + size(8); + format %{ "SUBS $tmp,$op1.low,$op2.low\t\t! unsigned long\n\t" + "SBCS $tmp,$op1.hi,$op2.hi" %} + ins_encode %{ + __ subs($tmp$$Register, $op1$$Register, $op2$$Register); + __ sbcs($tmp$$Register->successor(), $op1$$Register->successor(), $op2$$Register->successor()); + %} + ins_pipe(ialu_cconly_reg_reg); +%} #endif #ifdef AARCH64 @@ -10496,6 +10578,19 @@ ins_pipe(ialu_cconly_reg_imm); %} + +instruct compUL_reg_con(flagsRegU xcc, iRegL op1, aimmL con) %{ + match(Set xcc (CmpUL op1 con)); + effect(DEF xcc, USE op1, USE con); + + size(8); + format %{ "CMP $op1,$con\t\t! unsigned long" %} + ins_encode %{ + __ cmp($op1$$Register, $con$$constant); + %} + + ins_pipe(ialu_cconly_reg_imm); +%} #else instruct compL_reg_reg_EQNE(flagsRegL_EQNE xcc, iRegL op1, iRegL op2) %{ match(Set xcc (CmpL op1 op2)); @@ -10575,6 +10670,85 @@ ins_pipe(ialu_cconly_reg_reg); %} + +instruct compUL_reg_reg_EQNE(flagsRegUL_EQNE xcc, iRegL op1, iRegL op2) %{ + match(Set xcc (CmpUL op1 op2)); + effect(DEF xcc, USE op1, USE op2); + + size(8); + format %{ "TEQ $op1.hi,$op2.hi\t\t! unsigned long\n\t" + "TEQ.eq $op1.lo,$op2.lo" %} + ins_encode %{ + __ teq($op1$$Register->successor(), $op2$$Register->successor()); + __ teq($op1$$Register, $op2$$Register, eq); + %} + ins_pipe(ialu_cconly_reg_reg); +%} + +instruct compUL_reg_reg_LEGT(flagsRegUL_LEGT xcc, iRegL op1, iRegL op2, iRegL tmp) %{ + match(Set xcc (CmpUL op1 op2)); + effect(DEF xcc, USE op1, USE op2, TEMP tmp); + + size(8); + format %{ "SUBS $tmp,$op2.low,$op1.low\t\t! unsigned long\n\t" + "SBCS $tmp,$op2.hi,$op1.hi" %} + ins_encode %{ + __ subs($tmp$$Register, $op2$$Register, $op1$$Register); + __ sbcs($tmp$$Register->successor(), $op2$$Register->successor(), $op1$$Register->successor()); + %} + ins_pipe(ialu_cconly_reg_reg); +%} + +// TODO: try immLRot2 instead, (0, $con$$constant) becomes +// (hi($con$$constant), lo($con$$constant)) becomes +instruct compUL_reg_con_LTGE(flagsRegUL_LTGE xcc, iRegL op1, immLlowRot con, iRegL tmp) %{ + match(Set xcc (CmpUL op1 con)); + effect(DEF xcc, USE op1, USE con, TEMP tmp); + + size(8); + format %{ "SUBS $tmp,$op1.low,$con\t\t! unsigned long\n\t" + "SBCS $tmp,$op1.hi,0" %} + ins_encode %{ + __ subs($tmp$$Register, $op1$$Register, $con$$constant); + __ sbcs($tmp$$Register->successor(), $op1$$Register->successor(), 0); + %} + + ins_pipe(ialu_cconly_reg_reg); +%} + +// TODO: try immLRot2 instead, (0, $con$$constant) becomes +// (hi($con$$constant), lo($con$$constant)) becomes +instruct compUL_reg_con_EQNE(flagsRegUL_EQNE xcc, iRegL op1, immLlowRot con) %{ + match(Set xcc (CmpUL op1 con)); + effect(DEF xcc, USE op1, USE con); + + size(8); + format %{ "TEQ $op1.hi,0\t\t! unsigned long\n\t" + "TEQ.eq $op1.lo,$con" %} + ins_encode %{ + __ teq($op1$$Register->successor(), 0); + __ teq($op1$$Register, $con$$constant, eq); + %} + + ins_pipe(ialu_cconly_reg_reg); +%} + +// TODO: try immLRot2 instead, (0, $con$$constant) becomes +// (hi($con$$constant), lo($con$$constant)) becomes +instruct compUL_reg_con_LEGT(flagsRegUL_LEGT xcc, iRegL op1, immLlowRot con, iRegL tmp) %{ + match(Set xcc (CmpUL op1 con)); + effect(DEF xcc, USE op1, USE con, TEMP tmp); + + size(8); + format %{ "RSBS $tmp,$op1.low,$con\t\t! unsigned long\n\t" + "RSCS $tmp,$op1.hi,0" %} + ins_encode %{ + __ rsbs($tmp$$Register, $op1$$Register, $con$$constant); + __ rscs($tmp$$Register->successor(), $op1$$Register->successor(), 0); + %} + + ins_pipe(ialu_cconly_reg_reg); +%} #endif /* instruct testL_reg_reg(flagsRegL xcc, iRegL op1, iRegL op2, immL0 zero) %{ */ @@ -11126,6 +11300,48 @@ %} ins_pipe(br_cc); %} + +instruct branchConUL_LTGE(cmpOpUL cmp, flagsRegUL_LTGE xcc, label labl) %{ + match(If cmp xcc); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ge); + + size(4); + ins_cost(BRANCH_COST); + format %{ "B$cmp $xcc,$labl" %} + ins_encode %{ + __ b(*($labl$$label), (AsmCondition)($cmp$$cmpcode)); + %} + ins_pipe(br_cc); +%} + +instruct branchConUL_EQNE(cmpOpUL cmp, flagsRegUL_EQNE xcc, label labl) %{ + match(If cmp xcc); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::eq || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne); + + size(4); + ins_cost(BRANCH_COST); + format %{ "B$cmp $xcc,$labl" %} + ins_encode %{ + __ b(*($labl$$label), (AsmCondition)($cmp$$cmpcode)); + %} + ins_pipe(br_cc); +%} + +instruct branchConUL_LEGT(cmpOpUL_commute cmp, flagsRegUL_LEGT xcc, label labl) %{ + match(If cmp xcc); + effect(USE labl); + predicate(_kids[0]->_leaf->as_Bool()->_test._test == BoolTest::gt || _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le); + + size(4); + ins_cost(BRANCH_COST); + format %{ "B$cmp $xcc,$labl" %} + ins_encode %{ + __ b(*($labl$$label), (AsmCondition)($cmp$$cmpcode)); + %} + ins_pipe(br_cc); +%} #endif instruct branchLoopEnd(cmpOp cmp, flagsReg icc, label labl) %{ diff -r 791a8c45a354 -r d2661aa42bff src/cpu/ppc/vm/ppc.ad --- a/src/cpu/ppc/vm/ppc.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/ppc/vm/ppc.ad Thu Jul 13 21:32:55 2017 -0700 @@ -11048,6 +11048,29 @@ 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + __ cmpld($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpli); + __ cmpldi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 791a8c45a354 -r d2661aa42bff src/cpu/s390/vm/s390.ad --- a/src/cpu/s390/vm/s390.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/s390/vm/s390.ad Thu Jul 13 21:32:55 2017 -0700 @@ -8475,6 +8475,24 @@ %} // LONG unsigned +// Added CmpUL for LoopPredicate. +instruct compUL_reg_reg(flagsReg cr, iRegL op1, iRegL op2) %{ + match(Set cr (CmpUL op1 op2)); + size(4); + format %{ "CLGR $op1,$op2\t # long" %} + opcode(CLGR_ZOPC); + ins_encode(z_rreform(op1, op2)); + ins_pipe(pipe_class_dummy); +%} + +instruct compUL_reg_imm32(flagsReg cr, iRegL op1, uimmL32 con) %{ + match(Set cr (CmpUL op1 con)); + size(6); + format %{ "CLGFI $op1,$con" %} + opcode(CLGFI_ZOPC); + ins_encode(z_rilform_unsigned(op1, con)); + ins_pipe(pipe_class_dummy); +%} // PTR unsigned diff -r 791a8c45a354 -r d2661aa42bff src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/sparc/vm/sparc.ad Thu Jul 13 21:32:55 2017 -0700 @@ -3403,6 +3403,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 non-negative operand immU31() %{ @@ -3936,6 +3946,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)); @@ -8797,6 +8816,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 ); @@ -8883,6 +8913,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)); @@ -9256,6 +9297,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); @@ -9484,6 +9563,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); @@ -9722,6 +9837,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 791a8c45a354 -r d2661aa42bff src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/x86/vm/x86_32.ad Thu Jul 13 21:32:55 2017 -0700 @@ -4030,6 +4030,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 ); @@ -4588,7 +4608,7 @@ %} %} -// Comparision Code used in long compares +// Comparison Code used in long compares operand cmpOp_commute() %{ match(Bool); @@ -4605,6 +4625,23 @@ %} %} +// 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"); + overflow(0x0, "o"); + no_overflow(0x1, "no"); + %} +%} + //----------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 @@ -12639,6 +12676,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))); @@ -12767,6 +12842,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))); @@ -12900,6 +13010,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 791a8c45a354 -r d2661aa42bff src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Thu Jul 13 17:32:49 2017 +0000 +++ b/src/cpu/x86/vm/x86_64.ad Thu Jul 13 21:32:55 2017 -0700 @@ -11518,6 +11518,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 791a8c45a354 -r d2661aa42bff src/share/vm/adlc/archDesc.cpp --- a/src/share/vm/adlc/archDesc.cpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/adlc/archDesc.cpp Thu Jul 13 21:32:55 2017 -0700 @@ -1166,6 +1166,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 791a8c45a354 -r d2661aa42bff src/share/vm/opto/classes.hpp --- a/src/share/vm/opto/classes.hpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/classes.hpp Thu Jul 13 21:32:55 2017 -0700 @@ -81,6 +81,7 @@ macro(CmpLTMask) macro(CmpP) macro(CmpU) +macro(CmpUL) macro(CompareAndSwapB) macro(CompareAndSwapS) macro(CompareAndSwapI) diff -r 791a8c45a354 -r d2661aa42bff src/share/vm/opto/loopPredicate.cpp --- a/src/share/vm/opto/loopPredicate.cpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/loopPredicate.cpp Thu Jul 13 21:32:55 2017 -0700 @@ -29,6 +29,7 @@ #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" +#include "opto/matcher.hpp" #include "opto/mulnode.hpp" #include "opto/opaquenode.hpp" #include "opto/rootnode.hpp" @@ -629,45 +630,138 @@ // 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) { - // Limit is not exact. - // Calculate exact limit here. - // Note, counted loop's test is '<' or '>'. - limit = exact_limit(loop); - max_idx_expr = new SubINode(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; + if ((stride > 0 && (java_subtract(limit_lo, stride) < limit_lo)) || + (stride < 0 && (java_subtract(limit_hi, stride) > limit_hi))) { + // No overflow possible + ConINode* con_stride = _igvn.intcon(stride); + set_ctrl(con_stride, C->root()); + max_idx_expr = new SubINode(limit, con_stride); + idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen); + } else { + // May overflow + overflow = true; + limit = new ConvI2LNode(limit); + register_new_node(limit, ctrl); + ConLNode* con_stride = _igvn.longcon(stride); + set_ctrl(con_stride, C->root()); + max_idx_expr = new SubLNode(limit, con_stride); + } register_new_node(max_idx_expr, ctrl); - if (TraceLoopPredicate) predString->print("(limit - stride) "); } 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); set_ctrl(con_scale, C->root()); - max_idx_expr = new MulINode(max_idx_expr, con_scale); + if (TraceLoopPredicate) { + predString->print("* %d ", scale); + } + // Check if (scale * max_idx_expr) may overflow + const TypeInt* scale_type = TypeInt::make(scale); + MulINode* mul = new 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 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 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 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 = java_add(idx_type->_lo, offset_type->_lo); + jint hi = java_add(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 ConvI2LNode(max_idx_expr); + register_new_node(max_idx_expr, ctrl); + } + overflow = true; + offset = new ConvI2LNode(offset); + register_new_node(offset, ctrl); + max_idx_expr = new AddLNode(max_idx_expr, offset); + } else { + // No overflow possible + max_idx_expr = new 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 CmpUNode(max_idx_expr, range); + CmpNode* cmp = NULL; + if (overflow) { + // Integer expressions may overflow, do long comparison + range = new 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 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 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 AndLNode(max_idx_expr, remove_sign_mask); + register_new_node(max_idx_expr, ctrl); + + cmp = new CmpLNode(max_idx_expr, range); + } else { + cmp = new CmpULNode(max_idx_expr, range); + } + } else { + cmp = new CmpUNode(max_idx_expr, range); + } register_new_node(cmp, ctrl); BoolNode* bol = new BoolNode(cmp, BoolTest::lt); register_new_node(bol, ctrl); @@ -814,28 +908,30 @@ 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 // cloned or created nodes will use the lower bound test as // their declared control. - ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, iff->Opcode()); - ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, iff->Opcode()); - assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate"); - Node *ctrl = lower_bound_proj->in(0)->as_If()->in(0); // Perform cloning to keep Invariance state correct since the // late schedule will place invariant things in the loop. + Node *ctrl = predicate_proj->in(0)->as_If()->in(0); rng = invar.clone(rng, ctrl); if (offset && offset != zero) { 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 - BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false); + BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow); // Negate test if necessary bool negated = false; if (proj->_con != predicate_proj->_con) { @@ -843,19 +939,22 @@ register_new_node(lower_bound_bol, ctrl); negated = true; } + ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, overflow ? Op_If : iff->Opcode()); 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: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); // Test the upper bound - BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true); + BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow); negated = false; if (proj->_con != predicate_proj->_con) { upper_bound_bol = new BoolNode(upper_bound_bol->in(1), upper_bound_bol->_test.negate()); register_new_node(upper_bound_bol, ctrl); negated = true; } + ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, overflow ? Op_If : iff->Opcode()); + assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate"); 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 791a8c45a354 -r d2661aa42bff src/share/vm/opto/loopnode.hpp --- a/src/share/vm/opto/loopnode.hpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/loopnode.hpp Thu Jul 13 21:32:55 2017 -0700 @@ -983,8 +983,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 791a8c45a354 -r d2661aa42bff src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/output.cpp Thu Jul 13 21:32:55 2017 -0700 @@ -1982,6 +1982,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 791a8c45a354 -r d2661aa42bff src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/subnode.cpp Thu Jul 13 21:32:55 2017 -0700 @@ -738,6 +738,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 791a8c45a354 -r d2661aa42bff src/share/vm/opto/subnode.hpp --- a/src/share/vm/opto/subnode.hpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/opto/subnode.hpp Thu Jul 13 21:32:55 2017 -0700 @@ -198,6 +198,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 791a8c45a354 -r d2661aa42bff src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Thu Jul 13 17:32:49 2017 +0000 +++ b/src/share/vm/runtime/vmStructs.cpp Thu Jul 13 21:32:55 2017 -0700 @@ -2008,6 +2008,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) \