From 5a9d58ab3adb8ca2d9484765d4c266e2b8029480 Mon Sep 17 00:00:00 2001 From: Alastair Robertson Date: Mon, 26 Jun 2023 08:07:12 -0700 Subject: [PATCH] Flattener: Attempt to take params from parent allocator in case of bad DWARF --- oi/EnumBitset.h | 10 +++++ oi/type_graph/Flattener.cpp | 42 +++++++++++++++++++ oi/type_graph/Printer.cpp | 15 +++++++ oi/type_graph/Printer.h | 1 + oi/type_graph/TypeIdentifier.cpp | 37 +++++++--------- oi/type_graph/TypeIdentifier.h | 1 + test/test_codegen.cpp | 70 +++++++++++++++++++++++++++++++ test/test_flattener.cpp | 72 ++++++++++++++++++++++++++++++++ test/test_type_identifier.cpp | 16 +++++-- 9 files changed, 237 insertions(+), 27 deletions(-) diff --git a/oi/EnumBitset.h b/oi/EnumBitset.h index b001318d..1370a18b 100644 --- a/oi/EnumBitset.h +++ b/oi/EnumBitset.h @@ -37,6 +37,16 @@ class EnumBitset { return bitset[static_cast(v)]; } + bool all() const noexcept { + return bitset.all(); + } + bool any() const noexcept { + return bitset.any(); + } + bool none() const noexcept { + return bitset.none(); + } + private: BitsetType bitset; }; diff --git a/oi/type_graph/Flattener.cpp b/oi/type_graph/Flattener.cpp index bc0f9d94..f3276c90 100644 --- a/oi/type_graph/Flattener.cpp +++ b/oi/type_graph/Flattener.cpp @@ -16,6 +16,7 @@ #include "Flattener.h" #include "TypeGraph.h" +#include "TypeIdentifier.h" namespace type_graph { @@ -75,6 +76,44 @@ void flattenParent(const Parent& parent, throw std::runtime_error("Invalid type for parent"); } } + +/* + * Some compilers generate bad DWARF for allocators, such that they are missing + * their template parameter (which represents the type to be allocated). + * + * Try to add this missing parameter back in by pulling it from the allocator's + * parent class. + */ +void fixAllocatorParams(Class& alloc) { + if (!TypeIdentifier::isAllocator(alloc)) { + return; + } + + if (!alloc.templateParams.empty()) { + // The DWARF looks ok + return; + } + + if (alloc.parents.empty()) { + // Nothing we can do + return; + } + + Type& parent = stripTypedefs(*alloc.parents[0].type); + Class* parentClass = dynamic_cast(&parent); + if (!parentClass) { + // Not handled + return; + } + + if (parentClass->templateParams.empty()) { + // Nothing we can do + return; + } + + Type& typeToAllocate = stripTypedefs(*parentClass->templateParams[0].type); + alloc.templateParams.push_back(TemplateParam{&typeToAllocate}); +} } // namespace void Flattener::visit(Class& c) { @@ -152,6 +191,9 @@ void Flattener::visit(Class& c) { flattenParent(parent, flattenedMembers); } + // Perform fixups for bad DWARF + fixAllocatorParams(c); + c.parents.clear(); c.members = std::move(flattenedMembers); diff --git a/oi/type_graph/Printer.cpp b/oi/type_graph/Printer.cpp index 8e33a97f..6784b4c7 100644 --- a/oi/type_graph/Printer.cpp +++ b/oi/type_graph/Printer.cpp @@ -159,6 +159,7 @@ void Printer::print_param(const TemplateParam& param) { } else { print(*param.type); } + print_qualifiers(param.qualifiers); depth_--; } @@ -204,6 +205,20 @@ void Printer::print_value(const std::string& value) { depth_--; } +void Printer::print_qualifiers(const QualifierSet& qualifiers) { + if (qualifiers.none()) { + return; + } + depth_++; + prefix(); + out_ << "Qualifiers:"; + if (qualifiers[Qualifier::Const]) { + out_ << " const"; + } + out_ << std::endl; + depth_--; +} + std::string Printer::align_str(uint64_t align) { if (align == 0) return ""; diff --git a/oi/type_graph/Printer.h b/oi/type_graph/Printer.h index 1b0f6421..36723f5e 100644 --- a/oi/type_graph/Printer.h +++ b/oi/type_graph/Printer.h @@ -50,6 +50,7 @@ class Printer : public ConstVisitor { void print_function(const Function& function); void print_child(const Type& child); void print_value(const std::string& value); + void print_qualifiers(const QualifierSet& qualifiers); static std::string align_str(uint64_t align); std::ostream& out_; diff --git a/oi/type_graph/TypeIdentifier.cpp b/oi/type_graph/TypeIdentifier.cpp index b1a351b3..94ef4784 100644 --- a/oi/type_graph/TypeIdentifier.cpp +++ b/oi/type_graph/TypeIdentifier.cpp @@ -31,17 +31,8 @@ Pass TypeIdentifier::createPass() { return Pass("TypeIdentifier", fn); } -void TypeIdentifier::visit(Type& type) { - if (visited_.count(&type) != 0) - return; - - visited_.insert(&type); - type.accept(*this); -} - -namespace { -bool isAllocator(Type* t) { - auto* c = dynamic_cast(t); +bool TypeIdentifier::isAllocator(Type& t) { + auto* c = dynamic_cast(&t); if (!c) return false; @@ -54,7 +45,14 @@ bool isAllocator(Type* t) { } return false; } -} // namespace + +void TypeIdentifier::visit(Type& type) { + if (visited_.count(&type) != 0) + return; + + visited_.insert(&type); + type.accept(*this); +} void TypeIdentifier::visit(Container& c) { const auto& stubParams = c.containerInfo_.stubTemplateParams; @@ -78,19 +76,12 @@ void TypeIdentifier::visit(Container& c) { size = 0; } - if (isAllocator(param.type)) { + if (isAllocator(*param.type)) { auto* allocator = dynamic_cast(param.type); // TODO please don't do this... - Type* typeToAllocate; - if (!allocator->templateParams.empty()) { - typeToAllocate = allocator->templateParams.at(0).type; - } else { - // We've encountered some bad DWARF. Let's guess that the type to - // allocate is the first parameter of the container. - typeToAllocate = c.templateParams[0].type; - } - auto* dummy = typeGraph_.make_type( - *typeToAllocate, size, param.type->align()); + Type& typeToAllocate = *allocator->templateParams.at(0).type; + auto* dummy = typeGraph_.make_type(typeToAllocate, size, + param.type->align()); c.templateParams[i] = dummy; } else { auto* dummy = typeGraph_.make_type(size, param.type->align()); diff --git a/oi/type_graph/TypeIdentifier.h b/oi/type_graph/TypeIdentifier.h index 93779943..46d2ec05 100644 --- a/oi/type_graph/TypeIdentifier.h +++ b/oi/type_graph/TypeIdentifier.h @@ -33,6 +33,7 @@ class TypeGraph; class TypeIdentifier : public RecursiveVisitor { public: static Pass createPass(); + static bool isAllocator(Type& t); TypeIdentifier(TypeGraph& typeGraph) : typeGraph_(typeGraph) { } diff --git a/test/test_codegen.cpp b/test/test_codegen.cpp index 69a2feb8..6514131a 100644 --- a/test/test_codegen.cpp +++ b/test/test_codegen.cpp @@ -39,6 +39,7 @@ TEST(CodeGenTest, TransformContainerAllocator) { auto myint = Primitive{Primitive::Kind::Int32}; auto myalloc = Class{Class::Kind::Struct, "MyAlloc", 8}; + myalloc.templateParams.push_back(TemplateParam{&myint}); myalloc.functions.push_back(Function{"allocate"}); myalloc.functions.push_back(Function{"deallocate"}); @@ -52,6 +53,8 @@ TEST(CodeGenTest, TransformContainerAllocator) { Primitive: int32_t Param [1] Struct: MyAlloc (size: 8) + Param + Primitive: int32_t Function: allocate Function: deallocate )", @@ -64,3 +67,70 @@ TEST(CodeGenTest, TransformContainerAllocator) { Primitive: int32_t )"); } + +TEST(CodeGenTest, TransformContainerAllocatorParamInParent) { + ContainerInfo pairInfo{"std::pair", SEQ_TYPE, "utility"}; + + ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"}; + mapInfo.stubTemplateParams = {2, 3}; + + Primitive myint{Primitive::Kind::Int32}; + + Container pair{pairInfo, 8}; + pair.templateParams.push_back(TemplateParam{&myint, {Qualifier::Const}}); + pair.templateParams.push_back(TemplateParam{&myint}); + + Class myallocBase{Class::Kind::Struct, + "MyAllocBase>", 1}; + myallocBase.templateParams.push_back(TemplateParam{&pair}); + myallocBase.functions.push_back(Function{"allocate"}); + myallocBase.functions.push_back(Function{"deallocate"}); + + Class myalloc{Class::Kind::Struct, "MyAlloc>", 1}; + myalloc.parents.push_back(Parent{&myallocBase, 0}); + myalloc.functions.push_back(Function{"allocate"}); + myalloc.functions.push_back(Function{"deallocate"}); + + Container map{mapInfo, 24}; + map.templateParams.push_back(TemplateParam{&myint}); + map.templateParams.push_back(TemplateParam{&myint}); + map.templateParams.push_back(TemplateParam{&myalloc}); + + testTransform(map, R"( +[0] Container: std::map (size: 24) + Param + Primitive: int32_t + Param + Primitive: int32_t + Param +[1] Struct: MyAlloc> (size: 1) + Parent (offset: 0) +[2] Struct: MyAllocBase> (size: 1) + Param +[3] Container: std::pair (size: 8) + Param + Primitive: int32_t + Qualifiers: const + Param + Primitive: int32_t + Function: allocate + Function: deallocate + Function: allocate + Function: deallocate +)", + R"( +[0] Container: std::map, 0, 0>> (size: 24) + Param + Primitive: int32_t + Param + Primitive: int32_t + Param + DummyAllocator (size: 0) +[1] Container: std::pair (size: 8) + Param + Primitive: int32_t + Qualifiers: const + Param + Primitive: int32_t +)"); +} diff --git a/test/test_flattener.cpp b/test/test_flattener.cpp index 12867420..62ea2338 100644 --- a/test/test_flattener.cpp +++ b/test/test_flattener.cpp @@ -762,3 +762,75 @@ TEST(FlattenerTest, ParentClassAndContainer) { Primitive: int32_t )"); } + +TEST(FlattenerTest, AllocatorParamInParent) { + ContainerInfo pairInfo{"std::pair", SEQ_TYPE, "utility"}; + + ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"}; + mapInfo.stubTemplateParams = {2, 3}; + + Primitive myint{Primitive::Kind::Int32}; + + Container pair{pairInfo, 8}; + pair.templateParams.push_back(TemplateParam{&myint, {Qualifier::Const}}); + pair.templateParams.push_back(TemplateParam{&myint}); + + Class myallocBase{Class::Kind::Struct, + "MyAllocBase>", 1}; + myallocBase.templateParams.push_back(TemplateParam{&pair}); + myallocBase.functions.push_back(Function{"allocate"}); + myallocBase.functions.push_back(Function{"deallocate"}); + + Class myalloc{Class::Kind::Struct, "MyAlloc>", 1}; + myalloc.parents.push_back(Parent{&myallocBase, 0}); + myalloc.functions.push_back(Function{"allocate"}); + myalloc.functions.push_back(Function{"deallocate"}); + + Container map{mapInfo, 24}; + map.templateParams.push_back(TemplateParam{&myint}); + map.templateParams.push_back(TemplateParam{&myint}); + map.templateParams.push_back(TemplateParam{&myalloc}); + + test(Flattener::createPass(), {map}, R"( +[0] Container: std::map (size: 24) + Param + Primitive: int32_t + Param + Primitive: int32_t + Param +[1] Struct: MyAlloc> (size: 1) + Parent (offset: 0) +[2] Struct: MyAllocBase> (size: 1) + Param +[3] Container: std::pair (size: 8) + Param + Primitive: int32_t + Qualifiers: const + Param + Primitive: int32_t + Function: allocate + Function: deallocate + Function: allocate + Function: deallocate +)", + R"( +[0] Container: std::map (size: 24) + Param + Primitive: int32_t + Param + Primitive: int32_t + Param +[1] Struct: MyAlloc> (size: 1) + Param +[2] Container: std::pair (size: 8) + Param + Primitive: int32_t + Qualifiers: const + Param + Primitive: int32_t + Function: allocate + Function: deallocate + Function: allocate + Function: deallocate +)"); +} diff --git a/test/test_type_identifier.cpp b/test/test_type_identifier.cpp index 172b85b8..b5ce6ab6 100644 --- a/test/test_type_identifier.cpp +++ b/test/test_type_identifier.cpp @@ -77,32 +77,40 @@ TEST(TypeIdentifierTest, Allocator) { )"); } -TEST(TypeIdentifierTest, AllocatorNoParam) { +TEST(TypeIdentifierTest, AllocatorSize1) { auto myint = Primitive{Primitive::Kind::Int32}; - auto myalloc = Class{Class::Kind::Struct, "MyAlloc", 8}; + auto myalloc = Class{Class::Kind::Struct, "MyAlloc", 1}; + myalloc.templateParams.push_back(TemplateParam{&myint}); myalloc.functions.push_back(Function{"allocate"}); myalloc.functions.push_back(Function{"deallocate"}); auto container = getVector(); container.templateParams.push_back(TemplateParam{&myint}); container.templateParams.push_back(TemplateParam{&myalloc}); + container.templateParams.push_back(TemplateParam{&myint}); test(TypeIdentifier::createPass(), {container}, R"( [0] Container: std::vector (size: 24) Param Primitive: int32_t Param -[1] Struct: MyAlloc (size: 8) +[1] Struct: MyAlloc (size: 1) + Param + Primitive: int32_t Function: allocate Function: deallocate + Param + Primitive: int32_t )", R"( [0] Container: std::vector (size: 24) Param Primitive: int32_t Param - DummyAllocator (size: 8) + DummyAllocator (size: 0) Primitive: int32_t + Param + Primitive: int32_t )"); }