1 /*
   2  * Copyright (c) 2015, 2016, 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.aarch64;
  25 
  26 import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
  27 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
  28 
  29 import java.util.function.Function;
  30 
  31 import org.graalvm.compiler.asm.Label;
  32 import org.graalvm.compiler.asm.NumUtil;
  33 import org.graalvm.compiler.asm.aarch64.AArch64Address.AddressingMode;
  34 import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag;
  35 import org.graalvm.compiler.core.aarch64.AArch64ArithmeticLIRGenerator;
  36 import org.graalvm.compiler.core.aarch64.AArch64LIRGenerator;
  37 import org.graalvm.compiler.core.common.LIRKind;
  38 import org.graalvm.compiler.core.common.calc.Condition;
  39 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
  40 import org.graalvm.compiler.core.common.spi.LIRKindTool;
  41 import org.graalvm.compiler.debug.GraalError;
  42 import org.graalvm.compiler.hotspot.CompressEncoding;
  43 import org.graalvm.compiler.hotspot.HotSpotBackend;
  44 import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder;
  45 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
  46 import org.graalvm.compiler.hotspot.HotSpotLIRGenerationResult;
  47 import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
  48 import org.graalvm.compiler.hotspot.HotSpotLockStack;
  49 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
  50 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
  51 import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
  52 import org.graalvm.compiler.hotspot.stubs.Stub;
  53 import org.graalvm.compiler.lir.LIRFrameState;
  54 import org.graalvm.compiler.lir.LIRInstruction;
  55 import org.graalvm.compiler.lir.LabelRef;
  56 import org.graalvm.compiler.lir.StandardOp.SaveRegistersOp;
  57 import org.graalvm.compiler.lir.SwitchStrategy;
  58 import org.graalvm.compiler.lir.Variable;
  59 import org.graalvm.compiler.lir.VirtualStackSlot;
  60 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
  61 import org.graalvm.compiler.lir.aarch64.AArch64Call;
  62 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow.StrategySwitchOp;
  63 import org.graalvm.compiler.lir.aarch64.AArch64FrameMapBuilder;
  64 import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreOp;
  65 import org.graalvm.compiler.lir.aarch64.AArch64PrefetchOp;
  66 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  67 
  68 import jdk.vm.ci.aarch64.AArch64;
  69 import jdk.vm.ci.aarch64.AArch64Kind;
  70 import jdk.vm.ci.code.CallingConvention;
  71 import jdk.vm.ci.code.Register;
  72 import jdk.vm.ci.code.RegisterValue;
  73 import jdk.vm.ci.code.StackSlot;
  74 import jdk.vm.ci.hotspot.HotSpotCompressedNullConstant;
  75 import jdk.vm.ci.hotspot.HotSpotObjectConstant;
  76 import jdk.vm.ci.meta.AllocatableValue;
  77 import jdk.vm.ci.meta.Constant;
  78 import jdk.vm.ci.meta.DeoptimizationAction;
  79 import jdk.vm.ci.meta.DeoptimizationReason;
  80 import jdk.vm.ci.meta.JavaConstant;
  81 import jdk.vm.ci.meta.JavaKind;
  82 import jdk.vm.ci.meta.PlatformKind;
  83 import jdk.vm.ci.meta.Value;
  84 
  85 /**
  86  * LIR generator specialized for AArch64 HotSpot.
  87  */
  88 public class AArch64HotSpotLIRGenerator extends AArch64LIRGenerator implements HotSpotLIRGenerator {
  89 
  90     final GraalHotSpotVMConfig config;
  91     private HotSpotDebugInfoBuilder debugInfoBuilder;
  92 
  93     protected AArch64HotSpotLIRGenerator(HotSpotProviders providers, GraalHotSpotVMConfig config, LIRGenerationResult lirGenRes) {
  94         this(new AArch64HotSpotLIRKindTool(), new AArch64ArithmeticLIRGenerator(), new AArch64HotSpotMoveFactory(), providers, config, lirGenRes);
  95     }
  96 
  97     protected AArch64HotSpotLIRGenerator(LIRKindTool lirKindTool, AArch64ArithmeticLIRGenerator arithmeticLIRGen, MoveFactory moveFactory, HotSpotProviders providers, GraalHotSpotVMConfig config,
  98                     LIRGenerationResult lirGenRes) {
  99         super(lirKindTool, arithmeticLIRGen, moveFactory, providers, lirGenRes);
 100         this.config = config;
 101     }
 102 
 103     @Override
 104     public HotSpotProviders getProviders() {
 105         return (HotSpotProviders) super.getProviders();
 106     }
 107 
 108     @Override
 109     public boolean needOnlyOopMaps() {
 110         // Stubs only need oop maps
 111         return getResult().getStub() != null;
 112     }
 113 
 114     @SuppressWarnings("unused") private LIRFrameState currentRuntimeCallInfo;
 115 
 116     @Override
 117     protected void emitForeignCallOp(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info) {
 118         currentRuntimeCallInfo = info;
 119         if (AArch64Call.isNearCall(linkage)) {
 120             append(new AArch64Call.DirectNearForeignCallOp(linkage, result, arguments, temps, info, label));
 121         } else {
 122             append(new AArch64Call.DirectFarForeignCallOp(linkage, result, arguments, temps, info, label));
 123         }
 124     }
 125 
 126     @Override
 127     public void emitTailcall(Value[] args, Value address) {
 128         throw GraalError.unimplemented();
 129     }
 130 
 131     @Override
 132     public SaveRegistersOp emitSaveAllRegisters() {
 133         throw GraalError.unimplemented();
 134     }
 135 
 136     @Override
 137     public VirtualStackSlot getLockSlot(int lockDepth) {
 138         return getLockStack().makeLockSlot(lockDepth);
 139     }
 140 
 141     private HotSpotLockStack getLockStack() {
 142         assert debugInfoBuilder != null && debugInfoBuilder.lockStack() != null;
 143         return debugInfoBuilder.lockStack();
 144     }
 145 
 146     @Override
 147     public void emitCompareBranch(PlatformKind cmpKind, Value x, Value y, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination,
 148                     double trueDestinationProbability) {
 149         Value localX = x;
 150         Value localY = y;
 151         if (localX instanceof HotSpotObjectConstant) {
 152             localX = load(localX);
 153         }
 154         if (localY instanceof HotSpotObjectConstant) {
 155             localY = load(localY);
 156         }
 157         super.emitCompareBranch(cmpKind, localX, localY, cond, unorderedIsTrue, trueDestination, falseDestination, trueDestinationProbability);
 158     }
 159 
 160     @Override
 161     protected boolean emitCompare(PlatformKind cmpKind, Value a, Value b, Condition condition, boolean unorderedIsTrue) {
 162         Value localA = a;
 163         Value localB = b;
 164         if (isConstantValue(a)) {
 165             Constant c = asConstant(a);
 166             if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(c)) {
 167                 localA = AArch64.zr.asValue(LIRKind.value(AArch64Kind.DWORD));
 168             } else if (c instanceof HotSpotObjectConstant) {
 169                 localA = load(localA);
 170             }
 171         }
 172         if (isConstantValue(b)) {
 173             Constant c = asConstant(b);
 174             if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(c)) {
 175                 localB = AArch64.zr.asValue(LIRKind.value(AArch64Kind.DWORD));
 176             } else if (c instanceof HotSpotObjectConstant) {
 177                 localB = load(localB);
 178             }
 179         }
 180         return super.emitCompare(cmpKind, localA, localB, condition, unorderedIsTrue);
 181     }
 182 
 183     @Override
 184     public Value emitCompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
 185         LIRKind inputKind = pointer.getValueKind(LIRKind.class);
 186         assert inputKind.getPlatformKind() == AArch64Kind.QWORD;
 187         if (inputKind.isReference(0)) {
 188             // oop
 189             Variable result = newVariable(LIRKind.reference(AArch64Kind.DWORD));
 190             append(new AArch64HotSpotMove.CompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull));
 191             return result;
 192         } else {
 193             // metaspace pointer
 194             Variable result = newVariable(LIRKind.value(AArch64Kind.DWORD));
 195             AllocatableValue base = Value.ILLEGAL;
 196             if (encoding.base != 0) {
 197                 base = emitLoadConstant(LIRKind.value(AArch64Kind.QWORD), JavaConstant.forLong(encoding.base));
 198             }
 199             append(new AArch64HotSpotMove.CompressPointer(result, asAllocatable(pointer), base, encoding, nonNull));
 200             return result;
 201         }
 202     }
 203 
 204     @Override
 205     public Value emitUncompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
 206         LIRKind inputKind = pointer.getValueKind(LIRKind.class);
 207         assert inputKind.getPlatformKind() == AArch64Kind.DWORD;
 208         if (inputKind.isReference(0)) {
 209             // oop
 210             Variable result = newVariable(LIRKind.reference(AArch64Kind.QWORD));
 211             append(new AArch64HotSpotMove.UncompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull));
 212             return result;
 213         } else {
 214             // metaspace pointer
 215             Variable result = newVariable(LIRKind.value(AArch64Kind.QWORD));
 216             AllocatableValue base = Value.ILLEGAL;
 217             if (encoding.base != 0) {
 218                 base = emitLoadConstant(LIRKind.value(AArch64Kind.QWORD), JavaConstant.forLong(encoding.base));
 219             }
 220             append(new AArch64HotSpotMove.UncompressPointer(result, asAllocatable(pointer), base, encoding, nonNull));
 221             return result;
 222         }
 223     }
 224 
 225     @Override
 226     public void emitPrefetchAllocate(Value address) {
 227         append(new AArch64PrefetchOp(asAddressValue(address), config.allocatePrefetchInstr));
 228     }
 229 
 230     @Override
 231     public void beforeRegisterAllocation() {
 232         super.beforeRegisterAllocation();
 233         boolean hasDebugInfo = getResult().getLIR().hasDebugInfo();
 234         if (hasDebugInfo) {
 235             getResult().setDeoptimizationRescueSlot(((AArch64FrameMapBuilder) getResult().getFrameMapBuilder()).allocateDeoptimizationRescueSlot());
 236         }
 237 
 238         getResult().setMaxInterpreterFrameSize(debugInfoBuilder.maxInterpreterFrameSize());
 239     }
 240 
 241     private Label label;
 242 
 243     @Override
 244     public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState state, Value... args) {
 245         HotSpotForeignCallLinkage hotspotLinkage = (HotSpotForeignCallLinkage) linkage;
 246         Variable result;
 247         LIRFrameState debugInfo = null;
 248         if (hotspotLinkage.needsDebugInfo()) {
 249             debugInfo = state;
 250             assert debugInfo != null || getStub() != null;
 251         }
 252 
 253         if (linkage.destroysRegisters() || hotspotLinkage.needsJavaFrameAnchor()) {
 254             HotSpotRegistersProvider registers = getProviders().getRegisters();
 255             Register thread = registers.getThreadRegister();
 256             Variable scratch = newVariable(LIRKind.value(target().arch.getWordKind()));
 257 
 258             // We need a label for the return address.
 259             label = new Label();
 260 
 261             append(new AArch64HotSpotCRuntimeCallPrologueOp(config.threadLastJavaSpOffset(), config.threadLastJavaPcOffset(), config.threadLastJavaFpOffset(), thread, scratch, label));
 262             result = super.emitForeignCall(hotspotLinkage, debugInfo, args);
 263             append(new AArch64HotSpotCRuntimeCallEpilogueOp(config.threadLastJavaSpOffset(), config.threadLastJavaFpOffset(), thread));
 264 
 265             // Clear it out so it's not being reused later.
 266             label = null;
 267         } else {
 268             result = super.emitForeignCall(hotspotLinkage, debugInfo, args);
 269         }
 270 
 271         return result;
 272     }
 273 
 274     @Override
 275     public void emitDeoptimizeCaller(DeoptimizationAction action, DeoptimizationReason reason) {
 276         Value actionAndReason = emitJavaConstant(getMetaAccess().encodeDeoptActionAndReason(action, reason, 0));
 277         Value nullValue = emitConstant(LIRKind.reference(AArch64Kind.QWORD), JavaConstant.NULL_POINTER);
 278         moveDeoptValuesToThread(actionAndReason, nullValue);
 279         append(new AArch64HotSpotDeoptimizeCallerOp(config));
 280     }
 281 
 282     @Override
 283     public void emitDeoptimize(Value actionAndReason, Value failedSpeculation, LIRFrameState state) {
 284         moveDeoptValuesToThread(actionAndReason, failedSpeculation);
 285         append(new AArch64HotSpotDeoptimizeOp(state));
 286     }
 287 
 288     private void moveDeoptValuesToThread(Value actionAndReason, Value speculation) {
 289         moveValueToThread(actionAndReason, config.pendingDeoptimizationOffset);
 290         moveValueToThread(speculation, config.pendingFailedSpeculationOffset);
 291     }
 292 
 293     private void moveValueToThread(Value value, int offset) {
 294         LIRKind wordKind = LIRKind.value(target().arch.getWordKind());
 295         RegisterValue thread = getProviders().getRegisters().getThreadRegister().asValue(wordKind);
 296         final int transferSize = value.getValueKind().getPlatformKind().getSizeInBytes();
 297         final int scaledDisplacement = offset >> NumUtil.log2Ceil(transferSize);
 298         AArch64AddressValue address = new AArch64AddressValue(value.getValueKind(), thread, Value.ILLEGAL, scaledDisplacement, true, AddressingMode.IMMEDIATE_SCALED);
 299         append(new StoreOp((AArch64Kind) value.getPlatformKind(), address, loadReg(value), null));
 300     }
 301 
 302     @Override
 303     public void emitUnwind(Value exception) {
 304         ForeignCallLinkage linkage = getForeignCalls().lookupForeignCall(HotSpotBackend.UNWIND_EXCEPTION_TO_CALLER);
 305         CallingConvention outgoingCc = linkage.getOutgoingCallingConvention();
 306         assert outgoingCc.getArgumentCount() == 2;
 307         RegisterValue exceptionParameter = (RegisterValue) outgoingCc.getArgument(0);
 308         emitMove(exceptionParameter, exception);
 309         append(new AArch64HotSpotUnwindOp(config, exceptionParameter));
 310     }
 311 
 312     @Override
 313     public void emitReturn(JavaKind kind, Value input) {
 314         AllocatableValue operand = Value.ILLEGAL;
 315         if (input != null) {
 316             operand = resultOperandFor(kind, input.getValueKind());
 317             emitMove(operand, input);
 318         }
 319         append(new AArch64HotSpotReturnOp(operand, getStub() != null, config));
 320     }
 321 
 322     /**
 323      * Gets the {@link Stub} this generator is generating code for or {@code null} if a stub is not
 324      * being generated.
 325      */
 326     public Stub getStub() {
 327         return getResult().getStub();
 328     }
 329 
 330     @Override
 331     public HotSpotLIRGenerationResult getResult() {
 332         return ((HotSpotLIRGenerationResult) super.getResult());
 333     }
 334 
 335     @Override
 336     protected StrategySwitchOp createStrategySwitchOp(SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, Variable key, AllocatableValue scratchValue,
 337                     Function<Condition, ConditionFlag> converter) {
 338         return new AArch64HotSpotStrategySwitchOp(strategy, keyTargets, defaultTarget, key, scratchValue, converter);
 339     }
 340 
 341     public void setDebugInfoBuilder(HotSpotDebugInfoBuilder debugInfoBuilder) {
 342         this.debugInfoBuilder = debugInfoBuilder;
 343     }
 344 
 345     @Override
 346     public SaveRegistersOp createZapRegisters(Register[] zappedRegisters, JavaConstant[] zapValues) {
 347         throw GraalError.unimplemented();
 348     }
 349 
 350     @Override
 351     public LIRInstruction createZapArgumentSpace(StackSlot[] zappedStack, JavaConstant[] zapValues) {
 352         throw GraalError.unimplemented();
 353     }
 354 }