1 /*
   2  * Copyright (c) 2014, 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 
  26 package org.graalvm.compiler.hotspot.amd64.test;
  27 
  28 import static jdk.vm.ci.code.ValueUtil.asRegister;
  29 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  30 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
  31 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
  32 
  33 import org.graalvm.compiler.asm.amd64.AMD64Address;
  34 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
  35 import org.graalvm.compiler.graph.NodeClass;
  36 import org.graalvm.compiler.hotspot.nodes.HotSpotCompressionNode;
  37 import org.graalvm.compiler.hotspot.test.HotSpotGraalCompilerTest;
  38 import org.graalvm.compiler.lir.LIRInstruction;
  39 import org.graalvm.compiler.lir.LIRInstructionClass;
  40 import org.graalvm.compiler.lir.Variable;
  41 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  42 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  43 import org.graalvm.compiler.nodeinfo.NodeInfo;
  44 import org.graalvm.compiler.nodes.FixedWithNextNode;
  45 import org.graalvm.compiler.nodes.NodeView;
  46 import org.graalvm.compiler.nodes.ValueNode;
  47 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  48 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  49 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  50 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  51 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  52 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  53 import org.graalvm.compiler.nodes.type.NarrowOopStamp;
  54 import org.junit.Assume;
  55 import org.junit.Before;
  56 import org.junit.Test;
  57 
  58 import jdk.vm.ci.amd64.AMD64;
  59 import jdk.vm.ci.meta.AllocatableValue;
  60 import jdk.vm.ci.meta.Constant;
  61 import jdk.vm.ci.meta.JavaKind;
  62 import jdk.vm.ci.meta.ResolvedJavaMethod;
  63 
  64 public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest {
  65 
  66     @Before
  67     public void checkAMD64() {
  68         Assume.assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64);
  69     }
  70 
  71     private static final Object object = new Object() {
  72 
  73         @Override
  74         public String toString() {
  75             return "testObject";
  76         }
  77     };
  78 
  79     private static Object loadThroughPatch(Object obj) {
  80         return obj;
  81     }
  82 
  83     public static Object oopSnippet() {
  84         Object patch = loadThroughPatch(object);
  85         if (object != patch) {
  86             return "invalid patch";
  87         }
  88         System.gc();
  89         patch = loadThroughPatch(object);
  90         if (object != patch) {
  91             return "failed after gc";
  92         }
  93         return patch;
  94     }
  95 
  96     @Test
  97     public void oopTest() {
  98         test("oopSnippet");
  99     }
 100 
 101     private static Object loadThroughCompressedPatch(Object obj) {
 102         return obj;
 103     }
 104 
 105     public static Object narrowOopSnippet() {
 106         Object patch = loadThroughCompressedPatch(object);
 107         if (object != patch) {
 108             return "invalid patch";
 109         }
 110         System.gc();
 111         patch = loadThroughCompressedPatch(object);
 112         if (object != patch) {
 113             return "failed after gc";
 114         }
 115         return patch;
 116     }
 117 
 118     @Test
 119     public void narrowOopTest() {
 120         Assume.assumeTrue("skipping narrow oop data patch test", runtime().getVMConfig().useCompressedOops);
 121         test("narrowOopSnippet");
 122     }
 123 
 124     public static Object compareSnippet() {
 125         Object uncompressed = loadThroughPatch(object);
 126         Object compressed = loadThroughCompressedPatch(object);
 127         if (object != uncompressed) {
 128             return "uncompressed failed";
 129         }
 130         if (object != compressed) {
 131             return "compressed failed";
 132         }
 133         if (uncompressed != compressed) {
 134             return "uncompressed != compressed";
 135         }
 136         return object;
 137     }
 138 
 139     @Test
 140     public void compareTest() {
 141         Assume.assumeTrue("skipping narrow oop data patch test", runtime().getVMConfig().useCompressedOops);
 142         test("compareSnippet");
 143     }
 144 
 145     @Override
 146     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
 147         Registration r = new Registration(invocationPlugins, DataPatchInConstantsTest.class);
 148 
 149         r.register1("loadThroughPatch", Object.class, new InvocationPlugin() {
 150             @Override
 151             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
 152                 b.addPush(JavaKind.Object, new LoadThroughPatchNode(arg));
 153                 return true;
 154             }
 155         });
 156 
 157         r.register1("loadThroughCompressedPatch", Object.class, new InvocationPlugin() {
 158             @Override
 159             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
 160                 ValueNode compressed = b.add(HotSpotCompressionNode.compress(arg, runtime().getVMConfig().getOopEncoding()));
 161                 ValueNode patch = b.add(new LoadThroughPatchNode(compressed));
 162                 b.addPush(JavaKind.Object, HotSpotCompressionNode.uncompress(patch, runtime().getVMConfig().getOopEncoding()));
 163                 return true;
 164             }
 165         });
 166         super.registerInvocationPlugins(invocationPlugins);
 167     }
 168 
 169     @NodeInfo(cycles = CYCLES_2, size = SIZE_1)
 170     private static final class LoadThroughPatchNode extends FixedWithNextNode implements LIRLowerable {
 171         public static final NodeClass<LoadThroughPatchNode> TYPE = NodeClass.create(LoadThroughPatchNode.class);
 172 
 173         @Input protected ValueNode input;
 174 
 175         protected LoadThroughPatchNode(ValueNode input) {
 176             super(TYPE, input.stamp(NodeView.DEFAULT));
 177             this.input = input;
 178         }
 179 
 180         @Override
 181         public void generate(NodeLIRBuilderTool generator) {
 182             assert input.isConstant();
 183 
 184             LIRGeneratorTool gen = generator.getLIRGeneratorTool();
 185             Variable ret = gen.newVariable(gen.getLIRKind(stamp(NodeView.DEFAULT)));
 186 
 187             gen.append(new LoadThroughPatchOp(input.asConstant(), stamp(NodeView.DEFAULT) instanceof NarrowOopStamp, ret));
 188             generator.setResult(this, ret);
 189         }
 190     }
 191 
 192     private static final class LoadThroughPatchOp extends LIRInstruction {
 193         public static final LIRInstructionClass<LoadThroughPatchOp> TYPE = LIRInstructionClass.create(LoadThroughPatchOp.class);
 194 
 195         final Constant c;
 196         final boolean compressed;
 197         @Def({REG}) AllocatableValue result;
 198 
 199         LoadThroughPatchOp(Constant c, boolean compressed, AllocatableValue result) {
 200             super(TYPE);
 201             this.c = c;
 202             this.compressed = compressed;
 203             this.result = result;
 204         }
 205 
 206         @Override
 207         public void emitCode(CompilationResultBuilder crb) {
 208             AMD64Address address = (AMD64Address) crb.recordDataReferenceInCode(c, compressed ? 4 : 8);
 209             AMD64MacroAssembler asm = (AMD64MacroAssembler) crb.asm;
 210             if (compressed) {
 211                 asm.movl(asRegister(result), address);
 212             } else {
 213                 asm.movq(asRegister(result), address);
 214             }
 215         }
 216     }
 217 }