8173770: Image conversion improvements
authorthartmann
Thu, 23 Mar 2017 15:14:18 +0100
changeset 45965 e29c1363af9a
parent 45766 4b5557c9b656
child 45966 b2019823b5bd
8173770: Image conversion improvements Reviewed-by: kvn, vlivanov, dlong, rhalade, mschoene, iignatyev
hotspot/src/cpu/arm/vm/arm.ad
hotspot/src/cpu/sparc/vm/sparc.ad
hotspot/src/cpu/x86/vm/x86_32.ad
hotspot/src/cpu/x86/vm/x86_64.ad
hotspot/src/share/vm/adlc/archDesc.cpp
hotspot/src/share/vm/opto/classes.hpp
hotspot/src/share/vm/opto/loopPredicate.cpp
hotspot/src/share/vm/opto/loopnode.hpp
hotspot/src/share/vm/opto/output.cpp
hotspot/src/share/vm/opto/subnode.cpp
hotspot/src/share/vm/opto/subnode.hpp
hotspot/src/share/vm/runtime/vmStructs.cpp
--- a/hotspot/src/cpu/arm/vm/arm.ad	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/cpu/arm/vm/arm.ad	Thu Mar 23 15:14:18 2017 +0100
@@ -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) %{
--- a/hotspot/src/cpu/sparc/vm/sparc.ad	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/cpu/sparc/vm/sparc.ad	Thu Mar 23 15:14:18 2017 +0100
@@ -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 ) %{
--- a/hotspot/src/cpu/x86/vm/x86_32.ad	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/cpu/x86/vm/x86_32.ad	Thu Mar 23 15:14:18 2017 +0100
@@ -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)));
--- a/hotspot/src/cpu/x86/vm/x86_64.ad	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/cpu/x86/vm/x86_64.ad	Thu Mar 23 15:14:18 2017 +0100
@@ -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
 
--- a/hotspot/src/share/vm/adlc/archDesc.cpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/adlc/archDesc.cpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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
--- a/hotspot/src/share/vm/opto/classes.hpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/classes.hpp	Thu Mar 23 15:14:18 2017 +0100
@@ -81,6 +81,7 @@
 macro(CmpLTMask)
 macro(CmpP)
 macro(CmpU)
+macro(CmpUL)
 macro(CompareAndSwapB)
 macro(CompareAndSwapS)
 macro(CompareAndSwapI)
--- a/hotspot/src/share/vm/opto/loopPredicate.cpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/loopPredicate.cpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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);
--- a/hotspot/src/share/vm/opto/loopnode.hpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/loopnode.hpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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);
--- a/hotspot/src/share/vm/opto/output.cpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/output.cpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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 ||
--- a/hotspot/src/share/vm/opto/subnode.cpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/subnode.cpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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.
--- a/hotspot/src/share/vm/opto/subnode.hpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/opto/subnode.hpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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 {
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp	Fri Jun 23 09:33:21 2017 +0200
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp	Thu Mar 23 15:14:18 2017 +0100
@@ -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)                                    \