Skip to content

Commit 1aec81a

Browse files
[lldb] Implement SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction
This is going to be useful for the Swift Language plugin to identify whether two symbol contexts are equivalent, an operation which is done by thread plans such as ThreadPlanStepOverRange. The implementation compares the following attributes of the mangling tree: * If these funclets come from a simple async function (as opposed to a closure), we compare the "Function" child node in the tree. * If these funclets come from an async closure: ** Their "Number" child node must be the same. This number distinguishes different closures in the same scope. ** The closure type must be the same. ** If they have a parent closure, it must be identical. ** If they have a parent function, it must be identical.
1 parent c8aadde commit 1aec81a

File tree

3 files changed

+154
-3
lines changed

3 files changed

+154
-3
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ 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

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

Lines changed: 71 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;

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)