1 /* 2 * Copyright (c) 2013, 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 package org.graalvm.compiler.hotspot.aarch64; 26 27 import static java.lang.reflect.Modifier.isStatic; 28 import static jdk.vm.ci.aarch64.AArch64.lr; 29 import static jdk.vm.ci.aarch64.AArch64.r10; 30 import static jdk.vm.ci.aarch64.AArch64.sp; 31 import static jdk.vm.ci.aarch64.AArch64.zr; 32 import static jdk.vm.ci.code.ValueUtil.asRegister; 33 import static jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig.fp; 34 import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; 35 import static org.graalvm.compiler.core.common.GraalOptions.ZapStackOnMethodEntry; 36 37 import jdk.internal.vm.compiler.collections.EconomicSet; 38 import org.graalvm.compiler.asm.Assembler; 39 import org.graalvm.compiler.asm.BranchTargetOutOfBoundsException; 40 import org.graalvm.compiler.asm.Label; 41 import org.graalvm.compiler.asm.aarch64.AArch64Address; 42 import org.graalvm.compiler.asm.aarch64.AArch64Assembler; 43 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; 44 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler.ScratchRegister; 45 import org.graalvm.compiler.code.CompilationResult; 46 import org.graalvm.compiler.core.aarch64.AArch64NodeMatchRules; 47 import org.graalvm.compiler.core.common.CompilationIdentifier; 48 import org.graalvm.compiler.core.common.LIRKind; 49 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; 50 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage; 51 import org.graalvm.compiler.core.gen.LIRGenerationProvider; 52 import org.graalvm.compiler.debug.DebugContext; 53 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 54 import org.graalvm.compiler.hotspot.HotSpotDataBuilder; 55 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; 56 import org.graalvm.compiler.hotspot.HotSpotHostBackend; 57 import org.graalvm.compiler.hotspot.HotSpotLIRGenerationResult; 58 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction; 59 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider; 60 import org.graalvm.compiler.hotspot.meta.HotSpotProviders; 61 import org.graalvm.compiler.hotspot.stubs.Stub; 62 import org.graalvm.compiler.lir.LIR; 63 import org.graalvm.compiler.lir.aarch64.AArch64Call; 64 import org.graalvm.compiler.lir.aarch64.AArch64FrameMap; 65 import org.graalvm.compiler.lir.aarch64.AArch64FrameMapBuilder; 66 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 67 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; 68 import org.graalvm.compiler.lir.asm.DataBuilder; 69 import org.graalvm.compiler.lir.asm.FrameContext; 70 import org.graalvm.compiler.lir.framemap.FrameMap; 71 import org.graalvm.compiler.lir.framemap.FrameMapBuilder; 72 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 73 import org.graalvm.compiler.lir.gen.LIRGeneratorTool; 74 import org.graalvm.compiler.nodes.StructuredGraph; 75 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 76 import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess; 77 78 import jdk.vm.ci.aarch64.AArch64Kind; 79 import jdk.vm.ci.code.CallingConvention; 80 import jdk.vm.ci.code.CompilationRequest; 81 import jdk.vm.ci.code.InstalledCode; 82 import jdk.vm.ci.code.Register; 83 import jdk.vm.ci.code.RegisterConfig; 84 import jdk.vm.ci.code.StackSlot; 85 import jdk.vm.ci.code.site.Mark; 86 import jdk.vm.ci.hotspot.HotSpotCallingConventionType; 87 import jdk.vm.ci.hotspot.HotSpotSentinelConstant; 88 import jdk.vm.ci.hotspot.aarch64.AArch64HotSpotRegisterConfig; 89 import jdk.vm.ci.meta.JavaKind; 90 import jdk.vm.ci.meta.JavaType; 91 import jdk.vm.ci.meta.ResolvedJavaMethod; 92 93 import sun.misc.Unsafe; 94 95 /** 96 * HotSpot AArch64 specific backend. 97 */ 98 public class AArch64HotSpotBackend extends HotSpotHostBackend implements LIRGenerationProvider { 99 100 public AArch64HotSpotBackend(GraalHotSpotVMConfig config, HotSpotGraalRuntimeProvider runtime, HotSpotProviders providers) { 101 super(config, runtime, providers); 102 } 103 104 private FrameMapBuilder newFrameMapBuilder(RegisterConfig registerConfig) { 105 RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig; 106 FrameMap frameMap = new AArch64FrameMap(getCodeCache(), registerConfigNonNull, this); 107 return new AArch64FrameMapBuilder(frameMap, getCodeCache(), registerConfigNonNull); 108 } 109 110 @Override 111 public LIRGeneratorTool newLIRGenerator(LIRGenerationResult lirGenRes) { 112 return new AArch64HotSpotLIRGenerator(getProviders(), config, lirGenRes); 113 } 114 115 @Override 116 public LIRGenerationResult newLIRGenerationResult(CompilationIdentifier compilationId, LIR lir, RegisterConfig registerConfig, StructuredGraph graph, Object stub) { 117 return new HotSpotLIRGenerationResult(compilationId, lir, newFrameMapBuilder(registerConfig), makeCallingConvention(graph, (Stub) stub), stub, 118 config.requiresReservedStackCheck(graph.getMethods())); 119 } 120 121 @Override 122 public NodeLIRBuilderTool newNodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool lirGen) { 123 return new AArch64HotSpotNodeLIRBuilder(graph, lirGen, new AArch64NodeMatchRules(lirGen)); 124 } 125 126 @Override 127 protected void bangStackWithOffset(CompilationResultBuilder crb, int bangOffset) { 128 AArch64MacroAssembler masm = (AArch64MacroAssembler) crb.asm; 129 try (ScratchRegister sc = masm.getScratchRegister()) { 130 Register scratch = sc.getRegister(); 131 AArch64Address address = masm.makeAddress(sp, -bangOffset, scratch, 8, /* allowOverwrite */false); 132 masm.str(64, zr, address); 133 } 134 } 135 136 @Override 137 public InstalledCode createInstalledCode(DebugContext debug, 138 ResolvedJavaMethod method, 139 CompilationRequest compilationRequest, 140 CompilationResult compilationResult, 141 InstalledCode predefinedInstalledCode, 142 boolean isDefault, 143 Object[] context) { 144 boolean isStub = (method == null); 145 boolean isAOT = compilationResult.isImmutablePIC(); 146 if (!isStub && !isAOT) { 147 // Non-stub compilation results are installed into HotSpot as nmethods. As AArch64 has 148 // a constraint that the instruction at nmethod verified entry point should be a nop or 149 // jump, AArch64HotSpotBackend always generate a nop placeholder before the code body 150 // for non-AOT compilations. See AArch64HotSpotBackend.emitInvalidatePlaceholder(). This 151 // assert checks if the nop placeholder is generated at all required places, including 152 // in manually assembled code in CodeGenTest cases. 153 assert hasInvalidatePlaceholder(compilationResult); 154 } 155 return super.createInstalledCode(debug, method, compilationRequest, compilationResult, predefinedInstalledCode, isDefault, context); 156 } 157 158 private boolean hasInvalidatePlaceholder(CompilationResult compilationResult) { 159 byte[] targetCode = compilationResult.getTargetCode(); 160 int verifiedEntryOffset = 0; 161 for (Mark mark : compilationResult.getMarks()) { 162 Object markId = mark.id; 163 if (markId instanceof Integer && (int) markId == config.MARKID_VERIFIED_ENTRY) { 164 // The nmethod verified entry is located at some pc offset. 165 verifiedEntryOffset = mark.pcOffset; 166 break; 167 } 168 } 169 Unsafe unsafe = GraalUnsafeAccess.getUnsafe(); 170 int instruction = unsafe.getIntVolatile(targetCode, unsafe.arrayBaseOffset(byte[].class) + verifiedEntryOffset); 171 AArch64MacroAssembler masm = new AArch64MacroAssembler(getTarget()); 172 masm.nop(); 173 return instruction == masm.getInt(0); 174 } 175 176 private class HotSpotFrameContext implements FrameContext { 177 final boolean isStub; 178 179 HotSpotFrameContext(boolean isStub) { 180 this.isStub = isStub; 181 } 182 183 @Override 184 public void enter(CompilationResultBuilder crb) { 185 FrameMap frameMap = crb.frameMap; 186 final int frameSize = frameMap.frameSize(); 187 final int totalFrameSize = frameMap.totalFrameSize(); 188 assert frameSize + 2 * crb.target.arch.getWordSize() == totalFrameSize : "total framesize should be framesize + 2 words"; 189 AArch64MacroAssembler masm = (AArch64MacroAssembler) crb.asm; 190 if (!isStub) { 191 emitStackOverflowCheck(crb); 192 } 193 crb.blockComment("[method prologue]"); 194 195 try (ScratchRegister sc = masm.getScratchRegister()) { 196 int wordSize = crb.target.arch.getWordSize(); 197 Register rscratch1 = sc.getRegister(); 198 assert totalFrameSize > 0; 199 if (frameSize < 1 << 9) { 200 masm.sub(64, sp, sp, totalFrameSize); 201 masm.stp(64, fp, lr, AArch64Address.createScaledImmediateAddress(sp, frameSize / wordSize)); 202 } else { 203 masm.stp(64, fp, lr, AArch64Address.createPreIndexedImmediateAddress(sp, -2)); 204 if (frameSize < 1 << 12) { 205 masm.sub(64, sp, sp, totalFrameSize - 2 * wordSize); 206 } else { 207 masm.mov(rscratch1, totalFrameSize - 2 * wordSize); 208 masm.sub(64, sp, sp, rscratch1); 209 } 210 } 211 } 212 if (ZapStackOnMethodEntry.getValue(crb.getOptions())) { 213 try (ScratchRegister sc = masm.getScratchRegister()) { 214 Register scratch = sc.getRegister(); 215 int longSize = 8; 216 masm.mov(64, scratch, sp); 217 AArch64Address address = AArch64Address.createPostIndexedImmediateAddress(scratch, longSize); 218 try (ScratchRegister sc2 = masm.getScratchRegister()) { 219 Register value = sc2.getRegister(); 220 masm.mov(value, 0xBADDECAFFC0FFEEL); 221 for (int i = 0; i < frameSize; i += longSize) { 222 masm.str(64, value, address); 223 } 224 } 225 226 } 227 } 228 crb.blockComment("[code body]"); 229 } 230 231 @Override 232 public void leave(CompilationResultBuilder crb) { 233 AArch64MacroAssembler masm = (AArch64MacroAssembler) crb.asm; 234 FrameMap frameMap = crb.frameMap; 235 final int totalFrameSize = frameMap.totalFrameSize(); 236 237 crb.blockComment("[method epilogue]"); 238 try (ScratchRegister sc = masm.getScratchRegister()) { 239 int wordSize = crb.target.arch.getWordSize(); 240 Register rscratch1 = sc.getRegister(); 241 final int frameSize = frameMap.frameSize(); 242 assert totalFrameSize > 0; 243 if (frameSize < 1 << 9) { 244 masm.ldp(64, fp, lr, AArch64Address.createScaledImmediateAddress(sp, frameSize / wordSize)); 245 masm.add(64, sp, sp, totalFrameSize); 246 } else { 247 if (frameSize < 1 << 12) { 248 masm.add(64, sp, sp, totalFrameSize - 2 * wordSize); 249 } else { 250 masm.mov(rscratch1, totalFrameSize - 2 * wordSize); 251 masm.add(64, sp, sp, rscratch1); 252 } 253 masm.ldp(64, fp, lr, AArch64Address.createPostIndexedImmediateAddress(sp, 2)); 254 } 255 } 256 257 } 258 259 @Override 260 public boolean hasFrame() { 261 return true; 262 } 263 264 } 265 266 @Override 267 public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult lirGenRen, FrameMap frameMap, CompilationResult compilationResult, CompilationResultBuilderFactory factory) { 268 HotSpotLIRGenerationResult gen = (HotSpotLIRGenerationResult) lirGenRen; 269 LIR lir = gen.getLIR(); 270 assert gen.getDeoptimizationRescueSlot() == null || frameMap.frameNeedsAllocating() : "method that can deoptimize must have a frame"; 271 272 Stub stub = gen.getStub(); 273 Assembler masm = new AArch64MacroAssembler(getTarget()); 274 HotSpotFrameContext frameContext = new HotSpotFrameContext(stub != null); 275 276 DataBuilder dataBuilder = new HotSpotDataBuilder(getCodeCache().getTarget()); 277 CompilationResultBuilder crb = factory.createBuilder(getCodeCache(), getForeignCalls(), frameMap, masm, dataBuilder, frameContext, lir.getOptions(), lir.getDebug(), compilationResult, 278 Register.None); 279 crb.setTotalFrameSize(frameMap.totalFrameSize()); 280 crb.setMaxInterpreterFrameSize(gen.getMaxInterpreterFrameSize()); 281 StackSlot deoptimizationRescueSlot = gen.getDeoptimizationRescueSlot(); 282 if (deoptimizationRescueSlot != null && stub == null) { 283 crb.compilationResult.setCustomStackAreaOffset(deoptimizationRescueSlot); 284 } 285 286 if (stub != null) { 287 EconomicSet<Register> destroyedCallerRegisters = gatherDestroyedCallerRegisters(lir); 288 updateStub(stub, destroyedCallerRegisters, gen.getCalleeSaveInfo(), frameMap); 289 } 290 return crb; 291 } 292 293 @Override 294 public void emitCode(CompilationResultBuilder crb, LIR lir, ResolvedJavaMethod installedCodeOwner) { 295 Label verifiedStub = new Label(); 296 crb.buildLabelOffsets(lir); 297 try { 298 emitCode(crb, lir, installedCodeOwner, verifiedStub); 299 } catch (BranchTargetOutOfBoundsException e) { 300 // A branch estimation was wrong, now retry with conservative label ranges, this 301 // should always work 302 crb.setConservativeLabelRanges(); 303 crb.resetForEmittingCode(); 304 lir.resetLabels(); 305 verifiedStub.reset(); 306 emitCode(crb, lir, installedCodeOwner, verifiedStub); 307 } 308 } 309 310 private void emitCode(CompilationResultBuilder crb, LIR lir, ResolvedJavaMethod installedCodeOwner, Label verifiedStub) { 311 AArch64MacroAssembler masm = (AArch64MacroAssembler) crb.asm; 312 FrameMap frameMap = crb.frameMap; 313 RegisterConfig regConfig = frameMap.getRegisterConfig(); 314 emitCodePrefix(crb, installedCodeOwner, masm, regConfig, verifiedStub); 315 emitCodeBody(crb, lir, masm); 316 emitCodeSuffix(crb, masm, frameMap); 317 } 318 319 private void emitCodePrefix(CompilationResultBuilder crb, ResolvedJavaMethod installedCodeOwner, AArch64MacroAssembler masm, RegisterConfig regConfig, Label verifiedStub) { 320 HotSpotProviders providers = getProviders(); 321 if (installedCodeOwner != null && !isStatic(installedCodeOwner.getModifiers())) { 322 crb.recordMark(config.MARKID_UNVERIFIED_ENTRY); 323 CallingConvention cc = regConfig.getCallingConvention(HotSpotCallingConventionType.JavaCallee, null, new JavaType[]{providers.getMetaAccess().lookupJavaType(Object.class)}, this); 324 // See definition of IC_Klass in c1_LIRAssembler_aarch64.cpp 325 // equal to scratch(1) careful! 326 Register inlineCacheKlass = AArch64HotSpotRegisterConfig.inlineCacheRegister; 327 Register receiver = asRegister(cc.getArgument(0)); 328 int transferSize = config.useCompressedClassPointers ? 4 : 8; 329 AArch64Address klassAddress = masm.makeAddress(receiver, config.hubOffset, transferSize); 330 331 // Are r10 and r11 available scratch registers here? One would hope so. 332 Register klass = r10; 333 if (config.useCompressedClassPointers) { 334 masm.ldr(32, klass, klassAddress); 335 AArch64HotSpotMove.decodeKlassPointer(crb, masm, klass, klass, config.getKlassEncoding(), config); 336 } else { 337 masm.ldr(64, klass, klassAddress); 338 } 339 masm.cmp(64, inlineCacheKlass, klass); 340 /* 341 * Conditional jumps have a much lower range than unconditional ones, which can be a 342 * problem because the miss handler could be out of range. 343 */ 344 masm.branchConditionally(AArch64Assembler.ConditionFlag.EQ, verifiedStub); 345 AArch64Call.directJmp(crb, masm, getForeignCalls().lookupForeignCall(IC_MISS_HANDLER)); 346 } 347 masm.align(config.codeEntryAlignment); 348 crb.recordMark(config.MARKID_OSR_ENTRY); 349 masm.bind(verifiedStub); 350 crb.recordMark(config.MARKID_VERIFIED_ENTRY); 351 352 if (GeneratePIC.getValue(crb.getOptions())) { 353 // Check for method state 354 HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext; 355 if (!frameContext.isStub) { 356 crb.recordInlineDataInCodeWithNote(new HotSpotSentinelConstant(LIRKind.value(AArch64Kind.QWORD), JavaKind.Long), HotSpotConstantLoadAction.MAKE_NOT_ENTRANT); 357 try (ScratchRegister sc = masm.getScratchRegister()) { 358 Register scratch = sc.getRegister(); 359 masm.addressOf(scratch); 360 masm.ldr(64, scratch, AArch64Address.createBaseRegisterOnlyAddress(scratch)); 361 Label noCall = new Label(); 362 masm.cbz(64, scratch, noCall); 363 AArch64Call.directJmp(crb, masm, getForeignCalls().lookupForeignCall(WRONG_METHOD_HANDLER)); 364 masm.bind(noCall); 365 } 366 } 367 } 368 } 369 370 private static void emitCodeBody(CompilationResultBuilder crb, LIR lir, AArch64MacroAssembler masm) { 371 emitInvalidatePlaceholder(crb, masm); 372 crb.emit(lir); 373 } 374 375 /** 376 * Insert a nop at the start of the prolog so we can patch in a branch if we need to invalidate 377 * the method later. 378 * 379 * @see "http://mail.openjdk.java.net/pipermail/aarch64-port-dev/2013-September/000273.html" 380 */ 381 public static void emitInvalidatePlaceholder(CompilationResultBuilder crb, AArch64MacroAssembler masm) { 382 if (!GeneratePIC.getValue(crb.getOptions())) { 383 crb.blockComment("[nop for method invalidation]"); 384 masm.nop(); 385 } 386 } 387 388 private void emitCodeSuffix(CompilationResultBuilder crb, AArch64MacroAssembler masm, FrameMap frameMap) { 389 HotSpotProviders providers = getProviders(); 390 HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext; 391 if (!frameContext.isStub) { 392 HotSpotForeignCallsProvider foreignCalls = providers.getForeignCalls(); 393 try (ScratchRegister sc = masm.getScratchRegister()) { 394 Register scratch = sc.getRegister(); 395 crb.recordMark(config.MARKID_EXCEPTION_HANDLER_ENTRY); 396 ForeignCallLinkage linkage = foreignCalls.lookupForeignCall(EXCEPTION_HANDLER); 397 Register helper = AArch64Call.isNearCall(linkage) ? null : scratch; 398 AArch64Call.directCall(crb, masm, linkage, helper, null); 399 } 400 crb.recordMark(config.MARKID_DEOPT_HANDLER_ENTRY); 401 ForeignCallLinkage linkage = foreignCalls.lookupForeignCall(DEOPTIMIZATION_HANDLER); 402 masm.adr(lr, 0); // Warning: the argument is an offset from the instruction! 403 AArch64Call.directJmp(crb, masm, linkage); 404 } else { 405 // No need to emit the stubs for entries back into the method since 406 // it has no calls that can cause such "return" entries 407 assert !frameMap.accessesCallerFrame(); 408 } 409 } 410 411 @Override 412 public RegisterAllocationConfig newRegisterAllocationConfig(RegisterConfig registerConfig, String[] allocationRestrictedTo) { 413 RegisterConfig registerConfigNonNull = registerConfig == null ? getCodeCache().getRegisterConfig() : registerConfig; 414 return new AArch64HotSpotRegisterAllocationConfig(registerConfigNonNull, allocationRestrictedTo); 415 } 416 417 @Override 418 public EconomicSet<Register> translateToCallerRegisters(EconomicSet<Register> calleeRegisters) { 419 return calleeRegisters; 420 } 421 }