[MC] Support .cfi_label
GNU assembler 2.26 introduced the .cfi_label directive. It does not expand to any CFI instructions, but defines a label in .eh_frame/.debug_frame, which can be used by runtime patching code to locate the FDE. .cfi_label is not allowed for CIE's initial instructions, and can therefore be used to force the next instruction to be placed in a FDE instead of a CIE. In glibc since 2018, sysdeps/riscv/start.S utilizes .cfi_label to force DW_CFA_undefined to be placed in a FDE. arc/csky/loongarch ports have copied this use. ``` .cfi_startproc // DW_CFA_undefined is allowed for CIE's initial instructions. // Without .cfi_label, gas would place DW_CFA_undefined in a CIE. .cfi_label .Ldummy .cfi_undefined ra .cfi_endproc ``` No CFI instruction is associated with .cfi_label, so the `case MCCFIInstruction::OpLabel:` code in BOLT is unreachable and onlt to make -Wswitch happy. Close #97222 Pull Request: https://github.com/llvm/llvm-project/pull/97922
This commit is contained in:
@@ -2538,6 +2538,7 @@ private:
|
||||
case MCCFIInstruction::OpWindowSave:
|
||||
case MCCFIInstruction::OpNegateRAState:
|
||||
case MCCFIInstruction::OpLLVMDefAspaceCfa:
|
||||
case MCCFIInstruction::OpLabel:
|
||||
llvm_unreachable("unsupported CFI opcode");
|
||||
break;
|
||||
case MCCFIInstruction::OpRememberState:
|
||||
@@ -2675,6 +2676,7 @@ struct CFISnapshotDiff : public CFISnapshot {
|
||||
case MCCFIInstruction::OpWindowSave:
|
||||
case MCCFIInstruction::OpNegateRAState:
|
||||
case MCCFIInstruction::OpLLVMDefAspaceCfa:
|
||||
case MCCFIInstruction::OpLabel:
|
||||
llvm_unreachable("unsupported CFI opcode");
|
||||
return false;
|
||||
case MCCFIInstruction::OpRememberState:
|
||||
@@ -2823,6 +2825,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
|
||||
case MCCFIInstruction::OpWindowSave:
|
||||
case MCCFIInstruction::OpNegateRAState:
|
||||
case MCCFIInstruction::OpLLVMDefAspaceCfa:
|
||||
case MCCFIInstruction::OpLabel:
|
||||
llvm_unreachable("unsupported CFI opcode");
|
||||
break;
|
||||
case MCCFIInstruction::OpGnuArgsSize:
|
||||
|
||||
@@ -500,7 +500,8 @@ public:
|
||||
OpRegister,
|
||||
OpWindowSave,
|
||||
OpNegateRAState,
|
||||
OpGnuArgsSize
|
||||
OpGnuArgsSize,
|
||||
OpLabel,
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -519,6 +520,7 @@ private:
|
||||
unsigned Register;
|
||||
unsigned Register2;
|
||||
} RR;
|
||||
MCSymbol *CfiLabel;
|
||||
} U;
|
||||
OpType Operation;
|
||||
SMLoc Loc;
|
||||
@@ -544,6 +546,12 @@ private:
|
||||
U.RIA = {R, O, AS};
|
||||
}
|
||||
|
||||
MCCFIInstruction(OpType Op, MCSymbol *L, MCSymbol *CfiLabel, SMLoc Loc)
|
||||
: Label(L), Operation(Op), Loc(Loc) {
|
||||
assert(Op == OpLabel);
|
||||
U.CfiLabel = CfiLabel;
|
||||
}
|
||||
|
||||
public:
|
||||
/// .cfi_def_cfa defines a rule for computing CFA as: take address from
|
||||
/// Register and add Offset to it.
|
||||
@@ -664,6 +672,11 @@ public:
|
||||
return MCCFIInstruction(OpGnuArgsSize, L, 0, Size, Loc);
|
||||
}
|
||||
|
||||
static MCCFIInstruction createLabel(MCSymbol *L, MCSymbol *CfiLabel,
|
||||
SMLoc Loc) {
|
||||
return MCCFIInstruction(OpLabel, L, CfiLabel, Loc);
|
||||
}
|
||||
|
||||
OpType getOperation() const { return Operation; }
|
||||
MCSymbol *getLabel() const { return Label; }
|
||||
|
||||
@@ -698,6 +711,11 @@ public:
|
||||
return U.RI.Offset;
|
||||
}
|
||||
|
||||
MCSymbol *getCfiLabel() const {
|
||||
assert(Operation == OpLabel);
|
||||
return U.CfiLabel;
|
||||
}
|
||||
|
||||
StringRef getValues() const {
|
||||
assert(Operation == OpEscape);
|
||||
return StringRef(&Values[0], Values.size());
|
||||
|
||||
@@ -1019,6 +1019,7 @@ public:
|
||||
SMLoc Loc = {});
|
||||
virtual void emitCFIWindowSave(SMLoc Loc = {});
|
||||
virtual void emitCFINegateRAState(SMLoc Loc = {});
|
||||
virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name);
|
||||
|
||||
virtual void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc = SMLoc());
|
||||
virtual void emitWinCFIEndProc(SMLoc Loc = SMLoc());
|
||||
|
||||
@@ -248,6 +248,7 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
|
||||
case MCCFIInstruction::OpWindowSave:
|
||||
case MCCFIInstruction::OpNegateRAState:
|
||||
case MCCFIInstruction::OpGnuArgsSize:
|
||||
case MCCFIInstruction::OpLabel:
|
||||
break;
|
||||
}
|
||||
if (CSRReg || CSROffset) {
|
||||
|
||||
@@ -356,6 +356,7 @@ public:
|
||||
void emitCFIWindowSave(SMLoc Loc) override;
|
||||
void emitCFINegateRAState(SMLoc Loc) override;
|
||||
void emitCFIReturnColumn(int64_t Register) override;
|
||||
void emitCFILabelDirective(SMLoc Loc, StringRef Name) override;
|
||||
|
||||
void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override;
|
||||
void emitWinCFIEndProc(SMLoc Loc) override;
|
||||
@@ -2126,6 +2127,12 @@ void MCAsmStreamer::emitCFIReturnColumn(int64_t Register) {
|
||||
EmitEOL();
|
||||
}
|
||||
|
||||
void MCAsmStreamer::emitCFILabelDirective(SMLoc Loc, StringRef Name) {
|
||||
MCStreamer::emitCFILabelDirective(Loc, Name);
|
||||
OS << "\t.cfi_label " << Name;
|
||||
EmitEOL();
|
||||
}
|
||||
|
||||
void MCAsmStreamer::emitCFIBKeyFrame() {
|
||||
MCStreamer::emitCFIBKeyFrame();
|
||||
OS << "\t.cfi_b_key_frame";
|
||||
|
||||
@@ -1465,6 +1465,9 @@ void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) {
|
||||
case MCCFIInstruction::OpEscape:
|
||||
Streamer.emitBytes(Instr.getValues());
|
||||
return;
|
||||
case MCCFIInstruction::OpLabel:
|
||||
Streamer.emitLabel(Instr.getCfiLabel(), Instr.getLoc());
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("Unhandled case in switch");
|
||||
}
|
||||
|
||||
@@ -520,6 +520,7 @@ private:
|
||||
DK_CFI_UNDEFINED,
|
||||
DK_CFI_REGISTER,
|
||||
DK_CFI_WINDOW_SAVE,
|
||||
DK_CFI_LABEL,
|
||||
DK_CFI_B_KEY_FRAME,
|
||||
DK_MACROS_ON,
|
||||
DK_MACROS_OFF,
|
||||
@@ -622,6 +623,7 @@ private:
|
||||
bool parseDirectiveCFIReturnColumn(SMLoc DirectiveLoc);
|
||||
bool parseDirectiveCFISignalFrame(SMLoc DirectiveLoc);
|
||||
bool parseDirectiveCFIUndefined(SMLoc DirectiveLoc);
|
||||
bool parseDirectiveCFILabel(SMLoc DirectiveLoc);
|
||||
|
||||
// macro directives
|
||||
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
|
||||
@@ -2224,6 +2226,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
|
||||
return parseDirectiveCFIRegister(IDLoc);
|
||||
case DK_CFI_WINDOW_SAVE:
|
||||
return parseDirectiveCFIWindowSave(IDLoc);
|
||||
case DK_CFI_LABEL:
|
||||
return parseDirectiveCFILabel(IDLoc);
|
||||
case DK_MACROS_ON:
|
||||
case DK_MACROS_OFF:
|
||||
return parseDirectiveMacrosOnOff(IDVal);
|
||||
@@ -4488,6 +4492,19 @@ bool AsmParser::parseDirectiveCFIUndefined(SMLoc DirectiveLoc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// parseDirectiveCFILabel
|
||||
/// ::= .cfi_label label
|
||||
bool AsmParser::parseDirectiveCFILabel(SMLoc Loc) {
|
||||
StringRef Name;
|
||||
Loc = Lexer.getLoc();
|
||||
if (parseIdentifier(Name))
|
||||
return TokError("expected identifier");
|
||||
if (parseEOL())
|
||||
return true;
|
||||
getStreamer().emitCFILabelDirective(Loc, Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// parseDirectiveAltmacro
|
||||
/// ::= .altmacro
|
||||
/// ::= .noaltmacro
|
||||
@@ -5560,6 +5577,7 @@ void AsmParser::initializeDirectiveKindMap() {
|
||||
DirectiveKindMap[".cfi_undefined"] = DK_CFI_UNDEFINED;
|
||||
DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
|
||||
DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
|
||||
DirectiveKindMap[".cfi_label"] = DK_CFI_LABEL;
|
||||
DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
|
||||
DirectiveKindMap[".cfi_mte_tagged_frame"] = DK_CFI_MTE_TAGGED_FRAME;
|
||||
DirectiveKindMap[".macros_on"] = DK_MACROS_ON;
|
||||
|
||||
@@ -689,6 +689,13 @@ void MCStreamer::emitCFIReturnColumn(int64_t Register) {
|
||||
CurFrame->RAReg = Register;
|
||||
}
|
||||
|
||||
void MCStreamer::emitCFILabelDirective(SMLoc Loc, StringRef Name) {
|
||||
MCSymbol *Label = emitCFILabel();
|
||||
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
||||
if (MCDwarfFrameInfo *F = getCurrentDwarfFrameInfo())
|
||||
F->Instructions.push_back(MCCFIInstruction::createLabel(Label, Sym, Loc));
|
||||
}
|
||||
|
||||
WinEH::FrameInfo *MCStreamer::EnsureValidWinFrameInfo(SMLoc Loc) {
|
||||
const MCAsmInfo *MAI = Context.getAsmInfo();
|
||||
if (!MAI->usesWindowsCFI()) {
|
||||
|
||||
61
llvm/test/MC/ELF/cfi-label.s
Normal file
61
llvm/test/MC/ELF/cfi-label.s
Normal file
@@ -0,0 +1,61 @@
|
||||
# RUN: llvm-mc -triple x86_64 %s | FileCheck %s --check-prefix=ASM
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
|
||||
# RUN: llvm-readelf -sX %t | FileCheck %s --check-prefix=SYMTAB
|
||||
# RUN: llvm-dwarfdump --eh-frame %t | FileCheck %s
|
||||
|
||||
# RUN: not llvm-mc -filetype=obj -triple=x86_64 --defsym ERR=1 %s -o /dev/null 2>&1 | \
|
||||
# RUN: FileCheck %s --check-prefix=ERR --implicit-check-not=error:
|
||||
|
||||
# ASM: nop
|
||||
# ASM-NEXT: .cfi_label cfi1
|
||||
# ASM-NEXT: .cfi_escape 0x00
|
||||
# ASM: .globl cfi2
|
||||
# ASM-NEXT: .cfi_label cfi2
|
||||
# ASM: nop
|
||||
# ASM-NEXT: .cfi_label .Lcfi3
|
||||
|
||||
# SYMTAB: 000000000000002b 0 NOTYPE LOCAL DEFAULT 3 (.eh_frame) cfi1
|
||||
# SYMTAB: 000000000000002d 0 NOTYPE GLOBAL DEFAULT 3 (.eh_frame) cfi2
|
||||
# SYMTAB-NOT: {{.}}
|
||||
|
||||
# CHECK: DW_CFA_remember_state:
|
||||
# CHECK-NEXT: DW_CFA_advance_loc: 1 to 0x1
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_advance_loc: 1 to 0x2
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_advance_loc: 1 to 0x3
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_nop:
|
||||
# CHECK-NEXT: DW_CFA_restore_state:
|
||||
|
||||
.globl foo
|
||||
foo:
|
||||
.cfi_startproc
|
||||
.cfi_remember_state
|
||||
nop
|
||||
.cfi_label cfi1
|
||||
.cfi_escape 0
|
||||
nop
|
||||
.globl cfi2
|
||||
.cfi_label cfi2
|
||||
.cfi_escape 0, 0
|
||||
nop
|
||||
.cfi_label .Lcfi3
|
||||
.cfi_escape 0, 0, 0
|
||||
.cfi_restore_state
|
||||
ret
|
||||
|
||||
# ERR: [[#@LINE+10]]:1: error: this directive must appear between .cfi_startproc and .cfi_endproc directives
|
||||
.ifdef ERR
|
||||
# ERR: [[#@LINE+1]]:12: error: symbol 'foo' is already defined
|
||||
.cfi_label foo
|
||||
# ERR: [[#@LINE+1]]:12: error: symbol '.Lcfi3' is already defined
|
||||
.cfi_label .Lcfi3
|
||||
.endif
|
||||
.cfi_endproc
|
||||
|
||||
.ifdef ERR
|
||||
.cfi_label after_endproc
|
||||
.endif
|
||||
Reference in New Issue
Block a user