Implement simpler approach for header file struct emission.

Rather than explicitly building a DAG and doing a topological sort,
just traverse structs recursively and emit declarations for all of
their dependent structs before emitting the original struct declaration.

Not only is this simpler than the previous implementation, but it
fixes a bug where we'd hit an assert if we had a struct with multiple
contained members of another struct type.
This commit is contained in:
Matt Pharr
2012-03-27 09:06:10 -07:00
parent 62cd3418ca
commit ffe484c31e

View File

@@ -755,109 +755,56 @@ Module::writeObjectFileOrAssembly(llvm::TargetMachine *targetMachine,
} }
/** Small structure used in representing dependency graphs of structures /** Emits a declaration for the given struct to the given file. This
(i.e. given a StructType, which other structure types does it have as function first makes sure that declarations for any structs that are
elements). (recursively) members of this struct are emitted first.
*/
struct StructDAGNode {
StructDAGNode()
: visited(false) { }
bool visited;
std::vector<const StructType *> dependents;
};
/** Visit a node for the topological sort.
*/ */
static void static void
lVisitNode(const StructType *structType, lEmitStructDecl(const StructType *st, std::vector<const StructType *> *emittedStructs,
std::map<const StructType *, StructDAGNode *> &structToNode, FILE *file) {
std::vector<const StructType *> &sortedTypes) { // Has this struct type already been declared? (This happens if it's a
Assert(structToNode.find(structType) != structToNode.end()); // member of another struct for which we emitted a declaration
// Get the node that encodes the structs that this one is immediately // previously.)
// dependent on. for (int i = 0; i < (int)emittedStructs->size(); ++i)
StructDAGNode *node = structToNode[structType]; if (Type::EqualIgnoringConst(st, (*emittedStructs)[i]))
if (node->visited) return;
return;
node->visited = true; // Otherwise first make sure any contained structs have been declared.
// Depth-first traversal: visit all of the dependent nodes... for (int i = 0; i < st->GetElementCount(); ++i) {
for (unsigned int i = 0; i < node->dependents.size(); ++i) const StructType *elementStructType =
lVisitNode(node->dependents[i], structToNode, sortedTypes); dynamic_cast<const StructType *>(st->GetElementType(i));
// ...and then add this one to the sorted list if (elementStructType != NULL)
sortedTypes.push_back(structType); lEmitStructDecl(elementStructType, emittedStructs, file);
}
// And now it's safe to declare this one
emittedStructs->push_back(st);
fprintf(file, "struct %s", st->GetStructName().c_str());
if (st->GetSOAWidth() > 0)
// This has to match the naming scheme in
// StructType::GetCDeclaration().
fprintf(file, "_SOA%d", st->GetSOAWidth());
fprintf(file, " {\n");
for (int i = 0; i < st->GetElementCount(); ++i) {
const Type *type = st->GetElementType(i)->GetAsNonConstType();
std::string d = type->GetCDeclaration(st->GetElementName(i));
fprintf(file, " %s;\n", d.c_str());
}
fprintf(file, "};\n\n");
} }
/** Given a set of structures that we want to print C declarations of in a /** Given a set of structures that we want to print C declarations of in a
header file, order them so that any struct that is used as a member header file, emit their declarations.
variable in another struct is printed before the struct that uses it
and then print them to the given file.
*/ */
static void static void
lEmitStructDecls(std::vector<const StructType *> &structTypes, FILE *file) { lEmitStructDecls(std::vector<const StructType *> &structTypes, FILE *file) {
// First, build a DAG among the struct types where there is an edge std::vector<const StructType *> emittedStructs;
// from node A to node B if struct type A depends on struct type B for (unsigned int i = 0; i < structTypes.size(); ++i)
lEmitStructDecl(structTypes[i], &emittedStructs, file);
// Records the struct types that have incoming edges in the Assert(emittedStructs.size() == structTypes.size());
// DAG--i.e. the ones that one or more other struct types depend on
std::set<const StructType *> hasIncomingEdges;
// Records the mapping between struct type pointers and the
// StructDagNode structures
std::map<const StructType *, StructDAGNode *> structToNode;
for (unsigned int i = 0; i < structTypes.size(); ++i) {
// For each struct type, create its DAG node and record the
// relationship between it and its node
const StructType *st = structTypes[i];
StructDAGNode *node = new StructDAGNode;
structToNode[st] = node;
for (int j = 0; j < st->GetElementCount(); ++j) {
const StructType *elementStructType =
dynamic_cast<const StructType *>(st->GetElementType(j));
// If this element is a struct type and we haven't already
// processed it for the current struct type, then upate th
// dependencies and record that this element type has other
// struct types that depend on it.
if (elementStructType != NULL &&
(std::find(node->dependents.begin(), node->dependents.end(),
elementStructType) == node->dependents.end())) {
node->dependents.push_back(elementStructType);
hasIncomingEdges.insert(elementStructType);
}
}
}
// Perform a topological sort of the struct types. Kick it off by
// visiting nodes with no incoming edges; i.e. the struct types that no
// other struct types depend on.
std::vector<const StructType *> sortedTypes;
for (unsigned int i = 0; i < structTypes.size(); ++i) {
const StructType *structType = structTypes[i];
if (hasIncomingEdges.find(structType) == hasIncomingEdges.end())
lVisitNode(structType, structToNode, sortedTypes);
}
Assert(sortedTypes.size() == structTypes.size());
// And finally we can emit the struct declarations by going through the
// sorted ones in order.
for (unsigned int i = 0; i < sortedTypes.size(); ++i) {
const StructType *st = sortedTypes[i];
fprintf(file, "struct %s", st->GetStructName().c_str());
if (st->GetSOAWidth() > 0)
// This has to match the naming scheme in
// StructType::GetCDeclaration().
fprintf(file, "_SOA%d", st->GetSOAWidth());
fprintf(file, " {\n");
for (int j = 0; j < st->GetElementCount(); ++j) {
const Type *type = st->GetElementType(j)->GetAsNonConstType();
std::string d = type->GetCDeclaration(st->GetElementName(j));
fprintf(file, " %s;\n", d.c_str());
}
fprintf(file, "};\n\n");
}
} }