Skip to content

[MC][AsmPrinter] Introduce llvm_reg_offset pseudo cfi instruction. #125104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions llvm/include/llvm/MC/MCDwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -504,6 +505,7 @@ class MCCFIInstruction {
OpRestoreState,
OpOffset,
OpLLVMDefAspaceCfa,
OpLLVMRegOffset,
OpDefCfaRegister,
OpDefCfaOffset,
OpDefCfa,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 ||
Expand All @@ -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 {
Expand All @@ -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 ||
Expand All @@ -748,7 +785,7 @@ class MCCFIInstruction {
}

StringRef getValues() const {
assert(Operation == OpEscape);
assert(Operation == OpEscape || Operation == OpLLVMRegOffset);
return StringRef(&Values[0], Values.size());
}

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/CFIInstrInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the impact of not handling this yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing emits OpLLVMRegOffset yet, I just added this to avoid a warning.

}
if (CSRReg || CSROffset) {
auto It = CSRLocMap.find(CFI.getRegister());
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/CodeGen/MIRParser/MIParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/CodeGen/MachineOperand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
41 changes: 41 additions & 0 deletions llvm/lib/MC/MCDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Copy link
Collaborator

@topperc topperc Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this using llvm's internal numbering for registers? That's not stable across releases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's using DwarfEH register numbers like all other CFIs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see other code does stuff like this:

if (!IsEH)
  Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);

Do we need to do something similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find a good way to pass IsEH. However currently, when we are emitting the same escape elsewhere we don't care about IsEH, so if in this patch if we ignore IsEH things should work the way they are now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need MRI->getDwarfRegNumFromDwarfEHRegNum(FrameReg);`?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think so

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");
Expand Down Expand Up @@ -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");
}
Expand Down
11 changes: 11 additions & 0 deletions llvm/test/CodeGen/MIR/RISCV/cfi-llvm-reg-offset.mir
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions llvm/test/CodeGen/RISCV/cfi-llvm-reg-offset.mir
Original file line number Diff line number Diff line change
@@ -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
Loading