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