Skip to content

Commit 61f615a

Browse files
Merge pull request #9691 from adrian-prantl/missing-cherry-picks-6.1
Missing cherry picks 6.1
2 parents 3bbc997 + 3f342ba commit 61f615a

File tree

9 files changed

+254
-40
lines changed

9 files changed

+254
-40
lines changed

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

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -361,23 +361,13 @@ class ThreadPlanRunToAddressOnAsyncCtx : public ThreadPlan {
361361

362362
/// Given a thread that is stopped at the start of swift_task_switch, create a
363363
/// thread plan that runs to the address of the resume function.
364-
static ThreadPlanSP CreateRunThroughTaskSwitchThreadPlan(Thread &thread) {
365-
// The signature for `swift_task_switch` is as follows:
366-
// SWIFT_CC(swiftasync)
367-
// void swift_task_switch(
368-
// SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
369-
// TaskContinuationFunction *resumeFunction,
370-
// ExecutorRef newExecutor);
371-
//
372-
// The async context given as the first argument is not passed using the
373-
// calling convention's first register, it's passed in the platform's async
374-
// context register. This means the `resumeFunction` parameter uses the
375-
// first ABI register (ex: x86-64: rdi, arm64: x0).
364+
static ThreadPlanSP
365+
CreateRunThroughTaskSwitchThreadPlan(Thread &thread,
366+
unsigned resume_fn_generic_regnum) {
376367
RegisterContextSP reg_ctx =
377368
thread.GetStackFrameAtIndex(0)->GetRegisterContext();
378-
constexpr unsigned resume_fn_regnum = LLDB_REGNUM_GENERIC_ARG1;
379369
unsigned resume_fn_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
380-
RegisterKind::eRegisterKindGeneric, resume_fn_regnum);
370+
RegisterKind::eRegisterKindGeneric, resume_fn_generic_regnum);
381371
uint64_t resume_fn_ptr = reg_ctx->ReadRegisterAsUnsigned(resume_fn_reg, 0);
382372
if (!resume_fn_ptr)
383373
return {};
@@ -397,6 +387,41 @@ static ThreadPlanSP CreateRunThroughTaskSwitchThreadPlan(Thread &thread) {
397387
thread, resume_fn_ptr, async_ctx);
398388
}
399389

