Flattener: Attempt to take params from parent allocator in case of bad DWARF

This commit is contained in:
Alastair Robertson 2023-06-26 08:07:12 -07:00 committed by Alastair Robertson
parent 5204753497
commit 1bb0c62987
9 changed files with 237 additions and 27 deletions

View File

@ -37,6 +37,16 @@ class EnumBitset {
return bitset[static_cast<size_t>(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;
};

View File

@ -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<Class*>(&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);

View File

@ -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 "";

View File

@ -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_;

View File

@ -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<Class*>(t);
bool TypeIdentifier::isAllocator(Type& t) {
auto* c = dynamic_cast<Class*>(&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<Class*>(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<DummyAllocator>(
*typeToAllocate, size, param.type->align());
Type& typeToAllocate = *allocator->templateParams.at(0).type;
auto* dummy = typeGraph_.make_type<DummyAllocator>(typeToAllocate, size,
param.type->align());
c.templateParams[i] = dummy;
} else {
auto* dummy = typeGraph_.make_type<Dummy>(size, param.type->align());

View File

@ -33,6 +33,7 @@ class TypeGraph;
class TypeIdentifier : public RecursiveVisitor {
public:
static Pass createPass();
static bool isAllocator(Type& t);
TypeIdentifier(TypeGraph& typeGraph) : typeGraph_(typeGraph) {
}

View File

@ -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<std::pair<const int, int>>", 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<std::pair<const int, int>>", 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<std::pair<const int, int>> (size: 1)
Parent (offset: 0)
[2] Struct: MyAllocBase<std::pair<const int, int>> (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<int32_t, int32_t, DummyAllocator<std::pair<const int32_t, int32_t>, 0, 0>> (size: 24)
Param
Primitive: int32_t
Param
Primitive: int32_t
Param
DummyAllocator (size: 0)
[1] Container: std::pair<const int32_t, int32_t> (size: 8)
Param
Primitive: int32_t
Qualifiers: const
Param
Primitive: int32_t
)");
}

View File

@ -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<std::pair<const int, int>>", 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<std::pair<const int, int>>", 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<std::pair<const int, int>> (size: 1)
Parent (offset: 0)
[2] Struct: MyAllocBase<std::pair<const int, int>> (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<std::pair<const int, int>> (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
)");
}

View File

@ -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
)");
}