[llvm] 714f032 - [SFrames] reland Emit and relax FREs #158154 (#159643)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 18 14:05:56 PDT 2025
Author: Sterling-Augustine
Date: 2025-09-18T14:05:52-07:00
New Revision: 714f032802fd1192b19188daba45de97b825c95d
URL: https://github.com/llvm/llvm-project/commit/714f032802fd1192b19188daba45de97b825c95d
DIFF: https://github.com/llvm/llvm-project/commit/714f032802fd1192b19188daba45de97b825c95d.diff
LOG: [SFrames] reland Emit and relax FREs #158154 (#159643)
[Previously reverted due to msan failures on two uninitialized padding
bits.]
This PR emits and relaxes the FREs generated in the previous PR.
After this change llvm emits usable sframe sections that can be linked
with the gnu linker. There are a few remaining cfi directives to handle
before they are generally usable, however.
The output isn't identical with gnu-gas in every case (this code
produces fewer identical FREs in a row than gas), but I'm reasonably
sure that they are correct regardless. There are even more size
optimizations that can be done later.
Also, while working on the tests, I found a few bugs in older portions
and cleaned those up.
This is a fairly big commit, but I'm not sure how to make it smaller.
Added:
llvm/test/MC/ELF/cfi-sframe-encoding.s
llvm/test/MC/ELF/cfi-sframe-fre-cases.s
Modified:
llvm/include/llvm/BinaryFormat/SFrame.h
llvm/include/llvm/MC/MCAsmBackend.h
llvm/include/llvm/MC/MCAssembler.h
llvm/include/llvm/MC/MCObjectStreamer.h
llvm/include/llvm/MC/MCSFrame.h
llvm/include/llvm/MC/MCSection.h
llvm/lib/MC/MCAssembler.cpp
llvm/lib/MC/MCFragment.cpp
llvm/lib/MC/MCObjectStreamer.cpp
llvm/lib/MC/MCSFrame.cpp
llvm/test/MC/ELF/cfi-sframe.s
Removed:
################################################################################
diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h
index 095db18b9c254..7b58043c60363 100644
--- a/llvm/include/llvm/BinaryFormat/SFrame.h
+++ b/llvm/include/llvm/BinaryFormat/SFrame.h
@@ -117,6 +117,7 @@ template <endianness E> struct FDEInfo {
Info = ((PAuthKey & 1) << 5) | ((static_cast<uint8_t>(FDE) & 1) << 4) |
(static_cast<uint8_t>(FRE) & 0xf);
}
+ uint8_t getFuncInfo() const { return Info; }
};
template <endianness E> struct FuncDescEntry {
@@ -155,6 +156,7 @@ template <endianness E> struct FREInfo {
Info = ((RA & 1) << 7) | ((static_cast<uint8_t>(Sz) & 3) << 5) |
((N & 0xf) << 1) | (static_cast<uint8_t>(Reg) & 1);
}
+ uint8_t getFREInfo() const { return Info; }
};
template <typename T, endianness E> struct FrameRowEntry {
diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h
index 1625355323692..58363f0b671e2 100644
--- a/llvm/include/llvm/MC/MCAsmBackend.h
+++ b/llvm/include/llvm/MC/MCAsmBackend.h
@@ -168,6 +168,7 @@ class LLVM_ABI MCAsmBackend {
virtual bool relaxAlign(MCFragment &F, unsigned &Size) { return false; }
virtual bool relaxDwarfLineAddr(MCFragment &) const { return false; }
virtual bool relaxDwarfCFA(MCFragment &) const { return false; }
+ virtual bool relaxSFrameCFA(MCFragment &) const { return false; }
// Defined by linker relaxation targets to possibly emit LEB128 relocations
// and set Value at the relocated location.
diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h
index 1316d8669239d..6e1d6421b8d33 100644
--- a/llvm/include/llvm/MC/MCAssembler.h
+++ b/llvm/include/llvm/MC/MCAssembler.h
@@ -117,6 +117,7 @@ class MCAssembler {
void relaxBoundaryAlign(MCBoundaryAlignFragment &BF);
void relaxDwarfLineAddr(MCFragment &F);
void relaxDwarfCallFrameFragment(MCFragment &F);
+ void relaxSFrameFragment(MCFragment &DF);
public:
/// Construct a new assembler instance.
diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h
index b9e813b9b0d28..1899cb6331c6f 100644
--- a/llvm/include/llvm/MC/MCObjectStreamer.h
+++ b/llvm/include/llvm/MC/MCObjectStreamer.h
@@ -150,6 +150,9 @@ class MCObjectStreamer : public MCStreamer {
MCSymbol *EndLabel = nullptr) override;
void emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel,
const MCSymbol *Label, SMLoc Loc);
+ void emitSFrameCalculateFuncOffset(const MCSymbol *FunCabsel,
+ const MCSymbol *FREBegin,
+ MCFragment *FDEFrag, SMLoc Loc);
void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line,
unsigned Column, bool PrologueEnd, bool IsStmt,
StringRef FileName, SMLoc Loc) override;
diff --git a/llvm/include/llvm/MC/MCSFrame.h b/llvm/include/llvm/MC/MCSFrame.h
index 8f182a86d1ab1..694aec55aefeb 100644
--- a/llvm/include/llvm/MC/MCSFrame.h
+++ b/llvm/include/llvm/MC/MCSFrame.h
@@ -16,9 +16,14 @@
#ifndef LLVM_MC_MCSFRAME_H
#define LLVM_MC_MCSFRAME_H
+#include "llvm/ADT/SmallVector.h"
+#include <cstdint>
+
namespace llvm {
+class MCContext;
class MCObjectStreamer;
+class MCFragment;
class MCSFrameEmitter {
public:
@@ -26,6 +31,15 @@ class MCSFrameEmitter {
//
// \param Streamer - Emit into this stream.
static void emit(MCObjectStreamer &Streamer);
+
+ // Encode the FRE's function offset.
+ //
+ // \param C - Context.
+ // \param Offset - Offset to encode.
+ // \param Out - Destination of the encoding.
+ // \param FDEFrag - Frag that specifies the encoding format.
+ static void encodeFuncOffset(MCContext &C, uint64_t Offset,
+ SmallVectorImpl<char> &Out, MCFragment *FDEFrag);
};
} // end namespace llvm
diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h
index 12389d623e588..a26e6cfb2158a 100644
--- a/llvm/include/llvm/MC/MCSection.h
+++ b/llvm/include/llvm/MC/MCSection.h
@@ -59,6 +59,7 @@ class MCFragment {
FT_Org,
FT_Dwarf,
FT_DwarfFrame,
+ FT_SFrame,
FT_BoundaryAlign,
FT_SymbolId,
FT_CVInlineLines,
@@ -143,6 +144,12 @@ class MCFragment {
// .loc dwarf directives.
int64_t LineDelta;
} dwarf;
+ struct {
+ // This FRE describes unwind info at AddrDelta from function start.
+ const MCExpr *AddrDelta;
+ // Fragment that records how many bytes of AddrDelta to emit.
+ MCFragment *FDEFragment;
+ } sframe;
} u{};
public:
@@ -296,6 +303,24 @@ class MCFragment {
assert(Kind == FT_Dwarf);
u.dwarf.LineDelta = LineDelta;
}
+
+ //== FT_SFrame functions
+ const MCExpr &getSFrameAddrDelta() const {
+ assert(Kind == FT_SFrame);
+ return *u.sframe.AddrDelta;
+ }
+ void setSFrameAddrDelta(const MCExpr *E) {
+ assert(Kind == FT_SFrame);
+ u.sframe.AddrDelta = E;
+ }
+ MCFragment *getSFrameFDE() const {
+ assert(Kind == FT_SFrame);
+ return u.sframe.FDEFragment;
+ }
+ void setSFrameFDE(MCFragment *F) {
+ assert(Kind == FT_SFrame);
+ u.sframe.FDEFragment = F;
+ }
};
// MCFragment subclasses do not use the fixed-size part or variable-size tail of
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
index b1031d7822604..cee281597cfed 100644
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -22,6 +22,7 @@
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCSFrame.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCValue.h"
@@ -199,6 +200,7 @@ uint64_t MCAssembler::computeFragmentSize(const MCFragment &F) const {
case MCFragment::FT_LEB:
case MCFragment::FT_Dwarf:
case MCFragment::FT_DwarfFrame:
+ case MCFragment::FT_SFrame:
case MCFragment::FT_CVInlineLines:
case MCFragment::FT_CVDefRange:
return F.getSize();
@@ -399,6 +401,7 @@ static void writeFragment(raw_ostream &OS, const MCAssembler &Asm,
case MCFragment::FT_LEB:
case MCFragment::FT_Dwarf:
case MCFragment::FT_DwarfFrame:
+ case MCFragment::FT_SFrame:
case MCFragment::FT_CVInlineLines:
case MCFragment::FT_CVDefRange: {
if (F.getKind() == MCFragment::FT_Data)
@@ -914,6 +917,24 @@ void MCAssembler::relaxDwarfCallFrameFragment(MCFragment &F) {
F.clearVarFixups();
}
+void MCAssembler::relaxSFrameFragment(MCFragment &F) {
+ assert(F.getKind() == MCFragment::FT_SFrame);
+ MCContext &C = getContext();
+ int64_t Value;
+ bool Abs = F.getSFrameAddrDelta().evaluateAsAbsolute(Value, *this);
+ if (!Abs) {
+ C.reportError(F.getSFrameAddrDelta().getLoc(),
+ "invalid CFI advance_loc expression in sframe");
+ F.setSFrameAddrDelta(MCConstantExpr::create(0, C));
+ return;
+ }
+
+ SmallVector<char, 4> Data;
+ MCSFrameEmitter::encodeFuncOffset(Context, Value, Data, F.getSFrameFDE());
+ F.setVarContents(Data);
+ F.clearVarFixups();
+}
+
bool MCAssembler::relaxFragment(MCFragment &F) {
auto Size = computeFragmentSize(F);
switch (F.getKind()) {
@@ -932,6 +953,9 @@ bool MCAssembler::relaxFragment(MCFragment &F) {
case MCFragment::FT_DwarfFrame:
relaxDwarfCallFrameFragment(F);
break;
+ case MCFragment::FT_SFrame:
+ relaxSFrameFragment(F);
+ break;
case MCFragment::FT_BoundaryAlign:
relaxBoundaryAlign(static_cast<MCBoundaryAlignFragment &>(F));
break;
diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp
index 21da79bb0aa30..85d1c5888f1da 100644
--- a/llvm/lib/MC/MCFragment.cpp
+++ b/llvm/lib/MC/MCFragment.cpp
@@ -53,6 +53,7 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
case MCFragment::FT_Org: OS << "Org"; break;
case MCFragment::FT_Dwarf: OS << "Dwarf"; break;
case MCFragment::FT_DwarfFrame: OS << "DwarfCallFrame"; break;
+ case MCFragment::FT_SFrame: OS << "SFrame"; break;
case MCFragment::FT_LEB: OS << "LEB"; break;
case MCFragment::FT_BoundaryAlign: OS<<"BoundaryAlign"; break;
case MCFragment::FT_SymbolId: OS << "SymbolId"; break;
@@ -79,7 +80,8 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
case MCFragment::FT_Align:
case MCFragment::FT_LEB:
case MCFragment::FT_Dwarf:
- case MCFragment::FT_DwarfFrame: {
+ case MCFragment::FT_DwarfFrame:
+ case MCFragment::FT_SFrame: {
if (isLinkerRelaxable())
OS << " LinkerRelaxable";
auto Fixed = getContents();
@@ -129,6 +131,7 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
OS << " LineDelta:" << getDwarfLineDelta();
break;
case MCFragment::FT_DwarfFrame:
+ case MCFragment::FT_SFrame:
OS << " AddrDelta:";
getDwarfAddrDelta().print(OS, nullptr);
break;
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index 59265bc8595ba..701a0836d2c70 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -583,6 +583,19 @@ void MCObjectStreamer::emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel,
newFragment();
}
+void MCObjectStreamer::emitSFrameCalculateFuncOffset(const MCSymbol *FuncBase,
+ const MCSymbol *FREBegin,
+ MCFragment *FDEFrag,
+ SMLoc Loc) {
+ assert(FuncBase && "No function base address");
+ assert(FREBegin && "FRE doesn't describe a location");
+ auto *F = getCurrentFragment();
+ F->Kind = MCFragment::FT_SFrame;
+ F->setSFrameAddrDelta(buildSymbolDiff(*this, FREBegin, FuncBase, Loc));
+ F->setSFrameFDE(FDEFrag);
+ newFragment();
+}
+
void MCObjectStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo,
unsigned Line, unsigned Column,
bool PrologueEnd, bool IsStmt,
diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
index a0d6c80ab72ea..066d1a34e1548 100644
--- a/llvm/lib/MC/MCSFrame.cpp
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -14,6 +14,7 @@
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSymbol.h"
+#include "llvm/Support/Endian.h"
#include "llvm/Support/EndianStream.h"
using namespace llvm;
@@ -33,10 +34,70 @@ struct SFrameFRE {
size_t CFAOffset = 0;
size_t FPOffset = 0;
size_t RAOffset = 0;
- bool FromFP = false;
+ FREInfo<endianness::native> Info;
bool CFARegSet = false;
- SFrameFRE(const MCSymbol *Start) : Label(Start) {}
+ SFrameFRE(const MCSymbol *Start) : Label(Start) { Info.Info = 0; }
+
+ void emitOffset(MCObjectStreamer &S, FREOffset OffsetSize, size_t Offset) {
+ switch (OffsetSize) {
+ case (FREOffset::B1):
+ S.emitInt8(Offset);
+ return;
+ case (FREOffset::B2):
+ S.emitInt16(Offset);
+ return;
+ case (FREOffset::B4):
+ S.emitInt32(Offset);
+ return;
+ }
+ }
+
+ void emit(MCObjectStreamer &S, const MCSymbol *FuncBegin,
+ MCFragment *FDEFrag) {
+ S.emitSFrameCalculateFuncOffset(FuncBegin, Label, FDEFrag, SMLoc());
+
+ // fre_cfa_base_reg_id already set during parsing
+
+ // fre_offset_count
+ unsigned RegsTracked = 1; // always track the cfa.
+ if (FPOffset != 0)
+ ++RegsTracked;
+ if (RAOffset != 0)
+ ++RegsTracked;
+ Info.setOffsetCount(RegsTracked);
+
+ // fre_offset_size
+ if (isInt<8>(CFAOffset) && isInt<8>(FPOffset) && isInt<8>(RAOffset))
+ Info.setOffsetSize(FREOffset::B1);
+ else if (isInt<16>(CFAOffset) && isInt<16>(FPOffset) && isInt<16>(RAOffset))
+ Info.setOffsetSize(FREOffset::B2);
+ else {
+ assert(isInt<32>(CFAOffset) && isInt<32>(FPOffset) &&
+ isInt<32>(RAOffset) && "Offset too big for sframe");
+ Info.setOffsetSize(FREOffset::B4);
+ }
+
+ // No support for fre_mangled_ra_p yet.
+ Info.setReturnAddressSigned(false);
+
+ // sframe_fre_info_word
+ S.emitInt8(Info.getFREInfo());
+
+ // FRE Offsets
+ [[maybe_unused]] unsigned OffsetsEmitted = 1;
+ emitOffset(S, Info.getOffsetSize(), CFAOffset);
+ if (FPOffset) {
+ ++OffsetsEmitted;
+ emitOffset(S, Info.getOffsetSize(), FPOffset);
+ }
+ if (RAOffset) {
+ ++OffsetsEmitted;
+ emitOffset(S, Info.getOffsetSize(), RAOffset);
+ }
+ assert(OffsetsEmitted == RegsTracked &&
+ "Didn't emit the right number of offsets");
+ }
};
// High-level structure to track info needed to emit a sframe_func_desc_entry
@@ -46,11 +107,13 @@ struct SFrameFDE {
const MCDwarfFrameInfo &DFrame;
// Label where this FDE's FREs start.
MCSymbol *FREStart;
+ // Frag where this FDE is emitted.
+ MCFragment *Frag;
// Unwinding fres
SmallVector<SFrameFRE> FREs;
SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
- : DFrame(DF), FREStart(FRES) {}
+ : DFrame(DF), FREStart(FRES), Frag(nullptr) {}
void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) {
MCContext &C = S.getContext();
@@ -74,13 +137,21 @@ struct SFrameFDE {
S.emitInt32(0);
// sfde_func_num_fres
- // TODO: When we actually emit fres, replace 0 with FREs.size()
- S.emitInt32(0);
+ S.emitInt32(FREs.size());
// sfde_func_info word
- FDEInfo<endianness::native> I;
- I.setFuncInfo(0 /* No pauth key */, FDEType::PCInc, FREType::Addr1);
- S.emitInt8(I.Info);
+
+ // All FREs within an FDE share the same sframe::FREType::AddrX. The value
+ // of 'X' is determined by the FRE with the largest offset, which is the
+ // last. This offset isn't known until relax time, so emit a frag which can
+ // calculate that now.
+ //
+ // At relax time, this FDE frag calculates the proper AddrX value (as well
+ // as the rest of the FDE FuncInfo word). Subsequent FRE frags will read it
+ // from this frag and emit the proper number of bytes.
+ Frag = S.getCurrentFragment();
+ S.emitSFrameCalculateFuncOffset(DFrame.Begin, FREs.back().Label, nullptr,
+ SMLoc());
// sfde_func_rep_size. Not relevant in non-PCMASK fdes.
S.emitInt8(0);
@@ -96,13 +167,16 @@ struct SFrameFDE {
class SFrameEmitterImpl {
MCObjectStreamer &Streamer;
SmallVector<SFrameFDE> FDEs;
+ uint32_t TotalFREs;
ABI SFrameABI;
// Target-specific convenience variables to detect when a CFI instruction
// references these registers. Unlike in dwarf frame descriptions, they never
- // escape into the sframe section itself.
+ // escape into the sframe section itself. TODO: These should be retrieved from
+ // the target.
unsigned SPReg;
unsigned FPReg;
unsigned RAReg;
+ int8_t FixedRAOffset;
MCSymbol *FDESubSectionStart;
MCSymbol *FRESubSectionStart;
MCSymbol *FRESubSectionEnd;
@@ -110,12 +184,12 @@ class SFrameEmitterImpl {
bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) {
if (I.getRegister() == SPReg) {
FRE.CFARegSet = true;
- FRE.FromFP = false;
+ FRE.Info.setBaseRegister(BaseReg::SP);
return true;
}
if (I.getRegister() == FPReg) {
FRE.CFARegSet = true;
- FRE.FromFP = true;
+ FRE.Info.setBaseRegister(BaseReg::FP);
return true;
}
Streamer.getContext().reportWarning(
@@ -182,7 +256,8 @@ class SFrameEmitterImpl {
}
public:
- SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) {
+ SFrameEmitterImpl(MCObjectStreamer &Streamer)
+ : Streamer(Streamer), TotalFREs(0) {
assert(Streamer.getContext()
.getObjectFileInfo()
->getSFrameABIArch()
@@ -195,6 +270,7 @@ class SFrameEmitterImpl {
SPReg = 31;
RAReg = 29;
FPReg = 30;
+ FixedRAOffset = 0;
break;
case ABI::AMD64EndianLittle:
SPReg = 7;
@@ -202,6 +278,7 @@ class SFrameEmitterImpl {
// MCDwarfFrameInfo constructor.
RAReg = static_cast<unsigned>(INT_MAX);
FPReg = 6;
+ FixedRAOffset = -8;
break;
}
@@ -219,10 +296,16 @@ class SFrameEmitterImpl {
bool equalIgnoringLocation(const SFrameFRE &Left, const SFrameFRE &Right) {
return Left.CFAOffset == Right.CFAOffset &&
Left.FPOffset == Right.FPOffset && Left.RAOffset == Right.RAOffset &&
- Left.FromFP == Right.FromFP && Left.CFARegSet == Right.CFARegSet;
+ Left.Info.getFREInfo() == Right.Info.getFREInfo() &&
+ Left.CFARegSet == Right.CFARegSet;
}
void buildSFDE(const MCDwarfFrameInfo &DF) {
+ // Functions with zero size can happen with assembler macros and
+ // machine-generated code. They don't need unwind info at all, so
+ // no need to warn.
+ if (atSameLocation(DF.Begin, DF.End))
+ return;
bool Valid = true;
SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol());
// This would have been set via ".cfi_return_column", but
@@ -277,8 +360,11 @@ class SFrameEmitterImpl {
LastLabel = L;
}
}
- if (Valid)
+
+ if (Valid) {
FDEs.push_back(FDE);
+ TotalFREs += FDE.FREs.size();
+ }
}
void emitPreamble() {
@@ -294,13 +380,12 @@ class SFrameEmitterImpl {
// sfh_cfa_fixed_fp_offset
Streamer.emitInt8(0);
// sfh_cfa_fixed_ra_offset
- Streamer.emitInt8(0);
+ Streamer.emitInt8(FixedRAOffset);
// sfh_auxhdr_len
Streamer.emitInt8(0);
// shf_num_fdes
Streamer.emitInt32(FDEs.size());
// shf_num_fres
- uint32_t TotalFREs = 0;
Streamer.emitInt32(TotalFREs);
// shf_fre_len
@@ -322,8 +407,11 @@ class SFrameEmitterImpl {
void emitFREs() {
Streamer.emitLabel(FRESubSectionStart);
- for (auto &FDE : FDEs)
+ for (auto &FDE : FDEs) {
Streamer.emitLabel(FDE.FREStart);
+ for (auto &FRE : FDE.FREs)
+ FRE.emit(Streamer, FDE.DFrame.Begin, FDE.Frag);
+ }
Streamer.emitLabel(FRESubSectionEnd);
}
};
@@ -359,3 +447,55 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
Emitter.emitFDEs();
Emitter.emitFREs();
}
+
+void MCSFrameEmitter::encodeFuncOffset(MCContext &C, uint64_t Offset,
+ SmallVectorImpl<char> &Out,
+ MCFragment *FDEFrag) {
+ // If encoding into the FDE Frag itself, generate the sfde_func_info.
+ if (FDEFrag == nullptr) {
+ // sfde_func_info
+
+ // Offset is the
diff erence between the function start label and the final
+ // FRE's offset, which is the max offset for this FDE.
+ FDEInfo<endianness::native> I;
+ I.Info = 0;
+ if (isUInt<8>(Offset))
+ I.setFREType(FREType::Addr1);
+ else if (isUInt<16>(Offset))
+ I.setFREType(FREType::Addr2);
+ else {
+ assert(isUInt<32>(Offset));
+ I.setFREType(FREType::Addr4);
+ }
+ I.setFDEType(FDEType::PCInc);
+ // TODO: When we support pauth keys, this will need to be retrieved
+ // from the frag itself.
+ I.setPAuthKey(0);
+
+ Out.push_back(I.getFuncInfo());
+ return;
+ }
+
+ const auto &FDEData = FDEFrag->getVarContents();
+ FDEInfo<endianness::native> I;
+ I.Info = FDEData.back();
+ FREType T = I.getFREType();
+ llvm::endianness E = C.getAsmInfo()->isLittleEndian()
+ ? llvm::endianness::little
+ : llvm::endianness::big;
+ // sfre_start_address
+ switch (T) {
+ case FREType::Addr1:
+ assert(isUInt<8>(Offset) && "Miscalculated Sframe FREType");
+ support::endian::write<uint8_t>(Out, Offset, E);
+ break;
+ case FREType::Addr2:
+ assert(isUInt<16>(Offset) && "Miscalculated Sframe FREType");
+ support::endian::write<uint16_t>(Out, Offset, E);
+ break;
+ case FREType::Addr4:
+ assert(isUInt<32>(Offset) && "Miscalculated Sframe FREType");
+ support::endian::write<uint32_t>(Out, Offset, E);
+ break;
+ }
+}
diff --git a/llvm/test/MC/ELF/cfi-sframe-encoding.s b/llvm/test/MC/ELF/cfi-sframe-encoding.s
new file mode 100644
index 0000000000000..1f293030c7dde
--- /dev/null
+++ b/llvm/test/MC/ELF/cfi-sframe-encoding.s
@@ -0,0 +1,85 @@
+# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o
+# RUN: llvm-readelf --sframe %t.o | FileCheck %s
+
+# Tests selection for the proper FDE AddrX encoding at the boundaries
+# between uint8_t, uint16_t, and uint32_t. The first FRE always fits
+# anywhere, because its address-offset is zero. The last FRE
+# determines the smallest AddrX it is possible to use. Align
+# functions to 1024 to make it easier to interpet offsets.
+
+ .cfi_sections .sframe
+
+ .align 1024
+fde0_uses_addr1:
+# CHECK: FuncDescEntry [0] {
+# CHECK: Start FRE Offset: 0x0
+# CHECK-NEXT: Num FREs: 2
+# CHECK: FRE Type: Addr1 (0x0)
+ .cfi_startproc
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x0
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 8
+# CHECK-NEXT: RA Offset: -8
+# CHECK-NEXT: }
+ .fill 0xFF
+ .cfi_def_cfa_offset 16
+# CHECK-NEXT: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0xFF
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 16
+# CHECK-NEXT: RA Offset: -8
+ nop
+ .cfi_endproc
+
+ .align 1024
+fde1_uses_addr2:
+# CHECK: FuncDescEntry [1] {
+# CHECK: Start FRE Offset: 0x6
+# CHECK-NEXT: Num FREs: 2
+# CHECK: FRE Type: Addr2 (0x1)
+ .cfi_startproc
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x400
+ .fill 0xFF + 1
+ .cfi_def_cfa_offset 16
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x500
+ .cfi_endproc
+
+.align 1024
+fde2_uses_addr2:
+# CHECK: FuncDescEntry [2] {
+# CHECK: Start FRE Offset: 0xE
+# CHECK-NEXT: Num FREs: 2
+# CHECK: FRE Type: Addr2 (0x1)
+ .cfi_startproc
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x800
+ .fill 0xFFFF
+ .cfi_def_cfa_offset 16
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x107FF
+ nop
+ .cfi_endproc
+
+ .align 1024
+fde3_uses_addr4:
+# CHECK: FuncDescEntry [3] {
+# CHECK: Start FRE Offset: 0x16
+# CHECK-NEXT: Num FREs: 2
+# CHECK: FRE Type: Addr4 (0x2)
+ .cfi_startproc
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x10800
+ .fill 0xFFFF + 1
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x20800
+ .cfi_def_cfa_offset 16
+ nop
+ .cfi_endproc
+
diff --git a/llvm/test/MC/ELF/cfi-sframe-fre-cases.s b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s
new file mode 100644
index 0000000000000..6d9e8c1b6480f
--- /dev/null
+++ b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s
@@ -0,0 +1,113 @@
+# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o
+# RUN: llvm-readelf --sframe %t.o | FileCheck %s
+
+# Tests selection for the proper FRE::BX encoding at the boundaries
+# between int8_t, int16_t, and int32_t. Ensures the largest offset
+# between CFA, RA, and FP governs. Align functions to 1024 to make it
+# easier to interpet offsets. Some directives require alignment, so
+# it isn't always possible to test exact boundaries.
+
+# Also, check that irrelevant cfi directives don't create new fres,
+# or affect the current ones. Checking the Start Address ensures that
+# the proper FRE gets the proper checks. Using .long makes addresses
+# architecture independent.
+
+ .align 1024
+fde4_fre_offset_sizes:
+# CHECK: FuncDescEntry [0] {
+# CHECK: Start FRE Offset: 0
+# CHECK: FRE Type: Addr1 (0x0)
+ .cfi_startproc
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x0
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 8
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+# Uninteresting register no new fre, no effect on cfa
+ .cfi_offset 0, 8
+ .long 0
+ .cfi_def_cfa_offset 0x78
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x8
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 120
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+# Uninteresting register no new fre, no effect on cfa
+ .cfi_rel_offset 1, 8
+ .long 0
+ .cfi_def_cfa_offset 0x80
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x10
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B2 (0x1)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 128
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+# Uninteresting register no new fre, no effect on cfa
+ .cfi_val_offset 1, 8
+ .long 0
+ .cfi_def_cfa_offset 0x7FFF
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x18
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B2 (0x1)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 32767
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+ .cfi_def_cfa_offset 0x8000
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x1C
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B4 (0x2)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 32768
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+ .cfi_def_cfa_offset 0x8
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x20
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 8
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+ .cfi_adjust_cfa_offset 0x8
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x24
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: SP (0x1)
+# CHECK-NEXT: CFA Offset: 16
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+ .cfi_def_cfa_register 6 # switch to fp
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x28
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B1 (0x0)
+# CHECK-NEXT: Base Register: FP (0x0)
+# CHECK-NEXT: CFA Offset: 16
+# CHECK-NEXT: RA Offset: -8
+ .long 0
+ .cfi_offset 7, 32
+ # sp not the cfa but with large offset still changes encoding.
+ .cfi_offset 6, 0x7FF8
+# CHECK: Frame Row Entry {
+# CHECK-NEXT: Start Address: 0x2C
+# CHECK-NEXT: Return Address Signed: No
+# CHECK-NEXT: Offset Size: B2 (0x1)
+# CHECK-NEXT: Base Register: FP (0x0)
+# CHECK-NEXT: CFA Offset: 16
+# CHECK-NEXT: RA Offset: -8
+# CHECK-NEXT: FP Offset: 32760
+ .long 0
+ .cfi_endproc
diff --git a/llvm/test/MC/ELF/cfi-sframe.s b/llvm/test/MC/ELF/cfi-sframe.s
index ecf77bc3ea6b3..c7116bc874423 100644
--- a/llvm/test/MC/ELF/cfi-sframe.s
+++ b/llvm/test/MC/ELF/cfi-sframe.s
@@ -1,82 +1,85 @@
-// TODO: Add other architectures as they gain sframe support
-// REQUIRES: x86-registered-target
-// RUN: llvm-mc --assemble --filetype=obj --gsframe -triple x86_64 %s -o %t.o
-// RUN: llvm-readelf --sframe %t.o | FileCheck %s
+# RUN: llvm-mc --filetype=obj --gsframe -triple x86_64 %s -o %t.o
+# RUN: llvm-readelf --sframe %t.o | FileCheck %s
.cfi_sections .sframe
-f1:
- .cfi_startproc // FRE 0
+f0:
+ .cfi_startproc # FRE 0
nop
- .cfi_def_cfa_offset 16 // FRE 1
- .cfi_def_cfa_offset 8 // location didn't change. No new FRE, but new offset.
+ .cfi_def_cfa_offset 16 # FRE 1
+ .cfi_def_cfa_offset 8 # location didn't change. No new FRE, but new offset.
nop
- .cfi_def_cfa_offset 8 // offset didn't change. No new FRE.
+ .cfi_def_cfa_offset 8 # offset didn't change. No new FRE.
nop
- .cfi_def_cfa_offset 16 // FRE 2. new location, new offset.
+ .cfi_def_cfa_offset 16 # FRE 2. new location, new offset.
nop
- .cfi_register 0, 1 // Uninteresting register. No new FRE.
+ .cfi_register 0, 1 # Uninteresting register. No new FRE.
nop
.cfi_endproc
-f2:
+f1:
.cfi_startproc
nop
nop
.cfi_endproc
-// CHECK: SFrame section '.sframe' {
-// CHECK-NEXT: Header {
-// CHECK-NEXT: Magic: 0xDEE2
-// CHECK-NEXT: Version: V2 (0x2)
-// CHECK-NEXT: Flags [ (0x4)
-// CHECK: ABI: AMD64EndianLittle (0x3)
-// CHECK-NEXT: CFA fixed FP offset (unused): 0
-// CHECK-NEXT: CFA fixed RA offset: 0
-// CHECK-NEXT: Auxiliary header length: 0
-// CHECK-NEXT: Num FDEs: 2
-// CHECK-NEXT: Num FREs: 0
-// CHECK-NEXT: FRE subsection length: 0
-// CHECK-NEXT: FDE subsection offset: 0
-// CHECK-NEXT: FRE subsection offset: 40
-// CHECK: Function Index [
-// CHECK-NEXT: FuncDescEntry [0] {
-// CHECK-NEXT: PC {
-// CHECK-NEXT: Relocation: {{.*}}PC32{{.*}}
-// CHECK-NEXT: Symbol Name: .text
-// CHECK-NEXT: Start Address: {{.*}}
-// CHECK-NEXT: }
-// CHECK-NEXT: Size: 0x5
-// CHECK-NEXT: Start FRE Offset: 0x0
-// CHECK-NEXT: Num FREs: 0
-// CHECK-NEXT: Info {
-// CHECK-NEXT: FRE Type: Addr1 (0x0)
-// CHECK-NEXT: FDE Type: PCInc (0x0)
-// CHECK-NEXT: Raw: 0x0
-// CHECK-NEXT: }
-// CHECK-NEXT: Repetitive block size (unused): 0x0
-// CHECK-NEXT: Padding2: 0x0
-// CHECK-NEXT: FREs [
-// CHECK-NEXT: ]
-// CHECK-NEXT: }
-// CHECK-NEXT: FuncDescEntry [1] {
-// CHECK-NEXT: PC {
-// CHECK-NEXT: Relocation: {{.*}}PC32{{.*}}
-// CHECK-NEXT: Symbol Name: .text
-// CHECK-NEXT: Start Address: {{.*}}
-// CHECK-NEXT: }
-// CHECK-NEXT: Size: 0x2
-// CHECK-NEXT: Start FRE Offset: 0x0
-// CHECK-NEXT: Num FREs: 0
-// CHECK-NEXT: Info {
-// CHECK-NEXT: FRE Type: Addr1 (0x0)
-// CHECK-NEXT: FDE Type: PCInc (0x0)
-// CHECK-NEXT: Raw: 0x0
-// CHECK-NEXT: }
-// CHECK-NEXT: Repetitive block size (unused): 0x0
-// CHECK-NEXT: Padding2: 0x0
-// CHECK-NEXT: FREs [
-// CHECK-NEXT: ]
-// CHECK-NEXT: }
-// CHECK-NEXT: ]
-// CHECK-NEXT: }
+f2:
+ .cfi_startproc
+ .cfi_endproc
+
+f3:
+ .cfi_startproc simple
+ .cfi_endproc
+
+# CHECK: SFrame section '.sframe' {
+# CHECK-NEXT: Header {
+# CHECK-NEXT: Magic: 0xDEE2
+# CHECK-NEXT: Version: V2 (0x2)
+# CHECK-NEXT: Flags [ (0x4)
+# CHECK: ABI: AMD64EndianLittle (0x3)
+# CHECK-NEXT: CFA fixed FP offset (unused): 0
+# CHECK-NEXT: CFA fixed RA offset: -8
+# CHECK-NEXT: Auxiliary header length: 0
+# CHECK-NEXT: Num FDEs: 2
+# CHECK-NEXT: Num FREs: 4
+# CHECK-NEXT: FRE subsection length: 12
+# CHECK-NEXT: FDE subsection offset: 0
+# CHECK-NEXT: FRE subsection offset: 40
+# CHECK: Function Index [
+# CHECK-NEXT: FuncDescEntry [0] {
+# CHECK-NEXT: PC {
+# CHECK-NEXT: Relocation: {{.*}}PC32{{.*}}
+# CHECK-NEXT: Symbol Name: .text
+# CHECK-NEXT: Start Address: {{.*}}
+# CHECK-NEXT: }
+# CHECK-NEXT: Size: 0x5
+# CHECK-NEXT: Start FRE Offset: 0x0
+# CHECK-NEXT: Num FREs: 3
+# CHECK-NEXT: Info {
+# CHECK-NEXT: FRE Type: Addr1 (0x0)
+# CHECK-NEXT: FDE Type: PCInc (0x0)
+# CHECK-NEXT: Raw: 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: Repetitive block size (unused): 0x0
+# CHECK-NEXT: Padding2: 0x0
+
+# Contents of FREs are tested elsewhere
+
+# CHECK: FuncDescEntry [1] {
+# CHECK-NEXT: PC {
+# CHECK-NEXT: Relocation: {{.*}}PC32{{.*}}
+# CHECK-NEXT: Symbol Name: .text
+# CHECK-NEXT: Start Address: {{.*}}
+# CHECK-NEXT: }
+# CHECK-NEXT: Size: 0x2
+# CHECK-NEXT: Start FRE Offset: 0x9
+# CHECK-NEXT: Num FREs: 1
+# CHECK-NEXT: Info {
+# CHECK-NEXT: FRE Type: Addr1 (0x0)
+# CHECK-NEXT: FDE Type: PCInc (0x0)
+# CHECK-NEXT: Raw: 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: Repetitive block size (unused): 0x0
+# CHECK-NEXT: Padding2: 0x0
+
+
More information about the llvm-commits
mailing list