Skip to content

Commit 6b75e8d

Browse files
Merge pull request #9107 from felipepiovezan/felipe/unwinding_q_funclets_rebranch
[lldb][swift] Fix unwinding of Q funclets
2 parents 189b328 + aabac97 commit 6b75e8d

File tree

2 files changed

+52
-46
lines changed

2 files changed

+52
-46
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,39 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) {
25822582
return LLDB_INVALID_ADDRESS;
25832583
}
25842584

2585+
/// Creates an expression accessing *(fp - 8) or **(fp - 8) if
2586+
/// `with_double_deref` is true. This is only valid for x86_64 or aarch64.
2587+
llvm::ArrayRef<uint8_t>
2588+
GetAsyncRegFromFramePointerDWARFExpr(llvm::Triple::ArchType triple,
2589+
bool with_double_deref) {
2590+
assert(triple == llvm::Triple::x86_64 || triple == llvm::Triple::aarch64);
2591+
2592+
// These expressions must have static storage, due to how UnwindPlan::Row
2593+
// works.
2594+
static const uint8_t g_cfa_dwarf_expression_x86_64[] = {
2595+
llvm::dwarf::DW_OP_breg6, // DW_OP_breg6, register 6 == rbp
2596+
0x78, // sleb128 -8 (ptrsize)
2597+
llvm::dwarf::DW_OP_deref,
2598+
llvm::dwarf::DW_OP_deref,
2599+
};
2600+
static const uint8_t g_cfa_dwarf_expression_arm64[] = {
2601+
llvm::dwarf::DW_OP_breg29, // DW_OP_breg29, register 29 == fp
2602+
0x78, // sleb128 -8 (ptrsize)
2603+
llvm::dwarf::DW_OP_deref,
2604+
llvm::dwarf::DW_OP_deref,
2605+
};
2606+
2607+
const uint8_t *expr = triple == llvm::Triple::x86_64
2608+
? g_cfa_dwarf_expression_x86_64
2609+
: g_cfa_dwarf_expression_arm64;
2610+
auto size = triple == llvm::Triple::x86_64
2611+
? sizeof(g_cfa_dwarf_expression_x86_64)
2612+
: sizeof(g_cfa_dwarf_expression_arm64);
2613+
if (with_double_deref)
2614+
return llvm::ArrayRef<uint8_t>(expr, size);
2615+
return llvm::ArrayRef<uint8_t>(expr, size - 1);
2616+
}
2617+
25852618
// Examine the register state and detect the transition from a real
25862619
// stack frame to an AsyncContext frame, or a frame in the middle of
25872620
// the AsyncContext chain, and return an UnwindPlan for these situations.
@@ -2671,64 +2704,28 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
26712704
const int32_t ptr_size = 8;
26722705
row->SetOffset(0);
26732706

