mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
ea3cbe4c1b
Type inlining was a feature that allowed type definitions of structs/unions/enums to be printed in C directly inside the definition of another parent struct/union, if the inner type was only used once in the parent type. This kind of reasoning is inherently global: a type definition of the subtype can be inlined in the parent type one only if *globally* the subtype it isn't referred anywhere else. This caused issues with type inlining inside definitions of stack types in the body of functions. Indeed, for a given function, due to type inlining, it was necessary to do global reasoning about what other types could be inlined in the definition of the function's stack frame type. This, in turn, had heavy consequences on invalidation, because any change to any type (even if it wasn't referred in a given function's body) was causing invalidation of all functions' bodies. For this reason it was decided to drop the type inlining feature.
142 lines
4.8 KiB
C++
142 lines
4.8 KiB
C++
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/Backend/DecompileFunction.h"
|
|
#include "revng/Backend/DecompileToDirectoryPipe.h"
|
|
#include "revng/Backend/DecompileToSingleFile.h"
|
|
#include "revng/EarlyFunctionAnalysis/ControlFlowGraphCache.h"
|
|
#include "revng/HeadersGeneration/Options.h"
|
|
#include "revng/HeadersGeneration/PTMLHeaderBuilder.h"
|
|
#include "revng/PTML/CBuilder.h"
|
|
#include "revng/Pipeline/AllRegistries.h"
|
|
#include "revng/Support/GzipTarFile.h"
|
|
#include "revng/Support/ResourceFinder.h"
|
|
|
|
namespace revng::pipes {
|
|
|
|
using namespace pipeline;
|
|
|
|
static RegisterDefaultConstructibleContainer<RecompilableArchiveContainer> Reg;
|
|
|
|
void DecompileToDirectory::run(pipeline::ExecutionContext &EC,
|
|
pipeline::LLVMContainer &IRContainer,
|
|
const revng::pipes::CFGMap &CFGMap,
|
|
RecompilableArchiveContainer &OutTarFile) {
|
|
|
|
std::error_code ErrorCode;
|
|
llvm::raw_fd_ostream OutputStream{ OutTarFile.getOrCreatePath(), ErrorCode };
|
|
if (ErrorCode)
|
|
revng_abort(ErrorCode.message().c_str());
|
|
|
|
GzipTarWriter TarWriter{ OutputStream };
|
|
|
|
llvm::Module &Module = IRContainer.getModule();
|
|
const model::Binary &Model = *getModelFromContext(EC);
|
|
|
|
namespace options = revng::options;
|
|
ptml::CTypeBuilder B(llvm::nulls(),
|
|
Model,
|
|
/* EnableTaglessMode = */ true,
|
|
// Disable stack frame inlining because enabling it could
|
|
// break the property that we emit syntactically valid C
|
|
// code, due to the stack frame type definition being
|
|
// duplicated in the global header and in the function's
|
|
// body. In this artifact, where all the decompiled
|
|
// functions are put in a single .c file, recompilability
|
|
// is still important.
|
|
{ .EnableStackFrameInlining = false });
|
|
|
|
{
|
|
ControlFlowGraphCache Cache{ CFGMap };
|
|
DecompileStringMap DecompiledFunctions("tmp");
|
|
for (pipeline::Target &Target : CFGMap.enumerate()) {
|
|
auto Entry = MetaAddress::fromString(Target.getPathComponents()[0]);
|
|
const model::Function &Function = Model.Functions().at(Entry);
|
|
auto *F = Module.getFunction(B.NameBuilder.llvmName(Function));
|
|
std::string CCode = decompile(Cache, *F, Model, B);
|
|
DecompiledFunctions.insert_or_assign(Entry, std::move(CCode));
|
|
}
|
|
|
|
std::string DecompiledC;
|
|
llvm::raw_string_ostream Out{ DecompiledC };
|
|
B.setOutputStream(Out);
|
|
printSingleCFile(B, DecompiledFunctions, {} /* Targets */);
|
|
|
|
Out.flush();
|
|
|
|
TarWriter.append("decompiled/functions.c",
|
|
llvm::ArrayRef{ DecompiledC.data(),
|
|
DecompiledC.length() });
|
|
}
|
|
|
|
{
|
|
std::string ModelHeader;
|
|
llvm::raw_string_ostream Out{ ModelHeader };
|
|
B.setOutputStream(Out);
|
|
|
|
ptml::HeaderBuilder HB = B;
|
|
HB.printModelHeader();
|
|
|
|
Out.flush();
|
|
|
|
TarWriter.append("decompiled/types-and-globals.h",
|
|
llvm::ArrayRef{ ModelHeader.data(),
|
|
ModelHeader.length() });
|
|
}
|
|
|
|
{
|
|
std::string HelpersHeader;
|
|
llvm::raw_string_ostream Out{ HelpersHeader };
|
|
B.setOutputStream(Out);
|
|
|
|
ptml::HeaderBuilder HB = B;
|
|
HB.printHelpersHeader(Module);
|
|
|
|
Out.flush();
|
|
|
|
TarWriter.append("decompiled/helpers.h",
|
|
llvm::ArrayRef{ HelpersHeader.data(),
|
|
HelpersHeader.length() });
|
|
}
|
|
|
|
{
|
|
auto Path = revng::ResourceFinder.findFile("share/revng/include/"
|
|
"attributes.h");
|
|
|
|
if (not Path or Path->empty())
|
|
revng_abort("can't find attributes.h");
|
|
|
|
auto BufferOrError = llvm::MemoryBuffer::getFileOrSTDIN(*Path);
|
|
auto Buffer = cantFail(errorOrToExpected(std::move(BufferOrError)));
|
|
|
|
TarWriter.append("decompiled/attributes.h",
|
|
{ Buffer->getBufferStart(), Buffer->getBufferSize() });
|
|
}
|
|
|
|
{
|
|
auto Path = revng::ResourceFinder.findFile("share/revng/include/"
|
|
"primitive-types.h");
|
|
|
|
if (not Path or Path->empty())
|
|
revng_abort("can't find primitive-types.h");
|
|
|
|
auto BufferOrError = llvm::MemoryBuffer::getFileOrSTDIN(*Path);
|
|
auto Buffer = cantFail(errorOrToExpected(std::move(BufferOrError)));
|
|
|
|
TarWriter.append("decompiled/primitive-types.h",
|
|
{ Buffer->getBufferStart(), Buffer->getBufferSize() });
|
|
}
|
|
|
|
TarWriter.close();
|
|
|
|
EC.commitUniqueTarget(OutTarFile);
|
|
}
|
|
|
|
} // end namespace revng::pipes
|
|
|
|
static pipeline::RegisterPipe<revng::pipes::DecompileToDirectory> Y;
|