diff --git a/llvm/lib/MC/GoObjObjectWriter.cpp b/llvm/lib/MC/GoObjObjectWriter.cpp index 2ab829bc6b364..fabc317c6d273 100644 --- a/llvm/lib/MC/GoObjObjectWriter.cpp +++ b/llvm/lib/MC/GoObjObjectWriter.cpp @@ -2369,8 +2369,6 @@ uint64_t GoObjObjectWriter::writeObject() { "GoObj private constants with relocations are not supported"); int64_t Addend = getGoObjRelocAddend(Reloc); - GoObjSymRef TargetSymRef = GetTargetSymRef(Reloc, Addend); - uint16_t RelocType = checkedUint16(Reloc.Type, "relocation type"); if (Source.Symbol) { if (const auto *Overrides = @@ -2392,6 +2390,16 @@ uint64_t GoObjObjectWriter::writeObject() { RelocType |= GoObj::R_WEAK; } + // Native x86 Go objects intentionally leave the internal-linking TLS + // relocation target empty. The linker resolves R_TLS_LE against its + // synthetic runtime.tlsg symbol and supplies that symbol itself when it + // translates the relocation for external ELF linking. + const Triple::ArchType Arch = Asm->getContext().getTargetTriple().getArch(); + const bool IsX86TLSLE = (Arch == Triple::x86 || Arch == Triple::x86_64) && + (RelocType & ~GoObj::R_WEAK) == GoObj::R_TLS_LE; + GoObjSymRef TargetSymRef = + IsX86TLSLE ? GoObjSymRef{} : GetTargetSymRef(Reloc, Addend); + Source.Relocations.push_back( {static_cast(LocalOffset), Reloc.Size, RelocType, Addend, TargetSymRef.PkgIdx, TargetSymRef.SymIdx, std::nullopt}); diff --git a/llvm/lib/Target/X86/CMakeLists.txt b/llvm/lib/Target/X86/CMakeLists.txt index 62987bdbd1c2b..07781dccc718b 100644 --- a/llvm/lib/Target/X86/CMakeLists.txt +++ b/llvm/lib/Target/X86/CMakeLists.txt @@ -37,6 +37,7 @@ set(sources X86CodeGenPassBuilder.cpp X86DomainReassignment.cpp X86GlobalBaseReg.cpp + X86GoABI.cpp X86LowerTileCopy.cpp X86LowerAMXType.cpp X86LowerAMXIntrinsics.cpp diff --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h index 48dedd9d2a758..1571113c42bfe 100644 --- a/llvm/lib/Target/X86/X86.h +++ b/llvm/lib/Target/X86/X86.h @@ -84,6 +84,16 @@ class X86InsertVZeroUpperPass }; FunctionPass *createX86InsertVZeroUpperLegacyPass(); + +/// This pass restores x86-64 Go ABIInternal's reserved register state at ABI0 +/// boundaries after register allocation and frame lowering. +class X86GoABIPass : public OptionalPassInfoMixin { +public: + PreservedAnalyses run(MachineFunction &MF, + MachineFunctionAnalysisManager &MFAM); +}; + +FunctionPass *createX86GoABILegacyPass(); /// This pass inserts ENDBR instructions before indirect jump/call /// destinations as part of CET IBT mechanism. class X86IndirectBranchTrackingPass @@ -480,6 +490,7 @@ void initializeCompressEVEXLegacyPass(PassRegistry &); void initializeX86FixupBWInstLegacyPass(PassRegistry &); void initializeFixupLEAsLegacyPass(PassRegistry &); void initializeX86ArgumentStackSlotLegacyPass(PassRegistry &); +void initializeX86GoABILegacyPass(PassRegistry &); void initializeX86AsmPrinterPass(PassRegistry &); void initializeX86FixupInstTuningLegacyPass(PassRegistry &); void initializeX86FixupVectorConstantsLegacyPass(PassRegistry &); diff --git a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp index e1762de764671..f257721a81f26 100644 --- a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp +++ b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp @@ -198,6 +198,7 @@ void X86CodeGenPassBuilder::addPreEmitPass(PassManagerWrapper &PMW) const { } addMachineFunctionPass(X86CompressEVEXPass(), PMW); addMachineFunctionPass(X86InsertX87WaitPass(), PMW); + addMachineFunctionPass(X86GoABIPass(), PMW); } void X86CodeGenPassBuilder::addPreEmitPass2(PassManagerWrapper &PMW) const { diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index 59d70e44253e3..2286709c65b8e 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -3121,6 +3121,19 @@ StackOffset X86FrameLowering::getFrameIndexReference(const MachineFunction &MF, bool IsWin64Prologue = MF.getTarget().getMCAsmInfo().usesWindowsCFI(); int64_t FPDelta = 0; + // Go keeps a frame-pointer chain for profiling and traceback, but its local + // frame slots are addressed from SP. This is more than a code-generation + // preference: runtime.gogo can resume a suspended frame after restoring SP + // while deliberately clearing BP. A BP-relative local would then become + // inaccessible even though the Go frame is otherwise valid. Go frames have + // a reserved call frame, so SP remains a stable base for ordinary locals. + if (goabi::isGoCallingConv(MF.getFunction().getCallingConv()) && !IsFixed && + !TRI->hasStackRealignment(MF) && !TRI->hasBasePointer(MF) && + hasReservedCallFrame(MF)) { + FrameReg = TRI->getStackRegister(); + return StackOffset::getFixed(Offset + StackSize); + } + // In an x86 interrupt, remove the offset we added to account for the return // address from any stack object allocated in the caller's frame. Interrupts // do not have a standard return address. Fixed objects in the current frame, diff --git a/llvm/lib/Target/X86/X86GoABI.cpp b/llvm/lib/Target/X86/X86GoABI.cpp new file mode 100644 index 0000000000000..c12cc25bf38b2 --- /dev/null +++ b/llvm/lib/Target/X86/X86GoABI.cpp @@ -0,0 +1,163 @@ +//===- X86GoABI.cpp - Repair reserved Go ABI state ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// R14 and XMM15 are reserved in every x86-64 Go function. ABIInternal requires +// them to contain g and zero respectively, while ABI0 does not establish or +// preserve those values. This late pass repairs the reserved state at ABI +// boundaries after register allocation, frame lowering, and scheduling. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/X86BaseInfo.h" +#include "X86.h" +#include "X86InstrInfo.h" +#include "X86RegisterInfo.h" +#include "X86Subtarget.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/GoObj.h" +#include "llvm/CodeGen/GoCallingConv.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/CodeGen/StackMaps.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Pass.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "x86-go-abi" + +namespace { + +class X86GoABILegacy : public MachineFunctionPass { +public: + static char ID; + + X86GoABILegacy() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "X86 Go ABI state repair"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().setNoVRegs(); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } +}; + +} // end anonymous namespace + +char X86GoABILegacy::ID = 0; + +INITIALIZE_PASS(X86GoABILegacy, DEBUG_TYPE, "X86 Go ABI state repair", false, + false) + +FunctionPass *llvm::createX86GoABILegacyPass() { return new X86GoABILegacy(); } + +static StringRef getDirectCalleeName(const MachineInstr &MI, + const X86InstrInfo &TII) { + const MachineOperand &Callee = MI.getOpcode() == TargetOpcode::STATEPOINT + ? StatepointOpers(&MI).getCallTarget() + : TII.getCalleeOperand(MI); + if (Callee.isGlobal()) + return Callee.getGlobal()->getName(); + if (Callee.isSymbol()) + return Callee.getSymbolName(); + if (Callee.isMCSymbol()) + return Callee.getMCSymbol()->getName(); + return {}; +} + +static bool isOrdinaryGoCall(const MachineInstr &MI, + const MachineFunction &MF) { + if (MI.getOpcode() == TargetOpcode::STATEPOINT) + return goabi::isGoCallingConv(StatepointOpers(&MI).getCallingConv()); + + const X86RegisterInfo &TRI = + *MF.getSubtarget().getRegisterInfo(); + const uint32_t *GoMask = + TRI.getCallPreservedMask(MF, CallingConv::GoABIInternal); + return llvm::any_of(MI.operands(), [GoMask](const MachineOperand &MO) { + return MO.isRegMask() && MO.getRegMask() == GoMask; + }); +} + +static void emitABIInternalState(MachineBasicBlock &MBB, + MachineBasicBlock::iterator Pos, + const DebugLoc &DL, const X86InstrInfo &TII) { + BuildMI(MBB, Pos, DL, TII.get(X86::XORPSrr), X86::XMM15) + .addReg(X86::XMM15, RegState::Undef) + .addReg(X86::XMM15, RegState::Undef); + BuildMI(MBB, Pos, DL, TII.get(X86::MOV64rm), X86::R14) + .addReg(X86::NoRegister) + .addImm(1) + .addReg(X86::NoRegister) + .addExternalSymbol("runtime.tlsg", X86II::MO_TPOFF) + .addReg(X86::FS); +} + +static bool repairGoABIState(MachineFunction &MF) { + CallingConv::ID CallerCC = MF.getFunction().getCallingConv(); + if (!goabi::isGoCallingConv(CallerCC)) + return false; + + const X86Subtarget &ST = MF.getSubtarget(); + if (!ST.is64Bit()) + return false; + + assert(MF.getRegInfo().isReserved(X86::R14) && + MF.getRegInfo().isReserved(X86::XMM15) && + "Go ABI state registers must be reserved"); + + const X86InstrInfo &TII = *ST.getInstrInfo(); + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + for (auto I = MBB.begin(); I != MBB.end(); ++I) { + MachineInstr &MI = *I; + if (!MI.isCall()) + continue; + if (!isOrdinaryGoCall(MI, MF)) + continue; + StringRef CalleeName = getDirectCalleeName(MI, TII); + + bool CalleeIsABI0 = CalleeName.ends_with(GoObj::ABI0SymbolSuffix); + bool RepairBefore = goabi::isGoABI0CallingConv(CallerCC) && !CalleeIsABI0; + bool RepairAfter = + goabi::isGoABIInternalCallingConv(CallerCC) && CalleeIsABI0; + if (RepairBefore) { + emitABIInternalState(MBB, I, MI.getDebugLoc(), TII); + Changed = true; + } else if (RepairAfter) { + emitABIInternalState(MBB, std::next(I), MI.getDebugLoc(), TII); + Changed = true; + } + } + } + return Changed; +} + +bool X86GoABILegacy::runOnMachineFunction(MachineFunction &MF) { + return repairGoABIState(MF); +} + +PreservedAnalyses X86GoABIPass::run(MachineFunction &MF, + MachineFunctionAnalysisManager &MFAM) { + return repairGoABIState(MF) ? getMachineFunctionPassPreservedAnalyses() + .preserveSet() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/X86/X86PassRegistry.def b/llvm/lib/Target/X86/X86PassRegistry.def index 45e7d0ebdbf7b..dc0fba50fa841 100644 --- a/llvm/lib/Target/X86/X86PassRegistry.def +++ b/llvm/lib/Target/X86/X86PassRegistry.def @@ -49,6 +49,7 @@ MACHINE_FUNCTION_PASS("x86-fixup-vector-constants", X86FixupVectorConstantsPass( MACHINE_FUNCTION_PASS("x86-flags-copy-lowering", X86FlagsCopyLoweringPass()) MACHINE_FUNCTION_PASS("x86-fp-stackifier", X86FPStackifierPass()) MACHINE_FUNCTION_PASS("x86-global-base-reg", X86GlobalBaseRegPass()) +MACHINE_FUNCTION_PASS("x86-go-abi", X86GoABIPass()) MACHINE_FUNCTION_PASS("x86-indirect-branch-tracking", X86IndirectBranchTrackingPass()) MACHINE_FUNCTION_PASS("x86-insert-vzeroupper", X86InsertVZeroUpperPass()) MACHINE_FUNCTION_PASS("x86-insert-x87-wait", X86InsertX87WaitPass()) diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index 932669b5cbac6..057d2b6bf784f 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -101,6 +101,7 @@ extern "C" LLVM_C_ABI void LLVMInitializeX86Target() { initializeX86ReturnThunksLegacyPass(PR); initializeX86DAGToDAGISelLegacyPass(PR); initializeX86ArgumentStackSlotLegacyPass(PR); + initializeX86GoABILegacyPass(PR); initializeX86AsmPrinterPass(PR); initializeX86FixupInstTuningLegacyPass(PR); initializeX86FixupVectorConstantsLegacyPass(PR); @@ -573,6 +574,7 @@ void X86PassConfig::addPreEmitPass() { } addPass(createX86CompressEVEXLegacyPass()); addPass(createX86InsertX87WaitLegacyPass()); + addPass(createX86GoABILegacyPass()); } void X86PassConfig::addPreEmitPass2() { diff --git a/llvm/test/CodeGen/X86/go-abi-transition.ll b/llvm/test/CodeGen/X86/go-abi-transition.ll new file mode 100644 index 0000000000000..171d7b0e884ef --- /dev/null +++ b/llvm/test/CodeGen/X86/go-abi-transition.ll @@ -0,0 +1,127 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O0 -verify-machineinstrs -o - %s | FileCheck %s --check-prefixes=ASM,ASM-O0 +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O2 -verify-machineinstrs -o - %s | FileCheck %s --check-prefixes=ASM,ASM-O2 +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O0 -verify-machineinstrs \ +; RUN: -stop-before=x86-go-abi -o - %s | FileCheck %s --check-prefix=PRE +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -O0 -verify-machineinstrs \ +; RUN: -stop-after=x86-go-abi -o - %s | FileCheck %s --check-prefix=LATE +; RUN: llc -mtriple=x86_64-unknown-linux-goobj -goobj-package-path=main \ +; RUN: -verify-machineinstrs -filetype=obj -o %t.o %s +; RUN: %python %S/../../MC/GoObj/Inputs/dump-goobj.py %t.o | \ +; RUN: FileCheck %s --check-prefix=OBJ + +declare goabiinternal void @internal_callee() +declare goabi0 void @"abi0_callee"() +declare goabi0 i64 @"abi0_result"() + +define goabi0 void @"abi0_to_internal"() "go-nosplit" { +; PRE-LABEL: name: 'abi0_to_internal' +; PRE-NOT: XORPSrr +; PRE: CALL64pcrel32 {{.*}}@internal_callee +; LATE-LABEL: name: 'abi0_to_internal' +; LATE: $xmm15 = XORPSrr +; LATE-NEXT: $r14 = MOV64rm {{.*}}runtime.tlsg +; LATE-NEXT: CALL64pcrel32 {{.*}}@internal_callee +; ASM-LABEL: "abi0_to_internal": +; ASM-NOT: pushq %r14 +; ASM-NOT: movaps %xmm15 +; ASM: xorps %xmm15, %xmm15 +; ASM-NEXT: movq %fs:runtime.tlsg@TPOFF, %r14 +; ASM-NEXT: callq internal_callee +; ASM-NOT: movaps {{.*}}, %xmm15 +; ASM-NOT: popq %r14 +; ASM: retq +entry: + call goabiinternal void @internal_callee() + ret void +} + +define goabiinternal void @internal_to_abi0() "go-nosplit" { +; LATE-LABEL: name: internal_to_abi0 +; LATE: CALL64pcrel32 {{.*}}@"abi0_callee" +; LATE-NEXT: $xmm15 = XORPSrr +; LATE-NEXT: $r14 = MOV64rm {{.*}}runtime.tlsg +; ASM-LABEL: internal_to_abi0: +; ASM: callq "abi0_callee" +; ASM-NEXT: xorps %xmm15, %xmm15 +; ASM-NEXT: movq %fs:runtime.tlsg@TPOFF, %r14 +; ASM-NOT: movaps {{.*}}, %xmm15 +; ASM-NOT: popq %r14 +; ASM: retq +entry: + call goabi0 void @"abi0_callee"() + ret void +} + +define goabiinternal void @internal_statepoint_to_abi0() + "go-nosplit" gc "statepoint-example" { +; ASM-LABEL: internal_statepoint_to_abi0: +; ASM: callq "abi0_callee" +; A statepoint label records the return PC between the call and the repair. +; ASM: xorps %xmm15, %xmm15 +; ASM-NEXT: movq %fs:runtime.tlsg@TPOFF, %r14 +; ASM: retq +entry: + call goabi0 token (i64, i32, ptr, i32, i32, ...) + @llvm.experimental.gc.statepoint.p0( + i64 0, i32 0, ptr elementtype(void ()) @"abi0_callee", + i32 0, i32 0, i32 0, i32 0) + ret void +} + +define goabiinternal i64 @internal_statepoint_result_from_abi0() + "go-nosplit" gc "statepoint-example" { +; The late machine pass repairs the reserved state immediately after the +; statepoint call. The ABI0 result remains in the caller frame and is loaded +; afterwards. +; ASM-LABEL: internal_statepoint_result_from_abi0: +; ASM: callq "abi0_result" +; ASM: xorps %xmm15, %xmm15 +; ASM-NEXT: movq %fs:runtime.tlsg@TPOFF, %r14 +; ASM-O0-NEXT: movq %rsp, [[RESULT_BASE:%r[a-z0-9]+]] +; ASM-O0-NEXT: movq ([[RESULT_BASE]]), %rax +; ASM-O2-NEXT: movq (%rsp), %rax +; ASM: retq +entry: + %token = call goabi0 token (i64, i32, ptr, i32, i32, ...) + @llvm.experimental.gc.statepoint.p0( + i64 0, i32 0, ptr elementtype(i64 ()) @"abi0_result", + i32 0, i32 0, i32 0, i32 0) + %result = call i64 @llvm.experimental.gc.result.i64(token %token) + ret i64 %result +} + +define goabiinternal i8 @go_local_is_sp_relative() "go-nosplit" + "frame-pointer"="non-leaf" { +; Go context restoration can restore SP while clearing BP. Keep local slots +; usable across that non-local resume while retaining the frame-pointer chain. +; ASM-LABEL: go_local_is_sp_relative: +; ASM: pushq %rbp +; ASM: movq %rsp, %rbp +; ASM: movb $7, {{[0-9]+}}(%rsp) +; ASM: callq internal_callee +; ASM: mov{{(b|zbl)}} {{[0-9]+}}(%rsp), %{{(al|eax)}} +; ASM: popq %rbp +; ASM: retq +entry: + %local = alloca i8, align 1 + store volatile i8 7, ptr %local, align 1 + call goabiinternal void @internal_callee() + %value = load volatile i8, ptr %local, align 1 + ret i8 %value +} + +declare token @llvm.experimental.gc.statepoint.p0( + i64 immarg, i32 immarg, ptr, i32 immarg, i32 immarg, ...) +declare i64 @llvm.experimental.gc.result.i64(token) + +; Each repair has one symbol-free R_TLS_LE relocation, matching native x86 Go +; objects. Calls retain their ABI-specific named targets. +; OBJ-NOT: nonpkgref {{[0-9]+}}: runtime.tlsg +; OBJ: reloc {{.*}} type=15 {{.*}} kind=unknown pkg=0 sym=0 +; OBJ: reloc {{.*}} kind=R_CALL +; OBJ: reloc {{.*}} kind=R_CALL +; OBJ: reloc {{.*}} type=15 {{.*}} kind=unknown pkg=0 sym=0 +; OBJ: reloc {{.*}} kind=R_CALL +; OBJ: reloc {{.*}} type=15 {{.*}} kind=unknown pkg=0 sym=0 +; OBJ: reloc {{.*}} kind=R_CALL +; OBJ: reloc {{.*}} type=15 {{.*}} kind=unknown pkg=0 sym=0 diff --git a/llvm/test/CodeGen/X86/go-gc-write-barrier.ll b/llvm/test/CodeGen/X86/go-gc-write-barrier.ll index 0a45b2abdd49e..c0962c5eb73d3 100644 --- a/llvm/test/CodeGen/X86/go-gc-write-barrier.ll +++ b/llvm/test/CodeGen/X86/go-gc-write-barrier.ll @@ -25,6 +25,8 @@ define goabiinternal ptr @acquire_one() gc "statepoint-example" { ; PSEUDO-SAME: implicit $rsp ; ; ASM-LABEL: acquire_one: +; The write-barrier entry is a dedicated ABIInternal thunk with its own +; clobber contract, not an ABI0 transition. ; ASM: callq runtime.gcWriteBarrier1 ; ASM-NEXT: movq %r11, %rax %buf = call ptr @llvm.go.gc.write.barrier(i32 1) diff --git a/llvm/test/CodeGen/X86/go-stack-alignment.ll b/llvm/test/CodeGen/X86/go-stack-alignment.ll index c1a0034cfe6dc..89c2fe02f0dbd 100644 --- a/llvm/test/CodeGen/X86/go-stack-alignment.ll +++ b/llvm/test/CodeGen/X86/go-stack-alignment.ll @@ -12,9 +12,10 @@ define goabiinternal void @stack_slot(ptr %value) #0 { ; CHECK-NOT: AND64 ; CHECK: $rsp = frame-setup SUB64ri32 $rsp, 40 ; CHECK-NOT: MOVAPSmr -; CHECK: MOVUPSmr $rbp, 1, $noreg, -16, $noreg +; Go locals stay SP-relative because runtime context restoration may clear BP. +; CHECK: MOVUPSmr $rsp, 1, $noreg, 24, $noreg ; CHECK-SAME: align 8 -; CHECK: MOVUPSmr $rbp, 1, $noreg, -32, $noreg +; CHECK: MOVUPSmr $rsp, 1, $noreg, 8, $noreg ; CHECK-SAME: align 8 ; CHECK: CALL64pcrel32 @sink entry: diff --git a/llvm/test/CodeGen/X86/goobj-pcsp-cfg.ll b/llvm/test/CodeGen/X86/goobj-pcsp-cfg.ll index 63ac8207196cb..86f136c5eab90 100644 --- a/llvm/test/CodeGen/X86/goobj-pcsp-cfg.ll +++ b/llvm/test/CodeGen/X86/goobj-pcsp-cfg.ll @@ -43,7 +43,7 @@ attributes #0 = { "frame-pointer"="non-leaf" } ; ASM: callq runtime.GC ; ASM: callq "runtime.morestack_noctxt" -; The return occupies PC quanta 57-58. The out-of-line then block at 59-66 +; The return occupies PC quanta 59-60. The out-of-line then block at 61-68 ; restores the 24-byte frame depth before morestack restores the entry depth. -; OBJ: aux 0.3: type=pcsp target= pc=[0-7:0,7-14:8,14-57:24,57-58:8,58-59:0,59-66:24,66-98:0] -; OBJ: reloc 0.2: off=60 size=4 type=7 add=0 target=runtime.GC +; OBJ: aux 0.3: type=pcsp target= pc=[0-7:0,7-14:8,14-59:24,59-60:8,60-61:0,61-68:24,68-100:0] +; OBJ: reloc 0.2: off=62 size=4 type=7 add=0 target=runtime.GC diff --git a/llvm/test/CodeGen/X86/goobj-stack-growth-metadata.ll b/llvm/test/CodeGen/X86/goobj-stack-growth-metadata.ll index 34cdba82af48b..20fa977ad695e 100644 --- a/llvm/test/CodeGen/X86/goobj-stack-growth-metadata.ll +++ b/llvm/test/CodeGen/X86/goobj-stack-growth-metadata.ll @@ -88,6 +88,8 @@ join: ; ASM: cmpq 16(%r14), %r12 ; ASM: ja [[BIG_BODY:.LBB0_[0-9]+]] ; ASM: [[BIG_MORESTACK]]: +; morestack resumes the function through runtime.gogo rather than returning as +; an ordinary ABI0 callee, so there is no call-boundary repair sequence here. ; ASM: callq "runtime.morestack_noctxt" ; ASM-NEXT: movq 8(%rsp), %rax ; ASM-NEXT: jmp [[BIG_CHECK]]