1 /*
   2  * Copyright (c) 2014, 2015, 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 package org.graalvm.compiler.hotspot.amd64.test;
  25 
  26 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
  29 import static jdk.vm.ci.code.ValueUtil.asRegister;
  30 
  31 import org.junit.Assume;
  32 import org.junit.Before;
  33 import org.junit.Test;
  34 
  35 import org.graalvm.compiler.asm.amd64.AMD64Address;
  36 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
  37 import org.graalvm.compiler.graph.NodeClass;
  38 import org.graalvm.compiler.hotspot.nodes.CompressionNode;
  39 import org.graalvm.compiler.hotspot.nodes.type.NarrowOopStamp;
  40 import org.graalvm.compiler.hotspot.test.HotSpotGraalCompilerTest;
  41 import org.graalvm.compiler.lir.LIRInstruction;
  42 import org.graalvm.compiler.lir.LIRInstructionClass;
  43 import org.graalvm.compiler.lir.Variable;
  44 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  45 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  46 import org.graalvm.compiler.nodeinfo.NodeInfo;
  47 import org.graalvm.compiler.nodes.FixedWithNextNode;
  48 import org.graalvm.compiler.nodes.ValueNode;
  49 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
  50 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  51 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  52 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  53 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  54 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  55 
  56 import jdk.vm.ci.amd64.AMD64;
  57 import jdk.vm.ci.meta.AllocatableValue;
  58 import jdk.vm.ci.meta.Constant;
  59 import jdk.vm.ci.meta.JavaKind;
  60 import jdk.vm.ci.meta.ResolvedJavaMethod;
  61 
  62 public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest {
  63 
  64     @Before
  65     public void checkAMD64() {
  66         Assume.assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64);
  67     }
  68 
  69     private static final Object object = new Object() {
  70 
  71         @Override
  72         public String toString() {
  73             return "testObject";
  74         }
  75     };
  76 
  77     private static Object loadThroughPatch(Object obj) {
  78         return obj;
  79     }
  80 
  81     public static Object oopSnippet() {
  82         Object patch = loadThroughPatch(object);
  83         if (object != patch) {
  84             return "invalid patch";
  85         }
  86         System.gc();
  87         patch = loadThroughPatch(object);
  88         if (object != patch) {
  89             return "failed after gc";
  90         }
  91         return patch;
  92     }
  93 
  94     @Test
  95     public void oopTest() {
  96         test("oopSnippet");
  97     }
  98 
  99     private static Object loadThroughCompressedPatch(Object obj) {
 100         return obj;
 101     }
 102 
 103     public static Object narrowOopSnippet() {
 104         Object patch = loadThroughCompressedPatch(object);
 105         if (object != patch) {
 106             return "invalid patch";
 107         }
 108         System.gc();
 109         patch = loadThroughCompressedPatch(object);
 110         if (object != patch) {
 111             return "failed after gc";
 112         }
 113         return patch;
 114     }
 115 
 116     @Test
 117     public void narrowOopTest() {
 118         Assume.assumeTrue("skipping narrow oop data patch test", runtime().getVMConfig().useCompressedOops);
 119         test("narrowOopSnippet");
 120     }
 121 
 122     public static Object compareSnippet() {
 123         Object uncompressed = loadThroughPatch(object);
 124         Object compressed = loadThroughCompressedPatch(object);
 125         if (object != uncompressed) {
 126             return "uncompressed failed";
 127         }
 128         if (object != compressed) {
 129             return "compressed failed";
 130         }
 131         if (uncompressed != compressed) {
 132             return "uncompressed != compressed";
 133         }
 134         return object;
 135     }
 136 
 137     @Test
 138     public void compareTest() {
 139         Assume.assumeTrue("skipping narrow oop data patch test", runtime().getVMConfig().useCompressedOops);
 140         test("compareSnippet");
 141     }
 142 
 143     @Override
 144     protected Plugins getDefaultGraphBuilderPlugins() {
 145         Plugins plugins = super.getDefaultGraphBuilderPlugins();
 146         Registration r = new Registration(plugins.getInvocationPlugins(), DataPatchInConstantsTest.class);
 147 
 148         r.register1("loadThroughPatch", Object.class, new InvocationPlugin() {
 149             @Override
 150             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
 151                 b.addPush(JavaKind.Object, new LoadThroughPatchNode(arg));
 152                 return true;
 153             }
 154         });
 155 
 156         r.register1("loadThroughCompressedPatch", Object.class, new InvocationPlugin() {
 157             @Override
 158             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
 159                 ValueNode compressed = b.add(CompressionNode.compress(arg, runtime().getVMConfig().getOopEncoding()));
 160                 ValueNode patch = b.add(new LoadThroughPatchNode(compressed));
 161                 b.addPush(JavaKind.Object, CompressionNode.uncompress(patch, runtime().getVMConfig().getOopEncoding()));
 162                 return true;
 163             }
 164         });
 165 
 166         return plugins;
 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());
 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()));
 186 
 187             gen.append(new LoadThroughPatchOp(input.asConstant(), stamp() 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 }