mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
662 lines
25 KiB
C++
662 lines
25 KiB
C++
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <unordered_map>
|
|
|
|
#include "llvm/ADT/DepthFirstIterator.h"
|
|
#include "llvm/ADT/GraphTraits.h"
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/ADT/GenericGraph.h"
|
|
#include "revng/ADT/STLExtras.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/Helpers.h"
|
|
#include "revng/Model/TypeDefinition.h"
|
|
#include "revng/Pipeline/Location.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/YAMLTraits.h"
|
|
#include "revng/Yield/PTML.h"
|
|
|
|
#include "revng-c/HeadersGeneration/ModelToHeader.h"
|
|
#include "revng-c/Pipes/Ranks.h"
|
|
#include "revng-c/Support/ModelHelpers.h"
|
|
#include "revng-c/Support/PTMLC.h"
|
|
#include "revng-c/TypeNames/ModelToPTMLTypeHelpers.h"
|
|
#include "revng-c/TypeNames/ModelTypeNames.h"
|
|
|
|
using TypeNameMap = std::map<model::UpcastableType, std::string>;
|
|
using DefinitionSet = std::set<const model::TypeDefinition *>;
|
|
using GraphInfo = TypeInlineHelper::GraphInfo;
|
|
using Node = TypeInlineHelper::Node;
|
|
using StackTypesMap = std::unordered_map<const model::Function *,
|
|
DefinitionSet>;
|
|
|
|
/// Collect candidates for emitting inline types.
|
|
static DefinitionSet findTypesToInline(const model::Binary &Model) {
|
|
using NumTypeRefMap = std::unordered_map<const model::TypeDefinition *,
|
|
uint64_t>;
|
|
NumTypeRefMap NumberOfRefsPerType;
|
|
DefinitionSet TypesWithBannedReferences;
|
|
|
|
for (const model::UpcastableTypeDefinition &T : Model.TypeDefinitions()) {
|
|
NumberOfRefsPerType.insert({ T.get(), 0 });
|
|
|
|
bool ParentDeclarationIsDefinition = declarationIsDefinition(*T);
|
|
for (const model::Type *Edge : T->edges()) {
|
|
const model::TypeDefinition *Dependency = Edge->skipToDefinition();
|
|
if (Dependency == nullptr)
|
|
continue; // Skip types without definitions (only primitives as of now)
|
|
|
|
NumberOfRefsPerType[Dependency]++;
|
|
|
|
// If the parent type has a declaration that is also a definition, we
|
|
// cannot inline it there, since we only allow inlining inside types whose
|
|
// full definition is separate from declaration
|
|
if (ParentDeclarationIsDefinition)
|
|
TypesWithBannedReferences.insert(Dependency);
|
|
|
|
// To inline an array or pointer type, we should basically inline the
|
|
// array element or the pointee type.
|
|
// At the moment we don't try to do this, and just prevent them to be
|
|
// inlined. We might try and do better in the future.
|
|
if (Edge->isPointer() or Edge->isArray())
|
|
TypesWithBannedReferences.insert(Dependency);
|
|
}
|
|
}
|
|
|
|
for (const model::Function &Function : Model.Functions())
|
|
if (const model::StructDefinition *Stack = Function.stackFrameType())
|
|
NumberOfRefsPerType[Stack]++;
|
|
|
|
// TODO: In principle we should do this for segments to, to enable inlining
|
|
// their type definition directly in the declaration of the global variable
|
|
// representing the segment.
|
|
// This is not urgent now though, and it would require more tweaks to
|
|
// ModelToHeader that are low-priority now.
|
|
//
|
|
// for (const model::Segment &Segment : Model.Segments())
|
|
// if (const model::StructDefinition *Type = Function.type())
|
|
// NumberOfRefsPerType[Type]++;
|
|
|
|
// A candidate for inline is the type IFF it was referenced only once.
|
|
auto F = std::views::filter([&TypesWithBannedReferences](const auto &Pair) {
|
|
auto [Definition, ReferenceCount] = Pair;
|
|
return ReferenceCount == 1
|
|
and not TypesWithBannedReferences.contains(Definition)
|
|
and not declarationIsDefinition(*Definition);
|
|
});
|
|
return NumberOfRefsPerType | F | std::views::keys
|
|
| revng::to<DefinitionSet>();
|
|
}
|
|
|
|
static GraphInfo buildTypeGraph(const model::Binary &Model) {
|
|
|
|
GraphInfo Result;
|
|
|
|
using NodeData = TypeInlineHelper::NodeData;
|
|
for (const UpcastablePointer<model::TypeDefinition> &T :
|
|
Model.TypeDefinitions()) {
|
|
Result.TypeToNode[T.get()] = Result.TypeGraph.addNode(NodeData{ T.get() });
|
|
}
|
|
|
|
// Create type system edges.
|
|
for (const model::UpcastableTypeDefinition &T : Model.TypeDefinitions())
|
|
for (const model::Type *Edge : T->edges())
|
|
if (const model::TypeDefinition *Def = Edge->skipToDefinition())
|
|
Result.TypeToNode.at(T.get())->addSuccessor(Result.TypeToNode.at(Def));
|
|
|
|
return Result;
|
|
}
|
|
|
|
TypeInlineHelper::TypeInlineHelper(const model::Binary &TheModel) :
|
|
Model(TheModel),
|
|
TypeGraph(buildTypeGraph(Model)),
|
|
TypesToInline(findTypesToInline(Model)) {
|
|
}
|
|
|
|
const DefinitionSet &TypeInlineHelper::getTypesToInline() const {
|
|
return TypesToInline;
|
|
}
|
|
|
|
/// Returns a set of types that are referred to by at least one other type in
|
|
/// the \a Model. It does not take into consideration other references to the
|
|
/// types that are not cross-references among types, like e.g. stack frame types
|
|
/// that refer to model::Types from model::Functions.
|
|
static DefinitionSet getCrossReferencedTypes(const model::Binary &Model) {
|
|
DefinitionSet Result;
|
|
|
|
for (const model::UpcastableTypeDefinition &T : Model.TypeDefinitions())
|
|
for (const model::Type *Edge : T->edges())
|
|
Result.insert(Edge->skipToDefinition());
|
|
|
|
return Result;
|
|
}
|
|
|
|
StackTypesMap TypeInlineHelper::findTypesToInlineInStacks() const {
|
|
StackTypesMap Result;
|
|
|
|
DefinitionSet CrossReferencedTypes = getCrossReferencedTypes(Model);
|
|
for (auto &Function : Model.Functions()) {
|
|
if (const model::StructDefinition *Stack = Function.stackFrameType()) {
|
|
// Do not inline stack types that are used by at least one other type.
|
|
if (CrossReferencedTypes.contains(Stack))
|
|
continue;
|
|
|
|
Result[&Function].insert(Stack);
|
|
auto AllNestedTypes = getTypesToInlineInTypeTy(*Stack);
|
|
Result[&Function].merge(AllNestedTypes);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
DefinitionSet TypeInlineHelper::collectTypesInlinableInStacks() const {
|
|
DefinitionSet Result;
|
|
|
|
for (auto [Function, TypesToInlineInStack] : findTypesToInlineInStacks())
|
|
Result.merge(std::move(TypesToInlineInStack));
|
|
|
|
return Result;
|
|
}
|
|
|
|
using TI = TypeInlineHelper;
|
|
DefinitionSet
|
|
TI::getNestedTypesToInline(const model::TypeDefinition &RootType,
|
|
const model::TypeDefinition &Nested) const {
|
|
const model::TypeDefinition *Current = &Nested;
|
|
DefinitionSet Result;
|
|
do {
|
|
Result.insert(Current);
|
|
auto ParentNode = TypeGraph.TypeToNode.at(Current)->predecessors().begin();
|
|
if ((*ParentNode)->data().T == &RootType) {
|
|
return Result;
|
|
} else if (TypesToInline.contains((*ParentNode)->data().T)) {
|
|
Current = (*ParentNode)->data().T;
|
|
} else {
|
|
return {};
|
|
}
|
|
} while (Current != nullptr);
|
|
|
|
return {};
|
|
}
|
|
|
|
DefinitionSet
|
|
TI::getTypesToInlineInTypeTy(const model::TypeDefinition &RootType) const {
|
|
DefinitionSet Result;
|
|
auto TheTypeToNode = TypeGraph.TypeToNode;
|
|
|
|
// Visit all the nodes reachable from RootType.
|
|
llvm::df_iterator_default_set<Node *> Visited;
|
|
for ([[maybe_unused]] Node *N :
|
|
depth_first_ext(TheTypeToNode.at(&RootType), Visited))
|
|
;
|
|
|
|
for (const auto &Type : Model.TypeDefinitions()) {
|
|
if (Visited.contains(TheTypeToNode.at(Type.get()))
|
|
and TypesToInline.contains(Type.get())
|
|
and TheTypeToNode.at(Type.get())->predecessorCount() == 1) {
|
|
auto &ParentNode = *TheTypeToNode.at(Type.get())->predecessors().begin();
|
|
// In the case the parent is stack type itself, just insert the type.
|
|
if (ParentNode->data().T == &RootType) {
|
|
Result.insert(Type.get());
|
|
} else if (TypesToInline.contains(ParentNode->data().T)) {
|
|
// In the case the parent type is not the type RootType itself, make
|
|
// sure that the parent is inlinable into the type RootType. NOTE: This
|
|
// goes as further as possible in opposite direction in order to find
|
|
// all types that we should inline into the type RootType.
|
|
auto NestedTypesToInline = getNestedTypesToInline(RootType, *Type);
|
|
Result.merge(NestedTypesToInline);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static ptml::Tag getTypeKeyword(const model::TypeDefinition &T,
|
|
const ptml::PTMLCBuilder &B) {
|
|
|
|
switch (T.Kind()) {
|
|
|
|
case model::TypeDefinitionKind::EnumDefinition: {
|
|
return B.getKeyword(ptml::PTMLCBuilder::Keyword::Enum);
|
|
}
|
|
|
|
case model::TypeDefinitionKind::StructDefinition: {
|
|
return B.getKeyword(ptml::PTMLCBuilder::Keyword::Struct);
|
|
}
|
|
|
|
case model::TypeDefinitionKind::UnionDefinition: {
|
|
return B.getKeyword(ptml::PTMLCBuilder::Keyword::Union);
|
|
}
|
|
|
|
default:
|
|
revng_abort("unexpected type kind");
|
|
}
|
|
}
|
|
|
|
void printForwardDeclaration(const model::TypeDefinition &T,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B) {
|
|
revng_assert(not declarationIsDefinition(T));
|
|
|
|
auto TypeNameReference = B.getLocationReference(T);
|
|
Header << B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " "
|
|
<< getTypeKeyword(T, B) << " " << B.getAttributePacked() << " "
|
|
<< TypeNameReference << " " << TypeNameReference << ";\n";
|
|
}
|
|
|
|
static void printDefinition(const model::EnumDefinition &E,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
bool ForEditing,
|
|
std::string &&Suffix = "") {
|
|
// We have to make the enum of the correct size of the underlying type
|
|
auto ByteSize = *E.size();
|
|
revng_assert(ByteSize <= 8);
|
|
size_t FullMask = std::numeric_limits<size_t>::max();
|
|
size_t MaxBitPatternInEnum = (ByteSize == 8) ?
|
|
FullMask :
|
|
((FullMask) xor (FullMask << (8 * ByteSize)));
|
|
|
|
Header << B.getModelComment(E)
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Enum) << " "
|
|
<< B.getAnnotateEnum(E.underlyingType().getCName()) << " "
|
|
<< B.getAttributePacked() << " " << B.getLocationDefinition(E) << " ";
|
|
|
|
{
|
|
Scope Scope(Header);
|
|
|
|
using PTMLOperator = ptml::PTMLCBuilder::Operator;
|
|
for (const auto &Entry : E.Entries()) {
|
|
Header << B.getModelComment(Entry) << B.getLocationDefinition(E, Entry)
|
|
<< " " << B.getOperator(PTMLOperator::Assign) << " "
|
|
<< B.getHex(Entry.Value()) << ",\n";
|
|
}
|
|
|
|
if (not ForEditing) {
|
|
// This ensures the enum is exactly as large as the Underlying type
|
|
Header << B.tokenTag(("_enum_max_value_" + E.name()).str(),
|
|
ptml::c::tokens::Field)
|
|
<< " " + B.getOperator(PTMLOperator::Assign) + " "
|
|
<< B.getHex(MaxBitPatternInEnum) << ",\n";
|
|
}
|
|
}
|
|
|
|
Header << std::move(Suffix) << ";\n";
|
|
}
|
|
|
|
static void printDefinition(Logger<> &Log,
|
|
const model::StructDefinition &S,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
TypeNameMap &AdditionalNames,
|
|
const DefinitionSet &TypesToInline,
|
|
std::string &&Suffix = "") {
|
|
|
|
Header << B.getModelComment(S)
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Struct) << " "
|
|
<< B.getAttributePacked() << " ";
|
|
Header << B.getLocationDefinition(S) << " ";
|
|
{
|
|
Scope Scope(Header, ptml::c::scopes::StructBody);
|
|
|
|
size_t NextOffset = 0ULL;
|
|
for (const auto &Field : S.Fields()) {
|
|
if (NextOffset < Field.Offset()) {
|
|
Header << B.tokenTag("uint8_t", ptml::c::tokens::Type) << " "
|
|
<< B.tokenTag(StructPaddingPrefix + std::to_string(NextOffset),
|
|
ptml::c::tokens::Field)
|
|
<< "[" << B.getNumber(Field.Offset() - NextOffset) << "];\n";
|
|
}
|
|
|
|
auto *MaybeDefinition = Field.Type()->skipToDefinition();
|
|
if (not MaybeDefinition or not TypesToInline.contains(MaybeDefinition)) {
|
|
auto F = B.getLocationDefinition(S, Field);
|
|
Header << B.getModelComment(Field)
|
|
<< getNamedCInstance(*Field.Type(), F, B) << ";\n";
|
|
} else {
|
|
printInlineDefinition(Log,
|
|
Field.name().str(),
|
|
*Field.Type(),
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline);
|
|
}
|
|
|
|
NextOffset = Field.Offset() + Field.Type()->size().value();
|
|
}
|
|
|
|
if (NextOffset < S.Size())
|
|
Header << B.tokenTag("uint8_t", ptml::c::tokens::Type) << " "
|
|
<< B.tokenTag(StructPaddingPrefix + std::to_string(NextOffset),
|
|
ptml::c::tokens::Field)
|
|
<< "[" << B.getNumber(S.Size() - NextOffset) << "];\n";
|
|
}
|
|
|
|
Header << std::move(Suffix) << ";\n";
|
|
}
|
|
|
|
static void printDefinition(Logger<> &Log,
|
|
const model::UnionDefinition &U,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
TypeNameMap &AdditionalTypeNames,
|
|
const DefinitionSet &TypesToInline,
|
|
std::string &&Suffix = "") {
|
|
Header << B.getModelComment(U)
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Union) << " "
|
|
<< B.getAttributePacked() << " ";
|
|
Header << B.getLocationDefinition(U) << " ";
|
|
|
|
{
|
|
Scope Scope(Header, ptml::c::scopes::UnionBody);
|
|
for (const auto &Field : U.Fields()) {
|
|
auto *MaybeDefinition = Field.Type()->skipToDefinition();
|
|
if (not MaybeDefinition or not TypesToInline.contains(MaybeDefinition)) {
|
|
auto F = B.getLocationDefinition(U, Field);
|
|
Header << B.getModelComment(Field)
|
|
<< getNamedCInstance(*Field.Type(), F, B) << ";\n";
|
|
} else {
|
|
printInlineDefinition(Log,
|
|
Field.name().str(),
|
|
*Field.Type(),
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalTypeNames,
|
|
TypesToInline);
|
|
}
|
|
}
|
|
}
|
|
|
|
Header << std::move(Suffix) << ";\n";
|
|
}
|
|
|
|
void printDeclaration(const model::TypedefDefinition &TD,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B) {
|
|
if (declarationIsDefinition(TD))
|
|
Header << B.getModelComment(TD);
|
|
|
|
auto Type = B.getLocationDefinition(TD);
|
|
Header << B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " "
|
|
<< getNamedCInstance(*TD.UnderlyingType(), Type, B) << ";\n";
|
|
}
|
|
|
|
/// Generate the definition of a new struct type that wraps all the return
|
|
/// values of \a F. The name of the struct type is provided by the caller.
|
|
static void generateReturnValueWrapper(Logger<> &Log,
|
|
const model::RawFunctionDefinition &F,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model) {
|
|
revng_assert(F.ReturnValues().size() > 1);
|
|
if (Log.isEnabled())
|
|
Header << B.getLineComment("definition the of return type "
|
|
"needed");
|
|
|
|
Header << B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " "
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Struct) << " "
|
|
<< B.getAttributePacked() << " ";
|
|
|
|
{
|
|
Scope Scope(Header, ptml::c::scopes::StructBody);
|
|
for (auto &[Index, ReturnValue] : llvm::enumerate(F.ReturnValues())) {
|
|
using pipeline::serializedLocation;
|
|
std::string
|
|
ActionLocation = serializedLocation(revng::ranks::ReturnRegister,
|
|
F.key(),
|
|
ReturnValue.key());
|
|
|
|
std::string
|
|
FieldString = B.tokenTag(ReturnValue.name(), ptml::c::tokens::Field)
|
|
.addAttribute(ptml::attributes::ActionContextLocation,
|
|
ActionLocation)
|
|
.serialize();
|
|
Header << getNamedCInstance(*ReturnValue.Type(), FieldString, B) << ";\n";
|
|
}
|
|
}
|
|
|
|
Header << " " << getReturnTypeName(F, B, true) << ";\n";
|
|
}
|
|
|
|
/// If the function has more than one return value, generate a wrapper struct
|
|
/// that contains them.
|
|
static void printRawFunctionWrappers(Logger<> &Log,
|
|
const model::RawFunctionDefinition *F,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model) {
|
|
if (F->ReturnValues().size() > 1)
|
|
generateReturnValueWrapper(Log, *F, Header, B, Model);
|
|
|
|
for (auto &Arg : F->Arguments())
|
|
revng_assert(Arg.Type()->isScalar());
|
|
}
|
|
|
|
/// Print a typedef for a RawFunctionDefinition, that can be used when you have
|
|
/// a variable that is a pointer to a function.
|
|
static void printDeclaration(Logger<> &Log,
|
|
const model::RawFunctionDefinition &F,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model) {
|
|
printRawFunctionWrappers(Log, &F, Header, B, Model);
|
|
|
|
Header << B.getModelComment(F)
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " ";
|
|
// In this case, we are defining a type for the function, not the function
|
|
// itself, so the token right before the parenthesis is the name of the type.
|
|
printFunctionTypeDeclaration(F, Header, B, Model);
|
|
Header << ";\n";
|
|
}
|
|
|
|
/// Generate the definition of a new struct type that wraps \a ArrayType.
|
|
/// This is used to wrap array arguments or array return values of
|
|
/// CABI functions.
|
|
static void generateArrayWrapper(const model::ArrayType &ArrayType,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
TypeNameMap &NamesCache) {
|
|
auto WrapperName = getArrayWrapper(ArrayType, B);
|
|
|
|
// Check if the wrapper was already added
|
|
auto [_, IsNew] = NamesCache.emplace(ArrayType, WrapperName);
|
|
if (not IsNew)
|
|
return;
|
|
|
|
Header << B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " "
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Struct) << " "
|
|
<< B.getAttributePacked() << " ";
|
|
{
|
|
Scope Scope(Header, ptml::c::scopes::StructBody);
|
|
Header << getNamedCInstance(ArrayType,
|
|
ArtificialTypes::ArrayWrapperFieldName,
|
|
B)
|
|
<< ";\n";
|
|
}
|
|
Header << " " << B.tokenTag(WrapperName, ptml::c::tokens::Type) << ";\n";
|
|
}
|
|
|
|
/// If the return value or any of the arguments is an array, generate a wrapper
|
|
/// struct for each of them, if it's not already in the cache.
|
|
static void printCABIFunctionWrappers(const model::CABIFunctionDefinition *F,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
TypeNameMap &NamesCache) {
|
|
if (not F->ReturnType().isEmpty())
|
|
if (auto *Array = F->ReturnType()->getArray())
|
|
generateArrayWrapper(*Array, Header, B, NamesCache);
|
|
|
|
for (auto &Arg : F->Arguments())
|
|
if (auto *Array = Arg.Type()->getArray())
|
|
generateArrayWrapper(*Array, Header, B, NamesCache);
|
|
}
|
|
|
|
/// Print a typedef for a CABI function, that can be used when you have
|
|
/// a variable that is a pointer to a function.
|
|
static void printDeclaration(const model::CABIFunctionDefinition &F,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
TypeNameMap &NamesCache,
|
|
const model::Binary &Model) {
|
|
printCABIFunctionWrappers(&F, Header, B, NamesCache);
|
|
|
|
Header << B.getModelComment(F)
|
|
<< B.getKeyword(ptml::PTMLCBuilder::Keyword::Typedef) << " ";
|
|
// In this case, we are defining a type for the function, not the function
|
|
// itself, so the token right before the parenthesis is the name of the type.
|
|
printFunctionTypeDeclaration(F, Header, B, Model);
|
|
Header << ";\n";
|
|
}
|
|
|
|
void printDeclaration(Logger<> &Log,
|
|
const model::TypeDefinition &T,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
TypeNameMap &AdditionalNames) {
|
|
if (Log.isEnabled()) {
|
|
auto Scope = helpers::LineComment(Header, B.isGenerateTagLessPTML());
|
|
Header << "Declaration of " << getNameFromYAMLScalar(T.key()) << "\n";
|
|
}
|
|
|
|
revng_log(Log, "Declaring " << getNameFromYAMLScalar(T.key()));
|
|
|
|
if (auto *Enum = llvm::dyn_cast<model::EnumDefinition>(&T))
|
|
printForwardDeclaration(*Enum, Header, B);
|
|
|
|
else if (auto *Struct = llvm::dyn_cast<model::StructDefinition>(&T))
|
|
printForwardDeclaration(*Struct, Header, B);
|
|
|
|
else if (auto *Union = llvm::dyn_cast<model::UnionDefinition>(&T))
|
|
printForwardDeclaration(*Union, Header, B);
|
|
|
|
else if (auto *Typedef = llvm::dyn_cast<model::TypedefDefinition>(&T))
|
|
printDeclaration(*Typedef, Header, B);
|
|
|
|
else if (auto *RFD = llvm::dyn_cast<model::RawFunctionDefinition>(&T))
|
|
printDeclaration(Log, *RFD, Header, B, Model);
|
|
|
|
else if (auto *CFD = llvm::dyn_cast<model::CABIFunctionDefinition>(&T))
|
|
printDeclaration(*CFD, Header, B, AdditionalNames, Model);
|
|
|
|
else
|
|
revng_abort("Unsupported type definition.");
|
|
}
|
|
|
|
void printDefinition(Logger<> &Log,
|
|
const model::TypeDefinition &T,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
TypeNameMap &AdditionalNames,
|
|
const DefinitionSet &TypesToInline,
|
|
bool ForEditing) {
|
|
if (Log.isEnabled())
|
|
Header << B.getLineComment("Definition of "
|
|
+ getNameFromYAMLScalar(T.key()));
|
|
|
|
revng_log(Log, "Defining " << getNameFromYAMLScalar(T.key()));
|
|
if (declarationIsDefinition(T)) {
|
|
printDeclaration(Log, T, Header, B, Model, AdditionalNames);
|
|
} else if (auto *Struct = llvm::dyn_cast<model::StructDefinition>(&T)) {
|
|
printDefinition(Log,
|
|
*Struct,
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline);
|
|
} else if (auto *Union = llvm::dyn_cast<model::UnionDefinition>(&T)) {
|
|
printDefinition(Log,
|
|
*Union,
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline);
|
|
} else if (auto *Enum = llvm::dyn_cast<model::EnumDefinition>(&T)) {
|
|
printDefinition(llvm::cast<model::EnumDefinition>(T),
|
|
Header,
|
|
B,
|
|
ForEditing);
|
|
} else {
|
|
revng_abort("Unsupported type definition.");
|
|
}
|
|
}
|
|
|
|
void printInlineDefinition(Logger<> &Log,
|
|
llvm::StringRef Name,
|
|
const model::Type &T,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
std::map<model::UpcastableType, std::string>
|
|
&AdditionalNames,
|
|
const std::set<const model::TypeDefinition *>
|
|
&TypesToInline) {
|
|
const model::TypeDefinition *Definition = T.skipToDefinition();
|
|
revng_assert(Definition, "Primitives cannot be printed inline.");
|
|
|
|
auto Suffix = getNamedCInstance(T, Name, B, {}, true).str().str();
|
|
if (auto *Struct = llvm::dyn_cast<model::StructDefinition>(Definition)) {
|
|
printDefinition(Log,
|
|
*Struct,
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline,
|
|
std::move(Suffix));
|
|
|
|
} else if (auto *Union = llvm::dyn_cast<model::UnionDefinition>(Definition)) {
|
|
printDefinition(Log,
|
|
*Union,
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline,
|
|
std::move(Suffix));
|
|
|
|
} else if (auto *Enum = llvm::dyn_cast<model::EnumDefinition>(Definition)) {
|
|
printDefinition(*Enum, Header, B, false, std::move(Suffix));
|
|
|
|
} else {
|
|
revng_abort("Only enums, structs, and unions can be printed inline.");
|
|
}
|
|
}
|
|
|
|
void printInlineDefinition(Logger<> &Log,
|
|
const model::StructDefinition &Struct,
|
|
ptml::PTMLIndentedOstream &Header,
|
|
ptml::PTMLCBuilder &B,
|
|
const model::Binary &Model,
|
|
TypeNameMap &AdditionalNames,
|
|
const DefinitionSet &TypesToInline,
|
|
std::string &&Suffix) {
|
|
printDefinition(Log,
|
|
Struct,
|
|
Header,
|
|
B,
|
|
Model,
|
|
AdditionalNames,
|
|
TypesToInline,
|
|
" " + std::move(Suffix));
|
|
}
|