From ead0da59524e80a8c6a9ea3a88b0fd62581fae43 Mon Sep 17 00:00:00 2001 From: Mikhail Gudim Date: Thu, 30 Jan 2025 09:58:58 -0800 Subject: [PATCH] [MC][AsmPrinter] Introduce llvm_reg_offset pseudo cfi instruction. Some targets which have scalable vectors (AArch64, RISCV) emit cfi escape expression of the form "deref(FrameReg + Offset)". Now instead of explicitly building up the expression, we can just emit the llvm_reg_offset. Also, we will need to handle such escape expressions in CFIInstrInserter. Without this pseudo, we would have to try to decode the escape expression inside the CFIInstrInserter. Now, when we have this pseudo, we can understand such escape expressions without decoding. --- llvm/include/llvm/MC/MCDwarf.h | 43 +++++++++++++++++-- .../CodeGen/AsmPrinter/AsmPrinterDwarf.cpp | 4 ++ llvm/lib/CodeGen/CFIInstrInserter.cpp | 2 + llvm/lib/CodeGen/MIRParser/MILexer.cpp | 1 + llvm/lib/CodeGen/MIRParser/MILexer.h | 1 + llvm/lib/CodeGen/MIRParser/MIParser.cpp | 11 +++++ llvm/lib/CodeGen/MachineOperand.cpp | 9 ++++ llvm/lib/MC/MCDwarf.cpp | 41 ++++++++++++++++++ .../CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir | 11 +++++ .../CodeGen/RISCV/cfi-llvm-reg-offset.mir | 25 +++++++++++ 10 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir create mode 100644 llvm/test/CodeGen/RISCV/cfi-llvm-reg-offset.mir diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h index 2fa7d73e1fa25..2efa4eccac1b3 100644 --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -15,6 +15,7 @@ #define LLVM_MC_MCDWARF_H #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -504,6 +505,7 @@ class MCCFIInstruction { OpRestoreState, OpOffset, OpLLVMDefAspaceCfa, + OpLLVMRegOffset, OpDefCfaRegister, OpDefCfaOffset, OpDefCfa, @@ -537,6 +539,11 @@ class MCCFIInstruction { unsigned Register; unsigned Register2; } RR; + struct { + unsigned Register; + unsigned Register2; + int64_t Offset; + } RRO; MCSymbol *CfiLabel; } U; OpType Operation; @@ -569,6 +576,14 @@ class MCCFIInstruction { U.CfiLabel = CfiLabel; } + MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, unsigned R2, int64_t O, + SMLoc Loc, StringRef V, StringRef Comment = "") + : Label(L), Operation(Op), Loc(Loc), Values(V.begin(), V.end()), + Comment(Comment) { + assert(Op == OpLLVMRegOffset); + U.RRO = {R, R2, O}; + } + public: /// .cfi_def_cfa defines a rule for computing CFA as: take address from /// Register and add Offset to it. @@ -634,6 +649,22 @@ class MCCFIInstruction { return MCCFIInstruction(OpRegister, L, Register1, Register2, Loc); } + /// Create the expression (FrameRegister + Offset) and write it to CFAExpr + static void createRegOffsetExpression(unsigned Reg, unsigned FrameReg, + int64_t Offset, + SmallString<64> &CFAExpr); + /// This is a "pseudo CFI" instruction which generates the escape expression + /// deref(FrameReg + Offset) for the register Reg. + static MCCFIInstruction createLLVMRegOffset(MCSymbol *L, unsigned Reg, + unsigned FrameReg, int64_t Offset, + SMLoc Loc = {}, + StringRef Comment = "") { + SmallString<64> CFAExpr; + createRegOffsetExpression(Reg, FrameReg, Offset, CFAExpr); + return MCCFIInstruction(OpLLVMRegOffset, L, Reg, FrameReg, Offset, Loc, + CFAExpr, Comment); + } + /// .cfi_window_save SPARC register window is saved. static MCCFIInstruction createWindowSave(MCSymbol *L, SMLoc Loc = {}) { return MCCFIInstruction(OpWindowSave, L, 0, INT64_C(0), Loc); @@ -715,6 +746,8 @@ class MCCFIInstruction { return U.RR.Register; if (Operation == OpLLVMDefAspaceCfa) return U.RIA.Register; + if (Operation == OpLLVMRegOffset) + return U.RRO.Register; assert(Operation == OpDefCfa || Operation == OpOffset || Operation == OpRestore || Operation == OpUndefined || Operation == OpSameValue || Operation == OpDefCfaRegister || @@ -723,8 +756,10 @@ class MCCFIInstruction { } unsigned getRegister2() const { - assert(Operation == OpRegister); - return U.RR.Register2; + if (Operation == OpRegister) + return U.RR.Register2; + assert(Operation == OpLLVMRegOffset); + return U.RRO.Register2; } unsigned getAddressSpace() const { @@ -735,6 +770,8 @@ class MCCFIInstruction { int64_t getOffset() const { if (Operation == OpLLVMDefAspaceCfa) return U.RIA.Offset; + if (Operation == OpLLVMRegOffset) + return U.RRO.Offset; assert(Operation == OpDefCfa || Operation == OpOffset || Operation == OpRelOffset || Operation == OpDefCfaOffset || Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize || @@ -748,7 +785,7 @@ class MCCFIInstruction { } StringRef getValues() const { - assert(Operation == OpEscape); + assert(Operation == OpEscape || Operation == OpLLVMRegOffset); return StringRef(&Values[0], Values.size()); } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp index 2a146eb15f709..46db960e7bd28 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -223,6 +223,10 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const { OutStreamer->emitCFILLVMDefAspaceCfa(Inst.getRegister(), Inst.getOffset(), Inst.getAddressSpace(), Loc); break; + case MCCFIInstruction::OpLLVMRegOffset: + OutStreamer->AddComment(Inst.getComment()); + OutStreamer->emitCFIEscape(Inst.getValues(), Loc); + break; case MCCFIInstruction::OpOffset: OutStreamer->emitCFIOffset(Inst.getRegister(), Inst.getOffset(), Loc); break; diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp index 14098bc821617..4843e24bac8ed 100644 --- a/llvm/lib/CodeGen/CFIInstrInserter.cpp +++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp @@ -265,6 +265,8 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) { case MCCFIInstruction::OpLabel: case MCCFIInstruction::OpValOffset: break; + case MCCFIInstruction::OpLLVMRegOffset: + llvm_unreachable("Can't handle llvm_reg_offset yet!"); } if (CSRReg || CSROffset) { auto It = CSRLocMap.find(CFI.getRegister()); diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp index 7153902fe2e7a..9d964155f8564 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp +++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp @@ -231,6 +231,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) { .Case("escape", MIToken::kw_cfi_escape) .Case("def_cfa", MIToken::kw_cfi_def_cfa) .Case("llvm_def_aspace_cfa", MIToken::kw_cfi_llvm_def_aspace_cfa) + .Case("llvm_reg_offset", MIToken::kw_cfi_llvm_reg_offset) .Case("remember_state", MIToken::kw_cfi_remember_state) .Case("restore", MIToken::kw_cfi_restore) .Case("restore_state", MIToken::kw_cfi_restore_state) diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h index d7cd06759cfbb..b7ddab9afa385 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.h +++ b/llvm/lib/CodeGen/MIRParser/MILexer.h @@ -90,6 +90,7 @@ struct MIToken { kw_cfi_escape, kw_cfi_def_cfa, kw_cfi_llvm_def_aspace_cfa, + kw_cfi_llvm_reg_offset, kw_cfi_register, kw_cfi_remember_state, kw_cfi_restore, diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp index 5c8e32d11cfb0..33c5f3eca54f2 100644 --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -2546,6 +2546,16 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) { CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMDefAspaceCfa( nullptr, Reg, Offset, AddressSpace, SMLoc())); break; + case MIToken::kw_cfi_llvm_reg_offset: { + unsigned FrameReg; + if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) || + parseCFIRegister(FrameReg) || expectAndConsume(MIToken::comma) || + parseCFIOffset(Offset)) + return true; + CFIIndex = MF.addFrameInst( + MCCFIInstruction::createLLVMRegOffset(nullptr, Reg, FrameReg, Offset)); + break; + } case MIToken::kw_cfi_remember_state: CFIIndex = MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr)); break; @@ -2925,6 +2935,7 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx, case MIToken::kw_cfi_escape: case MIToken::kw_cfi_def_cfa: case MIToken::kw_cfi_llvm_def_aspace_cfa: + case MIToken::kw_cfi_llvm_reg_offset: case MIToken::kw_cfi_register: case MIToken::kw_cfi_remember_state: case MIToken::kw_cfi_restore: diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index 231d66607b700..10c1b23315c1f 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -710,6 +710,15 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI, OS << ", " << CFI.getOffset(); OS << ", " << CFI.getAddressSpace(); break; + case MCCFIInstruction::OpLLVMRegOffset: + OS << "llvm_reg_offset "; + if (MCSymbol *Label = CFI.getLabel()) + MachineOperand::printSymbol(OS, *Label); + printCFIRegister(CFI.getRegister(), OS, TRI); + OS << ", "; + printCFIRegister(CFI.getRegister2(), OS, TRI); + OS << ", " << CFI.getOffset(); + break; case MCCFIInstruction::OpRelOffset: OS << "rel_offset "; if (MCSymbol *Label = CFI.getLabel()) diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp index 2525ff02878e4..625f3037d1d35 100644 --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -41,6 +41,45 @@ using namespace llvm; +void MCCFIInstruction::createRegOffsetExpression(unsigned Reg, + unsigned FrameReg, + int64_t Offset, + SmallString<64> &CFAExpr) { + // Below all the comments about specific CFI instructions and opcodes are + // taken directly from DWARF Standard version 5. + // + // Encode the expression: (Offset + FrameReg) into Expr: + SmallString<64> Expr; + uint8_t Buffer[16]; + // Encode offset: + Expr.push_back(dwarf::DW_OP_consts); + // The single operand of the DW_OP_consts operation provides a signed + // LEB128 integer constant + Expr.append(Buffer, Buffer + encodeSLEB128(Offset, Buffer)); + // Encode FrameReg: + Expr.push_back((uint8_t)dwarf::DW_OP_bregx); + // The DW_OP_bregx operation provides the sum of two values specified by its + // two operands. The first operand is a register number which is specified by + // an unsigned LEB128 number. The second operand is a signed LEB128 offset. + Expr.append(Buffer, Buffer + encodeULEB128(FrameReg, Buffer)); + Expr.push_back(0); + // The DW_OP_plus operation pops the top two stack entries, adds them + // together, and pushes the result. + Expr.push_back((uint8_t)dwarf::DW_OP_plus); + // Now pass the encoded Expr to DW_CFA_expression: + // + // The DW_CFA_expression instruction takes two operands: an unsigned + // LEB128 value representing a register number, and a DW_FORM_block value + // representing a DWARF expression + CFAExpr.push_back(dwarf::DW_CFA_expression); + CFAExpr.append(Buffer, Buffer + encodeULEB128(Reg, Buffer)); + // DW_FORM_block value is unsigned LEB128 length followed by the number of + // bytes specified by the length + CFAExpr.append(Buffer, Buffer + encodeULEB128(Expr.size(), Buffer)); + CFAExpr.append(Expr.str()); + return; +} + MCSymbol *mcdwarf::emitListsTableHeaderStart(MCStreamer &S) { MCSymbol *Start = S.getContext().createTempSymbol("debug_list_header_start"); MCSymbol *End = S.getContext().createTempSymbol("debug_list_header_end"); @@ -1514,6 +1553,8 @@ void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) { } return; } + case MCCFIInstruction::OpLLVMRegOffset: + llvm_unreachable("Should emit llvm_reg_offset as escape"); } llvm_unreachable("Unhandled case in switch"); } diff --git a/llvm/test/CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir b/llvm/test/CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir new file mode 100644 index 0000000000000..376d18046f172 --- /dev/null +++ b/llvm/test/CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir @@ -0,0 +1,11 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -mtriple=riscv64 -run-pass none -o - %s \ +# RUN: | FileCheck %s + +# This test ensures that the MIR parser parses the llvm_reg_offset cfi instruction correctly. + +name: func +body: | + bb.0: + ; CHECK: CFI_INSTRUCTION llvm_reg_offset $x1, $x2, -42 + CFI_INSTRUCTION llvm_reg_offset $x1, $x2, -42 diff --git a/llvm/test/CodeGen/RISCV/cfi-llvm-reg-offset.mir b/llvm/test/CodeGen/RISCV/cfi-llvm-reg-offset.mir new file mode 100644 index 0000000000000..46bfa1673136e --- /dev/null +++ b/llvm/test/CodeGen/RISCV/cfi-llvm-reg-offset.mir @@ -0,0 +1,25 @@ +# RUN: llc -mtriple=riscv64 -start-after=unpack-mi-bundles -o - %s \ +# RUN: | FileCheck %s + +# Check that `llvm_reg_offset` generates an escape expression corresponding to `deref(FrameReg + Offset)`. See comments in `MCCFIInstruction::createRegOffsetExpression`. +# DW_CFA_expression = 0x10 +# $x1 = 0x01 - the register to be defined +# 0x06 - length of expression encoding the (FrameReg + Offset) +# DW_OP_consts = 0x11 +# le128 signed encoding of -42 = 0x56 +# DW_OP_bregx = 0x92 +# $x2 = 0x02 +# the second argument of DW_OP_bregx = 0x0 +# DW_OP_plus = 0x92 +name: func +body: | + bb.0: + CFI_INSTRUCTION llvm_reg_offset $x1, $x2, -42 + PseudoRET +#CHECK-LABEL: func: +#CHECK: .cfi_startproc +#CHECK: .cfi_escape 0x10, 0x01, 0x06, 0x11, 0x56, 0x92, 0x02, 0x00, 0x22 +#CHECK: ret +#CHECK: .Lfunc_end0: +#CHECK: .size func, .Lfunc_end0-func +#CHECK: .cfi_endproc