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