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 }