Print opaque types in model header

Emit forward declarations and definitions of the opaque-array
artificial structs from the C model header (and from the helpers
header). Definitions are gated on a new DefineOpaqueTypes flag
threaded through PTMLHeaderBuilder::printModelHeader so that the
helpers header keeps emitting only the declarations.
This commit is contained in:
Pietro Fezzardi
2026-05-08 11:37:10 +02:00
parent 2f692127f9
commit 80a33109eb
7 changed files with 156 additions and 7 deletions
@@ -33,7 +33,7 @@ public:
/// Generate a C header containing a serialization of the type system,
/// i.e. function prototypes, structs, unions, typedefs, and anything that
/// resides in the model.
bool printModelHeader();
bool printModelHeader(bool DefineOpaqueTypes = false);
/// Generate a C header containing the declaration of each non-isolated
/// function in a given LLVM IR module, i.e. QEMU helpers and revng helpers,
+14
View File
@@ -873,6 +873,20 @@ public:
/// on every type, as types can depend on each other.
/// This method ensures they are printed in a valid order.
void printTypeDefinitions();
/// Print all opaque type definitions required by the types in the model.
void printModelOpaqueTypeDefinitions();
/// Print all opaque type definitions required by the types in the headers,
/// that are not otherwise printed by printModelOpaqueTypeDefinitions.
void
printHelperOpaqueTypeDefinitions(const std::set<uint64_t> &HelperByteSizes);
private:
std::set<uint64_t> getModelOpaqueByteSizes();
/// Print a single opaque type definitions with given ByteSize
void printOpaqueTypeDefinition(uint64_t ByteSize);
};
} // namespace ptml
+1 -1
View File
@@ -80,7 +80,7 @@ void DecompileToDirectory::run(pipeline::ExecutionContext &EC,
B.setOutputStream(Out);
ptml::HeaderBuilder HB = B;
HB.printModelHeader();
HB.printModelHeader(/*DefineOpaqueTypes*/ true);
Out.flush();
+20
View File
@@ -95,6 +95,8 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) {
+ B.getIncludeQuote("primitive-types.h") + "\n";
B.append(std::move(Includes));
std::set<uint64_t> OpaqueByteSizes;
for (const llvm::Function &F : M.functions()) {
// Skip non-helpers
@@ -140,6 +142,10 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) {
const auto *RetTy = F.getReturnType();
if (auto *RetStructTy = dyn_cast<llvm::StructType>(RetTy)) {
printLLVMTypeDeclaration(RetStructTy, F, B);
const auto &DataLayout = F.getParent()->getDataLayout();
OpaqueByteSizes
.insert(DataLayout.getStructLayout(RetStructTy)->getSizeInBytes());
B.append("\n");
}
@@ -148,8 +154,22 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) {
}
printHelperPrototype(&F, B);
B.append("\n");
}
// Always print the opaque type definitions here.
// The helper header is only used if we have the function body, which will
// need these definitions.
B.appendLineComment("\\defgroup Opaque Types");
B.appendLineComment("\\{");
auto OpaqueTypeScope = B.getScopeTag(ptml::tags::Div);
B.printHelperOpaqueTypeDefinitions(OpaqueByteSizes);
B.appendLineComment("\\}");
B.append("\n");
return true;
}
+21 -2
View File
@@ -27,7 +27,7 @@
static Logger Log{ "model-to-header" };
bool ptml::HeaderBuilder::printModelHeader() {
bool ptml::HeaderBuilder::printModelHeader(bool DefineOpaqueTypes) {
auto Scope = B.getScopeTag(ptml::tags::Div);
@@ -128,13 +128,32 @@ bool ptml::HeaderBuilder::printModelHeader() {
B.appendLineComment("\\defgroup Segments");
B.appendLineComment("\\{");
for (const model::Segment &Segment : B.Binary.Segments())
for (const model::Segment &Segment : B.Binary.Segments()) {
if (Segment.Type().isEmpty())
continue;
auto &SegmentType = Segment.Type()->toStruct();
if (B.Configuration.TypesToOmit.contains(SegmentType.key()))
continue;
B.printSegmentType(Segment);
}
B.append("\n");
B.appendLineComment("\\}");
B.append("\n");
}
if (DefineOpaqueTypes) {
B.appendLineComment("\\defgroup Opaque Types");
B.appendLineComment("\\{");
auto Scope = B.getScopeTag(ptml::tags::Div);
B.printModelOpaqueTypeDefinitions();
B.appendLineComment("\\}");
B.append("\n");
}
return true;
}
+2 -3
View File
@@ -60,8 +60,7 @@ public:
{ .EnableStackFrameInlining = options::EnableStackFrameInlining,
.EnablePrintingOfTheMaximumEnumValue = true,
.ExplicitTargetPointerSize = getExplicitPointerSize(Model) });
ptml::HeaderBuilder(B).printModelHeader();
ptml::HeaderBuilder(B).printModelHeader(/*DefineOpaqueTypes*/ true);
Header.flush();
ErrorCode = Header.error();
if (ErrorCode)
@@ -95,7 +94,7 @@ void ModelToHeader::run() {
{ .EnableStackFrameInlining = revng::options::EnableStackFrameInlining,
.EnablePrintingOfTheMaximumEnumValue = true,
.ExplicitTargetPointerSize = getExplicitPointerSize(Binary) });
ptml::HeaderBuilder(B).printModelHeader();
ptml::HeaderBuilder(B).printModelHeader(/*DefineOpaqueTypes*/ true);
Out->flush();
}
+97
View File
@@ -321,3 +321,100 @@ void ptml::ModelCBuilder::printTypeDefinitions() {
revng_log(TypePrinterLog, "PostOrder DONE");
}
}
void ptml::ModelCBuilder::printOpaqueTypeDefinition(uint64_t ByteSize) {
// Print the typedef inline with the struct definition.
std::string
StructLine = getKeyword(ptml::CBuilder::Keyword::Typedef) + " "
+ getKeyword(ptml::CBuilder::Keyword::Struct) + " "
+ ptml::Attributes.getAttributeString<"_PACKED">() + " "
+ getOpaqueTypeDeclarationTag</*IsDefinition*/ false>(ByteSize)
+ " ";
*Out << std::move(StructLine);
{
Scope Scope(*Out, ptml::c::scopes::StructBody);
std::string UInt8T = getPrimitiveTag(model::PrimitiveKind::Unsigned, 1);
std::string
ArrayName = getOpaqueTypeFieldTag</*IsDefinition*/ true>(ByteSize);
*Out << UInt8T << " " << ArrayName << "[" << getNumber(ByteSize) << "];\n";
}
*Out << " " << getOpaqueTypeDeclarationTag</*IsDefinition*/ true>(ByteSize)
<< ";\n";
}
std::set<uint64_t> ptml::ModelCBuilder::getModelOpaqueByteSizes() {
std::set<uint64_t> ByteSizes = { 1, 2, 4, 8, 10, 12, 16 };
for (const model::UpcastableTypeDefinition &Type : Binary.TypeDefinitions()) {
uint64_t ByteSize = Type->size().value_or(0);
if (ByteSize)
ByteSizes.insert(ByteSize);
llvm::SmallVector<model::UpcastableType> Dependencies;
const model::TypeDefinition *Definition = Type->tryGetAsDefinition();
if (not Definition)
continue;
if (const auto *TD = llvm::dyn_cast<model::TypedefDefinition>(Definition))
Dependencies.push_back(TD->UnderlyingType());
if (const auto *S = Definition->getStruct())
for (const auto &Field : S->Fields())
Dependencies.push_back(Field.Type());
if (const auto *U = Definition->getUnion())
for (const auto &Field : U->Fields())
Dependencies.push_back(Field.Type());
if (const auto *R = Definition->getRawFunction()) {
for (const auto &A : R->Arguments())
Dependencies.push_back(A.Type());
uint64_t ReturnTypeSize = 0;
for (const auto &RV : R->ReturnValues()) {
Dependencies.push_back(RV.Type());
ReturnTypeSize += RV.Type()->size().value_or(0);
}
if (ReturnTypeSize)
ByteSizes.insert(ReturnTypeSize);
}
if (const auto *C = Definition->getCABIFunction()) {
for (const auto &A : C->Arguments())
Dependencies.push_back(A.Type());
if (not C->ReturnType().isEmpty())
Dependencies.push_back(C->ReturnType());
}
for (const model::UpcastableType &D : Dependencies)
if (uint64_t ByeSize = D->trySize().value_or(0))
ByteSizes.insert(ByteSize);
}
return ByteSizes;
}
void ptml::ModelCBuilder::printModelOpaqueTypeDefinitions() {
std::set<uint64_t> ByteSizes = getModelOpaqueByteSizes();
for (uint64_t ByteSize : ByteSizes) {
*Out << "\n";
printOpaqueTypeDefinition(ByteSize);
}
}
using MCB = ptml::ModelCBuilder;
using SizeSet = std::set<uint64_t>;
void MCB::printHelperOpaqueTypeDefinitions(const SizeSet &HelperByteSizes) {
std::set<uint64_t> ModelByteSizes = getModelOpaqueByteSizes();
for (uint64_t ByteSize : HelperByteSizes) {
if (not ModelByteSizes.contains(ByteSize)) {
*Out << "\n";
printOpaqueTypeDefinition(ByteSize);
}
}
}