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