Skip to content

[SOL] Fix DW_OP_fbreg offsets for SBPFv3 stack variables - #195

Open
procdump wants to merge 1 commit into
anza-xyz:solana-rustc/20.1-2025-02-13from
procdump:solana-rustc/20.1-2025-02-13_sbf-v3-dwarf-fbreg-frame-base
Open

[SOL] Fix DW_OP_fbreg offsets for SBPFv3 stack variables#195
procdump wants to merge 1 commit into
anza-xyz:solana-rustc/20.1-2025-02-13from
procdump:solana-rustc/20.1-2025-02-13_sbf-v3-dwarf-fbreg-frame-base

Conversation

@procdump

Copy link
Copy Markdown

On SBPFv3, resolveInternalFrameIndex emits r10 + (Offset_FI - FrameLength) (negative), but the default getFrameIndexReference returns Offset_FI + StackSize, so DW_OP_fbreg + N resolves into the wrong frame slot and locals show as 0x0 in lldb. Override SBFFrameLowering::getFrameIndexReference to mirror the instruction-side adjustment for SBPFv3 (gated on getHasNoStackGaps() && !getHasDynamicFrames()); skip containsFrameIndex(FI). It is DWARF-only related and instruction encoding is unchanged.

For sBPFv3, the runtime auto-bumps R10 by FrameLength on each call so
R10 ends up at the high end of the callee's frame slot, and
SBFRegisterInfo::resolveInternalFrameIndex emits stack accesses as
`r10 + (Offset_FI - FrameLength)` (negative displacement). The default
TargetFrameLowering::getFrameIndexReference returned `Offset_FI +
StackSize` instead, so DW_AT_location entries for stack-resident
variables resolved to addresses in the next, uninitialized frame slot,
making local variables appear as 0x0 in lldb/gdb.

Override getFrameIndexReference in SBFFrameLowering to mirror the
instruction-encoding adjustment so DW_OP_fbreg + N names the same byte
the spill wrote. The override is gated on
getHasNoStackGaps() && !getHasDynamicFrames() (i.e. v3+) and skips
frame indices flagged by storeFrameIndexArgument (outgoing-call
temporaries from LowerCall); SBPF stack args are listed as unsupported
in the calling convention, so leaving those on the default path is
fine.

Affects DWARF emission (DwarfCompileUnit, LiveDebugValues) only;
SBFRegisterInfo::eliminateFrameIndex still routes through
resolveInternalFrameIndex, so instruction encoding is unchanged.

@nagisa nagisa left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Any chance this could have a test for the generated debuginfo?

From the looks of it tests for this tend to use llvm-dwarfdump.

Comment on lines +69 to +87
// For SBPFv3+ the runtime auto-bumps R10 by FrameLength on each call so
// that R10 ends up at the high end of the callee's frame slot.
// SBFRegisterInfo::resolveInternalFrameIndex therefore emits stores as
// `r10 + (Offset_FI - FrameLength)` (a negative displacement). Mirror that
// adjustment in the DWARF location so DW_OP_fbreg + N names the same byte
// the store wrote, instead of the default `Offset_FI + StackSize` which
// resolves to an address in the next, uninitialized frame slot.
//
// The override fires only for v3+ (HasNoStackGaps && !HasDynamicFrames):
// - v1/v2 have HasDynamicFrames=true, so emitPrologue inserts an
// `add r10, -StackSize`, making R10 the low end of the frame; the
// default formula matches that.
// - v0 has HasNoStackGaps=false and short-circuits to the default branch
// below; its (gapped) layout is intentionally left untouched here.
// Stack-passed argument frame indices (containsFrameIndex) also use the
// default — their bytes live in the caller's frame, which needs a separate
// adjustment.
if (Subtarget.getHasNoStackGaps() && !Subtarget.getHasDynamicFrames() &&
!SBFFuncInfo->containsFrameIndex(FI)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hm, I would normally say to abstract this logic and decision tree into some method in SBFFunctionInfo so it can be shared between these multiple sources, but this seems harmless enough to have.

At the same time I have a feeling that needing this override at all is because we're missing setting some preexisting value somewhere (MFI.get/setOffsetAdjustment() maybe?) but figuring out what it is and if it wouldn't have unexpected effects also seems onerous enough and maybe not worth the time and effort… 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants