Skip to content

Commit 6c40897

Browse files
Merge pull request #9441 from felipepiovezan/felipe/swift_compare_async_funclets
[lldb][swift] Implement async funclet comparison based on mangling
2 parents d2fbc38 + 063abb3 commit 6c40897

File tree

5 files changed

+185
-3
lines changed

5 files changed

+185
-3
lines changed

lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,23 @@ bool SwiftLanguage::IgnoreForLineBreakpoints(const SymbolContext &sc) const {
18341834
name);
18351835
}
18361836

1837+
std::optional<bool>
1838+
SwiftLanguage::AreEqualForFrameComparison(const SymbolContext &sc1,
1839+
const SymbolContext &sc2) const {
1840+
auto result = SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction(
1841+
sc1.GetFunctionName(Mangled::ePreferMangled),
1842+
sc2.GetFunctionName(Mangled::ePreferMangled));
1843+
switch (result) {
1844+
case SwiftLanguageRuntime::FuncletComparisonResult::NotBothFunclets:
1845+
return {};
1846+
case SwiftLanguageRuntime::FuncletComparisonResult::SameAsyncFunction:
1847+
return true;
1848+
case SwiftLanguageRuntime::FuncletComparisonResult::DifferentAsyncFunctions:
1849+
return false;
1850+
}
1851+
llvm_unreachable("unhandled enumeration in AreEquivalentFunctions");
1852+
}
1853+
18371854
//------------------------------------------------------------------
18381855
// Static Functions
18391856
//------------------------------------------------------------------

lldb/source/Plugins/Language/Swift/SwiftLanguage.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ class SwiftLanguage : public Language {
7575
ConstString
7676
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override;
7777

78+
/// Returns whether two SymbolContexts correspond to funclets of the same
79+
/// async function.
80+
/// If either SymbolContext is not a funclet, nullopt is returned.
81+
std::optional<bool>
82+
AreEqualForFrameComparison(const SymbolContext &sc1,
83+
const SymbolContext &sc2) const override;
7884
//------------------------------------------------------------------
7985
// Static Functions
8086
//------------------------------------------------------------------

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,31 @@ class SwiftLanguageRuntime : public LanguageRuntime {
132132
/// since some day we may want to support more than one swift variant.
133133
static bool IsSwiftMangledName(llvm::StringRef name);
134134

135+
enum class FuncletComparisonResult {
136+
NotBothFunclets,
137+
DifferentAsyncFunctions,
138+
SameAsyncFunction
139+
};
140+
141+
/// Compares name1 and name2 to decide whether they are both async funclets.
142+
/// If either is not an async funclet, returns NotBothFunclets.
143+
/// If they are both funclets but of different async functions, returns
144+
/// DifferentAsyncFunctions.
145+
/// Otherwise, returns SameAsyncFunction.
146+
static FuncletComparisonResult
147+
AreFuncletsOfSameAsyncFunction(StringRef name1, StringRef name2);
148+
135149
/// Return true if name is a Swift async function symbol.
136150
static bool IsSwiftAsyncFunctionSymbol(llvm::StringRef name);
137151

138152
/// Return true if name is a Swift async function, await resume partial
139153
/// function, or suspend resume partial function symbol.
140154
static bool IsAnySwiftAsyncFunctionSymbol(llvm::StringRef name);
141155

156+
/// Return true if node is a Swift async function, await resume partial
157+
/// function, or suspend resume partial function symbol.
158+
static bool IsAnySwiftAsyncFunctionSymbol(NodePointer node);
159+
142160
/// Return the async context address using the target's specific register.
143161
static lldb::addr_t GetAsyncContext(RegisterContext *regctx);
144162

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,77 @@ static bool IsSwiftAsyncFunctionSymbol(swift::Demangle::NodePointer node) {
105105
Node::Kind::AsyncAnnotation});
106106
}
107107

108+
/// Returns true if closure1 and closure2 have the same number, type, and
109+
/// parent closures / function.
110+
static bool AreFuncletsOfSameAsyncClosure(NodePointer closure1,
111+
NodePointer closure2) {
112+
NodePointer closure1_number = childAtPath(closure1, Node::Kind::Number);
113+
NodePointer closure2_number = childAtPath(closure2, Node::Kind::Number);
114+
if (!Node::deepEquals(closure1_number, closure2_number))
115+
return false;
116+
117+
NodePointer closure1_type = childAtPath(closure1, Node::Kind::Type);
118+
NodePointer closure2_type = childAtPath(closure2, Node::Kind::Type);
119+
if (!Node::deepEquals(closure1_type, closure2_type))
120+
return false;
121+
122+
// Because the tree is inverted, a parent closure (in swift code) is a child
123+
// *node* (in the demangle tree). Check that any such parents are identical.
124+
NodePointer closure1_parent =
125+
childAtPath(closure1, Node::Kind::ExplicitClosure);
126+
NodePointer closure2_parent =
127+
childAtPath(closure2, Node::Kind::ExplicitClosure);
128+
if (!Node::deepEquals(closure1_parent, closure2_parent))
129+
return false;
130+
131+
// If there are no ExplicitClosure as parents, there may still be a
132+
// Function. Also check that they are identical.
133+
NodePointer closure1_function = childAtPath(closure1, Node::Kind::Function);
134+
NodePointer closure2_function = childAtPath(closure2, Node::Kind::Function);
135+
return Node::deepEquals(closure1_function, closure2_function);
136+
}
137+
138+
SwiftLanguageRuntime::FuncletComparisonResult
139+
SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction(StringRef name1,
140+
StringRef name2) {
141+
using namespace swift::Demangle;
142+
Context ctx;
143+
NodePointer node1 = DemangleSymbolAsNode(name1, ctx);
144+
NodePointer node2 = DemangleSymbolAsNode(name2, ctx);
145+
146+
if (!IsAnySwiftAsyncFunctionSymbol(node1) ||
147+
!IsAnySwiftAsyncFunctionSymbol(node2))
148+
return FuncletComparisonResult::NotBothFunclets;
149+
150+
// Peel off Static nodes.
151+
NodePointer static_wrapper1 = childAtPath(node1, Node::Kind::Static);
152+
NodePointer static_wrapper2 = childAtPath(node2, Node::Kind::Static);
153+
if (static_wrapper1 || static_wrapper2) {
154+
if (!static_wrapper1 | !static_wrapper2)
155+
return FuncletComparisonResult::DifferentAsyncFunctions;
156+
node1 = static_wrapper1;
157+
node2 = static_wrapper2;
158+
}
159+
160+
// If there are closures involved, do the closure-specific comparison.
161+
NodePointer closure1 = childAtPath(node1, Node::Kind::ExplicitClosure);
162+
NodePointer closure2 = childAtPath(node2, Node::Kind::ExplicitClosure);
163+
if (closure1 || closure2) {
164+
if (!closure1 || !closure2)
165+
return FuncletComparisonResult::DifferentAsyncFunctions;
166+
return AreFuncletsOfSameAsyncClosure(closure1, closure2)
167+
? FuncletComparisonResult::SameAsyncFunction
168+
: FuncletComparisonResult::DifferentAsyncFunctions;
169+
}
170+
171+
// Otherwise, find the corresponding function and compare the two.
172+
NodePointer function1 = childAtPath(node1, Node::Kind::Function);
173+
NodePointer function2 = childAtPath(node2, Node::Kind::Function);
174+
return Node::deepEquals(function1, function2)
175+
? FuncletComparisonResult::SameAsyncFunction
176+
: FuncletComparisonResult::DifferentAsyncFunctions;
177+
}
178+
108179
bool SwiftLanguageRuntime::IsSwiftAsyncFunctionSymbol(StringRef name) {
109180
if (!IsSwiftMangledName(name))
110181
return false;
@@ -130,6 +201,10 @@ bool SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(StringRef name) {
130201
using namespace swift::Demangle;
131202
Context ctx;
132203
NodePointer node = SwiftLanguageRuntime::DemangleSymbolAsNode(name, ctx);
204+
return IsAnySwiftAsyncFunctionSymbol(node);
205+
}
206+
207+
bool SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(NodePointer node) {
133208
if (!node || node->getKind() != Node::Kind::Global || !node->getNumChildren())
134209
return false;
135210
auto marker = node->getFirstChild()->getKind();

lldb/unittests/Symbol/TestSwiftDemangler.cpp

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,39 @@ static constexpr auto IsAnySwiftAsyncFunctionSymbol = [](StringRef name) {
1111
return SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(name);
1212
};
1313

14+
using FuncletComparisonResult = SwiftLanguageRuntime::FuncletComparisonResult;
15+
static constexpr auto AreFuncletsOfSameAsyncFunction =
16+
SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction;
17+
18+
/// Checks that all names in \c funclets belong to the same function.
19+
static void CheckGroupOfFuncletsFromSameFunction(ArrayRef<StringRef> funclets) {
20+
for (StringRef funclet1 : funclets)
21+
for (StringRef funclet2 : funclets) {
22+
EXPECT_EQ(FuncletComparisonResult::SameAsyncFunction,
23+
AreFuncletsOfSameAsyncFunction(funclet1, funclet2))
24+
<< funclet1 << " -- " << funclet2;
25+
EXPECT_EQ(FuncletComparisonResult::SameAsyncFunction,
26+
AreFuncletsOfSameAsyncFunction(funclet2, funclet1))
27+
<< funclet1 << " -- " << funclet2;
28+
}
29+
}
30+
31+
/// Checks that all pairs of combinations of names from \c funclets1 and \c
32+
/// funclets2 belong to different functions.
33+
static void
34+
CheckGroupOfFuncletsFromDifferentFunctions(ArrayRef<StringRef> funclets1,
35+
ArrayRef<StringRef> funclets2) {
36+
for (StringRef funclet1 : funclets1)
37+
for (StringRef funclet2 : funclets2) {
38+
EXPECT_EQ(FuncletComparisonResult::DifferentAsyncFunctions,
39+
AreFuncletsOfSameAsyncFunction(funclet1, funclet2))
40+
<< funclet1 << " -- " << funclet2;
41+
EXPECT_EQ(FuncletComparisonResult::DifferentAsyncFunctions,
42+
AreFuncletsOfSameAsyncFunction(funclet2, funclet1))
43+
<< funclet1 << " -- " << funclet2;
44+
}
45+
}
46+
1447
TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
1548
// "sayBasic" == a basic async function
1649
// "sayGeneric" == a generic async function
@@ -31,6 +64,10 @@ TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
3164
EXPECT_TRUE(IsSwiftMangledName(async_name)) << async_name;
3265
EXPECT_TRUE(IsAnySwiftAsyncFunctionSymbol(async_name)) << async_name;
3366
}
67+
68+
CheckGroupOfFuncletsFromSameFunction(basic_funclets);
69+
CheckGroupOfFuncletsFromSameFunction(generic_funclets);
70+
CheckGroupOfFuncletsFromDifferentFunctions(basic_funclets, generic_funclets);
3471
}
3572

