**Summary** When filling out the LayoutInfo for a structure with the offsets from DWARF, LLDB fills gaps in the layout by creating unnamed bitfields and adding them to the AST. If we don't do this correctly and our layout has overlapping fields, we will hat an assertion in `clang::CGRecordLowering::lower()`. Specifically, if we have a derived class with a VTable and a bitfield immediately following the vtable pointer, we create a layout with overlapping fields. This is an oversight in some of the previous cleanups done around this area. In `D76808`, we prevented LLDB from creating unnamed bitfields if there was a gap between the last field of a base class and the start of a bitfield in the derived class. In `D112697`, we started accounting for the vtable pointer. The intention there was to make sure the offset bookkeeping accounted for the existence of a vtable pointer (but we didn't actually want to create any AST nodes for it). Now that `last_field_info.bit_size` was being set even for artifical fields, the previous fix `D76808` broke specifically for cases where the bitfield was the first member of a derived class with a vtable (this scenario wasn't tested so we didn't notice it). I.e., we started creating redundant unnamed bitfields for where the vtable pointer usually sits. This confused the lowering logic in clang. This patch adds a condition to `ShouldCreateUnnamedBitfield` which checks whether the first field in the derived class is a vtable ptr. **Testing** * Added API test case Differential Revision: https://reviews.llvm.org/D150591
182 lines
7.5 KiB
Python
182 lines
7.5 KiB
Python
"""Show bitfields and check that they display correctly."""
|
|
|
|
import lldb
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
|
|
|
|
class CppBitfieldsTestCase(TestBase):
|
|
|
|
@no_debug_info_test
|
|
def test_bitfields(self):
|
|
self.build()
|
|
lldbutil.run_to_source_breakpoint(self, '// break here',
|
|
lldb.SBFileSpec("main.cpp", False))
|
|
|
|
# Accessing LargeBitsA.
|
|
self.expect_expr("lba", result_children=[
|
|
ValueCheck(name="", type="int:32"),
|
|
ValueCheck(name="a", type="unsigned int:20", value="2")
|
|
])
|
|
self.expect_expr("lba.a", result_type="unsigned int", result_value="2")
|
|
|
|
|
|
# Accessing LargeBitsB.
|
|
self.expect_expr("lbb", result_children=[
|
|
ValueCheck(name="a", type="unsigned int:1", value="1"),
|
|
ValueCheck(name="", type="int:31"),
|
|
ValueCheck(name="b", type="unsigned int:20", value="3")
|
|
])
|
|
self.expect_expr("lbb.b", result_type="unsigned int", result_value="3")
|
|
|
|
|
|
# Accessing LargeBitsC.
|
|
self.expect_expr("lbc", result_children=[
|
|
ValueCheck(name="", type="int:22"),
|
|
ValueCheck(name="a", type="unsigned int:1", value="1"),
|
|
ValueCheck(name="b", type="unsigned int:1", value="0"),
|
|
ValueCheck(name="c", type="unsigned int:5", value="4"),
|
|
ValueCheck(name="d", type="unsigned int:1", value="1"),
|
|
ValueCheck(name="", type="int:2"),
|
|
ValueCheck(name="e", type="unsigned int:20", value="20"),
|
|
])
|
|
self.expect_expr("lbc.c", result_type="unsigned int", result_value="4")
|
|
|
|
|
|
# Accessing LargeBitsD.
|
|
self.expect_expr("lbd", result_children=[
|
|
ValueCheck(name="arr", type="char[3]", summary='"ab"'),
|
|
ValueCheck(name="", type="int:32"),
|
|
ValueCheck(name="a", type="unsigned int:20", value="5")
|
|
])
|
|
self.expect_expr("lbd.a", result_type="unsigned int", result_value="5")
|
|
|
|
|
|
# Test BitfieldsInStructInUnion.
|
|
# FIXME: This needs some more explanation for what it's actually testing.
|
|
nested_struct_children = [
|
|
ValueCheck(name="", type="int:22"),
|
|
ValueCheck(name="a", type="uint64_t:1", value="1"),
|
|
ValueCheck(name="b", type="uint64_t:1", value="0"),
|
|
ValueCheck(name="c", type="uint64_t:1", value="1"),
|
|
ValueCheck(name="d", type="uint64_t:1", value="0"),
|
|
ValueCheck(name="e", type="uint64_t:1", value="1"),
|
|
ValueCheck(name="f", type="uint64_t:1", value="0"),
|
|
ValueCheck(name="g", type="uint64_t:1", value="1"),
|
|
ValueCheck(name="h", type="uint64_t:1", value="0"),
|
|
ValueCheck(name="i", type="uint64_t:1", value="1"),
|
|
ValueCheck(name="j", type="uint64_t:1", value="0"),
|
|
ValueCheck(name="k", type="uint64_t:1", value="1")
|
|
]
|
|
self.expect_expr("bitfields_in_struct_in_union",
|
|
result_type="BitfieldsInStructInUnion",
|
|
result_children=[ValueCheck(name="", children=[
|
|
ValueCheck(name="f", children=nested_struct_children)
|
|
])]
|
|
)
|
|
self.expect_expr("bitfields_in_struct_in_union.f.a",
|
|
result_type="uint64_t", result_value="1")
|
|
|
|
|
|
# Unions with bitfields.
|
|
self.expect_expr("uwbf", result_type="UnionWithBitfields", result_children=[
|
|
ValueCheck(name="a", value="255"),
|
|
ValueCheck(name="b", value="65535"),
|
|
ValueCheck(name="c", value="4294967295"),
|
|
ValueCheck(name="x", value="4294967295")
|
|
])
|
|
self.expect_expr("uwubf", result_type="UnionWithUnnamedBitfield",
|
|
result_children=[
|
|
ValueCheck(name="a", value="16777215"),
|
|
ValueCheck(name="x", value="4294967295")
|
|
]
|
|
)
|
|
|
|
# Class with a base class and a bitfield.
|
|
self.expect_expr("derived", result_type="Derived", result_children=[
|
|
ValueCheck(name="Base", children=[
|
|
ValueCheck(name="b_a", value="2", type="uint32_t")
|
|
]),
|
|
ValueCheck(name="d_a", value="1", type="uint32_t:1")
|
|
])
|
|
|
|
|
|
# Struct with bool bitfields.
|
|
self.expect_expr("bb", result_type="", result_children=[
|
|
ValueCheck(name="a", value="true", type="bool:1"),
|
|
ValueCheck(name="b", value="false", type="bool:1"),
|
|
ValueCheck(name="c", value="true", type="bool:2"),
|
|
ValueCheck(name="d", value="true", type="bool:2")
|
|
])
|
|
|
|
bb = self.frame().FindVariable('bb')
|
|
self.assertSuccess(bb.GetError())
|
|
|
|
bb_a = bb.GetChildAtIndex(0)
|
|
self.assertSuccess(bb_a.GetError())
|
|
self.assertEqual(bb_a.GetValueAsUnsigned(), 1)
|
|
self.assertEqual(bb_a.GetValueAsSigned(), 1)
|
|
|
|
bb_b = bb.GetChildAtIndex(1)
|
|
self.assertSuccess(bb_b.GetError())
|
|
self.assertEqual(bb_b.GetValueAsUnsigned(), 0)
|
|
self.assertEqual(bb_b.GetValueAsSigned(), 0)
|
|
|
|
bb_c = bb.GetChildAtIndex(2)
|
|
self.assertSuccess(bb_c.GetError())
|
|
self.assertEqual(bb_c.GetValueAsUnsigned(), 1)
|
|
self.assertEqual(bb_c.GetValueAsSigned(), 1)
|
|
|
|
bb_d = bb.GetChildAtIndex(3)
|
|
self.assertSuccess(bb_d.GetError())
|
|
self.assertEqual(bb_d.GetValueAsUnsigned(), 1)
|
|
self.assertEqual(bb_d.GetValueAsSigned(), 1)
|
|
|
|
# Test a class with a base class that has a vtable ptr. The derived
|
|
# class has bitfields.
|
|
base_with_vtable_children = [
|
|
ValueCheck(name="a", type="unsigned int:4", value="5"),
|
|
ValueCheck(name="b", type="unsigned int:4", value="0"),
|
|
ValueCheck(name="c", type="unsigned int:4", value="5")
|
|
]
|
|
self.expect_expr("base_with_vtable", result_children=base_with_vtable_children)
|
|
self.expect_var_path("base_with_vtable", children=base_with_vtable_children)
|
|
|
|
@no_debug_info_test
|
|
def test_bitfield_behind_vtable_ptr(self):
|
|
self.build()
|
|
lldbutil.run_to_source_breakpoint(self, '// break here',
|
|
lldb.SBFileSpec("main.cpp", False))
|
|
|
|
# Test a class with a vtable ptr and bitfields.
|
|
with_vtable_children = [
|
|
ValueCheck(name="a", type="unsigned int:4", value="5"),
|
|
ValueCheck(name="b", type="unsigned int:4", value="0"),
|
|
ValueCheck(name="c", type="unsigned int:4", value="5")
|
|
]
|
|
self.expect_expr("with_vtable", result_children=with_vtable_children)
|
|
self.expect_var_path("with_vtable", children=with_vtable_children)
|
|
|
|
# Test a class with a vtable ptr and unnamed bitfield directly after.
|
|
with_vtable_and_unnamed_children = [
|
|
ValueCheck(name="", type="int:4", value="0"),
|
|
ValueCheck(name="b", type="unsigned int:4", value="0"),
|
|
ValueCheck(name="c", type="unsigned int:4", value="5")
|
|
]
|
|
self.expect_expr("with_vtable_and_unnamed",
|
|
result_children=with_vtable_and_unnamed_children)
|
|
self.expect_var_path("with_vtable_and_unnamed",
|
|
children=with_vtable_and_unnamed_children)
|
|
|
|
derived_with_vtable_children = [
|
|
ValueCheck(name="Base", children=[
|
|
ValueCheck(name="b_a", value="2", type="uint32_t")
|
|
]),
|
|
ValueCheck(name="a", value="1", type="unsigned int:1")
|
|
]
|
|
self.expect_expr("derived_with_vtable",
|
|
result_children=derived_with_vtable_children)
|
|
self.expect_var_path("derived_with_vtable",
|
|
children=derived_with_vtable_children)
|