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 }