changeset 49477:048139cd8f32 lworld

[lworld] Compiler tests for value types that implement interfaces
author thartmann
date Thu, 26 Apr 2018 18:04:02 +0200
parents e65277a7109a
children 66b5e022cb6a
files src/hotspot/share/opto/parse1.cpp src/hotspot/share/opto/parse2.cpp test/hotspot/jtreg/compiler/valhalla/valuetypes/MyInterface.java test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue1.java test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue2.java test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue3.java test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue4.java test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java
diffstat 8 files changed, 275 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/opto/parse1.cpp	Wed Apr 25 14:15:38 2018 -0700
+++ b/src/hotspot/share/opto/parse1.cpp	Thu Apr 26 18:04:02 2018 +0200
@@ -1887,7 +1887,7 @@
       // It is a bug if we create a phi which sees a garbage value on a live path.
 
       // Merging two value types?
-      assert(m->is_ValueType() == n->is_ValueType(), "value types should only be merged with other value types");
+      assert(n->is_top() || m->is_ValueType() == n->is_ValueType(), "value types should only be merged with other value types");
       if (phi != NULL && n->isa_ValueType()) {
         // Reload current state because it may have been updated by ensure_phi
         m = map()->in(j);
--- a/src/hotspot/share/opto/parse2.cpp	Wed Apr 25 14:15:38 2018 -0700
+++ b/src/hotspot/share/opto/parse2.cpp	Thu Apr 26 18:04:02 2018 +0200
@@ -2301,7 +2301,7 @@
       // TODO Ugly hack. Merge this with the new acmp implementation
       // A value type can only compare equal to null and obviously this one is not null
       // Return constant false
-      c =  _gvn.transform(new CmpINode(_gvn.intcon(0), _gvn.intcon(1)));
+      c = _gvn.transform(new CmpINode(_gvn.intcon(0), _gvn.intcon(1)));
     } else {
       if (!_gvn.type(b)->speculative_maybe_null() &&
           !too_many_traps(Deoptimization::Reason_speculate_null_check)) {
@@ -2332,7 +2332,7 @@
       // TODO Ugly hack. Merge this with the new acmp implementation
       // A value type can only compare equal to null and obviously this one is not null
       // Return constant false
-      c =  _gvn.transform(new CmpINode(_gvn.intcon(0), _gvn.intcon(1)));
+      c = _gvn.transform(new CmpINode(_gvn.intcon(0), _gvn.intcon(1)));
     } else {
       c = _gvn.transform( new CmpPNode(b, a) );
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyInterface.java	Thu Apr 26 18:04:02 2018 +0200
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package compiler.valhalla.valuetypes;
+
+public interface MyInterface {
+
+}
+
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue1.java	Wed Apr 25 14:15:38 2018 -0700
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue1.java	Thu Apr 26 18:04:02 2018 +0200
@@ -23,7 +23,7 @@
 
 package compiler.valhalla.valuetypes;
 
-__ByValue public final class MyValue1 {
+__ByValue public final class MyValue1 implements MyInterface {
     static int s;
     static final long sf = ValueTypeTest.rL;
     final int x;
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue2.java	Wed Apr 25 14:15:38 2018 -0700
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue2.java	Thu Apr 26 18:04:02 2018 +0200
@@ -58,7 +58,7 @@
     }
 }
 
-__ByValue public final class MyValue2 {
+__ByValue public final class MyValue2 implements MyInterface {
     final int x;
     final byte y;
     __Flattenable final MyValue2Inline v1;
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue3.java	Wed Apr 25 14:15:38 2018 -0700
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue3.java	Thu Apr 26 18:04:02 2018 +0200
@@ -63,7 +63,7 @@
 
 // Value type definition to stress test return of a value in registers
 // (uses all registers of calling convention on x86_64)
-__ByValue public final class MyValue3 {
+__ByValue public final class MyValue3 implements MyInterface {
     final char c;
     final byte bb;
     final short s;
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue4.java	Wed Apr 25 14:15:38 2018 -0700
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/MyValue4.java	Thu Apr 26 18:04:02 2018 +0200
@@ -24,7 +24,7 @@
 package compiler.valhalla.valuetypes;
 
 // Value type definition with too many fields to return in registers
-__ByValue final class MyValue4 {
+__ByValue final class MyValue4 implements MyInterface {
     __Flattenable final MyValue3 v1;
     __Flattenable final MyValue3 v2;
 
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java	Wed Apr 25 14:15:38 2018 -0700
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java	Thu Apr 26 18:04:02 2018 +0200
@@ -434,7 +434,9 @@
             valueField1 = (MyValue1)getNull.invoke();
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
+        nullField = (MyValue1)getNull.invoke(); // Should not throw
     }
 
     @DontCompile
@@ -461,6 +463,7 @@
             setNull.invoke(this);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
     }
 
@@ -488,6 +491,7 @@
             test15(false);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
     }
 
@@ -525,6 +529,7 @@
             test16(false);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
     }
 
@@ -563,43 +568,43 @@
         test17(false);
     }
 
-    // Disable for now: bug in MethodHandleBuilder
     // null constant
-    // private static final MethodHandle mergeNull2 = MethodHandleBuilder.loadCode(MethodHandles.lookup(),
-    //     "mergeNull2",
-    //     MethodType.methodType(void.class, TestLWorld.class, boolean.class),
-    //     CODE -> {
-    //         CODE.
-    //         iload_1().
-    //         iconst_0().
-    //         ifcmp(TypeTag.I, CondKind.EQ, "not_null").
-    //         aconst_null().
-    //         goto_("continue").
-    //         label("not_null").
-    //         aload_0().
-    //         getfield(TestLWorld.class, "valueField1", "Lcompiler/valhalla/valuetypes/MyValue1;").
-    //         label("continue").
-    //         aload_0().
-    //         swap().
-    //         putfield(TestLWorld.class, "valueField1", "Lcompiler/valhalla/valuetypes/MyValue1;").
-    //         return_();
-    //     }
-    //     );
+    private static final MethodHandle mergeNull2 = MethodHandleBuilder.loadCode(MethodHandles.lookup(),
+        "mergeNull2",
+        MethodType.methodType(void.class, TestLWorld.class, boolean.class),
+        CODE -> {
+            CODE.
+            iload_1().
+            iconst_0().
+            ifcmp(TypeTag.I, CondKind.EQ, "not_null").
+            aconst_null().
+            goto_("continue").
+            label("not_null").
+            aload_0().
+            getfield(TestLWorld.class, "valueField1", "Lcompiler/valhalla/valuetypes/MyValue1;").
+            label("continue").
+            aload_0().
+            swap().
+            putfield(TestLWorld.class, "valueField1", "Lcompiler/valhalla/valuetypes/MyValue1;").
+            return_();
+        }
+        );
 
-    // @Test
-    // public void test19(boolean flag) throws Throwable {
-    //     mergeNull2.invoke(this, flag);
-    // }
+    @Test
+    public void test19(boolean flag) throws Throwable {
+        mergeNull2.invoke(this, flag);
+    }
 
-    // @DontCompile
-    // public void test19_verifier(boolean warmup) throws Throwable {
-    //     test19(false);
-    //     try {
-    //         test19(true);
-    //         throw new RuntimeException("NullPointerException expected");
-    //     } catch (NullPointerException e) {
-    //     }
-    // }
+    @DontCompile
+    public void test19_verifier(boolean warmup) throws Throwable {
+        test19(false);
+        try {
+            test19(true);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
 
     // merge of values in a loop, stored in an object local
     @Test
@@ -660,6 +665,7 @@
             test22();
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
         if (test_22_cnt != 1) {
             throw new RuntimeException("call executed twice");
@@ -705,19 +711,218 @@
             test23(b);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
         try {
             test23(c);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
         try {
             test23(d);
             throw new RuntimeException("NullPointerException expected");
         } catch (NullPointerException e) {
+            // Expected
         }
     }
 
+
+    // Interface tests
+
+    @DontInline
+    public MyInterface test24_dontinline1(MyInterface o) {
+        return o;
+    }
+
+    @DontInline
+    public MyValue1 test24_dontinline2(MyInterface o) {
+        return (MyValue1)o;
+    }
+
+    @ForceInline
+    public MyInterface test24_inline1(MyInterface o) {
+        return o;
+    }
+
+    @ForceInline
+    public MyValue1 test24_inline2(MyInterface o) {
+        return (MyValue1)o;
+    }
+
+    @Test()
+    public MyValue1 test24() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        vt = (MyValue1)test24_dontinline1(vt);
+        vt =           test24_dontinline2(vt);
+        vt = (MyValue1)test24_inline1(vt);
+        vt =           test24_inline2(vt);
+        return vt;
+    }
+
+    @DontCompile
+    public void test24_verifier(boolean warmup) {
+        Asserts.assertEQ(test24().hash(), hash());
+    }
+
+    // Test storing/loading value types to/from interface and value type fields
+    MyInterface interfaceField1 = null;
+    MyInterface interfaceField2 = null;
+    MyInterface interfaceField3 = null;
+    MyInterface interfaceField4 = null;
+    MyInterface interfaceField5 = null;
+    MyInterface interfaceField6 = null;
+
+    @DontInline
+    public MyInterface readValueField5AsInterface() {
+        return (MyInterface)valueField5;
+    }
+
+    @DontInline
+    public MyInterface readStaticValueField4AsInterface() {
+        return (MyInterface)staticValueField4;
+    }
+
+    @Test()
+    public long test25(MyValue1 vt1, MyInterface vt2) {
+        interfaceField1 = vt1;
+        interfaceField2 = (MyValue1)vt2;
+        interfaceField3 = MyValue1.createWithFieldsInline(rI, rL);
+        interfaceField4 = MyValue1.createWithFieldsDontInline(rI, rL);
+        interfaceField5 = valueField1;
+        interfaceField6 = valueField3;
+        valueField1 = (MyValue1)interfaceField1;
+        valueField2 = (MyValue1)vt2;
+        valueField3 = (MyValue1)vt2;
+        staticValueField1 = (MyValue1)interfaceField1;
+        staticValueField2 = (MyValue1)vt1;
+        // Don't inline these methods because reading NULL will trigger a deoptimization
+        if (readValueField5AsInterface() != null || readStaticValueField4AsInterface() != null) {
+            throw new RuntimeException("Should be null");
+        }
+        return ((MyValue1)interfaceField1).hash() + ((MyValue1)interfaceField2).hash() +
+               ((MyValue1)interfaceField3).hash() + ((MyValue1)interfaceField4).hash() +
+               ((MyValue1)interfaceField5).hash() + ((MyValue1)interfaceField6).hash() +
+                valueField1.hash() + valueField2.hash() + valueField3.hash() + valueField4.hashPrimitive() +
+                staticValueField1.hash() + staticValueField2.hash() + staticValueField3.hashPrimitive();
+    }
+
+    @DontCompile
+    public void test25_verifier(boolean warmup) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 def = MyValue1.createDefaultDontInline();
+        long result = test25(vt, vt);
+        Asserts.assertEQ(result, 11*vt.hash() + 2*def.hashPrimitive());
+    }
+
+    class MyObject implements MyInterface {
+        public int x;
+
+        public MyObject(int x) {
+            this.x = x;
+        }
+    }
+
+    // Test merging value types and interfaces
+    @Test()
+    public MyInterface test26(int state) {
+        MyInterface res = null;
+        if (state == 0) {
+            res = new MyObject(rI);
+        } else if (state == 1) {
+            res = MyValue1.createWithFieldsInline(rI, rL);
+        } else if (state == 2) {
+            res = MyValue1.createWithFieldsDontInline(rI, rL);
+        } else if (state == 3) {
+            res = (MyValue1)objectField1;
+        } else if (state == 4) {
+            res = valueField1;
+        } else if (state == 5) {
+            res = null;
+        }
+        return res;
+    }
+
+    @DontCompile
+    public void test26_verifier(boolean warmup) {
+        interfaceField1 = valueField1;
+        MyInterface result = null;
+        result = test26(0);
+        Asserts.assertEQ(((MyObject)result).x, rI);
+        result = test26(1);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test26(2);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test26(3);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test26(4);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test26(5);
+        Asserts.assertEQ(result, null);
+    }
+
+    // Test merging value types and interfaces in loops
+    @Test()
+    public MyInterface test27(int iters) {
+        MyInterface res = new MyObject(rI);
+        for (int i = 0; i < iters; ++i) {
+            if (res instanceof MyObject) {
+                res = MyValue1.createWithFieldsInline(rI, rL);
+            } else {
+                res = MyValue1.createWithFieldsInline(((MyValue1)res).x + 1, rL);
+            }
+        }
+        return res;
+    }
+
+    @DontCompile
+    public void test27_verifier(boolean warmup) {
+        MyObject result1 = (MyObject)test27(0);
+        Asserts.assertEQ(result1.x, rI);
+        int iters = (Math.abs(rI) % 10) + 1;
+        MyValue1 result2 = (MyValue1)test27(iters);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + iters - 1, rL);
+        Asserts.assertEQ(result2.hash(), vt.hash());
+    }
+
+    // Test value types in interface variables that are live at safepoint
+    @Test(failOn = ALLOC + STORE + LOOP)
+    public long test28(MyValue1 arg, boolean deopt) {
+        MyInterface vt1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyInterface vt2 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyInterface vt3 = arg;
+        MyInterface vt4 = valueField1;
+        if (deopt) {
+            // uncommon trap
+            WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test5"));
+        }
+        return ((MyValue1)vt1).hash() + ((MyValue1)vt2).hash() +
+               ((MyValue1)vt3).hash() + ((MyValue1)vt4).hash();
+    }
+
+    @DontCompile
+    public void test28_verifier(boolean warmup) {
+        long result = test28(valueField1, !warmup);
+        Asserts.assertEQ(result, 4*hash());
+    }
+
+    // Test comparing value types with interfaces
+    @Test(failOn = ALLOC + LOAD + STORE + LOOP)
+    public boolean test29(Object arg) {
+        MyInterface vt = MyValue1.createWithFieldsInline(rI, rL);
+        if (vt == arg || vt == (MyInterface)valueField1 || vt == interfaceField1 || vt == null ||
+            arg == vt || (MyInterface)valueField1 == vt || interfaceField1 == vt || null == vt) {
+            return true;
+        }
+        return false;
+    }
+
+    @DontCompile
+    public void test29_verifier(boolean warmup) {
+        boolean result = test29(null);
+        Asserts.assertFalse(result);
+    }
+
 // TODO enable and add more tests
 /*
     // Test subtype check when casting to value type
@@ -741,9 +946,6 @@
 */
 
 // TODO Add tests for value type with non-flattened/non-flattenable value type field
-// TODO Add test for writing null to a non-flattenable value type field
-// TODO Add tests for null returns
-// TODO Test above code with value types implementing interfaces
 // TODO Add array tests
 
 }