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