Skip to content

Commit c8f19db

Browse files
authored
Merge pull request #2743 from fredriss/moar-async-testing
lldb: Swift async: handle 2 different coroutine ABIs
2 parents 1dbec56 + f5792e4 commit c8f19db

File tree

7 files changed

+271
-84
lines changed

7 files changed

+271
-84
lines changed

lldb/include/lldb/Target/SwiftLanguageRuntime.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ class SwiftLanguageRuntime : public LanguageRuntime {
131131

132132
/// Return true if name is a Swift async function symbol.
133133
static bool IsSwiftAsyncFunctionSymbol(llvm::StringRef name);
134-
134+
135+
static bool
136+
IsSwiftAsyncAwaitResumePartialFunctionSymbol(llvm::StringRef name);
137+
135138
enum DemangleMode { eSimplified, eTypeName, eDisplayTypeName };
136139
static std::string DemangleSymbolAsString(llvm::StringRef symbol,
137140
DemangleMode mode,

lldb/source/Expression/DWARFExpression.cpp

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,52 @@ bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
941941
object_address_ptr, result, error_ptr);
942942
}
943943

944+
namespace {
945+
/// The location description kinds described by the DWARF v5
946+
/// specification. Composite locations are handled out-of-band and
947+
/// thus aren't part of the enum.
948+
enum LocationDescriptionKind {
949+
Empty,
950+
Memory,
951+
Register,
952+
Implicit
953+
/* Composite*/
954+
};
955+
/// Adjust value's ValueType according to the kind of location description.
956+
void UpdateValueTypeFromLocationDescription(Log *log, const DWARFUnit *dwarf_cu,
957+
LocationDescriptionKind kind,
958+
Value *value = nullptr) {
959+
// Note that this function is conflating DWARF expressions with
960+
// DWARF location descriptions. Perhaps it would be better to define
961+
// a wrapper for DWARFExpresssion::Eval() that deals with DWARF
962+
// location descriptions (which consist of one or more DWARF
963+
// expressions). But doing this would mean we'd also need factor the
964+
// handling of DW_OP_(bit_)piece out of this function.
965+
if (dwarf_cu && dwarf_cu->GetVersion() >= 4) {
966+
const char *log_msg = "DWARF location description kind: %s";
967+
switch (kind) {
968+
case Empty:
969+
LLDB_LOGF(log, log_msg, "Empty");
970+
break;
971+
case Memory:
972+
LLDB_LOGF(log, log_msg, "Memory");
973+
if (value->GetValueType() == Value::eValueTypeScalar)
974+
value->SetValueType(Value::eValueTypeLoadAddress);
975+
break;
976+
case Register:
977+
LLDB_LOGF(log, log_msg, "Register");
978+
value->SetValueType(Value::eValueTypeScalar);
979+
break;
980+
case Implicit:
981+
LLDB_LOGF(log, log_msg, "Implicit");
982+
if (value->GetValueType() == Value::eValueTypeLoadAddress)
983+
value->SetValueType(Value::eValueTypeScalar);
984+
break;
985+
}
986+
}
987+
}
988+
} // namespace
989+
944990
bool DWARFExpression::Evaluate(
945991
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
946992
lldb::ModuleSP module_sp, const DataExtractor &opcodes,
@@ -989,6 +1035,11 @@ bool DWARFExpression::Evaluate(
9891035
!is_signed));
9901036
};
9911037

