From 00ccdc2dd550bab14ac88c2da9347f1d032398c9 Mon Sep 17 00:00:00 2001 From: Felipe de Azevedo Piovezan Date: Fri, 16 May 2025 18:58:29 -0700 Subject: [PATCH] [lldb][swift] Increase number of Rows inspected when unwinding async functions We were overly conservatives in our initial estimations: if callee saved registers are used, they are usually spilled first, increasing the number of rows in the prologue. This also revealed a bug in computing the offset of a vector iterator: ``` - int row_idx = fp_locs.end() - it; + int row_idx = it - fp_locs.begin(); ``` Testing this is unfortunately difficult: a way of forcing register spilling is required. For now, this commit tweaks one of the existing tests in a way that seems to reliably spill more registers, but there are no guarantees this will continue to be the case. Other testing mechanisms need to be created. --- .../LanguageRuntime/Swift/SwiftLanguageRuntime.cpp | 12 ++++++++---- .../TestSwiftAsyncSteppingManyTasks.py | 2 +- .../stepping/step_over_lots_of_tasks/main.swift | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp index 618645db3b88c..c026d6d27b49a 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp @@ -2584,9 +2584,13 @@ GetFrameSetupInfo(UnwindPlan &unwind_plan, RegisterContext ®ctx) { if (!fp_unwind_regdomain) return fp_unwind_regdomain.takeError(); - // Look at the first few (4) rows of the plan and store FP's location. - const int upper_bound = std::min(4, unwind_plan.GetRowCount()); - llvm::SmallVector fp_locs; + // Look at the first few (12) rows of the plan and store FP's location. + // This number is based on AAPCS, with 10 callee-saved GPRs and 8 floating + // point registers. When STP instructions are used, the plan would have one + // initial row, nine rows of saving callee-saved registers, and two standard + // prologue rows (fp+lr and sp). + const int upper_bound = std::min(12, unwind_plan.GetRowCount()); + llvm::SmallVector fp_locs; for (int row_idx = 0; row_idx < upper_bound; row_idx++) { RowSP row = unwind_plan.GetRowAtIndex(row_idx); AbstractRegisterLocation regloc; @@ -2613,7 +2617,7 @@ GetFrameSetupInfo(UnwindPlan &unwind_plan, RegisterContext ®ctx) { // Use subsequent row, if available. // Pointer auth may introduce more instructions, but they don't affect the // unwinder rows / store to the stack. - int row_idx = fp_locs.end() - it; + int row_idx = it - fp_locs.begin(); int next_row_idx = row_idx + 1; // If subsequent row is invalid, approximate through current row. diff --git a/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/TestSwiftAsyncSteppingManyTasks.py b/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/TestSwiftAsyncSteppingManyTasks.py index 92ed5634277cc..861080e0c330c 100644 --- a/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/TestSwiftAsyncSteppingManyTasks.py +++ b/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/TestSwiftAsyncSteppingManyTasks.py @@ -55,7 +55,7 @@ def test_step_over_top_level_fibonacci(self): fib_bp.SetEnabled(False) fib_first_line = thread_in_fib.frames[0].GetLineEntry().GetLine() - num_lines_fib = 5 + num_lines_fib = 7 for line_offset in range(1, num_lines_fib): thread_in_fib.StepOver() self.assertEqual(process.GetSelectedThread(), thread_in_fib) diff --git a/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/main.swift b/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/main.swift index 87fe4707041ae..0c0599e094478 100644 --- a/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/main.swift +++ b/lldb/test/API/lang/swift/async/stepping/step_over_lots_of_tasks/main.swift @@ -12,13 +12,17 @@ func fib(n: Int) async -> Int { if (n == 0) { + print("done!") return 1 } if (n == 1) { + print("done!") return 1 } async let n1_task = fib(n: n - 1) // Breakpoint fib async let n2_task = fib(n: n - 2) + print(n) + if (n == 6) { do { try await Task.sleep(for: .seconds(1)) } catch {} } let n1 = await n1_task let n2 = await n2_task return n1 + n2