Skip to content

Commit 3c162ba

Browse files
authored
[LLDB][NativePDB] Add non-overlapping fields in root struct (#166243)
When anonymous unions are used in a struct or vice versa, their fields are merged into the parent record when using PDB. LLDB tries to recreate the original definition of the record _with_ the anonymous unions/structs. For tagged unions (like `std::optional`) where the tag followed the anonymous union, the result was suboptimal: ```cpp // input: struct Foo { union { Bar b; char c; }; bool tag; }; // reconstructed: struct Foo { union { Bar b; struct { char c; bool tag; }; }; }; ``` Once the algorithm is in some nested union, it can't get out. In the above case, we can get to the correct reconstructed record if we always add fields that don't overlap others in the root struct. So when we see `tag`, we'll see that it comes after all other fields, so it's possible to add it in the root `Foo`.
1 parent d49c670 commit 3c162ba

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ void UdtRecordCompleter::Record::ConstructRecord() {
442442

443443
// The end offset to a vector of field/struct that ends at the offset.
444444
std::map<uint64_t, std::vector<Member *>> end_offset_map;
445+
auto is_last_end_offset = [&](auto it) {
446+
return it != end_offset_map.end() && ++it == end_offset_map.end();
447+
};
448+
445449
for (auto &pair : fields_map) {
446450
uint64_t offset = pair.first;
447451
auto &fields = pair.second;
@@ -462,8 +466,23 @@ void UdtRecordCompleter::Record::ConstructRecord() {
462466
}
463467
if (iter->second.empty())
464468
continue;
465-
parent = iter->second.back();
466-
iter->second.pop_back();
469+
470+
// If the new fields come after the already added ones
471+
// without overlap, go back to the root.
472+
if (iter->first <= offset && is_last_end_offset(iter)) {
473+
if (record.kind == Member::Struct) {
474+
parent = &record;
475+
} else {
476+
assert(record.kind == Member::Union &&
477+
"Current record must be a union");
478+
assert(!record.fields.empty());
479+
// For unions, append the field to the last struct
480+
parent = record.fields.back().get();
481+
}
482+
} else {
483+
parent = iter->second.back();
484+
iter->second.pop_back();
485+
}
467486
}
468487
// If it's a field, then the field is inside a union, so we can safely
469488
// increase its size by converting it to a struct to hold multiple fields.

lldb/test/Shell/SymbolFile/NativePDB/class_layout.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@
3434
// CHECK-NEXT: s4 = {
3535
// CHECK-NEXT: x = ([0] = 67, [1] = 68, [2] = 99)
3636
// CHECK-NEXT: }
37-
// CHECK-NEXT: s1 = {
38-
// CHECK-NEXT: x = ([0] = 69, [1] = 70, [2] = 71)
39-
// CHECK-NEXT: }
4037
// CHECK-NEXT: }
4138
// CHECK-NEXT: }
4239
// CHECK-NEXT: }
@@ -47,6 +44,9 @@
4744
// CHECK-NEXT: c2 = 'D'
4845
// CHECK-NEXT: }
4946
// CHECK-NEXT: }
47+
// CHECK-NEXT: s1 = {
48+
// CHECK-NEXT: x = ([0] = 69, [1] = 70, [2] = 71)
49+
// CHECK-NEXT: }
5050
// CHECK-NEXT: }
5151
// CHECK-NEXT: (lldb) type lookup C
5252
// CHECK-NEXT: struct C {
@@ -63,7 +63,6 @@
6363
// CHECK-NEXT: struct {
6464
// CHECK-NEXT: char c4;
6565
// CHECK-NEXT: S3 s4;
66-
// CHECK-NEXT: S3 s1;
6766
// CHECK-NEXT: };
6867
// CHECK-NEXT: };
6968
// CHECK-NEXT: };
@@ -72,6 +71,7 @@
7271
// CHECK-NEXT: char c2;
7372
// CHECK-NEXT: };
7473
// CHECK-NEXT: };
74+
// CHECK-NEXT: S3 s1;
7575
// CHECK-NEXT: }
7676

7777

lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Member *AddField(Member *member, StringRef name, uint64_t byte_offset,
9999
std::make_unique<Member>(name, byte_offset * 8, byte_size * 8,
100100
clang::QualType(), lldb::eAccessPublic, 0);
101101
field->kind = kind;
102-
field->base_offset = base_offset;
102+
field->base_offset = base_offset * 8;
103103
member->fields.push_back(std::move(field));
104104
return member->fields.back().get();
105105
}
@@ -111,6 +111,9 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
111111
CollectMember("m2", 0, 4);
112112
CollectMember("m3", 0, 1);
113113
CollectMember("m4", 0, 8);
114+
CollectMember("m5", 8, 8);
115+
CollectMember("m6", 16, 4);
116+
CollectMember("m7", 16, 8);
114117
ConstructRecord();
115118

116119
// struct {
@@ -120,6 +123,11 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
120123
// m3;
121124
// m4;
122125
// };
126+
// m5;
127+
// union {
128+
// m6;
129+
// m7;
130+
// };
123131
// };
124132
Record record;
125133
record.start_offset = 0;
@@ -128,6 +136,10 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
128136
AddField(u, "m2", 0, 4, Member::Field);
129137
AddField(u, "m3", 0, 1, Member::Field);
130138
AddField(u, "m4", 0, 8, Member::Field);
139+
AddField(&record.record, "m5", 8, 8, Member::Field);
140+
Member *u2 = AddField(&record.record, "", 16, 0, Member::Union);
141+
AddField(u2, "m6", 16, 4, Member::Field);
142+
AddField(u2, "m7", 16, 8, Member::Field);
131143
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
132144
}
133145

@@ -243,3 +255,41 @@ TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) {
243255
AddField(s2, "m4", 2, 4, Member::Field);
244256
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
245257
}
258+
259+
TEST_F(UdtRecordCompleterRecordTests, TestNestedStructInUnionInStructInUnion) {
260+
SetKind(Member::Kind::Union);
261+
CollectMember("m1", 0, 4);
262+
CollectMember("m2", 0, 2);
263+
CollectMember("m3", 0, 2);
264+
CollectMember("m4", 2, 4);
265+
CollectMember("m5", 6, 2);
266+
CollectMember("m6", 6, 2);
267+
CollectMember("m7", 8, 2);
268+
ConstructRecord();
269+
270+
// union {
271+
// m1;
272+
// m2;
273+
// struct {
274+
// m3;
275+
// m4;
276+
// union {
277+
// m5;
278+
// m6;
279+
// };
280+
// m7;
281+
// };
282+
// };
283+
Record record;
284+
record.start_offset = 0;
285+
AddField(&record.record, "m1", 0, 4, Member::Field);
286+
AddField(&record.record, "m2", 0, 2, Member::Field);
287+
Member *s = AddField(&record.record, "", 0, 0, Member::Struct);
288+
AddField(s, "m3", 0, 2, Member::Field);
289+
AddField(s, "m4", 2, 4, Member::Field);
290+
Member *u = AddField(s, "", 6, 0, Member::Union);
291+
AddField(u, "m5", 6, 2, Member::Field);
292+
AddField(u, "m6", 6, 2, Member::Field);
293+
AddField(s, "m7", 8, 2, Member::Field);
294+
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
295+
}

0 commit comments

Comments
 (0)