390+
/// Creates a thread plan to step over swift runtime functions that can trigger
391+
/// a task switch, like `async_task_switch` or `swift_asyncLet_get`.
392+
static ThreadPlanSP
393+
CreateRunThroughTaskSwitchingTrampolines(Thread &thread,
394+
StringRef trampoline_name) {
395+
// The signature for `swift_task_switch` is as follows:
396+
// SWIFT_CC(swiftasync)
397+
// void swift_task_switch(
398+
// SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
399+
// TaskContinuationFunction *resumeFunction,
400+
// ExecutorRef newExecutor);
401+
//
402+
// The async context given as the first argument is not passed using the
403+
// calling convention's first register, it's passed in the platform's async
404+
// context register. This means the `resumeFunction` parameter uses the
405+
// first ABI register (ex: x86-64: rdi, arm64: x0).
406+
if (trampoline_name == "swift_task_switch")
407+
return CreateRunThroughTaskSwitchThreadPlan(thread,
408+
LLDB_REGNUM_GENERIC_ARG1);
409+
// The signature for `swift_asyncLet_get` and `swift_asyncLet_finish` are the
410+
// same. Like `task_switch`, the async context (first argument) uses the async
411+
// context register, and not the arg1 register; as such, the continuation
412+
// funclet can be found in arg3.
413+
//
414+
// swift_asyncLet_get(SWIFT_ASYNC_CONTEXT AsyncContext *,
415+
// AsyncLet *,
416+
// void *,
417+
// TaskContinuationFunction *,
418+
if (trampoline_name == "swift_asyncLet_get" ||
419+
trampoline_name == "swift_asyncLet_finish")
420+
return CreateRunThroughTaskSwitchThreadPlan(thread,
421+
LLDB_REGNUM_GENERIC_ARG3);
422+
return nullptr;
423+
}
424+
400425
static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
401426
bool stop_others) {
402427
// Here are the trampolines we have at present.
@@ -429,8 +454,9 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
429454
Mangled &mangled_symbol_name = symbol->GetMangled();
430455
const char *symbol_name = mangled_symbol_name.GetMangledName().AsCString();
431456

432-
if (mangled_symbol_name.GetDemangledName() == "swift_task_switch")
433-
return CreateRunThroughTaskSwitchThreadPlan(thread);
457+
if (ThreadPlanSP thread_plan = CreateRunThroughTaskSwitchingTrampolines(
458+
thread, mangled_symbol_name.GetDemangledName()))
459+
return thread_plan;
434460

435461
ThunkKind thunk_kind = GetThunkKind(symbol);
436462
ThunkAction thunk_action = GetThunkAction(thunk_kind);

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "clang/AST/DeclObjC.h"
4949
#include "clang/AST/DeclTemplate.h"
5050
#include "clang/AST/Type.h"
51+
#include "clang/Basic/Specifiers.h"
5152
#include "llvm/ADT/StringExtras.h"
5253
#include "llvm/Demangle/Demangle.h"
5354
#include "llvm/Support/Casting.h"
@@ -2369,6 +2370,16 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
23692370
if (record_decl)
23702371
GetClangASTImporter().SetRecordLayout(record_decl, layout_info);
23712372

2373+
// DWARF doesn't have the attribute, but we can infer the value the same way
2374+
// as Clang Sema does. It's required to calculate the size of pointers to
2375+
// member functions of this type.
2376+
if (m_ast.getASTContext().getTargetInfo().getCXXABI().isMicrosoft()) {
2377+
auto IM = record_decl->calculateInheritanceModel();
2378+
record_decl->addAttr(clang::MSInheritanceAttr::CreateImplicit(
2379+
m_ast.getASTContext(), true, {},
2380+
clang::MSInheritanceAttr::Spelling(IM)));
2381+
}
2382+
23722383
// Now parse all contained types inside of the class. We make forward
23732384
// declarations to all classes, but we need the CXXRecordDecl to have decls
23742385
// for all contained types because we don't get asked for them via the

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,6 +2880,19 @@ static bool GetCompleteQualType(clang::ASTContext *ast,
28802880
ast, llvm::cast<clang::AttributedType>(qual_type)->getModifiedType(),
28812881
allow_completion);
28822882

2883+
case clang::Type::MemberPointer:
2884+
// MS C++ ABI requires type of the class to be complete of which the pointee
2885+
// is a member.
2886+
if (ast->getTargetInfo().getCXXABI().isMicrosoft()) {
2887+
auto *MPT = qual_type.getTypePtr()->castAs<clang::MemberPointerType>();
2888+
if (MPT->getClass()->isRecordType())
2889+
GetCompleteRecordType(ast, clang::QualType(MPT->getClass(), 0),
2890+
allow_completion);
2891+
2892+
return !qual_type.getTypePtr()->isIncompleteType();
2893+
}
2894+
break;
2895+
28832896
default:
28842897
break;
28852898
}

lldb/source/Utility/DiagnosticsRendering.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Utility/DiagnosticsRendering.h"
10+
#include <cstdint>
1011

1112
using namespace lldb_private;
1213
using namespace lldb;
@@ -98,7 +99,7 @@ void RenderDiagnosticDetails(Stream &stream,
9899
}
99100