3673
TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
@@ -69,19 +106,48 @@ TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
69106
EXPECT_TRUE(IsSwiftMangledName(async_name)) << async_name;
70107
EXPECT_TRUE(IsAnySwiftAsyncFunctionSymbol(async_name)) << async_name;
71108
}
109+
110+
CheckGroupOfFuncletsFromSameFunction(nested1_funclets);
111+
CheckGroupOfFuncletsFromSameFunction(nested2_funclets1);
112+
CheckGroupOfFuncletsFromSameFunction(nested2_funclets2);
113+
CheckGroupOfFuncletsFromSameFunction(nested2_funclets_top_not_async);
114+
115+
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
116+
nested2_funclets1);
117+
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
118+
nested2_funclets2);
119+
CheckGroupOfFuncletsFromDifferentFunctions(nested1_funclets,
120+
nested2_funclets_top_not_async);
121+
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets1,
122+
nested2_funclets2);
123+
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets1,
124+
nested2_funclets_top_not_async);
125+
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets2,
126+
nested2_funclets_top_not_async);
72127
}
73128

74129
TEST(TestSwiftDemangleAsyncNames, StaticAsync) {
75130
// static async functions
76-
SmallVector<StringRef> async_names = {
77-
"$s1a6StructV9sayStaticyySSYaFZ"
131+
SmallVector<StringRef> static_async_funclets = {
132+
"$s1a6StructV9sayStaticyySSYaFZ",
78133
"$s1a6StructV9sayStaticyySSYaFZTY0_",
79134
"$s1a6StructV9sayStaticyySSYaFZTQ1_",
80135
"$s1a6StructV9sayStaticyySSYaFZTY2_",
81136
};
82137

83-
for (StringRef async_name : async_names) {
138+
for (StringRef async_name : static_async_funclets) {
84139
EXPECT_TRUE(IsSwiftMangledName(async_name)) << async_name;
85140
EXPECT_TRUE(IsAnySwiftAsyncFunctionSymbol(async_name)) << async_name;
86141
}
142+
143+
CheckGroupOfFuncletsFromSameFunction(static_async_funclets);
144+
145+
// Make sure we can compare static funclets to other kinds of funclets
146+
SmallVector<StringRef> other_funclets = {
147+
// Nested funclets:
148+
"$s1a8sayHelloyyYaFyypYacfU_", "$s1a8sayHelloyyYaFyypYacfU_TY0_",
149+
// "Normal" funclets:
150+
"$s1a8sayBasicyySSYaF", "$s1a8sayBasicyySSYaFTY0_"};
151+
CheckGroupOfFuncletsFromDifferentFunctions(static_async_funclets,
152+
other_funclets);
87153
}

0 commit comments

Comments
 (0)