changeset 53968:1d216028b57b lworld

8215473: [lworld] Support for substitutability checks in C2 Reviewed-by: thartmann
author roland
date Fri, 25 Jan 2019 10:40:32 +0100
parents 000d494dc5cb
children 349408e9aa4c
files src/hotspot/cpu/x86/templateTable_x86.cpp src/hotspot/share/classfile/systemDictionary.hpp src/hotspot/share/classfile/vmSymbols.hpp src/hotspot/share/code/compiledMethod.cpp src/hotspot/share/opto/parse2.cpp src/hotspot/share/opto/subnode.cpp src/hotspot/share/runtime/globals.hpp src/hotspot/share/runtime/sharedRuntime.cpp test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java
diffstat 9 files changed, 528 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Fri Jan 25 10:40:32 2019 +0100
@@ -2478,7 +2478,7 @@
   __ pop_ptr(rdx);
 
   const int is_value_mask = markOopDesc::always_locked_pattern;
-  if (EnableValhalla && UsePointerPerturbation) {
+  if (EnableValhalla && ACmpOnValues == 1) {
     Label is_null;
     __ testptr(rdx, rdx);
     __ jcc(Assembler::zero, is_null);
@@ -2493,7 +2493,7 @@
 
   __ cmpoop(rdx, rax);
 
-  if (EnableValhalla && !UsePointerPerturbation) {
+  if (EnableValhalla && ACmpOnValues != 1) {
     __ jcc(Assembler::notEqual, (cc == not_equal) ? taken : not_taken);
     __ testptr(rdx, rdx);
     __ jcc(Assembler::zero, (cc == equal) ? taken : not_taken);
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Fri Jan 25 10:40:32 2019 +0100
@@ -171,6 +171,7 @@
   do_klass(Context_klass,                               java_lang_invoke_MethodHandleNatives_CallSiteContext  ) \
   do_klass(ConstantCallSite_klass,                      java_lang_invoke_ConstantCallSite                     ) \
   do_klass(MutableCallSite_klass,                       java_lang_invoke_MutableCallSite                      ) \
+  do_klass(ValueBootstrapMethods_klass,                 java_lang_invoke_ValueBootstrapMethods                ) \
   do_klass(VolatileCallSite_klass,                      java_lang_invoke_VolatileCallSite                     ) \
   /* Note: MethodHandle must be first, and VolatileCallSite last in group */                                    \
                                                                                                                 \
--- a/src/hotspot/share/classfile/vmSymbols.hpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/classfile/vmSymbols.hpp	Fri Jan 25 10:40:32 2019 +0100
@@ -511,6 +511,7 @@
   template(threadgroup_string_void_signature,         "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V")             \
   template(string_class_signature,                    "(Ljava/lang/String;)Ljava/lang/Class;")                    \
   template(object_object_object_signature,            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \
+  template(object_object_boolean_signature,           "(Ljava/lang/Object;Ljava/lang/Object;)Z") \
   template(string_string_string_signature,            "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") \
   template(string_string_signature,                   "(Ljava/lang/String;)Ljava/lang/String;")                   \
   template(classloader_string_long_signature,         "(Ljava/lang/ClassLoader;Ljava/lang/String;)J")             \
@@ -659,6 +660,8 @@
   template(toFileURL_signature,                    "(Ljava/lang/String;)Ljava/net/URL;")                          \
   template(url_void_signature,                     "(Ljava/net/URL;)V")                                           \
                                                                                                                   \
+  template(java_lang_invoke_ValueBootstrapMethods, "java/lang/invoke/ValueBootstrapMethods")                      \
+  template(isSubstitutable_name,                   "isSubstitutable")                                             \
   /*end*/
 
 // Here are all the intrinsics known to the runtime and the CI.
--- a/src/hotspot/share/code/compiledMethod.cpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/code/compiledMethod.cpp	Fri Jan 25 10:40:32 2019 +0100
@@ -345,15 +345,12 @@
 void CompiledMethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) {
   if (method() != NULL && !method()->is_native()) {
     address pc = fr.pc();
-    SimpleScopeDesc ssd(this, pc);
-    Bytecode_invoke call(ssd.method(), ssd.bci());
-    bool has_receiver = call.has_receiver();
-    bool has_appendix = call.has_appendix();
-    Symbol* signature = call.signature();
-
     // The method attached by JIT-compilers should be used, if present.
     // Bytecode can be inaccurate in such case.
     Method* callee = attached_method_before_pc(pc);
+    bool has_receiver = false;
+    bool has_appendix = false;
+    Symbol* signature = NULL;
     if (callee != NULL) {
       has_receiver = !(callee->access_flags().is_static());
       has_appendix = false;
@@ -366,6 +363,12 @@
         signature = SigEntry::create_symbol(sig);
         has_receiver = false; // The extended signature contains the receiver type
       }
+    } else {
+      SimpleScopeDesc ssd(this, pc);
+      Bytecode_invoke call(ssd.method(), ssd.bci());
+      has_receiver = call.has_receiver();
+      has_appendix = call.has_appendix();
+      signature = call.signature();
     }
 
     fr.oops_compiled_arguments_do(signature, has_receiver, has_appendix, reg_map, f);
--- a/src/hotspot/share/opto/parse2.cpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/opto/parse2.cpp	Fri Jan 25 10:40:32 2019 +0100
@@ -1816,6 +1816,211 @@
 }
 
 void Parse::do_acmp(BoolTest::mask btest, Node* a, Node* b) {
+  ciMethod* subst_method = ciEnv::current()->ValueBootstrapMethods_klass()->find_method(ciSymbol::isSubstitutable_name(), ciSymbol::object_object_boolean_signature());
+  // If current method is ValueBootstrapMethods::isSubstitutable(),
+  // compile the acmp as a regular pointer comparison otherwise we
+  // could call ValueBootstrapMethods::isSubstitutable() back
+  if (ACmpOnValues == 0 || !EnableValhalla || method() == subst_method) {
+    Node* cmp = CmpP(a, b);
+    cmp = optimize_cmp_with_klass(cmp);
+    do_if(btest, cmp);
+    return;
+  }
+
+  if (ACmpOnValues == 3) {
+    // Substituability test
+    if (a->is_ValueType()) {
+      inc_sp(2);
+      a = a->as_ValueType()->allocate(this, true)->get_oop();
+      dec_sp(2);
+    }
+    if (b->is_ValueType()) {
+      inc_sp(2);
+      b = b->as_ValueType()->allocate(this, true)->get_oop();
+      dec_sp(2);
+    }
+
+    const TypeOopPtr* ta = _gvn.type(a)->isa_oopptr();
+    const TypeOopPtr* tb = _gvn.type(b)->isa_oopptr();
+
+    if (ta == NULL || !ta->can_be_value_type() ||
+        tb == NULL || !tb->can_be_value_type()) {
+      Node* cmp = CmpP(a, b);
+      cmp = optimize_cmp_with_klass(cmp);
+      do_if(btest, cmp);
+      return;
+    }
+
+    Node* cmp = CmpP(a, b);
+    cmp = optimize_cmp_with_klass(cmp);
+    Node* eq_region = NULL;
+    if (btest == BoolTest::eq) {
+      do_if(btest, cmp, true);
+      if (stopped()) {
+        return;
+      }
+    } else {
+      assert(btest == BoolTest::ne, "only eq or ne");
+      Node* is_not_equal = NULL;
+      eq_region = new RegionNode(3);
+      {
+        PreserveJVMState pjvms(this);
+        do_if(btest, cmp, false, &is_not_equal);
+        if (!stopped()) {
+          eq_region->init_req(1, control());
+        }
+      }
+      if (is_not_equal == NULL || is_not_equal->is_top()) {
+        record_for_igvn(eq_region);
+        set_control(_gvn.transform(eq_region));
+        return;
+      }
+      set_control(is_not_equal);
+    }
+    // Pointers not equal, check for values
+    Node* ne_region = new RegionNode(6);
+    inc_sp(2);
+    Node* null_ctl = top();
+    Node* not_null_a = null_check_oop(a, &null_ctl, !too_many_traps(Deoptimization::Reason_null_check), false, false);
+    dec_sp(2);
+    ne_region->init_req(1, null_ctl);
+    if (stopped()) {
+      record_for_igvn(ne_region);
+      set_control(_gvn.transform(ne_region));
+      if (btest == BoolTest::ne) {
+        {
+          PreserveJVMState pjvms(this);
+          int target_bci = iter().get_dest();
+          merge(target_bci);
+        }
+        record_for_igvn(eq_region);
+        set_control(_gvn.transform(eq_region));
+      }
+      return;
+    }
+
+    Node* is_value = is_always_locked(not_null_a);
+    Node* value_mask = _gvn.MakeConX(markOopDesc::always_locked_pattern);
+    Node* is_value_cmp = _gvn.transform(new CmpXNode(is_value, value_mask));
+    Node* is_value_bol = _gvn.transform(new BoolNode(is_value_cmp, BoolTest::ne));
+    IfNode* is_value_iff = create_and_map_if(control(), is_value_bol, PROB_FAIR, COUNT_UNKNOWN);
+    Node* not_value = _gvn.transform(new IfTrueNode(is_value_iff));
+    set_control(_gvn.transform(new IfFalseNode(is_value_iff)));
+    ne_region->init_req(2, not_value);
+
+    // One of the 2 pointers refers to a value, check if both are of
+    // the same class
+    inc_sp(2);
+    null_ctl = top();
+    Node* not_null_b = null_check_oop(b, &null_ctl, !too_many_traps(Deoptimization::Reason_null_check), false, false);
+    dec_sp(2);
+    ne_region->init_req(3, null_ctl);
+    if (stopped()) {
+      record_for_igvn(ne_region);
+      set_control(_gvn.transform(ne_region));
+      if (btest == BoolTest::ne) {
+        {
+          PreserveJVMState pjvms(this);
+          int target_bci = iter().get_dest();
+          merge(target_bci);
+        }
+        record_for_igvn(eq_region);
+        set_control(_gvn.transform(eq_region));
+      }
+      return;
+    }
+    Node* kls_a = load_object_klass(not_null_a);
+    Node* kls_b = load_object_klass(not_null_b);
+    Node* kls_cmp = CmpP(kls_a, kls_b);
+    Node* kls_bol = _gvn.transform(new BoolNode(kls_cmp, BoolTest::ne));
+    IfNode* kls_iff = create_and_map_if(control(), kls_bol, PROB_FAIR, COUNT_UNKNOWN);
+    Node* kls_ne = _gvn.transform(new IfTrueNode(kls_iff));
+    set_control(_gvn.transform(new IfFalseNode(kls_iff)));
+    ne_region->init_req(4, kls_ne);
+
+    if (stopped()) {
+      record_for_igvn(ne_region);
+      set_control(_gvn.transform(ne_region));
+      if (btest == BoolTest::ne) {
+        {
+          PreserveJVMState pjvms(this);
+          int target_bci = iter().get_dest();
+          merge(target_bci);
+        }
+        record_for_igvn(eq_region);
+        set_control(_gvn.transform(eq_region));
+      }
+      return;
+    }
+    // Both are values of the same class, we need to perform a
+    // substitutability test. Delegate to
+    // ValueBootstrapMethods::isSubstitutable().
+
+    Node* ne_io_phi = PhiNode::make(ne_region, i_o());
+    Node* mem = reset_memory();
+    Node* ne_mem_phi = PhiNode::make(ne_region, mem);
+
+    Node* eq_io_phi = NULL;
+    Node* eq_mem_phi = NULL;
+    if (eq_region != NULL) {
+      eq_io_phi = PhiNode::make(eq_region, i_o());
+      eq_mem_phi = PhiNode::make(eq_region, mem);
+    }
+
+    set_all_memory(mem);
+
+    kill_dead_locals();
+    CallStaticJavaNode *call = new CallStaticJavaNode(C, TypeFunc::make(subst_method), SharedRuntime::get_resolve_static_call_stub(), subst_method, bci());
+    call->set_override_symbolic_info(true);
+    call->init_req(TypeFunc::Parms, not_null_a);
+    call->init_req(TypeFunc::Parms+1, not_null_b);
+    inc_sp(2);
+    set_edges_for_java_call(call, false, false);
+    Node* ret = set_results_for_java_call(call, false, true);
+    dec_sp(2);
+
+    // Test the return value of ValueBootstrapMethods::isSubstitutable()
+    Node* subst_cmp = _gvn.transform(new CmpINode(ret, intcon(1)));
+    if (btest == BoolTest::eq) {
+      do_if(btest, subst_cmp);
+    } else {
+      assert(btest == BoolTest::ne, "only eq or ne");
+      Node* is_not_equal = NULL;
+      {
+        PreserveJVMState pjvms(this);
+        do_if(btest, subst_cmp, false, &is_not_equal);
+        if (!stopped()) {
+          eq_region->init_req(2, control());
+          eq_io_phi->init_req(2, i_o());
+          eq_mem_phi->init_req(2, reset_memory());
+        }
+      }
+      set_control(is_not_equal);
+    }
+    ne_region->init_req(5, control());
+    ne_io_phi->init_req(5, i_o());
+    ne_mem_phi->init_req(5, reset_memory());
+
+    record_for_igvn(ne_region);
+    set_control(_gvn.transform(ne_region));
+    set_i_o(_gvn.transform(ne_io_phi));
+    set_all_memory(_gvn.transform(ne_mem_phi));
+
+    if (btest == BoolTest::ne) {
+      {
+        PreserveJVMState pjvms(this);
+        int target_bci = iter().get_dest();
+        merge(target_bci);
+      }
+
+      record_for_igvn(eq_region);
+      set_control(_gvn.transform(eq_region));
+      set_i_o(_gvn.transform(eq_io_phi));
+      set_all_memory(_gvn.transform(eq_mem_phi));
+    }
+
+    return;
+  }
   // In the case were both operands might be value types, we need to
   // use the new acmp implementation. Otherwise, i.e. if one operand
   // is not a value type, we can use the old acmp implementation.
@@ -1829,7 +2034,7 @@
 
   Node* ctrl = NULL;
   bool safe_for_replace = true;
-  if (!UsePointerPerturbation) {
+  if (ACmpOnValues != 1) {
     // Emit old acmp before new acmp for quick a != b check
     cmp = CmpP(a, b);
     cmp = optimize_cmp_with_klass(_gvn.transform(cmp));
@@ -1889,7 +2094,7 @@
   }
 
   Node* value_mask = _gvn.MakeConX(markOopDesc::always_locked_pattern);
-  if (UsePointerPerturbation) {
+  if (ACmpOnValues == 1) {
     Node* mark_addr = basic_plus_adr(not_null_a, oopDesc::mark_offset_in_bytes());
     Node* mark = make_load(NULL, mark_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
     Node* not_mark = _gvn.transform(new XorXNode(mark, _gvn.MakeConX(-1)));
@@ -1904,7 +2109,7 @@
   set_control(_gvn.transform(region));
   is_value = _gvn.transform(is_value);
 
-  if (UsePointerPerturbation) {
+  if (ACmpOnValues == 1) {
     // Perturbe oop if operand is a value type to make comparison fail
     Node* pert = _gvn.transform(new AddPNode(a, a, is_value));
     cmp = _gvn.transform(new CmpPNode(pert, b));
--- a/src/hotspot/share/opto/subnode.cpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/opto/subnode.cpp	Fri Jan 25 10:40:32 2019 +0100
@@ -834,9 +834,10 @@
 // Simplify an CmpP (compare 2 pointers) node, based on local information.
 // If both inputs are constants, compare them.
 const Type *CmpPNode::sub( const Type *t1, const Type *t2 ) const {
-  if (t1->isa_valuetype() || t2->isa_valuetype() ||
-      ((t1->is_valuetypeptr() || t2->is_valuetypeptr()) &&
-      (!t1->maybe_null() || !t2->maybe_null()))) {
+  if (ACmpOnValues != 3 &&
+      (t1->isa_valuetype() || t2->isa_valuetype() ||
+       ((t1->is_valuetypeptr() || t2->is_valuetypeptr()) &&
+        (!t1->maybe_null() || !t2->maybe_null())))) {
     // One operand is a value type and one operand is never null, fold to constant false
     return TypeInt::CC_GT;
   }
@@ -1106,7 +1107,7 @@
       // RawPtr comparison
       return NULL;
     }
-    assert(EnableValhalla && UsePointerPerturbation, "unexpected perturbed oop");
+    assert(EnableValhalla && ACmpOnValues == 1, "unexpected perturbed oop");
     return in(1);
   }
   return NULL;
--- a/src/hotspot/share/runtime/globals.hpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/runtime/globals.hpp	Fri Jan 25 10:40:32 2019 +0100
@@ -2621,8 +2621,12 @@
   develop(bool, ScalarizeValueTypes, true,                                  \
           "Scalarize value types in compiled code")                         \
                                                                             \
-  experimental(bool, UsePointerPerturbation, false,                         \
-               "With value types, use the perturbation scheme for acmp")    \
+  experimental(uint, ACmpOnValues, 2,                                       \
+               "0 = regular acmp"                                           \
+               "1 = always false for value, perturbation scheme"            \
+               "2 = always false for value"                                 \
+               "3 = substitutability test")                                 \
+               range(0, 3)                                                  \
 
 
 
--- a/src/hotspot/share/runtime/sharedRuntime.cpp	Thu Jan 24 12:07:59 2019 +0100
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp	Fri Jan 25 10:40:32 2019 +0100
@@ -1370,6 +1370,16 @@
   // CLEANUP - with lazy deopt shouldn't need this lock
   nmethodLocker caller_lock(caller_nm);
 
+  if (!is_virtual && !is_optimized) {
+    SimpleScopeDesc ssd(caller_nm, caller_frame.pc());
+    Bytecode bc(ssd.method(), ssd.method()->bcp_from(ssd.bci()));
+    // Substitutability test implementation piggy backs on static call resolution
+    if (bc.code() == Bytecodes::_if_acmpeq || bc.code() == Bytecodes::_if_acmpne) {
+      SystemDictionary::ValueBootstrapMethods_klass()->initialize(CHECK_NULL);
+      return SystemDictionary::ValueBootstrapMethods_klass()->find_method(vmSymbols::isSubstitutable_name(), vmSymbols::object_object_boolean_signature());
+    }
+  }
+
   // determine call info & receiver
   // note: a) receiver is NULL for static calls
   //       b) an exception is thrown if receiver is NULL for non-static calls
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java	Thu Jan 24 12:07:59 2019 +0100
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java	Fri Jan 25 10:40:32 2019 +0100
@@ -60,6 +60,45 @@
  *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
  *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
  *                   compiler.valhalla.valuetypes.TestNewAcmp 2
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 0
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+WhiteBoxAPI -Xbatch -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:+AlwaysIncrementalInline
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 0
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 1
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+WhiteBoxAPI -Xbatch -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:+AlwaysIncrementalInline
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 1
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 2
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+WhiteBoxAPI -Xbatch -XX:+EnableValhalla -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:ACmpOnValues=3
+ *                   -XX:+AlwaysIncrementalInline
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestNewAcmp::cmp*
+ *                   compiler.valhalla.valuetypes.TestNewAcmp 2
  */
 
 package compiler.valhalla.valuetypes;
@@ -71,17 +110,34 @@
 import java.lang.reflect.Method;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.Arrays;
 import sun.hotspot.WhiteBox;
 
 interface MyInterface {
 
 }
 
-value class MyValue implements MyInterface {
-    final int x = 42;
+value class MyValue1 implements MyInterface {
+    final int x = 0;
 
-    static MyValue createDefault() {
-        return MyValue.default;
+    static MyValue1 createDefault() {
+        return MyValue1.default;
+    }
+
+    static MyValue1 setX(MyValue1 v, int x) {
+        return __WithField(v.x, x);
+    }
+}
+
+value class MyValue2 implements MyInterface {
+    final int x = 0;
+
+    static MyValue2 createDefault() {
+        return MyValue2.default;
+    }
+
+    static MyValue2 setX(MyValue2 v, int x) {
+        return __WithField(v.x, x);
     }
 }
 
@@ -91,11 +147,15 @@
 
 // Mark test methods that return always false
 @Retention(RetentionPolicy.RUNTIME)
-@interface AlwaysFalse { }
+@interface AlwaysFalse {
+    int[] valid_for() default {1, 2};
+}
 
 // Mark test methods that return always true
 @Retention(RetentionPolicy.RUNTIME)
-@interface AlwaysTrue { }
+@interface AlwaysTrue {
+    int[] valid_for() default {1, 2};
+}
 
 // Mark test methods that return false if the argument is null
 @Retention(RetentionPolicy.RUNTIME)
@@ -134,138 +194,138 @@
         return getNotNull(u1) == getNotNull(u2); // new acmp without null check
     }
 
-    public boolean testEq02_1(MyValue v1, MyValue v2) {
+    public boolean testEq02_1(MyValue1 v1, MyValue1 v2) {
         return get(v1) == (Object)v2; // only true if both null
     }
 
-    public boolean testEq02_2(MyValue v1, MyValue v2) {
+    public boolean testEq02_2(MyValue1 v1, MyValue1 v2) {
         return (Object)v1 == get(v2); // only true if both null
     }
 
-    public boolean testEq02_3(MyValue v1, MyValue v2) {
+    public boolean testEq02_3(MyValue1 v1, MyValue1 v2) {
         return get(v1) == get(v2); // only true if both null
     }
 
-    public boolean testEq03_1(MyValue v, Object u) {
+    public boolean testEq03_1(MyValue1 v, Object u) {
         return get(v) == u; // only true if both null
     }
 
-    public boolean testEq03_2(MyValue v, Object u) {
+    public boolean testEq03_2(MyValue1 v, Object u) {
         return (Object)v == get(u); // only true if both null
     }
 
-    public boolean testEq03_3(MyValue v, Object u) {
+    public boolean testEq03_3(MyValue1 v, Object u) {
         return get(v) == get(u); // only true if both null
     }
 
-    public boolean testEq04_1(Object u, MyValue v) {
+    public boolean testEq04_1(Object u, MyValue1 v) {
         return get(u) == (Object)v; // only true if both null
     }
 
-    public boolean testEq04_2(Object u, MyValue v) {
+    public boolean testEq04_2(Object u, MyValue1 v) {
         return u == get(v); // only true if both null
     }
 
-    public boolean testEq04_3(Object u, MyValue v) {
+    public boolean testEq04_3(Object u, MyValue1 v) {
         return get(u) == get(v); // only true if both null
     }
 
-    public boolean testEq05_1(MyObject o, MyValue v) {
+    public boolean testEq05_1(MyObject o, MyValue1 v) {
         return get(o) == (Object)v; // only true if both null
     }
 
-    public boolean testEq05_2(MyObject o, MyValue v) {
+    public boolean testEq05_2(MyObject o, MyValue1 v) {
         return o == get(v); // only true if both null
     }
 
-    public boolean testEq05_3(MyObject o, MyValue v) {
+    public boolean testEq05_3(MyObject o, MyValue1 v) {
         return get(o) == get(v); // only true if both null
     }
 
-    public boolean testEq06_1(MyValue v, MyObject o) {
+    public boolean testEq06_1(MyValue1 v, MyObject o) {
         return get(v) == o; // only true if both null
     }
 
-    public boolean testEq06_2(MyValue v, MyObject o) {
+    public boolean testEq06_2(MyValue1 v, MyObject o) {
         return (Object)v == get(o); // only true if both null
     }
 
-    public boolean testEq06_3(MyValue v, MyObject o) {
+    public boolean testEq06_3(MyValue1 v, MyObject o) {
         return get(v) == get(o); // only true if both null
     }
 
     @AlwaysFalse
-    public boolean testEq07_1(MyValue v1, MyValue v2) {
+    public boolean testEq07_1(MyValue1 v1, MyValue1 v2) {
         return getNotNull(v1) == (Object)v2; // false
     }
 
     @AlwaysFalse
-    public boolean testEq07_2(MyValue v1, MyValue v2) {
+    public boolean testEq07_2(MyValue1 v1, MyValue1 v2) {
         return (Object)v1 == getNotNull(v2); // false
     }
 
     @AlwaysFalse
-    public boolean testEq07_3(MyValue v1, MyValue v2) {
+    public boolean testEq07_3(MyValue1 v1, MyValue1 v2) {
         return getNotNull(v1) == getNotNull(v2); // false
     }
 
     @AlwaysFalse
-    public boolean testEq08_1(MyValue v, Object u) {
+    public boolean testEq08_1(MyValue1 v, Object u) {
         return getNotNull(v) == u; // false
     }
 
     @AlwaysFalse
-    public boolean testEq08_2(MyValue v, Object u) {
+    public boolean testEq08_2(MyValue1 v, Object u) {
         return (Object)v == getNotNull(u); // false
     }
 
     @AlwaysFalse
-    public boolean testEq08_3(MyValue v, Object u) {
+    public boolean testEq08_3(MyValue1 v, Object u) {
         return getNotNull(v) == getNotNull(u); // false
     }
 
     @AlwaysFalse
-    public boolean testEq09_1(Object u, MyValue v) {
+    public boolean testEq09_1(Object u, MyValue1 v) {
         return getNotNull(u) == (Object)v; // false
     }
 
     @AlwaysFalse
-    public boolean testEq09_2(Object u, MyValue v) {
+    public boolean testEq09_2(Object u, MyValue1 v) {
         return u == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq09_3(Object u, MyValue v) {
+    public boolean testEq09_3(Object u, MyValue1 v) {
         return getNotNull(u) == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq10_1(MyObject o, MyValue v) {
+    public boolean testEq10_1(MyObject o, MyValue1 v) {
         return getNotNull(o) == (Object)v; // false
     }
 
     @AlwaysFalse
-    public boolean testEq10_2(MyObject o, MyValue v) {
+    public boolean testEq10_2(MyObject o, MyValue1 v) {
         return o == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq10_3(MyObject o, MyValue v) {
+    public boolean testEq10_3(MyObject o, MyValue1 v) {
         return getNotNull(o) == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq11_1(MyValue v, MyObject o) {
+    public boolean testEq11_1(MyValue1 v, MyObject o) {
         return getNotNull(v) == o; // false
     }
 
     @AlwaysFalse
-    public boolean testEq11_2(MyValue v, MyObject o) {
+    public boolean testEq11_2(MyValue1 v, MyObject o) {
         return (Object)v == getNotNull(o); // false
     }
 
     @AlwaysFalse
-    public boolean testEq11_3(MyValue v, MyObject o) {
+    public boolean testEq11_3(MyValue1 v, MyObject o) {
         return getNotNull(v) == getNotNull(o); // false
     }
 
@@ -329,57 +389,57 @@
         return get(u) == get(a); // old acmp
     }
 
-    public boolean testEq17_1(Object[] a, MyValue v) {
+    public boolean testEq17_1(Object[] a, MyValue1 v) {
         return get(a) == (Object)v; // only true if both null
     }
 
-    public boolean testEq17_2(Object[] a, MyValue v) {
+    public boolean testEq17_2(Object[] a, MyValue1 v) {
         return a == get(v); // only true if both null
     }
 
-    public boolean testEq17_3(Object[] a, MyValue v) {
+    public boolean testEq17_3(Object[] a, MyValue1 v) {
         return get(a) == get(v); // only true if both null
     }
 
-    public boolean testEq18_1(MyValue v, Object[] a) {
+    public boolean testEq18_1(MyValue1 v, Object[] a) {
         return get(v) == a; // only true if both null
     }
 
-    public boolean testEq18_2(MyValue v, Object[] a) {
+    public boolean testEq18_2(MyValue1 v, Object[] a) {
         return (Object)v == get(a); // only true if both null
     }
 
-    public boolean testEq18_3(MyValue v, Object[] a) {
+    public boolean testEq18_3(MyValue1 v, Object[] a) {
         return get(v) == get(a); // only true if both null
     }
 
     @AlwaysFalse
-    public boolean testEq19_1(Object[] a, MyValue v) {
+    public boolean testEq19_1(Object[] a, MyValue1 v) {
         return getNotNull(a) == (Object)v; // false
     }
 
     @AlwaysFalse
-    public boolean testEq19_2(Object[] a, MyValue v) {
+    public boolean testEq19_2(Object[] a, MyValue1 v) {
         return a == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq19_3(Object[] a, MyValue v) {
+    public boolean testEq19_3(Object[] a, MyValue1 v) {
         return getNotNull(a) == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq20_1(MyValue v, Object[] a) {
+    public boolean testEq20_1(MyValue1 v, Object[] a) {
         return getNotNull(v) == a; // false
     }
 
     @AlwaysFalse
-    public boolean testEq20_2(MyValue v, Object[] a) {
+    public boolean testEq20_2(MyValue1 v, Object[] a) {
         return (Object)v == getNotNull(a); // false
     }
 
     @AlwaysFalse
-    public boolean testEq20_3(MyValue v, Object[] a) {
+    public boolean testEq20_3(MyValue1 v, Object[] a) {
         return getNotNull(v) == getNotNull(a); // false
     }
 
@@ -410,57 +470,57 @@
         return getNotNull(u1) == getNotNull(u2); // new acmp without null check
     }
 
-    public boolean testEq22_1(MyValue v, MyInterface u) {
+    public boolean testEq22_1(MyValue1 v, MyInterface u) {
         return get(v) == u; // only true if both null
     }
 
-    public boolean testEq22_2(MyValue v, MyInterface u) {
+    public boolean testEq22_2(MyValue1 v, MyInterface u) {
         return (Object)v == get(u); // only true if both null
     }
 
-    public boolean testEq22_3(MyValue v, MyInterface u) {
+    public boolean testEq22_3(MyValue1 v, MyInterface u) {
         return get(v) == get(u); // only true if both null
     }
 
-    public boolean testEq23_1(MyInterface u, MyValue v) {
+    public boolean testEq23_1(MyInterface u, MyValue1 v) {
         return get(u) == (Object)v; // only true if both null
     }
 
-    public boolean testEq23_2(MyInterface u, MyValue v) {
+    public boolean testEq23_2(MyInterface u, MyValue1 v) {
         return u == get(v); // only true if both null
     }
 
-    public boolean testEq23_3(MyInterface u, MyValue v) {
+    public boolean testEq23_3(MyInterface u, MyValue1 v) {
         return get(u) == get(v); // only true if both null
     }
 
     @AlwaysFalse
-    public boolean testEq24_1(MyValue v, MyInterface u) {
+    public boolean testEq24_1(MyValue1 v, MyInterface u) {
         return getNotNull(v) == u; // false
     }
 
     @AlwaysFalse
-    public boolean testEq24_2(MyValue v, MyInterface u) {
+    public boolean testEq24_2(MyValue1 v, MyInterface u) {
         return (Object)v == getNotNull(u); // false
     }
 
     @AlwaysFalse
-    public boolean testEq24_3(MyValue v, MyInterface u) {
+    public boolean testEq24_3(MyValue1 v, MyInterface u) {
         return getNotNull(v) == getNotNull(u); // false
     }
 
     @AlwaysFalse
-    public boolean testEq25_1(MyInterface u, MyValue v) {
+    public boolean testEq25_1(MyInterface u, MyValue1 v) {
         return getNotNull(u) == (Object)v; // false
     }
 
     @AlwaysFalse
-    public boolean testEq25_2(MyInterface u, MyValue v) {
+    public boolean testEq25_2(MyInterface u, MyValue1 v) {
         return u == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq25_3(MyInterface u, MyValue v) {
+    public boolean testEq25_3(MyInterface u, MyValue1 v) {
         return getNotNull(u) == getNotNull(v); // false
     }
 
@@ -512,92 +572,92 @@
         return get(u) == get(a); // old acmp
     }
 
-    public boolean testEq30_1(MyInterface[] a, MyValue v) {
+    public boolean testEq30_1(MyInterface[] a, MyValue1 v) {
         return get(a) == (Object)v; // only true if both null
     }
 
-    public boolean testEq30_2(MyInterface[] a, MyValue v) {
+    public boolean testEq30_2(MyInterface[] a, MyValue1 v) {
         return a == get(v); // only true if both null
     }
 
-    public boolean testEq30_3(MyInterface[] a, MyValue v) {
+    public boolean testEq30_3(MyInterface[] a, MyValue1 v) {
         return get(a) == get(v); // only true if both null
     }
 
-    public boolean testEq31_1(MyValue v, MyInterface[] a) {
+    public boolean testEq31_1(MyValue1 v, MyInterface[] a) {
         return get(v) == a; // only true if both null
     }
 
-    public boolean testEq31_2(MyValue v, MyInterface[] a) {
+    public boolean testEq31_2(MyValue1 v, MyInterface[] a) {
         return (Object)v == get(a); // only true if both null
     }
 
-    public boolean testEq31_3(MyValue v, MyInterface[] a) {
+    public boolean testEq31_3(MyValue1 v, MyInterface[] a) {
         return get(v) == get(a); // only true if both null
     }
 
     @AlwaysFalse
-    public boolean testEq32_1(MyInterface[] a, MyValue v) {
+    public boolean testEq32_1(MyInterface[] a, MyValue1 v) {
         return getNotNull(a) == (Object)v; // false
     }
 
     @AlwaysFalse
-    public boolean testEq32_2(MyInterface[] a, MyValue v) {
+    public boolean testEq32_2(MyInterface[] a, MyValue1 v) {
         return a == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq32_3(MyInterface[] a, MyValue v) {
+    public boolean testEq32_3(MyInterface[] a, MyValue1 v) {
         return getNotNull(a) == getNotNull(v); // false
     }
 
     @AlwaysFalse
-    public boolean testEq33_1(MyValue v, MyInterface[] a) {
+    public boolean testEq33_1(MyValue1 v, MyInterface[] a) {
         return getNotNull(v) == a; // false
     }
 
     @AlwaysFalse
-    public boolean testEq33_2(MyValue v, MyInterface[] a) {
+    public boolean testEq33_2(MyValue1 v, MyInterface[] a) {
         return (Object)v == getNotNull(a); // false
     }
 
     @AlwaysFalse
-    public boolean testEq33_3(MyValue v, MyInterface[] a) {
+    public boolean testEq33_3(MyValue1 v, MyInterface[] a) {
         return getNotNull(v) == getNotNull(a); // false
     }
 
 
     // Null tests
 
-    public boolean testNull01_1(MyValue v) {
+    public boolean testNull01_1(MyValue1 v) {
         return (Object)v == null; // old acmp
     }
 
-    public boolean testNull01_2(MyValue v) {
+    public boolean testNull01_2(MyValue1 v) {
         return get(v) == null; // old acmp
     }
 
-    public boolean testNull01_3(MyValue v) {
+    public boolean testNull01_3(MyValue1 v) {
         return (Object)v == get((Object)null); // old acmp
     }
 
-    public boolean testNull01_4(MyValue v) {
+    public boolean testNull01_4(MyValue1 v) {
         return get(v) == get((Object)null); // old acmp
     }
 
-    public boolean testNull02_1(MyValue v) {
+    public boolean testNull02_1(MyValue1 v) {
         return null == (Object)v; // old acmp
     }
 
-    public boolean testNull02_2(MyValue v) {
+    public boolean testNull02_2(MyValue1 v) {
         return get((Object)null) == (Object)v; // old acmp
     }
 
-    public boolean testNull02_3(MyValue v) {
+    public boolean testNull02_3(MyValue1 v) {
         return null == get(v); // old acmp
     }
 
-    public boolean testNull02_4(MyValue v) {
+    public boolean testNull02_4(MyValue1 v) {
         return get((Object)null) == get(v); // old acmp
     }
 
@@ -726,138 +786,138 @@
         return getNotNull(u1) != getNotNull(u2); // new acmp without null check
     }
 
-    public boolean testNotEq02_1(MyValue v1, MyValue v2) {
+    public boolean testNotEq02_1(MyValue1 v1, MyValue1 v2) {
         return get(v1) != (Object)v2; // only false if both null
     }
 
-    public boolean testNotEq02_2(MyValue v1, MyValue v2) {
+    public boolean testNotEq02_2(MyValue1 v1, MyValue1 v2) {
         return (Object)v1 != get(v2); // only false if both null
     }
 
-    public boolean testNotEq02_3(MyValue v1, MyValue v2) {
+    public boolean testNotEq02_3(MyValue1 v1, MyValue1 v2) {
         return get(v1) != get(v2); // only false if both null
     }
 
-    public boolean testNotEq03_1(MyValue v, Object u) {
+    public boolean testNotEq03_1(MyValue1 v, Object u) {
         return get(v) != u; // only false if both null
     }
 
-    public boolean testNotEq03_2(MyValue v, Object u) {
+    public boolean testNotEq03_2(MyValue1 v, Object u) {
         return (Object)v != get(u); // only false if both null
     }
 
-    public boolean testNotEq03_3(MyValue v, Object u) {
+    public boolean testNotEq03_3(MyValue1 v, Object u) {
         return get(v) != get(u); // only false if both null
     }
 
-    public boolean testNotEq04_1(Object u, MyValue v) {
+    public boolean testNotEq04_1(Object u, MyValue1 v) {
         return get(u) != (Object)v; // only false if both null
     }
 
-    public boolean testNotEq04_2(Object u, MyValue v) {
+    public boolean testNotEq04_2(Object u, MyValue1 v) {
         return u != get(v); // only false if both null
     }
 
-    public boolean testNotEq04_3(Object u, MyValue v) {
+    public boolean testNotEq04_3(Object u, MyValue1 v) {
         return get(u) != get(v); // only false if both null
     }
 
-    public boolean testNotEq05_1(MyObject o, MyValue v) {
+    public boolean testNotEq05_1(MyObject o, MyValue1 v) {
         return get(o) != (Object)v; // only false if both null
     }
 
-    public boolean testNotEq05_2(MyObject o, MyValue v) {
+    public boolean testNotEq05_2(MyObject o, MyValue1 v) {
         return o != get(v); // only false if both null
     }
 
-    public boolean testNotEq05_3(MyObject o, MyValue v) {
+    public boolean testNotEq05_3(MyObject o, MyValue1 v) {
         return get(o) != get(v); // only false if both null
     }
 
-    public boolean testNotEq06_1(MyValue v, MyObject o) {
+    public boolean testNotEq06_1(MyValue1 v, MyObject o) {
         return get(v) != o; // only false if both null
     }
 
-    public boolean testNotEq06_2(MyValue v, MyObject o) {
+    public boolean testNotEq06_2(MyValue1 v, MyObject o) {
         return (Object)v != get(o); // only false if both null
     }
 
-    public boolean testNotEq06_3(MyValue v, MyObject o) {
+    public boolean testNotEq06_3(MyValue1 v, MyObject o) {
         return get(v) != get(o); // only false if both null
     }
 
     @AlwaysTrue
-    public boolean testNotEq07_1(MyValue v1, MyValue v2) {
+    public boolean testNotEq07_1(MyValue1 v1, MyValue1 v2) {
         return getNotNull(v1) != (Object)v2; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq07_2(MyValue v1, MyValue v2) {
+    public boolean testNotEq07_2(MyValue1 v1, MyValue1 v2) {
         return (Object)v1 != getNotNull(v2); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq07_3(MyValue v1, MyValue v2) {
+    public boolean testNotEq07_3(MyValue1 v1, MyValue1 v2) {
         return getNotNull(v1) != getNotNull(v2); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq08_1(MyValue v, Object u) {
+    public boolean testNotEq08_1(MyValue1 v, Object u) {
         return getNotNull(v) != u; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq08_2(MyValue v, Object u) {
+    public boolean testNotEq08_2(MyValue1 v, Object u) {
         return (Object)v != getNotNull(u); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq08_3(MyValue v, Object u) {
+    public boolean testNotEq08_3(MyValue1 v, Object u) {
         return getNotNull(v) != getNotNull(u); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq09_1(Object u, MyValue v) {
+    public boolean testNotEq09_1(Object u, MyValue1 v) {
         return getNotNull(u) != (Object)v; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq09_2(Object u, MyValue v) {
+    public boolean testNotEq09_2(Object u, MyValue1 v) {
         return u != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq09_3(Object u, MyValue v) {
+    public boolean testNotEq09_3(Object u, MyValue1 v) {
         return getNotNull(u) != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq10_1(MyObject o, MyValue v) {
+    public boolean testNotEq10_1(MyObject o, MyValue1 v) {
         return getNotNull(o) != (Object)v; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq10_2(MyObject o, MyValue v) {
+    public boolean testNotEq10_2(MyObject o, MyValue1 v) {
         return o != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq10_3(MyObject o, MyValue v) {
+    public boolean testNotEq10_3(MyObject o, MyValue1 v) {
         return getNotNull(o) != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq11_1(MyValue v, MyObject o) {
+    public boolean testNotEq11_1(MyValue1 v, MyObject o) {
         return getNotNull(v) != o; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq11_2(MyValue v, MyObject o) {
+    public boolean testNotEq11_2(MyValue1 v, MyObject o) {
         return (Object)v != getNotNull(o); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq11_3(MyValue v, MyObject o) {
+    public boolean testNotEq11_3(MyValue1 v, MyObject o) {
         return getNotNull(v) != getNotNull(o); // true
     }
 
@@ -921,57 +981,57 @@
         return get(u) != get(a); // old acmp
     }
 
-    public boolean testNotEq17_1(Object[] a, MyValue v) {
+    public boolean testNotEq17_1(Object[] a, MyValue1 v) {
         return get(a) != (Object)v; // only false if both null
     }
 
-    public boolean testNotEq17_2(Object[] a, MyValue v) {
+    public boolean testNotEq17_2(Object[] a, MyValue1 v) {
         return a != get(v); // only false if both null
     }
 
-    public boolean testNotEq17_3(Object[] a, MyValue v) {
+    public boolean testNotEq17_3(Object[] a, MyValue1 v) {
         return get(a) != get(v); // only false if both null
     }
 
-    public boolean testNotEq18_1(MyValue v, Object[] a) {
+    public boolean testNotEq18_1(MyValue1 v, Object[] a) {
         return get(v) != a; // only false if both null
     }
 
-    public boolean testNotEq18_2(MyValue v, Object[] a) {
+    public boolean testNotEq18_2(MyValue1 v, Object[] a) {
         return (Object)v != get(a); // only false if both null
     }
 
-    public boolean testNotEq18_3(MyValue v, Object[] a) {
+    public boolean testNotEq18_3(MyValue1 v, Object[] a) {
         return get(v) != get(a); // only false if both null
     }
 
     @AlwaysTrue
-    public boolean testNotEq19_1(Object[] a, MyValue v) {
+    public boolean testNotEq19_1(Object[] a, MyValue1 v) {
         return getNotNull(a) != (Object)v; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq19_2(Object[] a, MyValue v) {
+    public boolean testNotEq19_2(Object[] a, MyValue1 v) {
         return a != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq19_3(Object[] a, MyValue v) {
+    public boolean testNotEq19_3(Object[] a, MyValue1 v) {
         return getNotNull(a) != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq20_1(MyValue v, Object[] a) {
+    public boolean testNotEq20_1(MyValue1 v, Object[] a) {
         return getNotNull(v) != a; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq20_2(MyValue v, Object[] a) {
+    public boolean testNotEq20_2(MyValue1 v, Object[] a) {
         return (Object)v != getNotNull(a); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq20_3(MyValue v, Object[] a) {
+    public boolean testNotEq20_3(MyValue1 v, Object[] a) {
         return getNotNull(v) != getNotNull(a); // true
     }
 
@@ -1002,57 +1062,57 @@
         return getNotNull(u1) != getNotNull(u2); // new acmp without null check
     }
 
-    public boolean testNotEq22_1(MyValue v, MyInterface u) {
+    public boolean testNotEq22_1(MyValue1 v, MyInterface u) {
         return get(v) != u; // only false if both null
     }
 
-    public boolean testNotEq22_2(MyValue v, MyInterface u) {
+    public boolean testNotEq22_2(MyValue1 v, MyInterface u) {
         return (Object)v != get(u); // only false if both null
     }
 
-    public boolean testNotEq22_3(MyValue v, MyInterface u) {
+    public boolean testNotEq22_3(MyValue1 v, MyInterface u) {
         return get(v) != get(u); // only false if both null
     }
 
-    public boolean testNotEq23_1(MyInterface u, MyValue v) {
+    public boolean testNotEq23_1(MyInterface u, MyValue1 v) {
         return get(u) != (Object)v; // only false if both null
     }
 
-    public boolean testNotEq23_2(MyInterface u, MyValue v) {
+    public boolean testNotEq23_2(MyInterface u, MyValue1 v) {
         return u != get(v); // only false if both null
     }
 
-    public boolean testNotEq23_3(MyInterface u, MyValue v) {
+    public boolean testNotEq23_3(MyInterface u, MyValue1 v) {
         return get(u) != get(v); // only false if both null
     }
 
     @AlwaysTrue
-    public boolean testNotEq24_1(MyValue v, MyInterface u) {
+    public boolean testNotEq24_1(MyValue1 v, MyInterface u) {
         return getNotNull(v) != u; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq24_2(MyValue v, MyInterface u) {
+    public boolean testNotEq24_2(MyValue1 v, MyInterface u) {
         return (Object)v != getNotNull(u); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq24_3(MyValue v, MyInterface u) {
+    public boolean testNotEq24_3(MyValue1 v, MyInterface u) {
         return getNotNull(v) != getNotNull(u); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq25_1(MyInterface u, MyValue v) {
+    public boolean testNotEq25_1(MyInterface u, MyValue1 v) {
         return getNotNull(u) != (Object)v; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq25_2(MyInterface u, MyValue v) {
+    public boolean testNotEq25_2(MyInterface u, MyValue1 v) {
         return u != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq25_3(MyInterface u, MyValue v) {
+    public boolean testNotEq25_3(MyInterface u, MyValue1 v) {
         return getNotNull(u) != getNotNull(v); // true
     }
 
@@ -1104,91 +1164,91 @@
         return get(u) != get(a); // old acmp
     }
 
-    public boolean testNotEq30_1(MyInterface[] a, MyValue v) {
+    public boolean testNotEq30_1(MyInterface[] a, MyValue1 v) {
         return get(a) != (Object)v; // only false if both null
     }
 
-    public boolean testNotEq30_2(MyInterface[] a, MyValue v) {
+    public boolean testNotEq30_2(MyInterface[] a, MyValue1 v) {
         return a != get(v); // only false if both null
     }
 
-    public boolean testNotEq30_3(MyInterface[] a, MyValue v) {
+    public boolean testNotEq30_3(MyInterface[] a, MyValue1 v) {
         return get(a) != get(v); // only false if both null
     }
 
-    public boolean testNotEq31_1(MyValue v, MyInterface[] a) {
+    public boolean testNotEq31_1(MyValue1 v, MyInterface[] a) {
         return get(v) != a; // only false if both null
     }
 
-    public boolean testNotEq31_2(MyValue v, MyInterface[] a) {
+    public boolean testNotEq31_2(MyValue1 v, MyInterface[] a) {
         return (Object)v != get(a); // only false if both null
     }
 
-    public boolean testNotEq31_3(MyValue v, MyInterface[] a) {
+    public boolean testNotEq31_3(MyValue1 v, MyInterface[] a) {
         return get(v) != get(a); // only false if both null
     }
 
     @AlwaysTrue
-    public boolean testNotEq32_1(MyInterface[] a, MyValue v) {
+    public boolean testNotEq32_1(MyInterface[] a, MyValue1 v) {
         return getNotNull(a) != (Object)v; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq32_2(MyInterface[] a, MyValue v) {
+    public boolean testNotEq32_2(MyInterface[] a, MyValue1 v) {
         return a != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq32_3(MyInterface[] a, MyValue v) {
+    public boolean testNotEq32_3(MyInterface[] a, MyValue1 v) {
         return getNotNull(a) != getNotNull(v); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq33_1(MyValue v, MyInterface[] a) {
+    public boolean testNotEq33_1(MyValue1 v, MyInterface[] a) {
         return getNotNull(v) != a; // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq33_2(MyValue v, MyInterface[] a) {
+    public boolean testNotEq33_2(MyValue1 v, MyInterface[] a) {
         return (Object)v != getNotNull(a); // true
     }
 
     @AlwaysTrue
-    public boolean testNotEq33_3(MyValue v, MyInterface[] a) {
+    public boolean testNotEq33_3(MyValue1 v, MyInterface[] a) {
         return getNotNull(v) != getNotNull(a); // true
     }
 
     // Null tests
 
-    public boolean testNotNull01_1(MyValue v) {
+    public boolean testNotNull01_1(MyValue1 v) {
         return (Object)v != null; // old acmp
     }
 
-    public boolean testNotNull01_2(MyValue v) {
+    public boolean testNotNull01_2(MyValue1 v) {
         return get(v) != null; // old acmp
     }
 
-    public boolean testNotNull01_3(MyValue v) {
+    public boolean testNotNull01_3(MyValue1 v) {
         return (Object)v != get((Object)null); // old acmp
     }
 
-    public boolean testNotNull01_4(MyValue v) {
+    public boolean testNotNull01_4(MyValue1 v) {
         return get(v) != get((Object)null); // old acmp
     }
 
-    public boolean testNotNull02_1(MyValue v) {
+    public boolean testNotNull02_1(MyValue1 v) {
         return null != (Object)v; // old acmp
     }
 
-    public boolean testNotNull02_2(MyValue v) {
+    public boolean testNotNull02_2(MyValue1 v) {
         return get((Object)null) != (Object)v; // old acmp
     }
 
-    public boolean testNotNull02_3(MyValue v) {
+    public boolean testNotNull02_3(MyValue1 v) {
         return null != get(v); // old acmp
     }
 
-    public boolean testNotNull02_4(MyValue v) {
+    public boolean testNotNull02_4(MyValue1 v) {
         return get((Object)null) != get(v); // old acmp
     }
 
@@ -1298,12 +1358,12 @@
         return (u != null) ? u : new Object();
     }
 
-    public Object get(MyValue v) {
+    public Object get(MyValue1 v) {
         return v;
     }
 
-    public Object getNotNull(MyValue v) {
-        return ((Object)v != null) ? v : MyValue.createDefault();
+    public Object getNotNull(MyValue1 v) {
+        return ((Object)v != null) ? v : MyValue1.createDefault();
     }
 
     public Object get(MyObject o) {
@@ -1311,7 +1371,7 @@
     }
 
     public Object getNotNull(MyObject o) {
-        return (o != null) ? o : MyValue.createDefault();
+        return (o != null) ? o : MyValue1.createDefault();
     }
 
     public Object get(Object[] a) {
@@ -1331,11 +1391,13 @@
     }
 
     public boolean alwaysTrue(Method m) {
-        return m.isAnnotationPresent(AlwaysTrue.class);
+        return m.isAnnotationPresent(AlwaysTrue.class) &&
+            Arrays.asList(((AlwaysTrue)m.getAnnotation(AlwaysTrue.class)).valid_for()).contains(ACmpOnValues);
     }
 
     public boolean alwaysFalse(Method m) {
-        return m.isAnnotationPresent(AlwaysFalse.class);
+        return m.isAnnotationPresent(AlwaysFalse.class) &&
+            Arrays.asList(((AlwaysFalse)m.getAnnotation(AlwaysFalse.class)).valid_for()).contains(ACmpOnValues);
     }
 
     public boolean isNegated(Method m) {
@@ -1385,8 +1447,9 @@
 
     protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
     protected static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
+    protected static final long ACmpOnValues = (Long)WHITE_BOX.getVMFlag("ACmpOnValues");
 
-    public void runTest(Method m, Object[] args, int warmup, int nullMode) throws Exception {
+    public void runTest(Method m, Object[] args, int warmup, int nullMode, boolean[][] equalities) throws Exception {
         Class<?>[] parameterTypes = m.getParameterTypes();
         int parameterCount = parameterTypes.length;
         // Nullness mode for first argument
@@ -1397,7 +1460,7 @@
             if (args[i] != null && !parameterTypes[0].isInstance(args[i])) {
                 continue;
             }
-            if (args[i] == null && parameterTypes[0] == MyValue.class.asValueType()) {
+            if (args[i] == null && parameterTypes[0] == MyValue1.class.asValueType()) {
                 continue;
             }
             if (parameterCount == 1) {
@@ -1407,7 +1470,7 @@
                 boolean expected = isNegated(m) ? (i != 0) : (i == 0);
                 for (int run = 0; run < warmup; ++run) {
                     Boolean result = (Boolean)m.invoke(this, args[i]);
-                    if (result != expected) {
+                    if (result != expected && WHITE_BOX.isMethodCompiled(m, false)) {
                         System.out.println(" = " + result);
                         throw new RuntimeException("Test failed: should return " + expected);
                     }
@@ -1419,17 +1482,17 @@
                     if (args[j] != null && !parameterTypes[1].isInstance(args[j])) {
                         continue;
                     }
-                    if (args[j] == null && parameterTypes[1] == MyValue.class.asValueType()) {
+                    if (args[j] == null && parameterTypes[1] == MyValue1.class.asValueType()) {
                         continue;
                     }
                     System.out.print("Testing " + m.getName() + "(" + args[i] + ", " + args[j] + ")");
                     // Avoid acmp in the computation of the expected result!
-                    boolean equal = (i == j) && (i != 3);
+                    boolean equal = equalities[i][j];
                     equal = isNegated(m) ? !equal : equal;
                     boolean expected = alwaysTrue(m) || ((i == 0 || j == 0) && trueIfNull(m)) || (!alwaysFalse(m) && equal && !(i == 0 && falseIfNull(m)));
                     for (int run = 0; run < warmup; ++run) {
                         Boolean result = (Boolean)m.invoke(this, args[i], args[j]);
-                        if (result != expected) {
+                        if (result != expected && WHITE_BOX.isMethodCompiled(m, false) && warmup == 1) {
                             System.out.println(" = " + result);
                             throw new RuntimeException("Test failed: should return " + expected);
                         }
@@ -1442,28 +1505,45 @@
 
     public void run(int nullMode) throws Exception {
         // Prepare test arguments
-        Object[] args = new Object[6];
-        args[0] = null;
-        args[1] = new Object();
-        args[2] = new MyObject();
-        args[3] = MyValue.createDefault();
-        args[4] = new Object[10];
-        args[5] = new MyObject[10];
+        Object[] args =  { null,
+                           new Object(),
+                           new MyObject(),
+                           MyValue1.setX(MyValue1.createDefault(), 42),
+                           new Object[10],
+                           new MyObject[10],
+                           MyValue1.setX(MyValue1.createDefault(), 0x42),
+                           MyValue1.setX(MyValue1.createDefault(), 42),
+                           MyValue2.setX(MyValue2.createDefault(), 42), };
+
+        boolean[][] equalities = { { true,  false,  false, false,            false, false, false,             false,             false             },
+                                   { false, true,   false, false,            false, false, false,             false,             false             },
+                                   { false, false,  true,  false,            false, false, false,             false,             false             },
+                                   { false, false,  false, ACmpOnValues == 3,false, false, false,             ACmpOnValues == 3, false             },
+                                   { false, false,  false, false,            true,  false, false,             false,             false             },
+                                   { false, false,  false, false,            false, true,  false,             false,             false             },
+                                   { false, false,  false, false,            false, false, ACmpOnValues == 3, false,             false             },
+                                   { false, false,  false, ACmpOnValues == 3,false, false, false,             ACmpOnValues == 3, false             },
+                                   { false, false,  false, false,            false, false, false,             false,             ACmpOnValues == 3 } };
 
         // Run tests
         for (Method m : getClass().getMethods()) {
             if (m.getName().startsWith("test")) {
                 // Do some warmup runs
-                runTest(m, args, 1000, nullMode);
+                runTest(m, args, 1000, nullMode, equalities);
                 // Make sure method is compiled
                 WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_FULL_OPTIMIZATION);
                 Asserts.assertTrue(WHITE_BOX.isMethodCompiled(m, false), m + " not compiled");
                 // Run again to verify correctness of compiled code
-                runTest(m, args, 1, nullMode);
+                runTest(m, args, 1, nullMode, equalities);
             }
         }
 
-        for (int i = 0; i < 10_000; ++i) {
+        Method cmpAlwaysUnEqual3_m = getClass().getMethod("cmpAlwaysUnEqual3", Object.class);
+        Method cmpAlwaysUnEqual4_m = getClass().getMethod("cmpAlwaysUnEqual4", Object.class);
+        Method cmpSometimesEqual1_m = getClass().getMethod("cmpSometimesEqual1", Object.class);
+        Method cmpSometimesEqual2_m = getClass().getMethod("cmpSometimesEqual2", Object.class);
+
+        for (int i = 0; i < 20_000; ++i) {
             Asserts.assertTrue(cmpAlwaysEqual1(args[1], args[1]));
             Asserts.assertFalse(cmpAlwaysEqual2(args[1], args[1]));
             Asserts.assertTrue(cmpAlwaysEqual3(args[1]));
@@ -1471,12 +1551,36 @@
 
             Asserts.assertFalse(cmpAlwaysUnEqual1(args[1], args[2]));
             Asserts.assertTrue(cmpAlwaysUnEqual2(args[1], args[2]));
-            Asserts.assertFalse(cmpAlwaysUnEqual3(args[3]));
-            Asserts.assertTrue(cmpAlwaysUnEqual4(args[3]));
+            boolean compiled = WHITE_BOX.isMethodCompiled(cmpAlwaysUnEqual3_m, false);
+            boolean res = cmpAlwaysUnEqual3(args[3]);
+            if (ACmpOnValues != 3) {
+                Asserts.assertFalse(res);
+            } else if (compiled) {
+                Asserts.assertTrue(res);
+            }
+            compiled = WHITE_BOX.isMethodCompiled(cmpAlwaysUnEqual4_m, false);
+            res = cmpAlwaysUnEqual4(args[3]);
+            if (ACmpOnValues != 3) {
+                Asserts.assertTrue(res);
+            } else if (compiled) {
+                Asserts.assertFalse(res);
+            }
 
             int idx = i % args.length;
-            Asserts.assertEQ(cmpSometimesEqual1(args[idx]), idx != 3);
-            Asserts.assertNE(cmpSometimesEqual2(args[idx]), idx != 3);
+            compiled = WHITE_BOX.isMethodCompiled(cmpSometimesEqual1_m, false);
+            res = cmpSometimesEqual1(args[idx]);
+            if (ACmpOnValues != 3) {
+                Asserts.assertEQ(res, args[idx] == null || !args[idx].getClass().isValue());
+            } else if (compiled) {
+                Asserts.assertTrue(res);
+            }
+            compiled = WHITE_BOX.isMethodCompiled(cmpSometimesEqual2_m, false);
+            res = cmpSometimesEqual2(args[idx]);
+            if (ACmpOnValues != 3) {
+                Asserts.assertNE(res, args[idx] == null || !args[idx].getClass().isValue());
+            } else if (compiled) {
+                Asserts.assertFalse(res);
+            }
         }
     }
 
@@ -1491,4 +1595,3 @@
         t.run(nullMode);
     }
 }
-