2674-
// A DWARF Expression to set the CFA.
2675-
// pushes the frame pointer register - 8
2676-
// dereference
2677-
2678-
// FIXME: Row::RegisterLocation::RestoreType doesn't have a
2679-
// deref(reg-value + offset) yet, shortcut around it with
2680-
// a dwarf expression for now.
2681-
// The CFA of an async frame is the address of it's associated AsyncContext.
2682-
// In an async frame currently on the stack, this address is stored right
2683-
// before the saved frame pointer on the stack.
2684-
static const uint8_t g_cfa_dwarf_expression_x86_64[] = {
2685-
llvm::dwarf::DW_OP_breg6, // DW_OP_breg6, register 6 == rbp
2686-
0x78, // sleb128 -8 (ptrsize)
2687-
llvm::dwarf::DW_OP_deref,
2688-
};
2689-
static const uint8_t g_cfa_dwarf_expression_arm64[] = {
2690-
llvm::dwarf::DW_OP_breg29, // DW_OP_breg29, register 29 == fp
2691-
0x78, // sleb128 -8 (ptrsize)
2692-
llvm::dwarf::DW_OP_deref,
2693-
};
2694-
2695-
constexpr unsigned expr_size = sizeof(g_cfa_dwarf_expression_arm64);
2696-
2697-
static_assert(sizeof(g_cfa_dwarf_expression_x86_64) ==
2698-
sizeof(g_cfa_dwarf_expression_arm64),
2699-
"Code relies on DWARF expressions being the same size");
2700-
2701-
const uint8_t *expr = nullptr;
2702-
if (arch.GetMachine() == llvm::Triple::x86_64)
2703-
expr = g_cfa_dwarf_expression_x86_64;
2704-
else if (arch.GetMachine() == llvm::Triple::aarch64)
2705-
expr = g_cfa_dwarf_expression_arm64;
2706-
else
2707-
llvm_unreachable("Unsupported architecture");
2708-
27092707
if (in_prologue) {
27102708
if (indirect_context)
27112709
row->GetCFAValue().SetIsRegisterDereferenced(regnums->async_ctx_regnum);
27122710
else
27132711
row->GetCFAValue().SetIsRegisterPlusOffset(regnums->async_ctx_regnum, 0);
27142712
} else {
2715-
row->GetCFAValue().SetIsDWARFExpression(expr, expr_size);
2713+
// In indirect funclets, dereferencing (fp-8) once produces the CFA of the
2714+
// frame above. Dereferencing twice will produce the current frame's CFA.
2715+
bool with_double_deref = indirect_context;
2716+
llvm::ArrayRef<uint8_t> expr = GetAsyncRegFromFramePointerDWARFExpr(
2717+
arch.GetMachine(), with_double_deref);
2718+
row->GetCFAValue().SetIsDWARFExpression(expr.data(), expr.size());
27162719
}
27172720

27182721
if (indirect_context) {
27192722
if (in_prologue) {
27202723
row->SetRegisterLocationToSame(regnums->async_ctx_regnum, false);
27212724
} else {
2722-
// In a "resume" coroutine, the passed context argument needs to be
2723-
// dereferenced once to get the context. This is reflected in the debug
2724-
// info so we need to account for it and report am async register value
2725-
// that needs to be dereferenced to get to the context.
2726-
// Note that the size passed for the DWARF expression is the size of the
2727-
// array minus one. This skips the last deref for this use.
2728-
assert(expr[expr_size - 1] == llvm::dwarf::DW_OP_deref &&
2729-
"Should skip a deref");
2730-
row->SetRegisterLocationToIsDWARFExpression(regnums->async_ctx_regnum,
2731-
expr, expr_size - 1, false);
2725+
llvm::ArrayRef<uint8_t> expr = GetAsyncRegFromFramePointerDWARFExpr(
2726+
arch.GetMachine(), false /*with_double_deref*/);
2727+
row->SetRegisterLocationToIsDWARFExpression(
2728+
regnums->async_ctx_regnum, expr.data(), expr.size(), false);
27322729
}
27332730
} else {
27342731
// In the first part of a split async function, the context is passed

lldb/test/API/lang/swift/async/frame/variables_multiple_frames/TestSwiftAsyncFrameVarMultipleFrames.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ def test(self):
6969
self.check_pcs(async_frames, process, target)
7070
self.check_variables(async_frames, ["111", "222", "333", "444", "555"])
7171

72+
# Now stop at the Q funclet right after the await to ASYNC___1
73+
target.DeleteAllBreakpoints()
74+
target.BreakpointCreateByName("$s1a12ASYNC___2___SiyYaFTQ0_")
75+
process.Continue()
76+
async_frames = process.GetSelectedThread().frames
77+
self.check_cfas(async_frames, process)
78+
self.check_pcs(async_frames, process, target)
79+
self.check_variables(async_frames, ["222", "333", "444", "555"])
80+
7281
target.DeleteAllBreakpoints()
7382
target.BreakpointCreateBySourceRegex("breakpoint3", source_file)
7483
process.Continue()

0 commit comments

Comments
 (0)