@@ -2474,14 +2474,21 @@ static llvm::Expected<addr_t> ReadPtrFromAddr(Process &process, addr_t addr,
24742474// / simplified version of the methods in RegisterContextUnwind, since plumbing
24752475// / access to those here would be challenging.
24762476static llvm::Expected<addr_t > GetCFA (Process &process, RegisterContext ®ctx,
2477- RegisterKind regkind,
2478- UnwindPlan::Row::FAValue cfa_loc) {
2477+ addr_t pc_offset,
2478+ UnwindPlan &unwind_plan) {
2479+ UnwindPlan::RowSP row = unwind_plan.GetRowForFunctionOffset (pc_offset);
2480+ if (!row)
2481+ return llvm::createStringError (
2482+ " SwiftLanguageRuntime: Invalid Unwind Row when computing CFA" );
2483+
2484+ UnwindPlan::Row::FAValue cfa_loc = row->GetCFAValue ();
2485+
24792486 using ValueType = UnwindPlan::Row::FAValue::ValueType;
24802487 switch (cfa_loc.GetValueType ()) {
24812488 case ValueType::isRegisterPlusOffset: {
24822489 unsigned regnum = cfa_loc.GetRegisterNumber ();
2483- if (llvm::Expected<addr_t > regvalue =
2484- ReadRegisterAsAddress ( regctx, regkind , regnum))
2490+ if (llvm::Expected<addr_t > regvalue = ReadRegisterAsAddress (
2491+ regctx, unwind_plan. GetRegisterKind () , regnum))
24852492 return *regvalue + cfa_loc.GetOffset ();
24862493 else
24872494 return regvalue;
@@ -2513,13 +2520,8 @@ static UnwindPlanSP GetUnwindPlanForAsyncRegister(FuncUnwinders &unwinders,
25132520 return unwinders.GetUnwindPlanAtNonCallSite (target, thread);
25142521}
25152522
2516- // / Attempts to use UnwindPlans that inspect assembly to recover the entry value
2517- // / of the async context register. This is a simplified version of the methods
2518- // / in RegisterContextUnwind, since plumbing access to those here would be
2519- // / challenging.
2520- static llvm::Expected<addr_t > ReadAsyncContextRegisterFromUnwind (
2521- SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2522- RegisterContext ®ctx, AsyncUnwindRegisterNumbers regnums) {
2523+ static llvm::Expected<UnwindPlanSP>
2524+ GetAsmUnwindPlan (Address pc, SymbolContext &sc, Thread &thread) {
25232525 FuncUnwindersSP unwinders =
25242526 pc.GetModule ()->GetUnwindTable ().GetFuncUnwindersContainingAddress (pc,
25252527 sc);
@@ -2528,77 +2530,137 @@ static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
25282530 " function unwinder at address 0x%8.8" PRIx64,
25292531 pc.GetFileAddress ());
25302532
2531- Target &target = process.GetTarget ();
2532- UnwindPlanSP unwind_plan =
2533- GetUnwindPlanForAsyncRegister (*unwinders, target, regctx.GetThread ());
2533+ UnwindPlanSP unwind_plan = GetUnwindPlanForAsyncRegister (
2534+ *unwinders, thread.GetProcess ()->GetTarget (), thread);
25342535 if (!unwind_plan)
25352536 return llvm::createStringError (
25362537 " SwiftLanguageRuntime: Failed to find non call site unwind plan at "
25372538 " address 0x%8.8" PRIx64,
25382539 pc.GetFileAddress ());
2540+ return unwind_plan;
2541+ }
25392542
2540- const RegisterKind unwind_regkind = unwind_plan->GetRegisterKind ();
2541- UnwindPlan::RowSP row = unwind_plan->GetRowForFunctionOffset (
2542- pc.GetFileAddress () - func_start_addr.GetFileAddress ());
2543-
2544- // To request info about a register from the unwind plan, the register must
2545- // be in the same domain as the unwind plan's registers.
2546- uint32_t async_reg_unwind_regdomain;
2543+ static llvm::Expected<uint32_t > GetFpRegisterNumber (UnwindPlan &unwind_plan,
2544+ RegisterContext ®ctx) {
2545+ uint32_t fp_unwind_regdomain;
25472546 if (!regctx.ConvertBetweenRegisterKinds (
2548- regnums. GetRegisterKind (), regnums. async_ctx_regnum , unwind_regkind ,
2549- async_reg_unwind_regdomain )) {
2547+ lldb::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP ,
2548+ unwind_plan. GetRegisterKind (), fp_unwind_regdomain )) {
25502549 // This should never happen.
25512550 // If asserts are disabled, return an error to avoid creating an invalid
25522551 // unwind plan.
2553- auto error_msg = " SwiftLanguageRuntime: Failed to convert register domains" ;
2552+ const auto *error_msg =
2553+ " SwiftLanguageRuntime: Failed to convert register domains" ;
25542554 llvm_unreachable (error_msg);
25552555 return llvm::createStringError (error_msg);
25562556 }
2557+ return fp_unwind_regdomain;
2558+ }
25572559
2558- // If the plan doesn't have information about the async register, we can use
2559- // its current value, as this is a callee saved register.
2560- UnwindPlan::Row::AbstractRegisterLocation regloc;
2561- if (!row->GetRegisterInfo (async_reg_unwind_regdomain, regloc))
2562- return ReadRegisterAsAddress (regctx, regnums.GetRegisterKind (),
2563- regnums.async_ctx_regnum );
2560+ struct FrameSetupInfo {
2561+ addr_t frame_setup_func_offset;
2562+ int fp_cfa_offset;
2563+ };
25642564
2565- // Handle the few abstract locations we are likely to encounter.
2566- using RestoreType = UnwindPlan::Row::AbstractRegisterLocation::RestoreType;
2567- RestoreType loctype = regloc.GetLocationType ();
2568- switch (loctype) {
2569- case RestoreType::same:
2565+ // / Detect the point in the function where the prologue created a frame,
2566+ // / returning:
2567+ // / 1. The offset of the first instruction after that point. For a frameless
2568+ // / function, this offset is large positive number, so that PC can still be
2569+ // / compared against it.
2570+ // / 2. The CFA offset at which FP is stored, meaningless in the frameless case.
2571+ static llvm::Expected<FrameSetupInfo>
2572+ GetFrameSetupInfo (UnwindPlan &unwind_plan, RegisterContext ®ctx) {
2573+ using RowSP = UnwindPlan::RowSP;
2574+ using AbstractRegisterLocation = UnwindPlan::Row::AbstractRegisterLocation;
2575+
2576+ llvm::Expected<uint32_t > fp_unwind_regdomain =
2577+ GetFpRegisterNumber (unwind_plan, regctx);
2578+ if (!fp_unwind_regdomain)
2579+ return fp_unwind_regdomain.takeError ();
2580+
2581+ // Look at the first few (4) rows of the plan and store FP's location.
2582+ const int upper_bound = std::min (4 , unwind_plan.GetRowCount ());
2583+ llvm::SmallVector<AbstractRegisterLocation, 4 > fp_locs;
2584+ for (int row_idx = 0 ; row_idx < upper_bound; row_idx++) {
2585+ RowSP row = unwind_plan.GetRowAtIndex (row_idx);
2586+ AbstractRegisterLocation regloc;
2587+ if (!row->GetRegisterInfo (*fp_unwind_regdomain, regloc))
2588+ regloc.SetSame ();
2589+ fp_locs.push_back (regloc);
2590+ }
2591+
2592+ // Find first location where FP is stored *at* some CFA offset.
2593+ auto *it = llvm::find_if (
2594+ fp_locs, [](auto fp_loc) { return fp_loc.IsAtCFAPlusOffset (); });
2595+
2596+ // This is a frameless function, use large positive offset so that a PC can
2597+ // still be compared against it.
2598+ if (it == fp_locs.end ())
2599+ return FrameSetupInfo{std::numeric_limits<addr_t >::max (), 0 };
2600+
2601+ // This is an async function with a frame. The prologue roughly follows this
2602+ // sequence of instructions:
2603+ // adjust sp
2604+ // save lr @ CFA-8
2605+ // save fp @ CFA-16 << `it` points to this row.
2606+ // save async_reg @ CFA-24 << subsequent row.
2607+ // Use subsequent row, if available.
2608+ // Pointer auth may introduce more instructions, but they don't affect the
2609+ // unwinder rows / store to the stack.
2610+ int row_idx = fp_locs.end () - it;
2611+ int next_row_idx = row_idx + 1 ;
2612+
2613+ // If subsequent row is invalid, approximate through current row.
2614+ if (next_row_idx == unwind_plan.GetRowCount () ||
2615+ next_row_idx == upper_bound ||
2616+ !fp_locs[next_row_idx].IsAtCFAPlusOffset ()) {
2617+ LLDB_LOG (GetLog (LLDBLog::Unwind), " SwiftLanguageRuntime:: UnwindPlan did "
2618+ " not contain a valid row after FP setup" );
2619+ UnwindPlan::RowSP row = unwind_plan.GetRowAtIndex (row_idx);
2620+ return FrameSetupInfo{row->GetOffset (), fp_locs[row_idx].GetOffset ()};
2621+ }
2622+
2623+ UnwindPlan::RowSP subsequent_row = unwind_plan.GetRowAtIndex (next_row_idx);
2624+ return FrameSetupInfo{subsequent_row->GetOffset (),
2625+ fp_locs[next_row_idx].GetOffset ()};
2626+ }
2627+
2628+ // / Reads the async register from its ABI-guaranteed stack-slot, or directly
2629+ // / from the register depending on where pc is relative to the start of the
2630+ // / function.
2631+ static llvm::Expected<addr_t > ReadAsyncContextRegisterFromUnwind (
2632+ SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2633+ RegisterContext ®ctx, AsyncUnwindRegisterNumbers regnums) {
2634+ llvm::Expected<UnwindPlanSP> unwind_plan =
2635+ GetAsmUnwindPlan (pc, sc, regctx.GetThread ());
2636+ if (!unwind_plan)
2637+ return unwind_plan.takeError ();
2638+ llvm::Expected<FrameSetupInfo> frame_setup =
2639+ GetFrameSetupInfo (**unwind_plan, regctx);
2640+ if (!frame_setup)
2641+ return frame_setup.takeError ();
2642+
2643+ // Is PC before the frame formation? If so, use async register directly.
2644+ // This handles frameless functions, as frame_setup_func_offset is INT_MAX.
2645+ addr_t pc_offset = pc.GetFileAddress () - func_start_addr.GetFileAddress ();
2646+ if (pc_offset < frame_setup->frame_setup_func_offset )
25702647 return ReadRegisterAsAddress (regctx, regnums.GetRegisterKind (),
25712648 regnums.async_ctx_regnum );
2572- case RestoreType::inOtherRegister: {
2573- unsigned regnum = regloc.GetRegisterNumber ();
2574- return ReadRegisterAsAddress (regctx, unwind_regkind, regnum);
2575- }
2576- case RestoreType::atCFAPlusOffset: {
2577- llvm::Expected<addr_t > cfa =
2578- GetCFA (process, regctx, unwind_regkind, row->GetCFAValue ());
2579- if (!cfa)
2580- return cfa.takeError ();
2581- return ReadPtrFromAddr (process, *cfa, regloc.GetOffset ());
2582- }
2583- case RestoreType::isCFAPlusOffset: {
2584- if (llvm::Expected<addr_t > cfa =
2585- GetCFA (process, regctx, unwind_regkind, row->GetCFAValue ()))
2586- return *cfa + regloc.GetOffset ();
2587- else
2588- return cfa;
2589- }
2590- case RestoreType::isConstant:
2591- return regloc.GetConstant ();
2592- case RestoreType::unspecified:
2593- case RestoreType::undefined:
2594- case RestoreType::atAFAPlusOffset:
2595- case RestoreType::isAFAPlusOffset:
2596- case RestoreType::isDWARFExpression:
2597- case RestoreType::atDWARFExpression:
2598- break ;
2599- }
2600- return llvm::createStringError (
2601- " SwiftLanguageRuntime: Unsupported register location type = %d" , loctype);
2649+
2650+ // A frame was formed, and FP was saved at a CFA offset. Compute CFA and read
2651+ // the location beneath where FP was saved.
2652+ llvm::Expected<addr_t > cfa =
2653+ GetCFA (process, regctx, pc_offset, **unwind_plan);
2654+ if (!cfa)
2655+ return cfa.takeError ();
2656+
2657+ addr_t async_reg_addr = process.FixDataAddress (
2658+ *cfa + frame_setup->fp_cfa_offset - process.GetAddressByteSize ());
2659+ Status error;
2660+ addr_t async_reg = process.ReadPointerFromMemory (async_reg_addr, error);
2661+ if (error.Fail ())
2662+ return error.ToError ();
2663+ return async_reg;
26022664}
26032665
26042666// / Returns true if the async register should be dereferenced once to obtain the
0 commit comments