100101
// Sort the diagnostics.
101-
auto sort = [](auto &ds) {
102+
auto sort = [](std::vector<DiagnosticDetail> &ds) {
102103
std::stable_sort(ds.begin(), ds.end(), [](auto &d1, auto &d2) {
103104
auto l1 = d1.source_location.value_or(DiagnosticDetail::SourceLocation{});
104105
auto l2 = d2.source_location.value_or(DiagnosticDetail::SourceLocation{});
@@ -121,15 +122,27 @@ void RenderDiagnosticDetails(Stream &stream,
121122
continue;
122123

123124
stream << std::string(loc.column - x_pos, ' ') << cursor;
124-
++x_pos;
125+
x_pos = loc.column + 1;
125126
for (unsigned i = 0; i + 1 < loc.length; ++i) {
126127
stream << underline;
127-
++x_pos;
128+
x_pos += 1;
128129
}
129130
}
130131
}
131132
stream << '\n';
132133

134+
// Reverse the order within groups of diagnostics that are on the same column.
135+
auto group = [](std::vector<DiagnosticDetail> &details) {
136+
for (auto it = details.begin(), end = details.end(); it != end;) {
137+
auto eq_end = std::find_if(it, end, [&](const DiagnosticDetail &d) {
138+
return d.source_location->column != it->source_location->column;
139+
});
140+
std::reverse(it, eq_end);
141+
it = eq_end;
142+
}
143+
};
144+
group(remaining_details);
145+
133146
// Work through each detail in reverse order using the vector/stack.
134147
bool did_print = false;
135148
for (auto detail = remaining_details.rbegin();
@@ -142,14 +155,19 @@ void RenderDiagnosticDetails(Stream &stream,
142155
for (auto &remaining_detail :
143156
llvm::ArrayRef(remaining_details).drop_back(1)) {
144157
uint16_t column = remaining_detail.source_location->column;
145-
if (x_pos <= column)
158+
// Is this a note with the same column as another diagnostic?
159+
if (column == detail->source_location->column)
160+
continue;
161+
162+
if (column >= x_pos) {
146163
stream << std::string(column - x_pos, ' ') << vbar;
147-
x_pos = column + 1;
164+
x_pos = column + 1;
165+
}
148166
}
149167

150-
// Print the line connecting the ^ with the error message.
151168
uint16_t column = detail->source_location->column;
152-
if (x_pos <= column)
169+
// Print the line connecting the ^ with the error message.
170+
if (column >= x_pos)
153171
stream << std::string(column - x_pos, ' ') << joint << hbar << spacer;
154172

155173
// Print a colorized string based on the message's severity type.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SWIFT_SOURCES := main.swift
2+
SWIFTFLAGS_EXTRAS := -parse-as-library
3+
include Makefile.rules
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
import lldbsuite.test.lldbtest as lldbtest
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
@skipIfAsan # rdar://138777205
8+
class TestCase(lldbtest.TestBase):
9+
10+
def check_is_in_line(self, thread, linenum):
11+
frame = thread.frames[0]
12+
line_entry = frame.GetLineEntry()
13+
self.assertEqual(linenum, line_entry.GetLine())
14+
15+
@swiftTest
16+
@skipIf(oslist=["windows", "linux"])
17+
def test(self):
18+
"""Test conditions for async step-over."""
19+
self.build()
20+
21+
source_file = lldb.SBFileSpec("main.swift")
22+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
23+
self, "BREAK HERE", source_file
24+
)
25+
26+
# Step over should reach every line in the interval [10, 20]
27+
expected_line_nums = [10, 11, 12, 13, 14, 15]
28+
# FIXME: for some reason we loop back to the start of the do block after the last statement.
29+
# rdar://140159600
30+
expected_line_nums += [8]
31+
expected_line_nums += [17, 18, 19, 20]
32+
for expected_line_num in expected_line_nums:
33+
thread.StepOver()
34+
stop_reason = thread.GetStopReason()
35+
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
36+
self.check_is_in_line(thread, expected_line_num)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
func getTimestamp(x: Int) async -> Int {
2+
return 40 + x
3+
}
4+
5+
func work() {}
6+
7+
func foo() async {
8+
do {
9+
work() // BREAK HERE
10+
async let timestamp1 = getTimestamp(x:1)
11+
work()
12+
async let timestamp2 = getTimestamp(x:2)
13+
work()
14+
let timestamps = await [timestamp1, timestamp2]
15+
print(timestamps)
16+
}
17+
async let timestamp3 = getTimestamp(x:3)
18+
work()
19+
let actual_timestamp3 = await timestamp3
20+
print(actual_timestamp3)
21+
}
22+
23+
@main enum entry {
24+
static func main() async {
25+
await foo()
26+
}
27+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// REQUIRES: lld
2+
3+
// Itanium ABI:
4+
// RUN: %clang --target=x86_64-pc-linux -gdwarf -c -o %t_linux.o %s
5+
// RUN: %lldb -f %t_linux.o -b -o "target variable s1 s2 m1 m2 v1 v2 v3 v4" | FileCheck --check-prefix=CHECK-GNU %s
6+
//
7+
// CHECK-GNU: (void (Single1::*)()) s1 = 0x00000000000000000000000000000000
8+
// CHECK-GNU: (void (Single2::*)()) s2 = 0x00000000000000000000000000000000
9+
// CHECK-GNU: (void (Multiple1::*)()) m1 = 0x00000000000000000000000000000000
10+
// CHECK-GNU: (void (Multiple2::*)()) m2 = 0x00000000000000000000000000000000
11+
// CHECK-GNU: (void (Virtual1::*)()) v1 = 0x00000000000000000000000000000000
12+
// CHECK-GNU: (void (Virtual2::*)()) v2 = 0x00000000000000000000000000000000
13+
// CHECK-GNU: (void (Virtual3::*)()) v3 = 0x00000000000000000000000000000000
14+
// CHECK-GNU: (void (Virtual4::*)()) v4 = 0x00000000000000000000000000000000
15+
16+
// Microsoft ABI:
17+
// RUN: %clang_cl --target=x86_64-windows-msvc -c -gdwarf -o %t_win.obj /GS- -- %s
18+
// RUN: lld-link /out:%t_win.exe %t_win.obj /entry:main /debug /nodefaultlib
19+
// RUN: %lldb -f %t_win.exe -b -o "target variable s1 s2 m1 m2 v1 v2 v3 v4" | FileCheck --check-prefix=CHECK-MSVC %s
20+
//
21+
// CHECK-MSVC: (void (Single1::*)()) s1 = 0x0000000000000000
22+
// CHECK-MSVC: (void (Single2::*)()) s2 = 0x0000000000000000
23+
// CHECK-MSVC: (void (Multiple1::*)()) m1 = 0x00000000000000000000000000000000
24+
// CHECK-MSVC: (void (Multiple2::*)()) m2 = 0x00000000000000000000000000000000
25+
// CHECK-MSVC: (void (Virtual1::*)()) v1 = 0xffffffff000000000000000000000000
26+
// CHECK-MSVC: (void (Virtual2::*)()) v2 = 0xffffffff000000000000000000000000
27+
// CHECK-MSVC: (void (Virtual3::*)()) v3 = 0xffffffff000000000000000000000000
28+
// CHECK-MSVC: (void (Virtual4::*)()) v4 = 0xffffffff000000000000000000000000
29+
30+
// clang-format off
31+
struct Single1 { void s1() {} };
32+
struct Single2 : Single1 { void s2() {} };
33+
34+
struct Helper {};
35+
struct Multiple1 : Single1, Helper { void m1() {} };
36+
struct Multiple2 : Multiple1 { void m2() {} };
37+
38+
struct Virtual1 : virtual Single1 { void v1() {} };
39+
struct Virtual2 : Virtual1 { void v2() {} };
40+
struct Virtual3 : virtual Multiple1 { void v3() {} };
41+
struct Virtual4 : Virtual1, Helper { void v4() {} };
42+
43+
void (Single1::*s1)() = nullptr;
44+
void (Single2::*s2)() = nullptr;
45+
void (Multiple1::*m1)() = nullptr;
46+
void (Multiple2::*m2)() = nullptr;
47+
void (Virtual1::*v1)() = nullptr;
48+
void (Virtual2::*v2)() = nullptr;
49+
void (Virtual3::*v3)() = nullptr;
50+
void (Virtual4::*v4)() = nullptr;
51+
52+
int main(int argc, char *argv[]) {
53+
// We need to force emission of type info to DWARF. That's reasonable, because
54+
// Clang doesn't know that we need it to infer member-pointer sizes. We could
55+
// probably teach Clang to do so, but in most real-world scenarios this might
56+
// be a non-issue.
57+
Virtual1 vi1;
58+
Virtual2 vi2;
59+
Virtual3 vi3;
60+
Virtual4 vi4;
61+
int sum = sizeof(Single2) + sizeof(Multiple2);
62+
return argc < sum ? 0 : 1;
63+
}

lldb/unittests/Utility/DiagnosticsRenderingTest.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,22 @@ TEST_F(ErrorDisplayTest, RenderStatus) {
2929
{
3030
// Test that diagnostics on the same column can be handled and all
3131
// three errors are diagnosed.
32-
SourceLocation loc1 = {FileSpec{"a.c"}, 13, 11, 0, false, true};
33-
SourceLocation loc2 = {FileSpec{"a.c"}, 13, 13, 0, false, true};
32+
SourceLocation loc1 = {FileSpec{"a.c"}, 13, 5, 0, false, true};
33+
SourceLocation loc2 = {FileSpec{"a.c"}, 13, 7, 0, false, true};
34+
SourceLocation loc3 = {FileSpec{"a.c"}, 13, 9, 0, false, true};
3435
std::string result =
3536
Render({DiagnosticDetail{loc1, eSeverityError, "1", "1"},
36-
DiagnosticDetail{loc1, eSeverityError, "2", "2"},
37-
DiagnosticDetail{loc2, eSeverityError, "3", "3"}});
38-
ASSERT_TRUE(StringRef(result).contains("error: 1"));
39-
ASSERT_TRUE(StringRef(result).contains("error: 2"));
40-
ASSERT_TRUE(StringRef(result).contains("error: 3"));
37+
DiagnosticDetail{loc2, eSeverityError, "2a", "2a"},
38+
DiagnosticDetail{loc2, eSeverityInfo, "2b", "2b"},
39+
DiagnosticDetail{loc3, eSeverityError, "3", "3"}});
40+
llvm::SmallVector<StringRef> lines;
41+
StringRef(result).split(lines, '\n');
42+
// 1234567890123
43+
ASSERT_EQ(lines[0], " ^ ^ ^");
44+
ASSERT_EQ(lines[1], " | | error: 3");
45+
ASSERT_EQ(lines[2], " | error: 2a");
46+
ASSERT_EQ(lines[3], " | note: 2b");
47+
ASSERT_EQ(lines[4], " error: 1");
4148
}
4249
{
4350
// Test that diagnostics in reverse order are emitted correctly.
@@ -68,15 +75,25 @@ TEST_F(ErrorDisplayTest, RenderStatus) {
6875
std::string result =
6976
Render({DiagnosticDetail{loc1, eSeverityError, "X", "X"},
7077
DiagnosticDetail{loc2, eSeverityError, "Y", "Y"}});
71-
auto lines = StringRef(result).split('\n');
72-
auto line1 = lines.first;
73-
lines = lines.second.split('\n');
74-
auto line2 = lines.first;
75-
lines = lines.second.split('\n');
76-
auto line3 = lines.first;
77-
// 1234567
78-
ASSERT_EQ(line1, "^~~ ^~~");
79-
ASSERT_EQ(line2, "| error: Y");
80-
ASSERT_EQ(line3, "error: X");
78+
llvm::SmallVector<StringRef> lines;
79+
StringRef(result).split(lines, '\n');
80+
// 1234567
81+
ASSERT_EQ(lines[0], "^~~ ^~~");
82+
ASSERT_EQ(lines[1], "| error: Y");
83+
ASSERT_EQ(lines[2], "error: X");
84+
}
85+
{
86+
// Test diagnostics on the same line are emitted correctly.
87+
SourceLocation loc1 = {FileSpec{"a.c"}, 1, 2, 0, false, true};
88+
SourceLocation loc2 = {FileSpec{"a.c"}, 1, 6, 0, false, true};
89+
std::string result =
90+
Render({DiagnosticDetail{loc1, eSeverityError, "X", "X"},
91+
DiagnosticDetail{loc2, eSeverityError, "Y", "Y"}});
92+
llvm::SmallVector<StringRef> lines;
93+
StringRef(result).split(lines, '\n');
94+
// 1234567
95+
ASSERT_EQ(lines[0], " ^ ^");
96+
ASSERT_EQ(lines[1], " | error: Y");
97+
ASSERT_EQ(lines[2], " error: X");
8198
}
8299
}

0 commit comments

Comments
 (0)