1038+
// The default kind is a memory location. This is updated by any
1039+
// operation that changes this, such as DW_OP_stack_value, and reset
1040+
// by composition operations like DW_OP_piece.
1041+
LocationDescriptionKind dwarf4_location_description_kind = Memory;
1042+
9921043
while (opcodes.ValidOffset(offset)) {
9931044
const lldb::offset_t op_offset = offset;
9941045
const uint8_t op = opcodes.GetU8(&offset);
@@ -1981,6 +2032,7 @@ bool DWARFExpression::Evaluate(
19812032
case DW_OP_reg29:
19822033
case DW_OP_reg30:
19832034
case DW_OP_reg31: {
2035+
dwarf4_location_description_kind = Register;
19842036
reg_num = op - DW_OP_reg0;
19852037

19862038
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
@@ -1993,6 +2045,7 @@ bool DWARFExpression::Evaluate(
19932045
// ULEB128 literal operand that encodes the register.
19942046
// DESCRIPTION: Push the value in register on the top of the stack.
19952047
case DW_OP_regx: {
2048+
dwarf4_location_description_kind = Register;
19962049
reg_num = opcodes.GetULEB128(&offset);
19972050
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
19982051
stack.push_back(tmp);
@@ -2116,12 +2169,18 @@ bool DWARFExpression::Evaluate(
21162169
// provides a way of describing how large a part of a variable a particular
21172170
// DWARF expression refers to.
21182171
case DW_OP_piece: {
2172+
LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind;
2173+
// Reset for the next piece.
2174+
dwarf4_location_description_kind = Memory;
2175+
21192176
const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
21202177

21212178
if (piece_byte_size > 0) {
21222179
Value curr_piece;
21232180

21242181
if (stack.empty()) {
2182+
UpdateValueTypeFromLocationDescription(
2183+
log, dwarf_cu, LocationDescriptionKind::Empty);
21252184
// In a multi-piece expression, this means that the current piece is
21262185
// not available. Fill with zeros for now by resizing the data and
21272186
// appending it
@@ -2137,6 +2196,8 @@ bool DWARFExpression::Evaluate(
21372196
// Extract the current piece into "curr_piece"
21382197
Value curr_piece_source_value(stack.back());
21392198
stack.pop_back();
2199+
UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc,
2200+
&curr_piece_source_value);
21402201

21412202
const Value::ValueType curr_piece_source_value_type =
21422203
curr_piece_source_value.GetValueType();
@@ -2245,11 +2306,19 @@ bool DWARFExpression::Evaluate(
22452306

22462307
case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
22472308
if (stack.size() < 1) {
2309+
UpdateValueTypeFromLocationDescription(log, dwarf_cu,
2310+
LocationDescriptionKind::Empty);
2311+
// Reset for the next piece.
2312+
dwarf4_location_description_kind = Memory;
22482313
if (error_ptr)
22492314
error_ptr->SetErrorString(
22502315
"Expression stack needs at least 1 item for DW_OP_bit_piece.");
22512316
return false;
22522317
} else {
2318+
UpdateValueTypeFromLocationDescription(
2319+
log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
2320+
// Reset for the next piece.
2321+
dwarf4_location_description_kind = Memory;
22532322
const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
22542323
const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
22552324
switch (stack.back().GetValueType()) {
@@ -2288,6 +2357,8 @@ bool DWARFExpression::Evaluate(
22882357
// DESCRIPTION: Value is immediately stored in block in the debug info with
22892358
// the memory representation of the target.
22902359
case DW_OP_implicit_value: {
2360+
dwarf4_location_description_kind = Implicit;
2361+
22912362
const uint32_t len = opcodes.GetULEB128(&offset);
22922363
const void *data = opcodes.GetData(&offset, len);
22932364

@@ -2303,6 +2374,12 @@ bool DWARFExpression::Evaluate(
23032374
break;
23042375
}
23052376

2377+
case DW_OP_implicit_pointer: {
2378+
dwarf4_location_description_kind = Implicit;
2379+
LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op));
2380+
return false;
2381+
}
2382+
23062383
// OPCODE: DW_OP_push_object_address
23072384
// OPERANDS: none
23082385
// DESCRIPTION: Pushes the address of the object currently being
@@ -2374,6 +2451,7 @@ bool DWARFExpression::Evaluate(
23742451
// rather is a constant value. The value from the top of the stack is the
23752452
// value to be used. This is the actual object value and not the location.
23762453
case DW_OP_stack_value:
2454+
dwarf4_location_description_kind = Implicit;
23772455
if (stack.empty()) {
23782456
if (error_ptr)
23792457
error_ptr->SetErrorString(
@@ -2594,25 +2672,28 @@ bool DWARFExpression::Evaluate(
25942672
// or DW_OP_bit_piece opcodes
25952673
if (pieces.GetBuffer().GetByteSize()) {
25962674
result = pieces;
2597-
} else {
2598-
if (error_ptr)
2599-
error_ptr->SetErrorString("Stack empty after evaluation.");
2600-
return false;
2675+
return true;
26012676
}
2602-
} else {
2603-
if (log && log->GetVerbose()) {
2604-
size_t count = stack.size();
2605-
LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:",
2606-
(uint64_t)count);
2607-
for (size_t i = 0; i < count; ++i) {
2608-
StreamString new_value;
2609-
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
2610-
stack[i].Dump(&new_value);
2611-
LLDB_LOGF(log, " %s", new_value.GetData());
2612-
}
2677+
if (error_ptr)
2678+
error_ptr->SetErrorString("Stack empty after evaluation.");
2679+
return false;
2680+
}
2681+
2682+
UpdateValueTypeFromLocationDescription(
2683+
log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
2684+
2685+
if (log && log->GetVerbose()) {
2686+
size_t count = stack.size();
2687+
LLDB_LOGF(log,
2688+
"Stack after operation has %" PRIu64 " values:", (uint64_t)count);
2689+
for (size_t i = 0; i < count; ++i) {
2690+
StreamString new_value;
2691+
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
2692+
stack[i].Dump(&new_value);
2693+
LLDB_LOGF(log, " %s", new_value.GetData());
26132694
}
2614-
result = stack.back();
26152695
}
2696+
result = stack.back();
26162697
return true; // Return true on success
26172698
}
26182699

lldb/source/Target/SwiftLanguageRuntime.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,15 +2420,17 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
24202420
llvm_unreachable("Unsupported architexture");
24212421

24222422
row->GetCFAValue().SetIsDWARFExpression(expr, expr_size);
2423-
// The first "shard" of an async fucntion split into coroutines will take its
2424-
// context directly in r14 when called. The other "shards", which have
2425-
// .resume. in their names take the parent pointer in the ABI context register
2426-
// and thus it needs to be dereferenced to get to the context for the
2427-
// function. The debug info for locals reflects this difference, so our
2428-
// unwinding of the context reister needs to reflect it too.
2429-
bool in_resume_function =
2430-
sc.symbol->GetName().GetStringRef().contains(".resume.");
2431-
if (in_resume_function) {
2423+
// The coroutine funclets split from an async function have 2 different ABIs:
2424+
// - Async suspend partial functions and the first funclet get their async
2425+
// context directly in the async register.
2426+
// - Async await resume partial functions take their context indirectly, it
2427+
// needs to be dereferenced to get the actual function's context.
2428+
// The debug info for locals reflects this difference, so our unwinding of the
2429+
// context reister needs to reflect it too.
2430+
bool indirect_context = IsSwiftAsyncAwaitResumePartialFunctionSymbol(
2431+
sc.symbol->GetMangled().GetMangledName().GetStringRef());
2432+
2433+
if (indirect_context) {
24322434
// In a "resume" coroutine, the passed context argument needs to be
24332435
// dereferenced once to get the context. This is reflected in the debug
24342436
// info so we need to account for it and report am async register value

lldb/source/Target/SwiftLanguageRuntimeNames.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ bool SwiftLanguageRuntime::IsSwiftAsyncFunctionSymbol(StringRef name) {
9393
return ::IsSwiftAsyncFunctionSymbol(node);
9494
}
9595

96+
bool SwiftLanguageRuntime::IsSwiftAsyncAwaitResumePartialFunctionSymbol(
97+
StringRef name) {
98+
if (!IsSwiftMangledName(name))
99+
return false;
100+
using namespace swift::Demangle;
101+
Context ctx;
102+
NodePointer node = ctx.demangleSymbolAsNode(name);
103+
return hasChild(node, Node::Kind::AsyncAwaitResumePartialFunction);
104+
}
105+
96106
static ThunkKind GetThunkKind(Symbol *symbol) {
97107
auto symbol_name = symbol->GetMangled().GetMangledName().GetStringRef();
98108

0 commit comments

Comments
 (0)