1 /* 2 * Copyright (c) 2016, 2018, 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 24 25 package org.graalvm.compiler.core.test; 26 27 import org.graalvm.compiler.api.directives.GraalDirectives; 28 import org.graalvm.compiler.nodes.StructuredGraph; 29 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 30 import org.graalvm.compiler.nodes.extended.UnsafeAccessNode; 31 import org.graalvm.compiler.nodes.memory.ReadNode; 32 import org.graalvm.compiler.nodes.memory.WriteNode; 33 import org.graalvm.compiler.nodes.spi.CoreProviders; 34 import org.graalvm.compiler.nodes.spi.LoweringTool; 35 import org.graalvm.compiler.options.OptionValues; 36 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 37 import org.graalvm.compiler.phases.common.LoweringPhase; 38 import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase; 39 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; 40 import org.junit.Assert; 41 import org.junit.Test; 42 43 import sun.misc.Unsafe; 44 45 public class UnsafeReadEliminationTest extends GraalCompilerTest { 46 47 public static long[] Memory = new long[]{1, 2}; 48 public static double SideEffectD; 49 public static double SideEffectL; 50 51 private static final long booleanArrayBaseOffset; 52 private static final long byteArrayBaseOffset; 53 private static final long intArrayBaseOffset; 54 private static final long longArrayBaseOffset; 55 56 static { 57 booleanArrayBaseOffset = UNSAFE.arrayBaseOffset(boolean[].class); 58 byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class); 59 intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class); 60 longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class); 61 } 62 63 private static final long ARRAY_LONG_BASE_OFFSET = Unsafe.ARRAY_LONG_BASE_OFFSET; 64 65 public static long test1Snippet(double a) { 66 final Object m = Memory; 67 if (a > 0) { 68 UNSAFE.putDouble(m, ARRAY_LONG_BASE_OFFSET, a); 69 } else { 70 SideEffectL = UNSAFE.getLong(m, ARRAY_LONG_BASE_OFFSET); 71 } 72 return UNSAFE.getLong(m, ARRAY_LONG_BASE_OFFSET); 73 } 74 75 public static class A { 76 long[][] o; 77 long[][] p; 78 } 79 80 public static Object test2Snippet(A a, int c) { 81 Object phi = null; 82 if (c != 0) { 83 long[][] r = a.o; 84 phi = r; 85 UNSAFE.putDouble(r, ARRAY_LONG_BASE_OFFSET, 12d); 86 } else { 87 long[][] r = a.p; 88 phi = r; 89 UNSAFE.putLong(r, ARRAY_LONG_BASE_OFFSET, 123); 90 } 91 GraalDirectives.controlFlowAnchor(); 92 SideEffectD = UNSAFE.getDouble(phi, ARRAY_LONG_BASE_OFFSET); 93 return phi; 94 } 95 96 @Test 97 public void test01() { 98 StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.NO); 99 testEarlyReadElimination(graph, 3, 2); 100 } 101 102 @Test 103 public void test02() { 104 StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.NO); 105 testPartialEscapeReadElimination(graph, 3, 2); 106 } 107 108 @Test 109 public void test03() { 110 StructuredGraph graph = parseEager("test2Snippet", AllowAssumptions.NO); 111 testEarlyReadElimination(graph, 3, 3); 112 } 113 114 @Test 115 public void test04() { 116 StructuredGraph graph = parseEager("test2Snippet", AllowAssumptions.NO); 117 testEarlyReadElimination(graph, 3, 3); 118 } 119 120 public void testEarlyReadElimination(StructuredGraph graph, int reads, int writes) { 121 CoreProviders context = getDefaultHighTierContext(); 122 CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); 123 canonicalizer.apply(graph, context); 124 new EarlyReadEliminationPhase(canonicalizer).apply(graph, context); 125 Assert.assertEquals(3, graph.getNodes().filter(UnsafeAccessNode.class).count()); 126 // after lowering the same applies for reads and writes 127 new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context); 128 canonicalizer.apply(graph, context); 129 new EarlyReadEliminationPhase(canonicalizer).apply(graph, context); 130 Assert.assertEquals(reads, graph.getNodes().filter(ReadNode.class).count()); 131 Assert.assertEquals(writes, graph.getNodes().filter(WriteNode.class).count()); 132 } 133 134 public void testPartialEscapeReadElimination(StructuredGraph graph, int reads, int writes) { 135 OptionValues options = graph.getOptions(); 136 CoreProviders context = getDefaultHighTierContext(); 137 CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); 138 canonicalizer.apply(graph, context); 139 new PartialEscapePhase(true, true, canonicalizer, null, options).apply(graph, context); 140 Assert.assertEquals(3, graph.getNodes().filter(UnsafeAccessNode.class).count()); 141 // after lowering the same applies for reads and writes 142 new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context); 143 canonicalizer.apply(graph, context); 144 new PartialEscapePhase(true, true, canonicalizer, null, options).apply(graph, context); 145 Assert.assertEquals(reads, graph.getNodes().filter(ReadNode.class).count()); 146 Assert.assertEquals(writes, graph.getNodes().filter(WriteNode.class).count()); 147 } 148 149 public static int testWriteIntToByteArraySnippet() { 150 byte[] array = new byte[4]; 151 UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304); 152 return array[0]; 153 } 154 155 @Test 156 public void testWriteIntToByteArray() { 157 test("testWriteIntToByteArraySnippet"); 158 } 159 160 public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) { 161 byte[] array = new byte[4]; 162 array[0] = 0x01; 163 array[1] = 0x02; 164 array[2] = 0x03; 165 array[3] = 0x04; 166 UNSAFE.putInt(array, byteArrayBaseOffset, b); 167 return array[3]; 168 } 169 170 @Test 171 public void testWriteSignedExtendedByteToByteArray() { 172 test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0); 173 } 174 175 public static int testWriteLongToIntArraySnippet() { 176 int[] array = new int[2]; 177 UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L); 178 return array[0]; 179 } 180 181 @Test 182 public void testWriteLongToIntArray() { 183 test("testWriteLongToIntArraySnippet"); 184 } 185 186 public static int testWriteByteToIntArraySnippet() { 187 int[] array = new int[1]; 188 array[0] = 0x01020304; 189 UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05); 190 return array[0]; 191 } 192 193 @Test 194 public void testWriteByteToIntArray() { 195 test("testWriteByteToIntArraySnippet"); 196 } 197 198 public static long testWriteIntToLongArraySnippet() { 199 long[] array = new long[1]; 200 array[0] = 0x0102030405060708L; 201 UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201); 202 return array[0]; 203 } 204 205 @Test 206 public void testWriteIntToLongArray() { 207 test("testWriteIntToLongArraySnippet"); 208 } 209 210 public static float testWriteFloatToIntArraySnippet() { 211 float[] array = new float[1]; 212 UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f)); 213 return array[0]; 214 } 215 216 @Test 217 public void testWriteFloatToIntArray() { 218 test("testWriteFloatToIntArraySnippet"); 219 } 220 221 public static final byte[] FINAL_BYTE_ARRAY = new byte[16]; 222 223 public static boolean alignedKill() { 224 int beforeKill = UNSAFE.getInt(FINAL_BYTE_ARRAY, byteArrayBaseOffset); 225 FINAL_BYTE_ARRAY[0] = 1; 226 int afterKill = UNSAFE.getInt(FINAL_BYTE_ARRAY, byteArrayBaseOffset); 227 228 FINAL_BYTE_ARRAY[0] = 0; // reset 229 return beforeKill == afterKill; 230 } 231 232 @Test 233 public void testAlignedKill() { 234 test("alignedKill"); 235 } 236 237 public static boolean unalignedKill() { 238 int beforeKill = UNSAFE.getInt(FINAL_BYTE_ARRAY, byteArrayBaseOffset); 239 FINAL_BYTE_ARRAY[1] = 1; 240 int afterKill = UNSAFE.getInt(FINAL_BYTE_ARRAY, byteArrayBaseOffset); 241 242 FINAL_BYTE_ARRAY[1] = 0; // reset 243 return beforeKill == afterKill; 244 } 245 246 @Test 247 public void testUnalignedKill() { 248 test("unalignedKill"); 249 } 250 251 public static final boolean[] FINAL_BOOLEAN_ARRAY = new boolean[16]; 252 253 public static boolean killBooleanAccessToBooleanArrayViaBASTORE() { 254 boolean beforeKill = UNSAFE.getBoolean(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset); 255 FINAL_BOOLEAN_ARRAY[0] = true; 256 boolean afterKill = UNSAFE.getBoolean(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset); 257 258 FINAL_BOOLEAN_ARRAY[0] = false; // reset 259 return beforeKill == afterKill; 260 } 261 262 @Test 263 public void testKillBooleanAccessToBooleanArrayViaBASTORE() { 264 test("killBooleanAccessToBooleanArrayViaBASTORE"); 265 } 266 267 public static boolean killByteAccessToBooleanArrayViaBASTORE() { 268 byte beforeKill = UNSAFE.getByte(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset); 269 FINAL_BOOLEAN_ARRAY[0] = true; 270 byte afterKill = UNSAFE.getByte(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset); 271 272 FINAL_BOOLEAN_ARRAY[0] = false; // reset 273 return beforeKill == afterKill; 274 } 275 276 @Test 277 public void testKillByteAccessToBooleanArrayViaBASTORE() { 278 test("killByteAccessToBooleanArrayViaBASTORE"); 279 } 280 281 public static boolean unsafeWriteToBooleanArray() { 282 UNSAFE.putByte(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset, (byte) 2); 283 boolean result = UNSAFE.getBoolean(FINAL_BOOLEAN_ARRAY, booleanArrayBaseOffset); 284 285 FINAL_BOOLEAN_ARRAY[0] = false; // reset 286 return result; 287 } 288 289 @Test 290 public void testUnsafeWriteToBooleanArray() { 291 test("unsafeWriteToBooleanArray"); 292 } 293 294 }