1 /*
   2  * Copyright (c) 2015, 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.replacements.test;
  26 
  27 import java.util.Objects;
  28 
  29 import org.graalvm.compiler.api.directives.GraalDirectives;
  30 import org.graalvm.compiler.debug.DebugContext;
  31 import org.graalvm.compiler.debug.DebugContext.Scope;
  32 import org.graalvm.compiler.debug.GraalError;
  33 import org.graalvm.compiler.nodes.StructuredGraph;
  34 import org.graalvm.compiler.nodes.ValueNode;
  35 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  36 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  37 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  38 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  39 import org.graalvm.compiler.replacements.Snippets;
  40 import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
  41 import org.graalvm.compiler.word.Word;
  42 import org.graalvm.compiler.word.WordCastNode;
  43 import org.junit.Assert;
  44 import org.junit.Rule;
  45 import org.junit.Test;
  46 import org.junit.rules.ExpectedException;
  47 
  48 import jdk.vm.ci.meta.ResolvedJavaMethod;
  49 
  50 /**
  51  * Tests for derived oops in reference maps.
  52  */
  53 public class DerivedOopTest extends ReplacementsTest implements Snippets {
  54 
  55     private static class Pointers {
  56         public long basePointer;
  57         public long internalPointer;
  58 
  59         public long delta() {
  60             return internalPointer - basePointer;
  61         }
  62 
  63         @Override
  64         public boolean equals(Object obj) {
  65             if (!(obj instanceof Pointers)) {
  66                 return false;
  67             }
  68 
  69             Pointers other = (Pointers) obj;
  70             return this.delta() == other.delta();
  71         }
  72 
  73         @Override
  74         public int hashCode() {
  75             return (int) delta();
  76         }
  77     }
  78 
  79     private static class Result {
  80         public Pointers beforeGC;
  81         public Pointers afterGC;
  82 
  83         Result() {
  84             beforeGC = new Pointers();
  85             afterGC = new Pointers();
  86         }
  87 
  88         @Override
  89         public int hashCode() {
  90             final int prime = 31;
  91             int result = 1;
  92             result = prime * result + ((afterGC == null) ? 0 : afterGC.hashCode());
  93             result = prime * result + ((beforeGC == null) ? 0 : beforeGC.hashCode());
  94             return result;
  95         }
  96 
  97         @Override
  98         public boolean equals(Object obj) {
  99             if (!(obj instanceof Result)) {
 100                 return false;
 101             }
 102             Result other = (Result) obj;
 103             return Objects.equals(this.beforeGC, other.beforeGC) && Objects.equals(this.afterGC, other.afterGC);
 104         }
 105     }
 106 
 107     @Test
 108     public void testFieldOffset() {
 109         // Run a couple times to encourage objects to move
 110         for (int i = 0; i < 4; i++) {
 111             Result r = new Result();
 112             test("fieldOffsetSnippet", r, 16L);
 113 
 114             Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
 115         }
 116     }
 117 
 118     static long getRawPointer(Object obj) {
 119         // fake implementation for interpreter
 120         return obj.hashCode();
 121     }
 122 
 123     static long getRawPointerIntrinsic(Object obj) {
 124         return Word.objectToTrackedPointer(obj).rawValue();
 125     }
 126 
 127     public static Result fieldOffsetSnippet(Result obj, long offset) {
 128         long internalPointer = getRawPointer(obj) + offset;
 129 
 130         // make sure the internal pointer is computed before the safepoint
 131         GraalDirectives.blackhole(internalPointer);
 132 
 133         obj.beforeGC.basePointer = getRawPointer(obj);
 134         obj.beforeGC.internalPointer = internalPointer;
 135 
 136         System.gc();
 137 
 138         obj.afterGC.basePointer = getRawPointer(obj);
 139         obj.afterGC.internalPointer = internalPointer;
 140 
 141         return obj;
 142     }
 143 
 144     @Rule public final ExpectedException thrown = ExpectedException.none();
 145     private static final String UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG = "should not reach here: unknown reference alive across safepoint";
 146 
 147     @Test
 148     @SuppressWarnings("try")
 149     public void testFieldOffsetMergeNonLiveBasePointer() {
 150         thrown.expect(GraalError.class);
 151         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
 152         DebugContext debug = getDebugContext();
 153         try (Scope s = debug.disable()) {
 154             // Run a couple times to encourage objects to move
 155             for (int i = 0; i < 4; i++) {
 156                 Result r = new Result();
 157                 test("fieldOffsetMergeSnippet01", r, 8L, 16L);
 158                 Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
 159             }
 160         }
 161     }
 162 
 163     @Test
 164     public void testFieldOffsetMergeNonLiveBasePointerNotAccrossSafepoint() {
 165         // Run a couple times to encourage objects to move
 166         for (int i = 0; i < 4; i++) {
 167             Result r = new Result();
 168             test("fieldOffsetMergeSnippet02", r, 8L, 16L);
 169         }
 170     }
 171 
 172     @Test
 173     @SuppressWarnings("try")
 174     public void testFieldOffsetMergeLiveBasePointer() {
 175         thrown.expect(GraalError.class);
 176         thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
 177         DebugContext debug = getDebugContext();
 178         try (Scope s = debug.disable()) {
 179             // Run a couple times to encourage objects to move
 180             for (int i = 0; i < 4; i++) {
 181                 Result r = new Result();
 182                 test("fieldOffsetMergeSnippet03", r, new Result(), new Result(), 8L, 16L);
 183                 Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
 184             }
 185         }
 186     }
 187 
 188     public static boolean SideEffectB;
 189     public static long SideEffect1 = 16;
 190     public static long SideEffect2 = 16;
 191     public static Object o1 = new Result();
 192     public static Object o2 = o1;
 193 
 194     public static Result fieldOffsetMergeSnippet01(Result objResult, long offsetA, long offsetB) {
 195         long internalPointer;
 196         if (SideEffectB) {
 197             internalPointer = getRawPointer(o1) + offsetA;
 198             SideEffect1 = internalPointer;
 199         } else {
 200             internalPointer = getRawPointer(o2) + offsetB;
 201             SideEffect2 = internalPointer;
 202         }
 203         GraalDirectives.controlFlowAnchor();
 204         // make sure the internal pointer is computed before the safepoint
 205         GraalDirectives.blackhole(internalPointer);
 206         objResult.beforeGC.basePointer = getRawPointer(objResult);
 207         objResult.beforeGC.internalPointer = internalPointer;
 208         System.gc();
 209         objResult.afterGC.basePointer = getRawPointer(objResult);
 210         objResult.afterGC.internalPointer = internalPointer;
 211         return objResult;
 212     }
 213 
 214     public static Result fieldOffsetMergeSnippet02(Result objResult, long offsetA, long offsetB) {
 215         long internalPointer;
 216         if (SideEffectB) {
 217             internalPointer = getRawPointer(o1) + offsetA;
 218             SideEffect1 = internalPointer;
 219         } else {
 220             internalPointer = getRawPointer(o2) + offsetB;
 221             SideEffect2 = internalPointer;
 222         }
 223         GraalDirectives.controlFlowAnchor();
 224         // make sure the internal pointer is computed before the safepoint
 225         GraalDirectives.blackhole(internalPointer);
 226         objResult.beforeGC.basePointer = getRawPointer(objResult);
 227         objResult.beforeGC.internalPointer = internalPointer;
 228         objResult.afterGC.basePointer = getRawPointer(objResult);
 229         objResult.afterGC.internalPointer = internalPointer;
 230         return objResult;
 231     }
 232 
 233     public static Result fieldOffsetMergeSnippet03(Result objResult, Result a, Result b, long offsetA, long offsetB) {
 234         long internalPointer;
 235         if (SideEffectB) {
 236             internalPointer = getRawPointer(a) + offsetA;
 237             SideEffect1 = internalPointer;
 238         } else {
 239             internalPointer = getRawPointer(b) + offsetB;
 240             SideEffect2 = internalPointer;
 241         }
 242         GraalDirectives.controlFlowAnchor();
 243         // make sure the internal pointer is computed before the safepoint
 244         GraalDirectives.blackhole(internalPointer);
 245         objResult.beforeGC.basePointer = getRawPointer(objResult);
 246         objResult.beforeGC.internalPointer = internalPointer;
 247         System.gc();
 248         objResult.afterGC.basePointer = getRawPointer(objResult);
 249         objResult.afterGC.internalPointer = internalPointer;
 250         return objResult;
 251     }
 252 
 253     @Override
 254     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
 255         Registration r = new Registration(invocationPlugins, DerivedOopTest.class);
 256         ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
 257 
 258         ResolvedJavaMethod intrinsic = getResolvedJavaMethod("getRawPointerIntrinsic");
 259         r.register1("getRawPointer", Object.class, new InvocationPlugin() {
 260             @Override
 261             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
 262                 return b.intrinsify(bytecodeProvider, targetMethod, intrinsic, receiver, new ValueNode[]{arg});
 263             }
 264         });
 265         super.registerInvocationPlugins(invocationPlugins);
 266     }
 267 
 268     @Override
 269     protected void checkHighTierGraph(StructuredGraph graph) {
 270         assert graph.getNodes().filter(WordCastNode.class).count() > 0 : "DerivedOopTest.toLong should be intrinsified";
 271         super.checkHighTierGraph(graph);
 272     }
 273 }