[llvm] [MC][AsmPrinter] Introduce llvm_reg_offset pseudo cfi instruction. (PR #125104)
Mikhail Gudim via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 11 01:50:27 PST 2025
https://github.com/mgudim updated https://github.com/llvm/llvm-project/pull/125104
>From bb72c5f54e6ca438cd1aa6e7ce7e30651b0d56d5 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
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 2fa7d73e1fa25ad..2efa4eccac1b31f 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 2a146eb15f709da..46db960e7bd2898 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 be8393cd3867466..5fbd5b535f724a9 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 7153902fe2e7a6a..9d964155f8564ab 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 d7cd06759cfbb88..b7ddab9afa385a0 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 a4e513d05f9cd01..40773c266d17e27 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: {
+ Register 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 231d66607b700b4..10c1b23315c1f1e 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 c17e9151ee487ec..4b9af91b6ea27c5 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");
@@ -1518,6 +1557,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 000000000000000..376d18046f1721e
--- /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 000000000000000..46bfa1673136e30
--- /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
More information about the llvm-commits
mailing list