This patch is essentially an alternative to https://reviews.llvm.org/D75836 and was mentioned by @lhames in a comment.
The gist of the issue is that Mach-O has restrictions on which kind of sections are allowed after debug info has been emitted, which is also properly asserted within LLVM. Problem is that stack maps are currently emitted as one of the last sections in each target-specific AsmPrinter so far, which would cause the assertion to trigger. The current approach of special casing for the `__LLVM_STACKMAPS` section is not viable either, as downstream users can overwrite the stackmap format using plugins, which may want to use different sections.
This patch fixes the issue by emitting the stack map earlier, right before debug info is emitted. The way this is implemented is by taking the choice when to emit the StackMap away from the target AsmPrinter and doing so in the base class. The only disadvantage of this approach is that the `StackMaps` member is now part of the base class, even for targets that do not support them. This is functionaly not a problem however, as emitting an empty `StackMaps` is a no-op.
Differential Revision: https://reviews.llvm.org/D132708
#include "llvm/CodeGen/AsmPrinterHandler.h"
#include "llvm/CodeGen/DwarfStringPoolEntry.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/StackMaps.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/Support/ErrorHandling.h"
#include <cstdint>
class Module;
class PseudoProbeHandler;
class raw_ostream;
-class StackMaps;
class StringRef;
class TargetLoweringObjectFile;
class TargetMachine;
std::vector<HandlerInfo> Handlers;
size_t NumUserHandlers = 0;
+ StackMaps SM;
+
private:
/// If generated on the fly this own the instance.
std::unique_ptr<MachineDominatorTree> OwnedMDT;
void emitGlobalGOTEquivs();
/// Emit the stack maps.
- void emitStackMaps(StackMaps &SM);
+ void emitStackMaps();
//===------------------------------------------------------------------===//
// Overridable Hooks
AsmPrinter::AsmPrinter(TargetMachine &tm, std::unique_ptr<MCStreamer> Streamer)
: MachineFunctionPass(ID), TM(tm), MAI(tm.getMCAsmInfo()),
- OutContext(Streamer->getContext()), OutStreamer(std::move(Streamer)) {
+ OutContext(Streamer->getContext()), OutStreamer(std::move(Streamer)),
+ SM(*this) {
VerboseAsm = OutStreamer->isVerboseAsm();
}
if (auto *TS = OutStreamer->getTargetStreamer())
TS->emitConstantPools();
+ // Emit Stack maps before any debug info. Mach-O requires that no data or
+ // text sections come after debug info has been emitted. This matters for
+ // stack maps as they are arbitrary data, and may even have a custom format
+ // through user plugins.
+ emitStackMaps();
+
// Finalize debug and EH information.
for (const HandlerInfo &HI : Handlers) {
NamedRegionTimer T(HI.TimerName, HI.TimerDescription, HI.TimerGroupName,
report_fatal_error("no GCMetadataPrinter registered for GC: " + Twine(Name));
}
-void AsmPrinter::emitStackMaps(StackMaps &SM) {
+void AsmPrinter::emitStackMaps() {
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
bool NeedsDefault = false;
if (SegName == "__DWARF")
CreatedADWARFSection = true;
else if (Created && DWARFMustBeAtTheEnd && !canGoAfterDWARF(MSec))
- assert((!CreatedADWARFSection ||
- Section == getContext().getObjectFileInfo()->getStackMapSection())
- && "Creating regular section after DWARF");
+ assert(!CreatedADWARFSection && "Creating regular section after DWARF");
// Output a linker-local symbol so we don't need section-relative local
// relocations. The linker hates us when we do that.
class AArch64AsmPrinter : public AsmPrinter {
AArch64MCInstLower MCInstLowering;
- StackMaps SM;
FaultMaps FM;
const AArch64Subtarget *STI;
bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = false;
public:
AArch64AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this),
- SM(*this), FM(*this) {}
+ FM(*this) {}
StringRef getPassName() const override { return "AArch64 Assembly Printer"; }
// generates code that does this, it is always safe to set.
OutStreamer->emitAssemblerFlag(MCAF_SubsectionsViaSymbols);
}
-
+
// Emit stack and fault map information.
- emitStackMaps(SM);
FM.serializeToFaultMapSection();
}
MCSymbol *>
TOC;
const PPCSubtarget *Subtarget = nullptr;
- StackMaps SM;
public:
explicit PPCAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)), SM(*this) {}
+ : AsmPrinter(TM, std::move(Streamer)) {}
StringRef getPassName() const override { return "PowerPC Assembly Printer"; }
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) override;
- void emitEndOfAsmFile(Module &M) override;
-
void LowerSTACKMAP(StackMaps &SM, const MachineInstr &MI);
void LowerPATCHPOINT(StackMaps &SM, const MachineInstr &MI);
void EmitTlsCall(const MachineInstr *MI, MCSymbolRefExpr::VariantKind VK);
return TOCEntry;
}
-void PPCAsmPrinter::emitEndOfAsmFile(Module &M) {
- emitStackMaps(SM);
-}
-
void PPCAsmPrinter::LowerSTACKMAP(StackMaps &SM, const MachineInstr &MI) {
unsigned NumNOPBytes = MI.getOperand(1).getImm();
return false;
}
-void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) {
- emitStackMaps(SM);
-}
-
void SystemZAsmPrinter::emitFunctionBodyEnd() {
if (TM.getTargetTriple().isOSzOS()) {
// Emit symbol for the end of function if the z/OS target streamer
class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
private:
- StackMaps SM;
MCSymbol *CurrentFnPPA1Sym; // PPA1 Symbol.
MCSymbol *CurrentFnEPMarkerSym; // Entry Point Marker.
public:
SystemZAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)), SM(*this),
- CurrentFnPPA1Sym(nullptr), CurrentFnEPMarkerSym(nullptr) {}
+ : AsmPrinter(TM, std::move(Streamer)), CurrentFnPPA1Sym(nullptr),
+ CurrentFnEPMarkerSym(nullptr) {}
// Override AsmPrinter.
StringRef getPassName() const override { return "SystemZ Assembly Printer"; }
void emitInstruction(const MachineInstr *MI) override;
void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override;
- void emitEndOfAsmFile(Module &M) override;
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &OS) override;
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
X86AsmPrinter::X86AsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)), SM(*this), FM(*this) {}
+ : AsmPrinter(TM, std::move(Streamer)), FM(*this) {}
//===----------------------------------------------------------------------===//
// Primitive Helper Functions.
// global table for symbol lookup.
emitNonLazyStubs(MMI, *OutStreamer);
- // Emit stack and fault map information.
- emitStackMaps(SM);
+ // Emit fault map information.
FM.serializeToFaultMapSection();
// This flag tells the linker that no global symbols contain code that fall
OutStreamer->emitSymbolAttribute(S, MCSA_Global);
return;
}
- emitStackMaps(SM);
} else if (TT.isOSBinFormatELF()) {
- emitStackMaps(SM);
FM.serializeToFaultMapSection();
}
class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
const X86Subtarget *Subtarget = nullptr;
- StackMaps SM;
FaultMaps FM;
std::unique_ptr<MCCodeEmitter> CodeEmitter;
bool EmitFPOData = false;
--- /dev/null
+; RUN: llc -verify-machineinstrs < %s | FileCheck %s
+
+; Used to crash with assertions when emitting object files.
+; RUN: llc -filetype=obj %s -o /dev/null
+
+; Check stack map section is emitted before debug info.
+; CHECK: .section __LLVM_STACKMAPS
+; CHECK: .section __DWARF
+
+target triple = "x86_64-apple-macosx12.0"
+
+declare void @func()
+
+define ptr addrspace(1) @test1(ptr addrspace(1) %arg) gc "statepoint-example" {
+entry:
+ %safepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void ()) @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(ptr addrspace(1) %arg)]
+ %reloc1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0)
+ ret ptr addrspace(1) %reloc1
+}
+
+declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
+declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2}
+!0 = distinct !DICompileUnit(language: DW_LANG_C89, producer: "clang", isOptimized: true, emissionKind: FullDebug, file: !1, enums: !{}, retainedTypes: !{})
+!1 = !DIFile(filename: "t.c", directory: "")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
; Without removal of duplicate entries, the size is 62 lines
; CHECK: .section .llvm_stackmaps,{{.*$}}
; CHECK-NEXT:{{(.+$[[:space:]]){48}[[:space:]]}}
-; CHECK-NOT:{{.|[[:space:]]}}
+; CHECK-SAME: .section
target triple = "x86_64-pc-linux-gnu"