1 /*
  2  * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package runtime.valhalla.inlinetypes;
 24 
 25 import java.lang.ref.*;
 26 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 27 import jdk.internal.vm.annotation.LooselyConsistentValue;
 28 
 29 
 30 /*
 31  * @test Ifacmp
 32  * @requires vm.gc == null
 33  * @summary if_acmpeq/ne bytecode test
 34  * @modules java.base/jdk.internal.value
 35  *          java.base/jdk.internal.vm.annotation
 36  * @enablePreview
 37  * @compile --source 22 Ifacmp.java
 38  * @run main/othervm -Xms16m -Xmx16m -XX:+UseSerialGC runtime.valhalla.inlinetypes.Ifacmp
 39  */
 40 public class Ifacmp {
 41 
 42     @ImplicitlyConstructible
 43     @LooselyConsistentValue
 44     static value class MyValue {
 45         int value;
 46         public MyValue(int v) { this.value = v; }
 47     };
 48     @ImplicitlyConstructible
 49     @LooselyConsistentValue
 50     static value class MyValue2 {
 51         int value;
 52         public MyValue2(int v) { this.value = v; }
 53     };
 54 
 55     boolean acmpModeInlineAlwaysFalse = false;
 56 
 57     Object aNull = null;
 58     Object bNull = null;
 59 
 60     Object aObject = new String("Hi");
 61     Object bObject = new String("Hi");
 62 
 63     Object aValue = new MyValue(1);
 64     Object bValue = new MyValue(1);
 65     Object cValue = new MyValue(0);
 66     Object aValue2 = new MyValue2(4711);
 67 
 68     Object[][] equalUseCases = {
 69         { aNull, bNull },
 70         { aObject, aObject },
 71         { aValue, bValue },
 72         { cValue, cValue },
 73         { aValue2, aValue2 }
 74     };
 75 
 76     int objectEqualsUseCases = 2; // Nof object equals use cases
 77 
 78     // Would just generate these fail case from the "equal set" above,
 79     // but to do so needs ==, so write out by hand it is...
 80     Object[][] notEqualUseCases = {
 81         { aNull, aObject },
 82         { aNull, bObject },
 83         { aNull, aValue },
 84         { aNull, bValue },
 85         { aNull, cValue },
 86         { aNull, aValue2 },
 87         { aObject, bObject },
 88         { aObject, aValue },
 89         { aObject, bValue },
 90         { aObject, cValue },
 91         { aObject, aValue2 },
 92         { bObject, cValue },
 93         { bObject, aValue2 },
 94         { aValue, cValue },
 95         { aValue, aValue2 },
 96     };
 97 
 98     public Ifacmp() { this(false); }
 99     public Ifacmp(boolean acmpModeInlineAlwaysFalse) {
100         this.acmpModeInlineAlwaysFalse = acmpModeInlineAlwaysFalse;
101         if (acmpModeInlineAlwaysFalse) {
102             System.out.println("ifacmp always false for inline types");
103         } else {
104             System.out.println("ifacmp substitutability inline types");
105         }
106     }
107 
108     public void test() {
109         testAllUseCases();
110     }
111 
112     public void testUntilGc(int nofGc) {
113         for (int i = 0; i < nofGc; i++) {
114             System.out.println("GC num " + (i + 1));
115             testUntilGc();
116         }
117     }
118 
119     public void testUntilGc() {
120         Reference ref = new WeakReference<Object>(new Object(), new ReferenceQueue<>());
121         do {
122             test();
123         } while (ref.get() != null);
124     }
125 
126     public void testAllUseCases() {
127         int useCase = 0;
128         for (Object[] pair : equalUseCases) {
129             useCase++;
130             boolean equal = acmpModeInlineAlwaysFalse ? (useCase <= objectEqualsUseCases) : true;
131             checkEqual(pair[0], pair[1], equal);
132         }
133         for (Object[] pair : notEqualUseCases) {
134             checkEqual(pair[0], pair[1], false);
135         }
136         testLocalValues();
137         testAlot();
138     }
139 
140     public void testValues() {
141         checkEqual(aValue, bValue, true);
142 
143         checkEqual(aValue, cValue, false);
144         checkEqual(aValue, aValue2, false);
145         checkEqual(aValue2, bValue, false);
146         checkEqual(aValue2, cValue, false);
147         testLocalValues();
148     }
149 
150     public void testLocalValues() {
151         // "aload + ifacmp" should be same as "aaload + ifamcp"
152         // but let's be paranoid...
153         MyValue a = new MyValue(11);
154         MyValue b = new MyValue(11);
155         MyValue c = a;
156         MyValue a1 = new MyValue(7);
157         MyValue2 a2 = new MyValue2(13);
158 
159         if (acmpModeInlineAlwaysFalse) {
160             if (a == b) throw new RuntimeException("Always false fail " + a + " == " + b);
161             if (a == c) throw new RuntimeException("Always false fail " + a + " == " + c);
162         } else {
163             if (a != b) throw new RuntimeException("Substitutability test failed" + a + " != " + b);
164             if (a != c) throw new RuntimeException("Substitutability test failed");
165         }
166         if (a == a1) throw new RuntimeException();
167         checkEqual(a, a2, false);
168     }
169 
170     public void testAlot() {
171         MyValue a = new MyValue(4711);
172         Reference ref = new WeakReference<Object>(new Object(), new ReferenceQueue<>());
173         do {
174             for (int i = 0; i < 1000; i++) {
175                 MyValue b = new MyValue(4711);
176                 if (acmpModeInlineAlwaysFalse) {
177                     if (a == b) throw new RuntimeException("Always false fail " + a + " == " + b);
178                 } else {
179                     if (a != b) throw new RuntimeException("Substitutability test failed" + a + " != " + b);
180                 }
181             }
182             System.gc();
183         } while (ref.get() != null);
184     }
185 
186     boolean shouldEqualSelf(Object a) {
187         return acmpModeInlineAlwaysFalse ? (!(a != null && a.getClass().isValue())) : true;
188     }
189 
190     void checkEqual(Object a, Object b, boolean isEqual) {
191         testEquals(a, a, shouldEqualSelf(a));
192         testEquals(b, b, shouldEqualSelf(b));
193         testEquals(a, b, isEqual);
194         testNotEquals(a, b, !isEqual);
195     }
196 
197     public static void testEquals(Object a, Object b, boolean expected) {
198         boolean isEqual = (a == b);
199         if (isEqual != expected) {
200             throw new RuntimeException("Expected " + expected + " : "
201                                        + a + " == " + b);
202         }
203     }
204 
205     public static void testNotEquals(Object a, Object b, boolean expected) {
206         boolean isNotEqual = (a != b);
207         if (isNotEqual != expected) {
208             throw new RuntimeException("Expected " + expected + " : "
209                                        + a + " != " + b);
210         }
211     }
212 
213     public static void main(String[] args) {
214         boolean inlineTypesAlwaysFalse = (args.length > 0) && args[0].equals("alwaysFalse");
215         new Ifacmp(inlineTypesAlwaysFalse).test();
216         new Ifacmp(inlineTypesAlwaysFalse).testUntilGc(3);
217     }
218 }