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.LoweringTool;
34 import org.graalvm.compiler.options.OptionValues;
35 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
36 import org.graalvm.compiler.phases.common.LoweringPhase;
37 import org.graalvm.compiler.phases.tiers.PhaseContext;
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 
test1Snippet(double a)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 
test2Snippet(A a, int c)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
test01()97     public void test01() {
98         StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.NO);
99         testEarlyReadElimination(graph, 3, 2);
100     }
101 
102     @Test
test02()103     public void test02() {
104         StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.NO);
105         testPartialEscapeReadElimination(graph, 3, 2);
106     }
107 
108     @Test
test03()109     public void test03() {
110         StructuredGraph graph = parseEager("test2Snippet", AllowAssumptions.NO);
111         testEarlyReadElimination(graph, 3, 3);
112     }
113 
114     @Test
test04()115     public void test04() {
116         StructuredGraph graph = parseEager("test2Snippet", AllowAssumptions.NO);
117         testEarlyReadElimination(graph, 3, 3);
118     }
119 
testEarlyReadElimination(StructuredGraph graph, int reads, int writes)120     public void testEarlyReadElimination(StructuredGraph graph, int reads, int writes) {
121         PhaseContext 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 
testPartialEscapeReadElimination(StructuredGraph graph, int reads, int writes)134     public void testPartialEscapeReadElimination(StructuredGraph graph, int reads, int writes) {
135         OptionValues options = graph.getOptions();
136         PhaseContext 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 
testWriteIntToByteArraySnippet()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
testWriteIntToByteArray()156     public void testWriteIntToByteArray() {
157         test("testWriteIntToByteArraySnippet");
158     }
159 
testWriteSignedExtendedByteToByteArraySnippet(byte b)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
testWriteSignedExtendedByteToByteArray()171     public void testWriteSignedExtendedByteToByteArray() {
172         test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0);
173     }
174 
testWriteLongToIntArraySnippet()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
testWriteLongToIntArray()182     public void testWriteLongToIntArray() {
183         test("testWriteLongToIntArraySnippet");
184     }
185 
testWriteByteToIntArraySnippet()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
testWriteByteToIntArray()194     public void testWriteByteToIntArray() {
195         test("testWriteByteToIntArraySnippet");
196     }
197 
testWriteIntToLongArraySnippet()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
testWriteIntToLongArray()206     public void testWriteIntToLongArray() {
207         test("testWriteIntToLongArraySnippet");
208     }
209 
testWriteFloatToIntArraySnippet()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
testWriteFloatToIntArray()217     public void testWriteFloatToIntArray() {
218         test("testWriteFloatToIntArraySnippet");
219     }
220 
221     public static final byte[] FINAL_BYTE_ARRAY = new byte[16];
222 
alignedKill()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
testAlignedKill()233     public void testAlignedKill() {
234         test("alignedKill");
235     }
236 
unalignedKill()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
testUnalignedKill()247     public void testUnalignedKill() {
248         test("unalignedKill");
249     }
250 
251     public static final boolean[] FINAL_BOOLEAN_ARRAY = new boolean[16];
252 
killBooleanAccessToBooleanArrayViaBASTORE()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
testKillBooleanAccessToBooleanArrayViaBASTORE()263     public void testKillBooleanAccessToBooleanArrayViaBASTORE() {
264         test("killBooleanAccessToBooleanArrayViaBASTORE");
265     }
266 
killByteAccessToBooleanArrayViaBASTORE()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
testKillByteAccessToBooleanArrayViaBASTORE()277     public void testKillByteAccessToBooleanArrayViaBASTORE() {
278         test("killByteAccessToBooleanArrayViaBASTORE");
279     }
280 
unsafeWriteToBooleanArray()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
testUnsafeWriteToBooleanArray()290     public void testUnsafeWriteToBooleanArray() {
291         test("unsafeWriteToBooleanArray");
292     }
293 
294 }
295