1 /*
   2  * Copyright (c) 2011, 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.ea;
  26 
  27 import java.nio.ByteBuffer;
  28 
  29 import org.graalvm.compiler.api.directives.GraalDirectives;
  30 import org.graalvm.compiler.graph.Graph;
  31 import org.graalvm.compiler.graph.Node;
  32 import org.graalvm.compiler.nodes.NamedLocationIdentity;
  33 import org.graalvm.compiler.nodes.PhiNode;
  34 import org.graalvm.compiler.nodes.ValuePhiNode;
  35 import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode;
  36 import org.graalvm.compiler.nodes.extended.RawLoadNode;
  37 import org.graalvm.compiler.nodes.extended.RawStoreNode;
  38 import org.graalvm.compiler.nodes.extended.UnsafeAccessNode;
  39 import org.graalvm.compiler.nodes.java.LoadFieldNode;
  40 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  41 import org.junit.Assert;
  42 import org.junit.Test;
  43 
  44 import jdk.vm.ci.meta.JavaConstant;
  45 import jdk.vm.ci.meta.JavaKind;
  46 import jdk.vm.ci.meta.ResolvedJavaMethod;
  47 
  48 public class UnsafeEATest extends EATestBase {
  49 
  50     public static int zero = 0;
  51 
  52     private static final long fieldOffset1;
  53     private static final long fieldOffset2;
  54 
  55     static {
  56         try {
  57             long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x"));
  58             // Make the fields 8 byte aligned (Required for testing setLong on Architectures which
  59             // does not support unaligned memory access
  60             if (localFieldOffset1 % 8 == 0) {
  61                 fieldOffset1 = localFieldOffset1;
  62                 fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("y"));
  63             } else {
  64                 fieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("y"));
  65                 fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z"));
  66             }
  67             assert fieldOffset2 == fieldOffset1 + 4;
  68         } catch (Exception e) {
  69             throw new RuntimeException(e);
  70         }
  71     }
  72 
  73     @Override
  74     protected void testEscapeAnalysis(String snippet, JavaConstant expectedConstantResult, boolean iterativeEscapeAnalysis) {
  75         // Exercise both a graph containing UnsafeAccessNodes and one which has been possibly been
  76         // canonicalized into AccessFieldNodes.
  77         testingUnsafe = true;
  78         super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis);
  79         testingUnsafe = false;
  80         super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis);
  81         if (expectedConstantResult != null) {
  82             // Check that a compiled version of this method returns the same value if we expect a
  83             // constant result.
  84             ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
  85             JavaKind[] javaKinds = method.getSignature().toParameterKinds(false);
  86             Object[] args = new Object[javaKinds.length];
  87             int i = 0;
  88             for (JavaKind k : javaKinds) {
  89                 args[i++] = JavaConstant.defaultForKind(k).asBoxedPrimitive();
  90             }
  91             Result result = executeExpected(method, null, args);
  92             assertTrue(result.returnValue.equals(expectedConstantResult.asBoxedPrimitive()));
  93         }
  94     }
  95 
  96     @Override
  97     protected void canonicalizeGraph() {
  98         if (testingUnsafe) {
  99             // For testing purposes we'd like to ensure that our raw unsafe operations stay as
 100             // unsafe nodes, so force them to appear to have LocationIdentity.any to disable
 101             // transformation into field access nodes.
 102             for (Node node : graph.getNodes().filter(x -> x instanceof UnsafeAccessNode).snapshot()) {
 103                 if (node instanceof RawStoreNode) {
 104                     RawStoreNode store = (RawStoreNode) node;
 105                     RawStoreNode newStore = graph.add(new RawStoreNode(store.object(), store.offset(), store.value(), store.accessKind(), NamedLocationIdentity.any(),
 106                                     store.needsBarrier(), store.stateAfter(), true));
 107                     graph.replaceFixedWithFixed(store, newStore);
 108                 } else if (node instanceof RawLoadNode) {
 109                     RawLoadNode load = (RawLoadNode) node;
 110                     RawLoadNode newLoad = graph.add(new RawLoadNode(load.object(), load.offset(), load.accessKind(), NamedLocationIdentity.any(),
 111                                     true));
 112                     graph.replaceFixedWithFixed(load, newLoad);
 113                 }
 114             }
 115         }
 116         super.canonicalizeGraph();
 117     }
 118 
 119     @Override
 120     protected void postEACanonicalizeGraph() {
 121         // Simplify any UnpackEndianHalfNode so we end up with constants.
 122         Graph.Mark mark = graph.getMark();
 123         for (UnpackEndianHalfNode node : graph.getNodes().filter(UnpackEndianHalfNode.class)) {
 124             node.lower(getTarget().arch.getByteOrder());
 125         }
 126         new CanonicalizerPhase().applyIncremental(graph, context, mark);
 127     }
 128 
 129     private boolean testingUnsafe;
 130 
 131     @Test
 132     public void testSimpleInt() {
 133         testEscapeAnalysis("testSimpleIntSnippet", JavaConstant.forInt(101), false);
 134     }
 135 
 136     public static int testSimpleIntSnippet() {
 137         TestClassInt x = new TestClassInt();
 138         UNSAFE.putInt(x, fieldOffset1, 101);
 139         return UNSAFE.getInt(x, fieldOffset1);
 140     }
 141 
 142     @Test
 143     public void testMaterializedInt() {
 144         test("testMaterializedIntSnippet");
 145     }
 146 
 147     public static TestClassInt testMaterializedIntSnippet() {
 148         TestClassInt x = new TestClassInt();
 149         UNSAFE.putInt(x, fieldOffset1, 101);
 150         return x;
 151     }
 152 
 153     @Test
 154     public void testSimpleDouble() {
 155         testEscapeAnalysis("testSimpleDoubleSnippet", JavaConstant.forDouble(10.1), false);
 156     }
 157 
 158     public static double testSimpleDoubleSnippet() {
 159         TestClassInt x = new TestClassInt();
 160         UNSAFE.putDouble(x, fieldOffset1, 10.1);
 161         return UNSAFE.getDouble(x, fieldOffset1);
 162     }
 163 
 164     @Test
 165     public void testSimpleDoubleOverwriteWithInt() {
 166         testEscapeAnalysis("testSimpleDoubleOverwriteWithIntSnippet", JavaConstant.forInt(10), false);
 167     }
 168 
 169     public static int testSimpleDoubleOverwriteWithIntSnippet() {
 170         TestClassInt x = new TestClassInt();
 171         UNSAFE.putDouble(x, fieldOffset1, 10.1);
 172         UNSAFE.putInt(x, fieldOffset1, 10);
 173         return UNSAFE.getInt(x, fieldOffset1);
 174     }
 175 
 176     @Test
 177     public void testSimpleDoubleOverwriteWithSecondInt() {
 178         ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder());
 179         bb.putDouble(10.1);
 180         int value = bb.getInt(4);
 181 
 182         testEscapeAnalysis("testSimpleDoubleOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false);
 183     }
 184 
 185     public static int testSimpleDoubleOverwriteWithSecondIntSnippet() {
 186         TestClassInt x = new TestClassInt();
 187         UNSAFE.putDouble(x, fieldOffset1, 10.1);
 188         UNSAFE.putInt(x, fieldOffset1, 10);
 189         return UNSAFE.getInt(x, fieldOffset2);
 190     }
 191 
 192     @Test
 193     public void testSimpleDoubleOverwriteWithFirstInt() {
 194         ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder());
 195         bb.putDouble(10.1);
 196         int value = bb.getInt(0);
 197 
 198         testEscapeAnalysis("testSimpleDoubleOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false);
 199     }
 200 
 201     public static int testSimpleDoubleOverwriteWithFirstIntSnippet() {
 202         TestClassInt x = new TestClassInt();
 203         UNSAFE.putDouble(x, fieldOffset1, 10.1);
 204         UNSAFE.putInt(x, fieldOffset2, 10);
 205         return UNSAFE.getInt(x, fieldOffset1);
 206     }
 207 
 208     @Test
 209     public void testSimpleLongOverwriteWithSecondInt() {
 210         ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder());
 211         bb.putLong(0, 0x1122334455667788L);
 212         int value = bb.getInt(4);
 213 
 214         testEscapeAnalysis("testSimpleLongOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false);
 215     }
 216 
 217     public static int testSimpleLongOverwriteWithSecondIntSnippet() {
 218         TestClassInt x = new TestClassInt();
 219         UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L);
 220         UNSAFE.putInt(x, fieldOffset1, 10);
 221         return UNSAFE.getInt(x, fieldOffset2);
 222     }
 223 
 224     @Test
 225     public void testSimpleLongOverwriteWithFirstInt() {
 226         ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder());
 227         bb.putLong(0, 0x1122334455667788L);
 228         int value = bb.getInt(0);
 229 
 230         testEscapeAnalysis("testSimpleLongOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false);
 231     }
 232 
 233     public static int testSimpleLongOverwriteWithFirstIntSnippet() {
 234         TestClassInt x = new TestClassInt();
 235         UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L);
 236         UNSAFE.putInt(x, fieldOffset2, 10);
 237         return UNSAFE.getInt(x, fieldOffset1);
 238     }
 239 
 240     @Test
 241     public void testMergedDouble() {
 242         testEscapeAnalysis("testMergedDoubleSnippet", null, false);
 243         Assert.assertEquals(1, returnNodes.size());
 244         Assert.assertTrue(returnNodes.get(0).result() instanceof ValuePhiNode);
 245         PhiNode phi = (PhiNode) returnNodes.get(0).result();
 246         Assert.assertTrue(phi.valueAt(0) instanceof LoadFieldNode);
 247         Assert.assertTrue(phi.valueAt(1) instanceof LoadFieldNode);
 248     }
 249 
 250     public static double testMergedDoubleSnippet(boolean a) {
 251         TestClassInt x;
 252         if (a) {
 253             x = new TestClassInt(0, 0);
 254             UNSAFE.putDouble(x, fieldOffset1, doubleField);
 255         } else {
 256             x = new TestClassInt();
 257             UNSAFE.putDouble(x, fieldOffset1, doubleField2);
 258         }
 259         return UNSAFE.getDouble(x, fieldOffset1);
 260     }
 261 
 262     static class ExtendedTestClassInt extends TestClassInt {
 263         public long l;
 264     }
 265 
 266     @Test
 267     public void testMergedVirtualObjects() {
 268         testEscapeAnalysis("testMergedVirtualObjectsSnippet", null, false);
 269     }
 270 
 271     public static TestClassInt testMergedVirtualObjectsSnippet(int value) {
 272         TestClassInt x;
 273         if (value == 1) {
 274             x = new TestClassInt();
 275             UNSAFE.putDouble(x, fieldOffset1, 10);
 276         } else {
 277             x = new TestClassInt();
 278             UNSAFE.putInt(x, fieldOffset1, 0);
 279         }
 280         UNSAFE.putInt(x, fieldOffset1, 0);
 281         if (value == 2) {
 282             UNSAFE.putInt(x, fieldOffset2, 0);
 283         }
 284         GraalDirectives.deoptimizeAndInvalidate();
 285         return x;
 286     }
 287 
 288     @Test
 289     public void testMaterializedDouble() {
 290         test("testMaterializedDoubleSnippet");
 291     }
 292 
 293     public static TestClassInt testMaterializedDoubleSnippet() {
 294         TestClassInt x = new TestClassInt();
 295         UNSAFE.putDouble(x, fieldOffset1, 10.1);
 296         return x;
 297     }
 298 
 299     @Test
 300     public void testDeoptDoubleVar() {
 301         test("testDeoptDoubleVarSnippet");
 302     }
 303 
 304     public static double doubleField = 10.1e99;
 305     public static double doubleField2;
 306 
 307     public static TestClassInt testDeoptDoubleVarSnippet() {
 308         TestClassInt x = new TestClassInt();
 309         UNSAFE.putDouble(x, fieldOffset1, doubleField);
 310         doubleField2 = 123;
 311         try {
 312             doubleField = ((int) UNSAFE.getDouble(x, fieldOffset1)) / zero;
 313         } catch (RuntimeException e) {
 314             return x;
 315         }
 316         return x;
 317     }
 318 
 319     @Test
 320     public void testDeoptDoubleConstant() {
 321         test("testDeoptDoubleConstantSnippet");
 322     }
 323 
 324     public static TestClassInt testDeoptDoubleConstantSnippet() {
 325         TestClassInt x = new TestClassInt();
 326         UNSAFE.putDouble(x, fieldOffset1, 10.123);
 327         doubleField2 = 123;
 328         try {
 329             doubleField = ((int) UNSAFE.getDouble(x, fieldOffset1)) / zero;
 330         } catch (RuntimeException e) {
 331             return x;
 332         }
 333         return x;
 334     }
 335 
 336     @Test
 337     public void testDeoptLongVar() {
 338         test("testDeoptLongVarSnippet");
 339     }
 340 
 341     public static long longField = 0x133443218aaaffffL;
 342     public static long longField2;
 343 
 344     public static TestClassInt testDeoptLongVarSnippet() {
 345         TestClassInt x = new TestClassInt();
 346         UNSAFE.putLong(x, fieldOffset1, longField);
 347         longField2 = 123;
 348         try {
 349             longField = UNSAFE.getLong(x, fieldOffset1) / zero;
 350         } catch (RuntimeException e) {
 351             return x;
 352         }
 353         return x;
 354     }
 355 
 356     @Test
 357     public void testDeoptLongConstant() {
 358         test("testDeoptLongConstantSnippet");
 359     }
 360 
 361     public static TestClassInt testDeoptLongConstantSnippet() {
 362         TestClassInt x = new TestClassInt();
 363         UNSAFE.putLong(x, fieldOffset1, 0x2222222210123L);
 364         longField2 = 123;
 365         try {
 366             longField = UNSAFE.getLong(x, fieldOffset1) / zero;
 367         } catch (RuntimeException e) {
 368             return x;
 369         }
 370         return x;
 371     }
 372 
 373 }