-
Notifications
You must be signed in to change notification settings - Fork 15.4k
Parse CFI instructions to create SFrame FREs #155496
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
Changes from 9 commits
6ce089d
1b1eb72
67b9205
f5b9edd
d2b2503
c1a97b0
8b54030
3415154
8dd4b52
3ea761c
c9e9ee8
be4da9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,13 +21,33 @@ using namespace sframe; | |
|
|
||
| namespace { | ||
|
|
||
| // High-level structure to track info needed to emit a | ||
| // sframe_frame_row_entry_addrX. On disk these have both a fixed portion of type | ||
| // sframe_frame_row_entry_addrX and trailing data of X * S bytes, where X is the | ||
| // datum size, and S is 1, 2, or 3 depending on which of CFA, SP, and FP are | ||
| // being tracked. | ||
| struct SFrameFRE { | ||
| // An FRE describes how to find the registers when the PC is at this | ||
| // Label from function start. | ||
| const MCSymbol *Label = nullptr; | ||
| size_t CFAOffset = 0; | ||
| size_t FPOffset = 0; | ||
| size_t RAOffset = 0; | ||
| bool FromFP = false; | ||
| bool CFARegSet = false; | ||
|
|
||
| SFrameFRE(const MCSymbol *Start) : Label(Start) {} | ||
| }; | ||
|
|
||
| // High-level structure to track info needed to emit a sframe_func_desc_entry | ||
| // and its associated FREs. | ||
| struct SFrameFDE { | ||
| // Reference to the original dwarf frame to avoid copying. | ||
| const MCDwarfFrameInfo &DFrame; | ||
| // Label where this FDE's FREs start. | ||
| MCSymbol *FREStart; | ||
| // Unwinding fres | ||
| SmallVector<SFrameFRE> FREs; | ||
|
|
||
| SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES) | ||
| : DFrame(DF), FREStart(FRES) {} | ||
|
|
@@ -53,7 +73,8 @@ struct SFrameFDE { | |
| MCFixup::getDataKindForSize(4))); | ||
| S.emitInt32(0); | ||
|
|
||
| // sfde_func_start_num_fres | ||
| // sfde_func_num_fres | ||
| // TODO: When we actually emit fres, replace 0 with FREs.size() | ||
| S.emitInt32(0); | ||
|
|
||
| // sfde_func_info word | ||
|
|
@@ -76,10 +97,90 @@ class SFrameEmitterImpl { | |
| MCObjectStreamer &Streamer; | ||
| SmallVector<SFrameFDE> FDEs; | ||
| 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. | ||
| unsigned SPReg; | ||
| unsigned FPReg; | ||
| unsigned RAReg; | ||
| MCSymbol *FDESubSectionStart; | ||
| MCSymbol *FRESubSectionStart; | ||
| MCSymbol *FRESubSectionEnd; | ||
|
|
||
| bool setCFARegister(SFrameFRE &FRE, const MCCFIInstruction &I) { | ||
| if (I.getRegister() == SPReg) { | ||
| FRE.CFARegSet = true; | ||
| FRE.FromFP = false; | ||
| return true; | ||
| } | ||
| if (I.getRegister() == FPReg) { | ||
| FRE.CFARegSet = true; | ||
| FRE.FromFP = true; | ||
| return true; | ||
| } | ||
| Streamer.getContext().reportWarning( | ||
| I.getLoc(), "Canonical Frame Address not in stack- or frame-pointer. " | ||
| "Omitting SFrame unwind info for this function."); | ||
| return false; | ||
| } | ||
|
|
||
| bool setCFAOffset(SFrameFRE &FRE, const SMLoc &Loc, size_t Offset) { | ||
| if (!FRE.CFARegSet) { | ||
| Streamer.getContext().reportWarning( | ||
| Loc, "Adjusting CFA offset without a base register. " | ||
| "Omitting SFrame unwind info for this function."); | ||
| return false; | ||
| } | ||
| FRE.CFAOffset = Offset; | ||
| return true; | ||
| } | ||
|
|
||
| // Add the effects of CFI to the current FDE, creating a new FRE when | ||
| // necessary. | ||
| bool handleCFI(SFrameFDE &FDE, SFrameFRE &FRE, const MCCFIInstruction &CFI) { | ||
| switch (CFI.getOperation()) { | ||
| case MCCFIInstruction::OpDefCfaRegister: | ||
| return setCFARegister(FRE, CFI); | ||
| case MCCFIInstruction::OpDefCfa: | ||
| case MCCFIInstruction::OpLLVMDefAspaceCfa: | ||
| if (!setCFARegister(FRE, CFI)) | ||
| return false; | ||
| return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset()); | ||
| case MCCFIInstruction::OpOffset: | ||
| if (CFI.getRegister() == FPReg) | ||
| FRE.FPOffset = CFI.getOffset(); | ||
| else if (CFI.getRegister() == RAReg) | ||
| FRE.RAOffset = CFI.getOffset(); | ||
| return true; | ||
| case MCCFIInstruction::OpRelOffset: | ||
| if (CFI.getRegister() == FPReg) | ||
| FRE.FPOffset += CFI.getOffset(); | ||
| else if (CFI.getRegister() == RAReg) | ||
| FRE.RAOffset += CFI.getOffset(); | ||
| return true; | ||
| case MCCFIInstruction::OpDefCfaOffset: | ||
| return setCFAOffset(FRE, CFI.getLoc(), CFI.getOffset()); | ||
| case MCCFIInstruction::OpAdjustCfaOffset: | ||
| return setCFAOffset(FRE, CFI.getLoc(), FRE.CFAOffset + CFI.getOffset()); | ||
| case MCCFIInstruction::OpRememberState: | ||
| // TODO: Implement. Will use FDE. | ||
| return true; | ||
| case MCCFIInstruction::OpRestore: | ||
| // TODO: Implement. Will use FDE. | ||
| return true; | ||
| case MCCFIInstruction::OpRestoreState: | ||
| // TODO: Implement. Will use FDE. | ||
| return true; | ||
| case MCCFIInstruction::OpEscape: | ||
| // TODO: Implement. Will use FDE. | ||
| return true; | ||
| default: | ||
| // Instructions that don't affect the CFA, RA, and SP can be safely | ||
| // ignored. | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| public: | ||
| SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) { | ||
| assert(Streamer.getContext() | ||
|
|
@@ -88,13 +189,96 @@ class SFrameEmitterImpl { | |
| .has_value()); | ||
| FDEs.reserve(Streamer.getDwarfFrameInfos().size()); | ||
| SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch(); | ||
| switch (SFrameABI) { | ||
| case ABI::AArch64EndianBig: | ||
| case ABI::AArch64EndianLittle: | ||
| SPReg = 31; | ||
| RAReg = 29; | ||
| FPReg = 30; | ||
| break; | ||
| case ABI::AMD64EndianLittle: | ||
| SPReg = 7; | ||
| // RARegister untracked in this abi. Value chosen to match | ||
| // MCDwarfFrameInfo constructor. | ||
| RAReg = static_cast<unsigned>(INT_MAX); | ||
| FPReg = 6; | ||
| break; | ||
| } | ||
|
|
||
| FDESubSectionStart = Streamer.getContext().createTempSymbol(); | ||
| FRESubSectionStart = Streamer.getContext().createTempSymbol(); | ||
| FRESubSectionEnd = Streamer.getContext().createTempSymbol(); | ||
| } | ||
|
|
||
| void BuildSFDE(const MCDwarfFrameInfo &DF) { | ||
| FDEs.emplace_back(DF, Streamer.getContext().createTempSymbol()); | ||
| bool atSameLocation(const MCSymbol *Left, const MCSymbol *Right) { | ||
| return Left != nullptr && Right != nullptr && | ||
| Left->getFragment() == Right->getFragment() && | ||
| Left->getOffset() == Right->getOffset(); | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| void buildSFDE(const MCDwarfFrameInfo &DF) { | ||
| bool Valid = true; | ||
| SFrameFDE FDE(DF, Streamer.getContext().createTempSymbol()); | ||
| // This would have been set via ".cfi_return_column", but | ||
| // MCObjectStreamer doesn't emit an MCCFIInstruction for that. It just | ||
| // sets the DF.RAReg. | ||
| // FIXME: This also prevents providing a proper location for the error. | ||
| // LLVM doesn't change the return column itself, so this was | ||
| // hand-written assembly. | ||
| if (DF.RAReg != RAReg) { | ||
| Streamer.getContext().reportWarning( | ||
| SMLoc(), "Non-default RA register in .cfi_return_column " + | ||
|
||
| Twine(DF.RAReg) + | ||
| ". Omitting SFrame unwind info for this function."); | ||
| Valid = false; | ||
| } | ||
| MCSymbol *LastLabel = DF.Begin; | ||
| SFrameFRE BaseFRE(LastLabel); | ||
| if (!DF.IsSimple) { | ||
| for (const auto &CFI : | ||
| Streamer.getContext().getAsmInfo()->getInitialFrameState()) | ||
| if (!handleCFI(FDE, BaseFRE, CFI)) | ||
| Valid = false; | ||
| } | ||
| FDE.FREs.push_back(BaseFRE); | ||
|
|
||
| for (const auto &CFI : DF.Instructions) { | ||
| // Instructions from InitialFrameState may not have a label, but if these | ||
| // instructions don't, then they are in dead code or otherwise unused. | ||
| // TODO: This check follows MCDwarf.cpp | ||
| // FrameEmitterImplementation::emitCFIInstructions, but nothing in the | ||
| // testsuite triggers it. We should see if it can be removed in both | ||
| // places, or alternately, add a test to exercise it. | ||
| auto *L = CFI.getLabel(); | ||
| if (L && !L->isDefined()) | ||
| continue; | ||
|
|
||
| SFrameFRE FRE = FDE.FREs.back(); | ||
| if (!handleCFI(FDE, FRE, CFI)) | ||
| Valid = false; | ||
|
|
||
| // If nothing relevant but the location changed, don't add the FRE. | ||
| if (equalIgnoringLocation(FRE, FDE.FREs.back())) | ||
| continue; | ||
|
|
||
| // If the location stayed the same, then update the current | ||
| // row. Otherwise, add a new one. | ||
| if (atSameLocation(LastLabel, L)) | ||
| FDE.FREs.back() = FRE; | ||
| else { | ||
| FDE.FREs.push_back(FRE); | ||
| FDE.FREs.back().Label = L; | ||
| LastLabel = L; | ||
| } | ||
| } | ||
| if (Valid) | ||
| FDEs.push_back(FDE); | ||
| } | ||
|
|
||
| void emitPreamble() { | ||
|
|
@@ -116,7 +300,9 @@ class SFrameEmitterImpl { | |
| // shf_num_fdes | ||
| Streamer.emitInt32(FDEs.size()); | ||
| // shf_num_fres | ||
| Streamer.emitInt32(0); | ||
| uint32_t TotalFREs = 0; | ||
| Streamer.emitInt32(TotalFREs); | ||
|
|
||
| // shf_fre_len | ||
| Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart, | ||
| sizeof(int32_t)); | ||
|
|
@@ -161,7 +347,7 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) { | |
| // Both the header itself and the FDEs include various offsets and counts. | ||
| // Therefore, all of this must be precomputed. | ||
| for (const auto &DFrame : FrameArray) | ||
| Emitter.BuildSFDE(DFrame); | ||
| Emitter.buildSFDE(DFrame); | ||
|
|
||
| MCSection *Section = Context.getObjectFileInfo()->getSFrameSection(); | ||
| // Not strictly necessary, but gas always aligns to 8, so match that. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // TODO: Add other architectures as they gain sframe support | ||
| // REQUIRES: x86-registered-target | ||
| // RUN: llvm-mc --assemble --filetype=obj -triple x86_64 %s -o %t.o 2>&1 | FileCheck %s | ||
| // RUN: llvm-readelf --sframe %t.o | FileCheck --check-prefix=CHECK-NOFDES %s | ||
|
|
||
|
|
||
| .cfi_sections .sframe | ||
labath marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| f1: | ||
| .cfi_startproc simple | ||
| // CHECK: Non-default RA register {{.*}} | ||
| .cfi_return_column 0 | ||
| nop | ||
| // CHECK: {{.*}} Adjusting CFA offset without a base register.{{.*}} | ||
| .cfi_def_cfa_offset 16 // no line number reported here. | ||
| nop | ||
| // CHECK: [[@LINE+1]]:{{.*}} Adjusting CFA offset without a base register.{{.*}} | ||
| .cfi_adjust_cfa_offset 16 | ||
| nop | ||
| .cfi_endproc | ||
|
|
||
| f2: | ||
| .cfi_startproc | ||
| nop | ||
| // CHECK: Canonical Frame Address not in stack- or frame-pointer. {{.*}} | ||
| .cfi_def_cfa 0, 4 | ||
| nop | ||
|
|
||
| .cfi_endproc | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. something wrong with the indentation here. Tabs?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's still something wrong with the indentation on this line (.cfi_endproc), at least when I view it in github UI. (I would expect it to start at the same column as the other .cfi directives. |
||
|
|
||
| // CHECK-NOFDES: Num FDEs: 0 | ||
| // CHECK-NOFDES: Num FREs: 0 | ||
Uh oh!
There was an error while loading. Please reload this page.