mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Merge branch 'feature/clift-based-headers-4'
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-2017 Alessandro Di Federico <ale@clearmind.me>
|
||||
Copyright (c) 2017-2024 rev.ng Labs Srl <info@rev.ng>
|
||||
Copyright (c) 2017-2026 rev.ng Labs Srl <info@rev.ng>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -249,6 +249,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] CDataModel getDataModel(const model::Binary &Binary);
|
||||
|
||||
} // namespace abi
|
||||
|
||||
#include "revng/ABI/Generated/Late/Definition.h"
|
||||
|
||||
@@ -68,10 +68,6 @@ getStrongModelInfo(const llvm::Instruction *Inst, const model::Binary &Model);
|
||||
extern llvm::SmallVector<model::UpcastableType>
|
||||
getExpectedModelType(const llvm::Use *U, const model::Binary &Model);
|
||||
|
||||
extern llvm::SmallVector<model::UpcastableType>
|
||||
flattenReturnTypes(const abi::FunctionType::Layout &Layout,
|
||||
const model::Binary &Model);
|
||||
|
||||
/// \note This is the final prototype, after segregate-stack-access
|
||||
template<bool LegacyLocalVariables>
|
||||
inline llvm::FunctionType &
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "revng/Support/Assert.h"
|
||||
|
||||
/// Stack where an element cannot be re-inserted in it's already in the stack
|
||||
template<typename T>
|
||||
class UniquedStack {
|
||||
public:
|
||||
void insert(T Element) {
|
||||
if (!Set.contains(Element)) {
|
||||
revng_assert(Element->getParent() != nullptr);
|
||||
Set.insert(Element);
|
||||
Queue.push_back(Element);
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const { return Queue.empty(); }
|
||||
|
||||
T pop() {
|
||||
T Result = Queue.back();
|
||||
Queue.pop_back();
|
||||
Set.erase(Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Reverses the stack in its current status
|
||||
void reverse() { std::reverse(Queue.begin(), Queue.end()); }
|
||||
|
||||
size_t size() const { return Queue.size(); }
|
||||
|
||||
private:
|
||||
std::set<T> Set;
|
||||
std::vector<T> Queue;
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "revng/EarlyFunctionAnalysis/CFGStringMap.h"
|
||||
#include "revng/Pipeline/Contract.h"
|
||||
#include "revng/Pipes/FileContainer.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
|
||||
namespace revng::pipes {
|
||||
|
||||
inline constexpr char RecompilableArchiveMime[] = "application/"
|
||||
"x.recompilable-archive";
|
||||
inline constexpr char RecompilableArchiveName[] = "recompilable-archive";
|
||||
inline constexpr char RecompilableArchiveExtension[] = ".tar.gz";
|
||||
using RecompilableArchiveContainer = FileContainer<
|
||||
&kinds::RecompilableArchive,
|
||||
RecompilableArchiveName,
|
||||
RecompilableArchiveMime,
|
||||
RecompilableArchiveExtension>;
|
||||
|
||||
class DecompileToDirectory {
|
||||
public:
|
||||
static constexpr auto Name = "decompile-to-directory";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(StackAccessesSegregated,
|
||||
0,
|
||||
RecompilableArchive,
|
||||
2,
|
||||
InputPreservation::Preserve),
|
||||
Contract(CFG,
|
||||
1,
|
||||
RecompilableArchive,
|
||||
2,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
pipeline::LLVMContainer &IRContainer,
|
||||
const revng::pipes::CFGMap &CFGMap,
|
||||
RecompilableArchiveContainer &OutTarFile);
|
||||
};
|
||||
|
||||
} // end namespace revng::pipes
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "revng/Backend/DecompilePipe.h"
|
||||
#include "revng/Pipeline/Context.h"
|
||||
#include "revng/Pipeline/Contract.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
#include "revng/Pipes/StringBufferContainer.h"
|
||||
#include "revng/Pipes/StringMap.h"
|
||||
|
||||
namespace revng::pipes {
|
||||
|
||||
inline constexpr char DecompiledMIMEType[] = "text/x.c+ptml";
|
||||
inline constexpr char DecompiledSuffix[] = ".c";
|
||||
inline constexpr char DecompiledName[] = "decompiled-c-code";
|
||||
using DecompiledFileContainer = StringBufferContainer<&kinds::DecompiledToC,
|
||||
DecompiledName,
|
||||
DecompiledMIMEType,
|
||||
DecompiledSuffix>;
|
||||
|
||||
class DecompileToSingleFile {
|
||||
public:
|
||||
static constexpr auto Name = "decompile-to-single-file";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(Decompiled,
|
||||
0,
|
||||
DecompiledToC,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const DecompileStringMap &DecompiledFunctionsContainer,
|
||||
DecompiledFileContainer &OutCFile);
|
||||
};
|
||||
|
||||
} // end namespace revng::pipes
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <bit>
|
||||
|
||||
#include "mlir/IR/FunctionInterfaces.h"
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
#include "mlir/IR/OpDefinition.h"
|
||||
#include "mlir/IR/Operation.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
@@ -102,11 +103,14 @@ public:
|
||||
|
||||
namespace clift {
|
||||
|
||||
/// Returns true if the module has a Clift module attribute.
|
||||
bool hasModuleAttr(mlir::ModuleOp Module);
|
||||
/// Creates a new MLIR context with the Clift dialect pre-loaded.
|
||||
std::unique_ptr<mlir::MLIRContext> makeContext();
|
||||
|
||||
/// Sets the Clift module attribute on the specified module.
|
||||
void setModuleAttr(mlir::ModuleOp Module);
|
||||
/// Creates a new Clift module.
|
||||
mlir::OwningOpRef<mlir::ModuleOp> makeModule(mlir::MLIRContext &Context);
|
||||
|
||||
/// Returns true if the module has a Clift module attribute.
|
||||
bool isCliftModule(mlir::ModuleOp Module);
|
||||
|
||||
/// Returns the data model for the specified module.
|
||||
/// \note The module must have an associated data model.
|
||||
|
||||
@@ -20,14 +20,23 @@
|
||||
|
||||
namespace clift {
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr
|
||||
makeNameAttr(mlir::MLIRContext *Context, llvm::StringRef Handle);
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr makeNameAttr(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
llvm::StringRef Name = "");
|
||||
llvm::StringRef Name);
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr
|
||||
makeCommentAttr(mlir::MLIRContext *Context, llvm::StringRef Handle);
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr makeCommentAttr(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
llvm::StringRef Name = "");
|
||||
llvm::StringRef Name);
|
||||
|
||||
} // namespace clift
|
||||
|
||||
@@ -38,24 +47,47 @@ MutableStringAttr makeCommentAttr(mlir::MLIRContext *Context,
|
||||
namespace clift {
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr makeNameAttr(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
llvm::StringRef Name) {
|
||||
MutableStringAttr
|
||||
makeNameAttr(mlir::MLIRContext *Context, llvm::StringRef Handle) {
|
||||
return MutableStringAttr::get(Context,
|
||||
StringPairAttr::get(Context,
|
||||
T::NameAttrKey,
|
||||
Handle),
|
||||
Name);
|
||||
Handle));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr makeNameAttr(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
llvm::StringRef Name) {
|
||||
auto Attr = makeNameAttr<T>(Context, Handle);
|
||||
if (not Attr.getValue().empty() and Attr.getValue() != Name) {
|
||||
std::string Error = "Name attribute already has a value that differs "
|
||||
"from the new one: '"
|
||||
+ Attr.getValue().str() + "' vs '" + Name.str() + "'";
|
||||
revng_abort(Error.c_str());
|
||||
}
|
||||
|
||||
Attr.setValue(Name);
|
||||
return Attr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr
|
||||
makeCommentAttr(mlir::MLIRContext *Context, llvm::StringRef Handle) {
|
||||
return MutableStringAttr::get(Context,
|
||||
StringPairAttr::get(Context,
|
||||
T::CommentAttrKey,
|
||||
Handle));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MutableStringAttr makeCommentAttr(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
llvm::StringRef Name) {
|
||||
return MutableStringAttr::get(Context,
|
||||
StringPairAttr::get(Context,
|
||||
T::CommentAttrKey,
|
||||
Handle),
|
||||
Name);
|
||||
auto Attr = makeCommentAttr<T>(Context, Handle);
|
||||
revng_assert(Attr.getValue().empty());
|
||||
Attr.setValue(Name);
|
||||
return Attr;
|
||||
}
|
||||
|
||||
// VERY IMPORTANT!!!
|
||||
|
||||
@@ -34,12 +34,6 @@ public:
|
||||
|
||||
static MutableStringAttr get(mlir::MLIRContext *Context, mlir::Attribute Key);
|
||||
|
||||
static MutableStringAttr
|
||||
get(mlir::MLIRContext *Context, mlir::Attribute Key, llvm::StringRef Value);
|
||||
|
||||
static MutableStringAttr getUnique(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Value = {});
|
||||
|
||||
mlir::Attribute getKey() const;
|
||||
llvm::StringRef getValue() const;
|
||||
void setValue(llvm::StringRef Value);
|
||||
|
||||
@@ -9,6 +9,5 @@
|
||||
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/Support/CDataModel.h"
|
||||
|
||||
void decompile(clift::FunctionOp Function, ptml::CTokenEmitter &Emitter);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
//
|
||||
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/Clift/CliftTypes.h"
|
||||
#include "revng/PTML/CDoxygenEmitter.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/Support/CDataModel.h"
|
||||
@@ -119,5 +120,5 @@ public:
|
||||
///
|
||||
/// This is true for `struct`s and `union`s. False for everything else.
|
||||
inline bool isSeparateDeclarationAllowed(clift::DefinedType Type) {
|
||||
return mlir::isa<clift::ClassType>(Type);
|
||||
return mlir::isa<clift::ClassType>(Type) or mlir::isa<clift::EnumType>(Type);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
//
|
||||
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/Support/CDataModel.h"
|
||||
|
||||
// TODO: does this really belong with the emitters?
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
struct TypeEmitterConfiguration {
|
||||
/// We don't always want the complete type system. For example, when one
|
||||
/// of the types is being edited, we cannot include the type in question!
|
||||
// As well as every type that depends on its definition.
|
||||
/// As well as every type that depends on its definition.
|
||||
///
|
||||
/// Note: the type is identified by its handle.
|
||||
llvm::StringRef TypeToOmit;
|
||||
llvm::StringRef TypeToOmit = "";
|
||||
|
||||
/// Because we are emitting C11, we cannot specify underlying enum type
|
||||
/// (the feature was only backported from C++ in C23), which means that
|
||||
@@ -21,7 +21,7 @@ struct TypeEmitterConfiguration {
|
||||
/// we emit the maximum value possible - and then use it to figure out
|
||||
/// the original size.
|
||||
/// Setting this flag to `true` enables emitting of such entry.
|
||||
bool EmitMaximumEnumValue;
|
||||
bool EmitMaximumEnumValue = true;
|
||||
|
||||
/// When editing types, it would be extremely annoying to have to do every
|
||||
/// change twice. As such, we need a way to disable emission of the explicit
|
||||
@@ -30,5 +30,5 @@ struct TypeEmitterConfiguration {
|
||||
/// Note that setting this leads to changes in the struct layout were they
|
||||
/// recompiled with a normal compiler (*our* wrapper used when editing types
|
||||
/// is not affected because it explicitly handles `_STARTS_AT` attribute).
|
||||
bool ExplicitPadding;
|
||||
bool ExplicitPadding = true;
|
||||
};
|
||||
|
||||
@@ -6,12 +6,30 @@
|
||||
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
|
||||
#include "revng/Clift/CliftTypeInterfaces.h"
|
||||
#include "revng/CliftEmitC/Configuration.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
|
||||
namespace model {
|
||||
class Binary;
|
||||
}
|
||||
|
||||
void emitCommonIncludes(ptml::CTokenEmitter &Tokens,
|
||||
const CDataModel &DataModel);
|
||||
|
||||
void emitTypes(ptml::CTokenEmitter &Tokens,
|
||||
mlir::ModuleOp Module,
|
||||
TypeEmitterConfiguration Configuration);
|
||||
|
||||
void emitTypeAndGlobalHeader(ptml::CTokenEmitter &Tokens,
|
||||
mlir::ModuleOp Module,
|
||||
TypeEmitterConfiguration Configuration);
|
||||
|
||||
void emitHelperHeader(ptml::CTokenEmitter &Tokens,
|
||||
llvm::ArrayRef<mlir::ModuleOp> Modules);
|
||||
llvm::ArrayRef<mlir::ModuleOp> Modules,
|
||||
const model::Binary &Binary);
|
||||
|
||||
void emitSingleTypeDefinition(ptml::CTokenEmitter &Tokens,
|
||||
const CDataModel &DataModel,
|
||||
clift::DefinedType Type,
|
||||
TypeEmitterConfiguration Configuration = {});
|
||||
|
||||
@@ -21,8 +21,9 @@ namespace clift {
|
||||
/// \return The corresponding Clift type, or null on failure.
|
||||
mlir::Type importType(llvm::function_ref<mlir::InFlightDiagnostic()> EmitError,
|
||||
mlir::MLIRContext *Context,
|
||||
const model::TypeDefinition &ModelType,
|
||||
const model::Binary &Binary);
|
||||
const model::TypeDefinition &ModelType);
|
||||
mlir::Type importType(mlir::MLIRContext *Context,
|
||||
const model::TypeDefinition &ModelType);
|
||||
|
||||
/// Convert the specified qualified model type to a Clift type in the specified
|
||||
/// context.
|
||||
@@ -30,8 +31,8 @@ mlir::Type importType(llvm::function_ref<mlir::InFlightDiagnostic()> EmitError,
|
||||
/// \return The corresponding Clift type, or null on failure.
|
||||
mlir::Type importType(llvm::function_ref<mlir::InFlightDiagnostic()> EmitError,
|
||||
mlir::MLIRContext *Context,
|
||||
const model::Type &ModelType,
|
||||
const model::Binary &Binary);
|
||||
const model::Type &ModelType);
|
||||
mlir::Type importType(mlir::MLIRContext *Context, const model::Type &ModelType);
|
||||
|
||||
/// Convert the specified model function into a clift function declaration.
|
||||
///
|
||||
@@ -55,6 +56,10 @@ GlobalVariableOp importSegmentDeclaration(mlir::ModuleOp Module,
|
||||
mlir::Type Type);
|
||||
|
||||
void importAllModelTypes(const model::Binary &Model, mlir::ModuleOp Module);
|
||||
void importAllModelFunctionDeclarations(const model::Binary &Model,
|
||||
mlir::ModuleOp Module);
|
||||
void importAllModelSegmentDeclarations(const model::Binary &Model,
|
||||
mlir::ModuleOp Module);
|
||||
|
||||
void importDescriptiveInfo(const model::Binary &Model, mlir::ModuleOp Module);
|
||||
|
||||
@@ -63,4 +68,7 @@ void importDescriptiveInfo(const model::Function &Function,
|
||||
const model::Binary &Model,
|
||||
mlir::ModuleOp Module);
|
||||
|
||||
clift::StructType makeOpaqueStruct(mlir::MLIRContext &Context,
|
||||
uint64_t NumBytes);
|
||||
|
||||
} // namespace clift
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/Support/YAMLTraits.h"
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
enum class EmissionMode {
|
||||
Recompilable,
|
||||
Editable
|
||||
};
|
||||
|
||||
struct CEmissionPipeConfiguration {
|
||||
bool DisableMarkup = false;
|
||||
EmissionMode Mode = EmissionMode::Recompilable;
|
||||
};
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
|
||||
namespace detail {
|
||||
|
||||
using EmissionMode = revng::pypeline::piperuns::EmissionMode;
|
||||
using PipeConfiguration = revng::pypeline::piperuns::CEmissionPipeConfiguration;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<>
|
||||
struct llvm::yaml::ScalarEnumerationTraits<detail::EmissionMode> {
|
||||
static void enumeration(IO &IO, ::detail::EmissionMode &Value) {
|
||||
IO.enumCase(Value, "recompilable", ::detail::EmissionMode::Recompilable);
|
||||
IO.enumCase(Value, "editable", ::detail::EmissionMode::Editable);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct llvm::yaml::MappingTraits<detail::PipeConfiguration> {
|
||||
static void mapping(IO &TheIO, ::detail::PipeConfiguration &Value) {
|
||||
TheIO.mapOptional("disable-markup", Value.DisableMarkup);
|
||||
TheIO.mapOptional("emission-mode", Value.Mode);
|
||||
}
|
||||
};
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
inline CEmissionPipeConfiguration
|
||||
parseCEmissionPipeConfiguration(llvm::StringRef Config) {
|
||||
if (Config.empty())
|
||||
return CEmissionPipeConfiguration{};
|
||||
|
||||
return llvm::cantFail(fromString<CEmissionPipeConfiguration>(Config));
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "mlir/Pass/PassManager.h"
|
||||
|
||||
#include "revng/CliftPipes/Configuration.h"
|
||||
#include "revng/Pipebox/Containers.h"
|
||||
#include "revng/PipeboxCommon/CliftContainers.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
@@ -17,6 +18,8 @@ private:
|
||||
CliftFunctionContainer &Input;
|
||||
PTMLCFunctionBytesContainer &Output;
|
||||
|
||||
CEmissionPipeConfiguration Configuration;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "emit-c";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftFunctionContainer,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/Pipebox/Containers.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
|
||||
namespace revng::pypeline {
|
||||
|
||||
namespace piperuns {
|
||||
|
||||
class EmitCAsDirectory {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
const PTMLCBytesContainer &InputC;
|
||||
const PTMLCBytesContainer &InputTypesAndGlobals;
|
||||
const PTMLCBytesContainer &InputHelpers;
|
||||
RecompilableArchiveContainer &Output;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "emit-c-as-directory";
|
||||
using Arguments = TypeList<PipeRunArgument<const PTMLCBytesContainer,
|
||||
"DecompiledFunctions",
|
||||
"Input decompiled functions">,
|
||||
PipeRunArgument<const PTMLCBytesContainer,
|
||||
"TypesAndGlobals",
|
||||
"Input type and global header">,
|
||||
PipeRunArgument<const PTMLCBytesContainer,
|
||||
"Helpers",
|
||||
"Input helper header">,
|
||||
PipeRunArgument<RecompilableArchiveContainer,
|
||||
"Output",
|
||||
"Output single archive containing "
|
||||
"everything needed for "
|
||||
"recompilation",
|
||||
Access::Write>>;
|
||||
|
||||
EmitCAsDirectory(const Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const PTMLCBytesContainer &InputC,
|
||||
const PTMLCBytesContainer &InputTypesAndGlobals,
|
||||
const PTMLCBytesContainer &InputHelpers,
|
||||
RecompilableArchiveContainer &Output) :
|
||||
Binary(*Model.get().get()),
|
||||
InputC(InputC),
|
||||
InputTypesAndGlobals(InputTypesAndGlobals),
|
||||
InputHelpers(InputHelpers),
|
||||
Output(Output) {}
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
} // namespace piperuns
|
||||
|
||||
} // namespace revng::pypeline
|
||||
+10
-22
@@ -4,36 +4,24 @@
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/Backend/DecompilePipe.h"
|
||||
#include "revng/CliftPipes/Configuration.h"
|
||||
#include "revng/Pipebox/Containers.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
#include "revng/Pipes/StringMap.h"
|
||||
#include "revng/Support/MetaAddress.h"
|
||||
|
||||
namespace ptml {
|
||||
class ModelCBuilder;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
using DecompiledStringMap = revng::pipes::DecompileStringMap;
|
||||
}
|
||||
|
||||
void printSingleCFile(ptml::ModelCBuilder &B,
|
||||
const detail::DecompiledStringMap &Functions,
|
||||
const std::set<MetaAddress> &Targets);
|
||||
|
||||
namespace revng::pypeline {
|
||||
|
||||
namespace piperuns {
|
||||
|
||||
class DecompileToSingleFile {
|
||||
class EmitCAsSingleFile {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
const PTMLCFunctionBytesContainer &Input;
|
||||
PTMLCBytesContainer &Output;
|
||||
|
||||
CEmissionPipeConfiguration Configuration;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "decompile-to-single-file";
|
||||
static constexpr llvm::StringRef Name = "emit-c-as-single-file";
|
||||
using Arguments = TypeList<PipeRunArgument<const PTMLCFunctionBytesContainer,
|
||||
"DecompiledFunctions",
|
||||
"Input decompiled function">,
|
||||
@@ -42,11 +30,11 @@ public:
|
||||
"Output single C+PTML",
|
||||
Access::Write>>;
|
||||
|
||||
DecompileToSingleFile(const Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const PTMLCFunctionBytesContainer &Input,
|
||||
PTMLCBytesContainer &Output);
|
||||
EmitCAsSingleFile(const Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const PTMLCFunctionBytesContainer &Input,
|
||||
PTMLCBytesContainer &Output);
|
||||
|
||||
void run();
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/CliftPipes/Configuration.h"
|
||||
#include "revng/Pipebox/Containers.h"
|
||||
#include "revng/PipeboxCommon/CliftContainers.h"
|
||||
#include "revng/PipeboxCommon/Common.h"
|
||||
#include "revng/PipeboxCommon/LLVMContainer.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
class EmitTypeAndGlobalHeader {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
const CliftModuleContainer &Input;
|
||||
PTMLCBytesContainer &Output;
|
||||
|
||||
CEmissionPipeConfiguration Configuration;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "emit-type-and-global-header";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftModuleContainer,
|
||||
"Input",
|
||||
"MLIR container containing "
|
||||
"the type system, as well as "
|
||||
"function and segment "
|
||||
"declarations",
|
||||
Access::Read>,
|
||||
PipeRunArgument<PTMLCBytesContainer,
|
||||
"Output",
|
||||
"The model header",
|
||||
Access::Write>>;
|
||||
|
||||
EmitTypeAndGlobalHeader(const class Model &Model,
|
||||
llvm::StringRef Configuration,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const CliftModuleContainer &Input,
|
||||
PTMLCBytesContainer &Output) :
|
||||
Binary(*Model.get().get()),
|
||||
Input(Input),
|
||||
Output(Output),
|
||||
Configuration(parseCEmissionPipeConfiguration(Configuration)){};
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
class EmitHelperHeader {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
const CliftFunctionContainer &Input;
|
||||
PTMLCBytesContainer &Output;
|
||||
|
||||
CEmissionPipeConfiguration Configuration;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "emit-helper-header";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftFunctionContainer,
|
||||
"Input",
|
||||
"MLIR container",
|
||||
Access::Read>,
|
||||
PipeRunArgument<PTMLCBytesContainer,
|
||||
"Output",
|
||||
"The helper header",
|
||||
Access::Write>>;
|
||||
|
||||
EmitHelperHeader(const class Model &Model,
|
||||
llvm::StringRef Configuration,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const CliftFunctionContainer &Input,
|
||||
PTMLCBytesContainer &Output) :
|
||||
Binary(*Model.get().get()),
|
||||
Input(Input),
|
||||
Output(Output),
|
||||
Configuration(parseCEmissionPipeConfiguration(Configuration)){};
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
class EmitSingleTypeDefinition {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
const CliftModuleContainer &Input;
|
||||
PTMLCTypeBytesContainer &Output;
|
||||
|
||||
CEmissionPipeConfiguration Configuration;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "emit-single-type-definition";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftModuleContainer,
|
||||
"Input",
|
||||
"MLIR container containing "
|
||||
"the type system",
|
||||
Access::Read>,
|
||||
PipeRunArgument<PTMLCTypeBytesContainer,
|
||||
"Output",
|
||||
"A single C Type",
|
||||
Access::Write>>;
|
||||
|
||||
EmitSingleTypeDefinition(const class Model &Model,
|
||||
llvm::StringRef Configuration,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const CliftModuleContainer &Input,
|
||||
PTMLCTypeBytesContainer &Output) :
|
||||
Binary(*Model.get().get()),
|
||||
Input(Input),
|
||||
Output(Output),
|
||||
Configuration(parseCEmissionPipeConfiguration(Configuration)){};
|
||||
|
||||
void runOnTypeDefinition(const model::UpcastableTypeDefinition &Type);
|
||||
};
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/PipeboxCommon/CliftContainers.h"
|
||||
#include "revng/PipeboxCommon/Common.h"
|
||||
#include "revng/PipeboxCommon/LLVMContainer.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
class ImportTypes {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
CliftModuleContainer &Output;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "import-types";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftModuleContainer,
|
||||
"Output",
|
||||
"Output MLIR container containing "
|
||||
"model type system",
|
||||
Access::Write>>;
|
||||
|
||||
ImportTypes(const class Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
CliftModuleContainer &Output) :
|
||||
Binary(*Model.get().get()), Output(Output){};
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
class ImportFunctionDeclarations {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
CliftModuleContainer &Module;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "import-function-declarations";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftModuleContainer,
|
||||
"Module",
|
||||
"MLIR container containing model "
|
||||
"type system and, now, function "
|
||||
"declarations",
|
||||
Access::ReadWrite>>;
|
||||
|
||||
ImportFunctionDeclarations(const class Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
CliftModuleContainer &Module) :
|
||||
Binary(*Model.get().get()), Module(Module){};
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
class ImportSegmentDeclarations {
|
||||
private:
|
||||
const model::Binary &Binary;
|
||||
CliftModuleContainer &Module;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "import-segment-declarations";
|
||||
using Arguments = TypeList<PipeRunArgument<CliftModuleContainer,
|
||||
"Module",
|
||||
"MLIR container containing model "
|
||||
"type system, function "
|
||||
"declarations and, now, segment "
|
||||
"declarations",
|
||||
Access::ReadWrite>>;
|
||||
|
||||
ImportSegmentDeclarations(const class Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
CliftModuleContainer &Module) :
|
||||
Binary(*Model.get().get()), Module(Module){};
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -10,8 +10,6 @@
|
||||
#include "revng/PipeboxCommon/Helpers/PipeRuns/LLVMFunctionMixin.h"
|
||||
#include "revng/PipeboxCommon/LLVMContainer.h"
|
||||
|
||||
class EnforceABI;
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
class EnforceABI : public LLVMFunctionMixin<EnforceABI> {
|
||||
|
||||
@@ -20,7 +20,7 @@ private:
|
||||
PTMLCBytesContainer &Buffer;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "model-to-header";
|
||||
static constexpr llvm::StringRef Name = "legacy-model-to-header";
|
||||
using Arguments = TypeList<PipeRunArgument<PTMLCBytesContainer,
|
||||
"Buffer",
|
||||
"The output C header of the model",
|
||||
|
||||
@@ -22,7 +22,8 @@ private:
|
||||
PTMLCTypeContainer &Output;
|
||||
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "generate-model-type-definition";
|
||||
static constexpr llvm::StringRef Name = "legacy-generate-model-type-"
|
||||
"definition";
|
||||
using Arguments = TypeList<PipeRunArgument<PTMLCTypeContainer,
|
||||
"Output",
|
||||
"The output C headers of each "
|
||||
|
||||
@@ -225,6 +225,10 @@ public:
|
||||
bool verifyTypeDefinitions(bool Assert) const debug_function;
|
||||
bool verifyTypeDefinitions() const debug_function;
|
||||
|
||||
public:
|
||||
/// \returns a set of all the possible type byte sizes in this binary.
|
||||
std::set<uint64_t> collectAllTypeSizes() const;
|
||||
|
||||
public:
|
||||
void dumpTypeGraph(const char *Path) const debug_function;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "revng/PTML/Doxygen.h"
|
||||
#include "revng/PTML/IndentedOstream.h"
|
||||
#include "revng/PTML/Tag.h"
|
||||
#include "revng/PTML/TokenDefinitions.h"
|
||||
#include "revng/Pipeline/Location.h"
|
||||
#include "revng/Pipes/Ranks.h"
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "revng/PTML/Tag.h"
|
||||
|
||||
namespace tokenDefinition::types {
|
||||
|
||||
using StringToken = llvm::SmallString<128>;
|
||||
using TypeString = StringToken;
|
||||
|
||||
} // namespace tokenDefinition::types
|
||||
|
||||
/// Utility structure, used mainly in the C backend when generating variable
|
||||
/// names in conjunction with PTML
|
||||
/// It will have 2 Fields:
|
||||
/// \property Declaration : Contains the string that's to be used when declaring
|
||||
/// the variable. For example:
|
||||
/// \code{.c}
|
||||
/// int foo;
|
||||
/// //^^^-- This is a declaration of foo
|
||||
/// \endcode
|
||||
/// \property Use : Contains the string that's to be used when using the
|
||||
/// variable, such as:
|
||||
/// \code{.c}
|
||||
/// foo = bar + baz * bal;
|
||||
/// // In this example foo, bar, baz and bal are "used"
|
||||
/// \endcode
|
||||
struct VariableTokens {
|
||||
public:
|
||||
tokenDefinition::types::StringToken Declaration;
|
||||
tokenDefinition::types::StringToken Use;
|
||||
|
||||
public:
|
||||
VariableTokens() : Declaration(), Use() {}
|
||||
VariableTokens(const ptml::Tag &Declaration, const ptml::Tag &Use) :
|
||||
Declaration(Declaration.toString()), Use(Use.toString()) {}
|
||||
VariableTokens(const llvm::StringRef Declaration, const llvm::StringRef Use) :
|
||||
Declaration(Declaration), Use(Use) {}
|
||||
VariableTokens(const llvm::StringRef Use) : Use(Use) {}
|
||||
|
||||
bool hasDeclaration() const { return !Declaration.empty(); }
|
||||
};
|
||||
|
||||
/// Addition to VariableTokens that also preserves the 'naked' variable name
|
||||
/// in the \property VariableName
|
||||
/// Used mainly with helper structs where the struct's name is otherwise not
|
||||
/// trivially obtainable
|
||||
struct VariableTokensWithName : public VariableTokens {
|
||||
public:
|
||||
tokenDefinition::types::StringToken VariableName;
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
VariableTokensWithName(const llvm::StringRef VariableName,
|
||||
const T Declaration,
|
||||
const T Use) :
|
||||
VariableTokens(Declaration, Use), VariableName(VariableName) {}
|
||||
};
|
||||
@@ -15,4 +15,12 @@ using PTMLCFunctionBytesContainer = FunctionToBytesContainer<"PTMLCFunction"
|
||||
"BytesContainer",
|
||||
"text/x.c+ptml">;
|
||||
|
||||
using PTMLCTypeBytesContainer = TypeDefinitionToBytesContainer<"PTMLCType"
|
||||
"BytesContainer",
|
||||
"text/x.c+ptml">;
|
||||
|
||||
using RecompilableArchiveContainer = BytesContainer<"RecompilableArchive"
|
||||
"Container",
|
||||
"application/x-object">;
|
||||
|
||||
} // namespace revng::pypeline
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "mlir/Bytecode/BytecodeWriter.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
#include "mlir/IR/OwningOpRef.h"
|
||||
#include "mlir/Interfaces/DataLayoutInterfaces.h"
|
||||
#include "mlir/Parser/Parser.h"
|
||||
|
||||
@@ -23,22 +24,13 @@ public:
|
||||
static constexpr Kind Kind = Kinds::Function;
|
||||
static constexpr llvm::StringRef MimeType = "application/x.mlir.bc";
|
||||
|
||||
private:
|
||||
static constexpr auto Threading = mlir::MLIRContext::Threading::DISABLED;
|
||||
static inline const mlir::DialectRegistry MLIRDialectRegistry = ([]() {
|
||||
mlir::DialectRegistry Registry;
|
||||
Registry.insert<clift::CliftDialect>();
|
||||
return Registry;
|
||||
})();
|
||||
|
||||
private:
|
||||
bool Disposable = false;
|
||||
std::optional<mlir::MLIRContext> Context;
|
||||
std::unique_ptr<mlir::MLIRContext> Context;
|
||||
std::map<ObjectID, mlir::OwningOpRef<mlir::ModuleOp>> Modules;
|
||||
|
||||
public:
|
||||
CliftFunctionContainer() :
|
||||
Context(std::in_place_t{}, MLIRDialectRegistry, Threading) {}
|
||||
CliftFunctionContainer() : Context(clift::makeContext()) {}
|
||||
|
||||
public:
|
||||
std::set<ObjectID> objects() const {
|
||||
@@ -47,12 +39,12 @@ public:
|
||||
|
||||
void
|
||||
deserialize(const std::map<const ObjectID *, llvm::ArrayRef<char>> Data) {
|
||||
const mlir::ParserConfig Config(&*Context);
|
||||
const mlir::ParserConfig Config(Context.get());
|
||||
for (auto const &[Object, Buffer] : Data) {
|
||||
llvm::StringRef String(Buffer.data(), Buffer.size());
|
||||
auto NewModule = mlir::parseSourceString<mlir::ModuleOp>(String, Config);
|
||||
revng_assert(NewModule);
|
||||
revng_assert(clift::hasModuleAttr(NewModule.get()));
|
||||
revng_assert(clift::isCliftModule(NewModule.get()));
|
||||
Modules[*Object] = std::move(NewModule);
|
||||
}
|
||||
}
|
||||
@@ -83,22 +75,19 @@ public:
|
||||
return;
|
||||
|
||||
Modules.clear();
|
||||
Context.emplace(MLIRDialectRegistry, Threading);
|
||||
Context = clift::makeContext();
|
||||
Disposable = false;
|
||||
}
|
||||
|
||||
public:
|
||||
mlir::MLIRContext *getContext() { return &*Context; }
|
||||
|
||||
const mlir::ModuleOp getModule(const ObjectID &ID) const {
|
||||
return *Modules.at(ID);
|
||||
}
|
||||
|
||||
mlir::MLIRContext *getContext() const { return Context.get(); }
|
||||
mlir::ModuleOp getModule(const ObjectID &ID) const { return *Modules.at(ID); }
|
||||
mlir::ModuleOp getModule(const ObjectID &ID) { return *Modules.at(ID); }
|
||||
|
||||
void assign(const ObjectID &ID, mlir::ModuleOp NewModule) {
|
||||
void assign(const ObjectID &ID,
|
||||
mlir::OwningOpRef<mlir::ModuleOp> &&NewModule) {
|
||||
revng_assert(&*Context == NewModule->getContext());
|
||||
Modules[ID] = NewModule;
|
||||
Modules[ID] = std::move(NewModule);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,22 +97,13 @@ public:
|
||||
static constexpr Kind Kind = Kinds::TypeDefinition;
|
||||
static constexpr llvm::StringRef MimeType = "application/x.mlir.bc";
|
||||
|
||||
private:
|
||||
static constexpr auto Threading = mlir::MLIRContext::Threading::DISABLED;
|
||||
static inline const mlir::DialectRegistry MLIRDialectRegistry = ([]() {
|
||||
mlir::DialectRegistry Registry;
|
||||
Registry.insert<clift::CliftDialect>();
|
||||
return Registry;
|
||||
})();
|
||||
|
||||
private:
|
||||
bool Disposable = false;
|
||||
std::optional<mlir::MLIRContext> Context;
|
||||
std::unique_ptr<mlir::MLIRContext> Context;
|
||||
std::map<ObjectID, mlir::OwningOpRef<mlir::ModuleOp>> Modules;
|
||||
|
||||
public:
|
||||
CliftSingleTypeContainer() :
|
||||
Context(std::in_place_t{}, MLIRDialectRegistry, Threading) {}
|
||||
CliftSingleTypeContainer() : Context(clift::makeContext()) {}
|
||||
|
||||
public:
|
||||
std::set<ObjectID> objects() const {
|
||||
@@ -137,7 +117,7 @@ public:
|
||||
llvm::StringRef String(Buffer.data(), Buffer.size());
|
||||
auto NewModule = mlir::parseSourceString<mlir::ModuleOp>(String, Config);
|
||||
revng_assert(NewModule);
|
||||
revng_assert(clift::hasModuleAttr(NewModule.get()));
|
||||
revng_assert(clift::isCliftModule(NewModule.get()));
|
||||
Modules[*Object] = std::move(NewModule);
|
||||
}
|
||||
}
|
||||
@@ -168,22 +148,19 @@ public:
|
||||
return;
|
||||
|
||||
Modules.clear();
|
||||
Context.emplace(MLIRDialectRegistry, Threading);
|
||||
Context = clift::makeContext();
|
||||
Disposable = false;
|
||||
}
|
||||
|
||||
public:
|
||||
mlir::MLIRContext *getContext() { return &*Context; }
|
||||
|
||||
const mlir::ModuleOp getModule(const ObjectID &ID) const {
|
||||
return *Modules.at(ID);
|
||||
}
|
||||
|
||||
mlir::MLIRContext *getContext() const { return Context.get(); }
|
||||
mlir::ModuleOp getModule(const ObjectID &ID) const { return *Modules.at(ID); }
|
||||
mlir::ModuleOp getModule(const ObjectID &ID) { return *Modules.at(ID); }
|
||||
|
||||
void assign(const ObjectID &ID, mlir::ModuleOp NewModule) {
|
||||
void assign(const ObjectID &ID,
|
||||
mlir::OwningOpRef<mlir::ModuleOp> &&NewModule) {
|
||||
revng_assert(&*Context == NewModule->getContext());
|
||||
Modules[ID] = NewModule;
|
||||
Modules[ID] = std::move(NewModule);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -193,26 +170,14 @@ public:
|
||||
static constexpr Kind Kind = Kinds::Binary;
|
||||
static constexpr llvm::StringRef MimeType = "application/x.mlir.bc";
|
||||
|
||||
private:
|
||||
static constexpr auto Threading = mlir::MLIRContext::Threading::DISABLED;
|
||||
static inline const mlir::DialectRegistry MLIRDialectRegistry = ([]() {
|
||||
mlir::DialectRegistry Registry;
|
||||
Registry.insert<clift::CliftDialect>();
|
||||
return Registry;
|
||||
})();
|
||||
|
||||
private:
|
||||
bool Disposable = false;
|
||||
std::optional<mlir::MLIRContext> Context;
|
||||
std::unique_ptr<mlir::MLIRContext> Context;
|
||||
mlir::OwningOpRef<mlir::ModuleOp> Module;
|
||||
|
||||
public:
|
||||
CliftModuleContainer() :
|
||||
Context(std::in_place_t{}, MLIRDialectRegistry, Threading),
|
||||
Module(mlir::ModuleOp::create(mlir::UnknownLoc::get(&Context.value()))) {
|
||||
|
||||
clift::setModuleAttr(Module.get());
|
||||
}
|
||||
Context(clift::makeContext()), Module(clift::makeModule(*Context)) {}
|
||||
|
||||
public:
|
||||
std::set<ObjectID> objects() const {
|
||||
@@ -233,7 +198,7 @@ public:
|
||||
llvm::StringRef String(Buffer.data(), Buffer.size());
|
||||
Module = mlir::parseSourceString<mlir::ModuleOp>(String, Config);
|
||||
revng_assert(Module);
|
||||
revng_assert(clift::hasModuleAttr(Module.get()));
|
||||
revng_assert(clift::isCliftModule(Module.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,19 +225,19 @@ public:
|
||||
return;
|
||||
|
||||
Module = {};
|
||||
Context.emplace(MLIRDialectRegistry, Threading);
|
||||
Context = clift::makeContext();
|
||||
Disposable = false;
|
||||
}
|
||||
|
||||
public:
|
||||
mlir::MLIRContext *getContext() { return &*Context; }
|
||||
|
||||
const mlir::ModuleOp getModule() const { return Module.get(); }
|
||||
mlir::MLIRContext *getContext() const { return Context.get(); }
|
||||
mlir::ModuleOp getModule() const { return Module.get(); }
|
||||
mlir::ModuleOp getModule() { return Module.get(); }
|
||||
|
||||
void assign(mlir::ModuleOp NewModule) {
|
||||
void assign(const ObjectID &ID,
|
||||
mlir::OwningOpRef<mlir::ModuleOp> &&NewModule) {
|
||||
revng_assert(&*Context == NewModule->getContext());
|
||||
Module = NewModule;
|
||||
Module = std::move(NewModule);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -189,7 +189,10 @@ struct RegisterPipe {
|
||||
});
|
||||
|
||||
// Native
|
||||
revng_assert(native::Registry.Pipes.count(T::Name) == 0);
|
||||
if (native::Registry.Pipes.count(T::Name) != 0) {
|
||||
std::string Error = "Duplicate pipes: '" + T::Name.str() + "'";
|
||||
revng_abort(Error.c_str());
|
||||
}
|
||||
native::Registry.Pipes[T::Name] =
|
||||
[](llvm::StringRef Config) -> std::unique_ptr<native::Pipe> {
|
||||
return std::make_unique<native::PipeImpl<T>>(Config);
|
||||
|
||||
@@ -16,4 +16,5 @@ using DecompileStringMap = FunctionStringMap<&kinds::Decompiled,
|
||||
DecompileName,
|
||||
DecompileMime,
|
||||
DecompileExtension>;
|
||||
|
||||
} // namespace revng::pipes
|
||||
|
||||
+21
-34
@@ -80,32 +80,33 @@ inline TaggedFunctionKind
|
||||
FunctionTags::StackAccessesSegregated);
|
||||
|
||||
extern FunctionKind Decompiled;
|
||||
inline pipeline::SingleElementKind ModelHeader("model-header",
|
||||
Binary,
|
||||
ranks::Binary,
|
||||
fat(ranks::TypeDefinition,
|
||||
ranks::StructField,
|
||||
ranks::UnionField,
|
||||
ranks::EnumEntry,
|
||||
ranks::DynamicFunction,
|
||||
ranks::Segment,
|
||||
ranks::ArtificialStruct),
|
||||
{ &Decompiled });
|
||||
inline pipeline::SingleElementKind
|
||||
LegacyModelHeader("legacy-model-header",
|
||||
Binary,
|
||||
ranks::Binary,
|
||||
fat(ranks::TypeDefinition,
|
||||
ranks::StructField,
|
||||
ranks::UnionField,
|
||||
ranks::EnumEntry,
|
||||
ranks::DynamicFunction,
|
||||
ranks::Segment,
|
||||
ranks::ArtificialStruct),
|
||||
{ &Decompiled });
|
||||
|
||||
inline FunctionKind Decompiled("decompiled",
|
||||
ModelHeader,
|
||||
LegacyModelHeader,
|
||||
ranks::Function,
|
||||
fat(ranks::Function),
|
||||
{ &ModelHeader });
|
||||
{ &LegacyModelHeader });
|
||||
|
||||
inline TypeKind ModelTypeDefinition("model-type-definition",
|
||||
ModelHeader,
|
||||
ranks::TypeDefinition,
|
||||
{},
|
||||
{});
|
||||
inline TypeKind LegacyModelTypeDefinition("legacy-model-type-definition",
|
||||
LegacyModelHeader,
|
||||
ranks::TypeDefinition,
|
||||
{},
|
||||
{});
|
||||
|
||||
inline pipeline::SingleElementKind
|
||||
HelpersHeader("helpers-header", Binary, ranks::Binary, {}, {});
|
||||
LegacyHelpersHeader("legacy-helpers-header", Binary, ranks::Binary, {}, {});
|
||||
|
||||
inline pipeline::SingleElementKind
|
||||
CliftModule("clift-module", ranks::Binary, {}, {});
|
||||
@@ -115,20 +116,6 @@ inline pipeline::SingleElementKind DecompiledToC("decompiled-to-c",
|
||||
Binary,
|
||||
ranks::Binary,
|
||||
fat(ranks::Function),
|
||||
{ &ModelHeader });
|
||||
|
||||
inline pipeline::SingleElementKind
|
||||
RecompilableArchive("recompilable-archive",
|
||||
Binary,
|
||||
ranks::Binary,
|
||||
fat(ranks::Function,
|
||||
ranks::TypeDefinition,
|
||||
ranks::StructField,
|
||||
ranks::UnionField,
|
||||
ranks::EnumEntry,
|
||||
ranks::DynamicFunction,
|
||||
ranks::Segment,
|
||||
ranks::ArtificialStruct),
|
||||
{});
|
||||
{ &LegacyModelHeader });
|
||||
|
||||
} // namespace revng::kinds
|
||||
|
||||
@@ -44,6 +44,10 @@ concept TraitedTupleLike = requires {
|
||||
|
||||
typename TupleLikeTraits<T>::tuple;
|
||||
typename TupleLikeTraits<T>::Fields;
|
||||
|
||||
// TODO: this should also check for `get<I>(std::declval<T>())`, but there's
|
||||
// an empty struct in `SerializableGraph.h` for which it's impossible
|
||||
// to instantiate.
|
||||
} && detail::HasTuple<T> && std::is_enum_v<typename TupleLikeTraits<T>::Fields>;
|
||||
|
||||
//
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "revng/ABI/DefaultFunctionPrototype.h"
|
||||
#include "revng/ABI/Definition.h"
|
||||
#include "revng/ABI/FunctionType/Support.h"
|
||||
#include "revng/Support/EnumSwitch.h"
|
||||
|
||||
static model::UpcastableType defaultPrototype(model::Binary &Binary,
|
||||
model::ABI::Values ABI) {
|
||||
|
||||
@@ -515,4 +515,8 @@ CDataModel Definition::getDataModel() const {
|
||||
return DM;
|
||||
}
|
||||
|
||||
[[nodiscard]] CDataModel getDataModel(const model::Binary &Binary) {
|
||||
return abi::Definition::get(Binary.targetABI()).getDataModel();
|
||||
}
|
||||
|
||||
} // namespace abi
|
||||
|
||||
@@ -385,6 +385,8 @@ convertToRaw(const model::CABIFunctionDefinition &FunctionType,
|
||||
return ToRaw.convert(FunctionType, Binary);
|
||||
}
|
||||
|
||||
static Logger LayoutLog("function-type-layout");
|
||||
|
||||
Layout::Layout(const model::CABIFunctionDefinition &Function) {
|
||||
const abi::Definition &ABI = abi::Definition::get(Function.ABI());
|
||||
ToRawConverter Converter(ABI);
|
||||
@@ -491,6 +493,10 @@ Layout::Layout(const model::CABIFunctionDefinition &Function) {
|
||||
llvm::copy(ABI.CalleeSavedRegisters(), CalleeSavedRegisters.begin());
|
||||
|
||||
FinalStackOffset = Converter.finalStackOffset(StackStructSize);
|
||||
|
||||
revng_log(LayoutLog,
|
||||
"Layout of " + toString(Function.key()) + " is:\n"
|
||||
<< toString(*this));
|
||||
}
|
||||
|
||||
Layout::Layout(const model::RawFunctionDefinition &Function) {
|
||||
@@ -527,6 +533,10 @@ Layout::Layout(const model::RawFunctionDefinition &Function) {
|
||||
|
||||
// Set the final offset.
|
||||
FinalStackOffset = Function.FinalStackOffset();
|
||||
|
||||
revng_log(LayoutLog,
|
||||
"Layout of " + toString(Function.key()) + " is:\n"
|
||||
<< toString(*this));
|
||||
}
|
||||
|
||||
bool Layout::verify() const {
|
||||
|
||||
@@ -200,7 +200,7 @@ static model::UpcastableType traverseModelGEP(const model::Binary &Model,
|
||||
return *Result;
|
||||
}
|
||||
|
||||
llvm::SmallVector<model::UpcastableType>
|
||||
static llvm::SmallVector<model::UpcastableType>
|
||||
flattenReturnTypes(const abi::FunctionType::Layout &Layout,
|
||||
const model::Binary &Model) {
|
||||
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
revng_add_analyses_library_internal(
|
||||
revngBackend
|
||||
ALAPVariableDeclaration.cpp
|
||||
DecompilePipe.cpp
|
||||
DecompileFunction.cpp
|
||||
DecompileToDirectoryPipe.cpp
|
||||
DecompileToSingleFile.cpp
|
||||
DecompileToSingleFilePipe.cpp)
|
||||
revng_add_analyses_library_internal(revngBackend ALAPVariableDeclaration.cpp
|
||||
DecompilePipe.cpp DecompileFunction.cpp)
|
||||
|
||||
target_link_libraries(
|
||||
revngBackend
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ranks = revng::ranks;
|
||||
namespace attributes = ptml::attributes;
|
||||
namespace tokens = ptml::c::tokens;
|
||||
|
||||
using tokenDefinition::types::StringToken;
|
||||
using StringToken = llvm::SmallString<128>;
|
||||
|
||||
using TokenMapT = std::map<const llvm::Value *, std::string>;
|
||||
using ModelTypesMap = std::map<const llvm::Value *,
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
//
|
||||
// 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::ModelCBuilder 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]);
|
||||
revng_assert(Entry.isValid());
|
||||
const model::Function &Function = Model.Functions().at(Entry);
|
||||
auto *F = Module.getFunction(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(/*DefineOpaqueTypes*/ true);
|
||||
|
||||
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;
|
||||
@@ -1,68 +0,0 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "revng/Backend/DecompileToSingleFile.h"
|
||||
#include "revng/TypeNames/ModelCBuilder.h"
|
||||
|
||||
using namespace revng::pipes;
|
||||
|
||||
void printSingleCFile(ptml::ModelCBuilder &B,
|
||||
const DecompileStringMap &Functions,
|
||||
const std::set<MetaAddress> &Targets) {
|
||||
auto Scope = B.getScopeTag(ptml::tags::Div);
|
||||
// Print headers
|
||||
B.append(B.getIncludeQuote("types-and-globals.h")
|
||||
+ B.getIncludeQuote("helpers.h") + "\n");
|
||||
|
||||
if (Targets.empty()) {
|
||||
// If Targets is empty print all the Functions' bodies
|
||||
for (const auto &[MetaAddress, CFunction] : Functions)
|
||||
B.append(CFunction + '\n');
|
||||
} else {
|
||||
// Otherwise only print the bodies of the Targets
|
||||
auto End = Functions.end();
|
||||
for (const auto &MetaAddress : Targets)
|
||||
if (auto It = Functions.find(MetaAddress); It != End)
|
||||
B.append(It->second + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
DecompileToSingleFile::DecompileToSingleFile(const class Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const PTMLCFunctionBytesContainer
|
||||
&Input,
|
||||
PTMLCBytesContainer &Output) :
|
||||
Binary(*Model.get().get()), Input(Input), Output(Output) {
|
||||
}
|
||||
|
||||
void DecompileToSingleFile::run() {
|
||||
auto Out = Output.getOStream(ObjectID());
|
||||
ptml::ModelCBuilder B(*Out,
|
||||
Binary,
|
||||
/* EnableTaglessMode = */ false,
|
||||
// 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 the single file artifact
|
||||
// recompilability is still important.
|
||||
{ .EnableStackFrameInlining = false });
|
||||
|
||||
auto Scope = B.getScopeTag(ptml::tags::Div);
|
||||
// Print headers
|
||||
B.append(B.getIncludeQuote("types-and-globals.h")
|
||||
+ B.getIncludeQuote("helpers.h") + "\n");
|
||||
|
||||
for (const auto &Object : Input.objects()) {
|
||||
auto Buffer = Input.getMemoryBuffer(Object);
|
||||
B.append(Buffer->getBuffer().str() + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/Backend/DecompileToSingleFile.h"
|
||||
#include "revng/Backend/DecompileToSingleFilePipe.h"
|
||||
#include "revng/HeadersGeneration/Options.h"
|
||||
#include "revng/Pipeline/AllRegistries.h"
|
||||
#include "revng/Pipeline/RegisterContainerFactory.h"
|
||||
#include "revng/Pipes/FileContainer.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
#include "revng/TypeNames/ModelCBuilder.h"
|
||||
|
||||
using namespace revng::kinds;
|
||||
|
||||
namespace revng::pipes {
|
||||
|
||||
static pipeline::RegisterDefaultConstructibleContainer<DecompiledFileContainer>
|
||||
Reg;
|
||||
|
||||
using Container = DecompileStringMap;
|
||||
void DecompileToSingleFile::run(pipeline::ExecutionContext &EC,
|
||||
const Container &DecompiledFunctions,
|
||||
DecompiledFileContainer &OutCFile) {
|
||||
|
||||
llvm::raw_string_ostream Out = OutCFile.asStream();
|
||||
|
||||
namespace options = revng::options;
|
||||
ptml::ModelCBuilder B(Out,
|
||||
*getModelFromContext(EC),
|
||||
/* EnableTaglessMode = */ false,
|
||||
// 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 the single file artifact
|
||||
// recompilability is still important.
|
||||
{ .EnableStackFrameInlining = false });
|
||||
|
||||
// Make a single C file with an empty set of targets, which means all the
|
||||
// functions in DecompiledFunctions
|
||||
printSingleCFile(B, DecompiledFunctions, {} /* Targets */);
|
||||
Out.flush();
|
||||
|
||||
EC.commitUniqueTarget(OutCFile);
|
||||
}
|
||||
|
||||
} // end namespace revng::pipes
|
||||
|
||||
static pipeline::RegisterPipe<revng::pipes::DecompileToSingleFile> Y;
|
||||
@@ -49,7 +49,7 @@ struct yield::StatementTraits<llvm::BasicBlock *> {
|
||||
|
||||
struct EmbedStatementComments {
|
||||
public:
|
||||
static constexpr auto Name = "embed-statement-comments";
|
||||
static constexpr auto Name = "legacy-embed-statement-comments";
|
||||
|
||||
private:
|
||||
llvm::FunctionCallee makeIRComment(llvm::Module &M) {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
#include "revng/ADT/SmallMap.h"
|
||||
#include "revng/ADT/ZipMapIterator.h"
|
||||
#include "revng/Model/FunctionTags.h"
|
||||
#include "revng/Support/Debug.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
|
||||
|
||||
+26
-6
@@ -13,6 +13,31 @@
|
||||
#include "revng/Clift/CliftAttributes.h"
|
||||
#include "revng/Clift/CliftOpHelpers.h"
|
||||
|
||||
std::unique_ptr<mlir::MLIRContext> clift::makeContext() {
|
||||
static constexpr auto Threading = mlir::MLIRContext::Threading::DISABLED;
|
||||
static const mlir::DialectRegistry Registry = []() -> mlir::DialectRegistry {
|
||||
mlir::DialectRegistry Registry;
|
||||
Registry.insert<clift::CliftDialect>();
|
||||
return Registry;
|
||||
}();
|
||||
|
||||
auto Result = std::make_unique<mlir::MLIRContext>(Registry, Threading);
|
||||
Result->loadDialect<CliftDialect>();
|
||||
return Result;
|
||||
}
|
||||
|
||||
mlir::OwningOpRef<mlir::ModuleOp>
|
||||
clift::makeModule(mlir::MLIRContext &Context) {
|
||||
auto DebugLocation = mlir::UnknownLoc::get(&Context);
|
||||
mlir::OwningOpRef<mlir::ModuleOp>
|
||||
Result = mlir::ModuleOp::create(DebugLocation);
|
||||
|
||||
Result.get()->setAttr(CliftDialect::getModuleAttrName(),
|
||||
mlir::UnitAttr::get(&Context));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
using UnresolvedOperandsVector = //
|
||||
llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand>;
|
||||
|
||||
@@ -103,16 +128,11 @@ void CliftDialect::registerOperations() {
|
||||
/* End of operations list */>();
|
||||
}
|
||||
|
||||
bool clift::hasModuleAttr(mlir::ModuleOp Module) {
|
||||
bool clift::isCliftModule(mlir::ModuleOp Module) {
|
||||
llvm::StringRef AttrName = CliftDialect::getModuleAttrName();
|
||||
return Module->hasAttrOfType<mlir::UnitAttr>(AttrName);
|
||||
}
|
||||
|
||||
void clift::setModuleAttr(mlir::ModuleOp Module) {
|
||||
Module->setAttr(CliftDialect::getModuleAttrName(),
|
||||
mlir::UnitAttr::get(Module.getContext()));
|
||||
}
|
||||
|
||||
const CDataModel &clift::getDataModel(mlir::ModuleOp Module) {
|
||||
if (auto Attr = Module->getAttr(CliftDialect::getDataModelAttrName()))
|
||||
return mlir::cast<DataModelAttr>(Attr).getDataModel();
|
||||
|
||||
@@ -111,14 +111,6 @@ MutableStringAttr MutableStringAttr::get(mlir::MLIRContext *Context,
|
||||
return Base::get(Context, Key);
|
||||
}
|
||||
|
||||
MutableStringAttr MutableStringAttr::get(mlir::MLIRContext *Context,
|
||||
mlir::Attribute Key,
|
||||
llvm::StringRef Value) {
|
||||
auto Attr = get(Context, Key);
|
||||
Attr.setValue(Value);
|
||||
return Attr;
|
||||
}
|
||||
|
||||
mlir::Attribute MutableStringAttr::getKey() const {
|
||||
return getImpl()->getKey();
|
||||
}
|
||||
@@ -795,10 +787,15 @@ StructAttr StructAttr::get(mlir::MLIRContext *Context,
|
||||
llvm::StringRef Handle,
|
||||
const ClassDefinition &Definition) {
|
||||
auto Attr = Base::get(Context, Handle);
|
||||
|
||||
auto R = Attr.Base::mutate(Definition);
|
||||
revng_assert(R.succeeded(),
|
||||
"Attempted to mutate the definition of an already defined "
|
||||
"struct attribute.");
|
||||
if (not R.succeeded()) {
|
||||
std::string Error = "Attempted to mutate the definition of an already "
|
||||
"defined struct attribute: '"
|
||||
+ Handle.str() + "'.";
|
||||
revng_abort(Error.c_str());
|
||||
}
|
||||
|
||||
return Attr;
|
||||
}
|
||||
|
||||
@@ -908,9 +905,13 @@ UnionAttr UnionAttr::get(mlir::MLIRContext *Context,
|
||||
}
|
||||
|
||||
auto R = Attr.Base::mutate(MutableDefinition);
|
||||
revng_assert(R.succeeded(),
|
||||
"Attempted to mutate the definition of an already defined "
|
||||
"union attribute.");
|
||||
if (not R.succeeded()) {
|
||||
std::string Error = "Attempted to mutate the definition of an already "
|
||||
"defined union attribute: '"
|
||||
+ Handle.str() + "'.";
|
||||
revng_abort(Error.c_str());
|
||||
}
|
||||
|
||||
return Attr;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,10 +158,15 @@ AddressableType VoidType::removeConst() const {
|
||||
|
||||
template<std::same_as<VoidType>>
|
||||
static VoidType readType(mlir::DialectBytecodeReader &Reader) {
|
||||
return VoidType::get(Reader.getContext());
|
||||
bool Const;
|
||||
if (readBool(Const, Reader).failed())
|
||||
return {};
|
||||
|
||||
return VoidType::get(Reader.getContext(), Const);
|
||||
}
|
||||
|
||||
static void writeType(VoidType Type, mlir::DialectBytecodeWriter &Writer) {
|
||||
writeBool(Type.getIsConst(), Writer);
|
||||
}
|
||||
|
||||
//===----------------------------- IntegerType ----------------------------===//
|
||||
|
||||
@@ -97,7 +97,7 @@ using HHBase = clift::impl::CliftEmitHelperHeaderBase<T>;
|
||||
clift::PassPtr<mlir::ModuleOp> clift::createEmitHelperHeaderPass() {
|
||||
static constexpr auto Impl = [](mlir::ModuleOp Module,
|
||||
ptml::CTokenEmitter &Tokens) {
|
||||
emitHelperHeader(Tokens, { Module });
|
||||
emitHelperHeader(Tokens, { Module }, model::Binary{});
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -82,10 +82,18 @@ private:
|
||||
return Name;
|
||||
}
|
||||
|
||||
std::optional<ptml::CTokenEmitter::Region> commentableReturnValueGuard() {
|
||||
std::optional<ptml::CTokenEmitter::Region>
|
||||
commentableReturnValueGuard(DeclaratorInfo const *Declarator) {
|
||||
if (OutermostFunctionType == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
// Only emitting the return value guard when parameters are available might
|
||||
// look weird at the first sight, but when we are emitting a prototype
|
||||
// without parameter information (the typedef), it's weird to allow adding
|
||||
// comments to a return type that will show up elsewhere.
|
||||
if (not Declarator->Parameters)
|
||||
return std::nullopt;
|
||||
|
||||
auto FTHandle = OutermostFunctionType.getHandle();
|
||||
auto FTLoc = pipeline::locationFromString(revng::ranks::TypeDefinition,
|
||||
FTHandle);
|
||||
@@ -141,7 +149,7 @@ private:
|
||||
}
|
||||
|
||||
{
|
||||
auto Guard = commentableReturnValueGuard();
|
||||
auto Guard = commentableReturnValueGuard(Declarator);
|
||||
|
||||
// Recurse through the declaration, pushing each level onto the stack
|
||||
// until a terminal type is encountered. Primitive types as well as
|
||||
@@ -504,6 +512,12 @@ void CEmitter::emitFunctionPrototype(FunctionOp Op) {
|
||||
Parameters = ParameterDeclarators;
|
||||
}
|
||||
|
||||
std::optional<ptml::CTokenEmitter::Region> MaybeRegion;
|
||||
if (not IsHelper) {
|
||||
using RegionKind = ptml::CTokenEmitter::RegionKind;
|
||||
MaybeRegion.emplace(Tokens, RegionKind::Commentable, Op.getHandle());
|
||||
}
|
||||
|
||||
emitDeclaration(Op.getFunctionType(),
|
||||
CEmitter::DeclaratorInfo{
|
||||
.Identifier = Op.getName(),
|
||||
@@ -518,9 +532,6 @@ void CEmitter::emitFunctionPrototype(FunctionOp Op) {
|
||||
static constexpr uint64_t ExtraKeywordIndentation = 2;
|
||||
|
||||
void CEmitter::emitFunctionDoxygenComment(FunctionOp Function) {
|
||||
auto Guard = Tokens.enterRegion(ptml::CTokenEmitter::RegionKind::Commentable,
|
||||
Function.getHandle());
|
||||
|
||||
std::optional<ptml::CDoxygenEmitter> Emitter = std::nullopt;
|
||||
|
||||
// Function comment
|
||||
@@ -529,6 +540,10 @@ void CEmitter::emitFunctionDoxygenComment(FunctionOp Function) {
|
||||
auto CommentBody = mlir::cast<mlir::StringAttr>(RawAttribute).getValue();
|
||||
if (not CommentBody.empty()) {
|
||||
ptml::CDoxygenEmitter::emitLineComment(Emitter, Tokens);
|
||||
|
||||
auto G = Tokens.enterRegion(ptml::CTokenEmitter::RegionKind::Commentable,
|
||||
Function.getHandle());
|
||||
|
||||
Emitter->emit(CommentBody);
|
||||
Emitter->emit("\n");
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ revng_add_library_internal(
|
||||
TypeDefinitionEmitter.cpp
|
||||
TypeDependencyGraph.cpp)
|
||||
|
||||
target_link_libraries(revngCliftEmitC MLIRPass revngClift revngPTML)
|
||||
target_link_libraries(revngCliftEmitC MLIRPass revngClift revngCliftImportModel
|
||||
revngPTML)
|
||||
|
||||
+117
-12
@@ -2,17 +2,47 @@
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "mlir/IR/Attributes.h"
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
|
||||
#include "revng/Clift/CliftAttributes.h"
|
||||
#include "revng/Clift/CliftTypes.h"
|
||||
#include "revng/Clift/ModuleVisitor.h"
|
||||
#include "revng/CliftEmitC/CEmitter.h"
|
||||
#include "revng/CliftEmitC/Headers.h"
|
||||
#include "revng/CliftEmitC/TypeDefinitionEmitter.h"
|
||||
#include "revng/CliftEmitC/TypeDependencyGraph.h"
|
||||
#include "revng/CliftImportModel/ImportModel.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/Pipeline/Location.h"
|
||||
#include "revng/Pipes/Ranks.h"
|
||||
|
||||
class OpaqueTypeSizeCollector
|
||||
: public clift::ModuleVisitor<OpaqueTypeSizeCollector> {
|
||||
private:
|
||||
using Base = clift::ModuleVisitor<OpaqueTypeSizeCollector>;
|
||||
|
||||
private:
|
||||
std::set<uint64_t> &Result;
|
||||
|
||||
public:
|
||||
OpaqueTypeSizeCollector(std::set<uint64_t> &Result) : Result(Result) {
|
||||
// TODO: do not hard-code this list here. Instead, share it with the model
|
||||
// verification logic (the only other current user of this).
|
||||
Result = { 1, 2, 4, 8, 10, 12, 16 };
|
||||
}
|
||||
|
||||
mlir::LogicalResult visitType(mlir::Type Type) {
|
||||
if (uint64_t Size = clift::getObjectSizeOrZero(Type))
|
||||
Result.emplace(Size);
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
};
|
||||
|
||||
class CHeaderEmitterImpl : TypeDefinitionEmitter {
|
||||
|
||||
public:
|
||||
@@ -116,23 +146,59 @@ public:
|
||||
CommentEmitted = true;
|
||||
}
|
||||
|
||||
emitGlobalDoxygenComment(Segment);
|
||||
{
|
||||
using RegionKind = ptml::CTokenEmitter::RegionKind;
|
||||
auto Guard = Tokens.enterRegion(RegionKind::Commentable,
|
||||
Segment.getHandle());
|
||||
|
||||
using EntityKind = ptml::CTokenEmitter::EntityKind;
|
||||
emitDeclaration(Segment.getType(),
|
||||
CEmitter::DeclaratorInfo{
|
||||
.Identifier = Segment.getName(),
|
||||
.Location = Segment.getHandle(),
|
||||
.CAttributes = {},
|
||||
.Kind = EntityKind::GlobalVariable,
|
||||
.Parameters = {} });
|
||||
emitGlobalDoxygenComment(Segment);
|
||||
|
||||
using EntityKind = ptml::CTokenEmitter::EntityKind;
|
||||
emitDeclaration(Segment.getType(),
|
||||
CEmitter::DeclaratorInfo{
|
||||
.Identifier = Segment.getName(),
|
||||
.Location = Segment.getHandle(),
|
||||
.CAttributes = {},
|
||||
.Kind = EntityKind::GlobalVariable,
|
||||
.Parameters = {} });
|
||||
|
||||
Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon);
|
||||
Tokens.emitNewline();
|
||||
}
|
||||
|
||||
Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon);
|
||||
Tokens.emitNewline();
|
||||
Tokens.emitNewline();
|
||||
});
|
||||
}
|
||||
|
||||
template<RangeOf<uint64_t> ByteSizeList = std::initializer_list<uint64_t>>
|
||||
void emitOpaqueTypes(mlir::MLIRContext &Context, const ByteSizeList &Sizes) {
|
||||
bool CommentEmitted = false;
|
||||
for (uint64_t Size : Sizes) {
|
||||
if (not CommentEmitted) {
|
||||
emitCategoryComment("Opaque types");
|
||||
CommentEmitted = true;
|
||||
}
|
||||
|
||||
auto Struct = clift::makeOpaqueStruct(Context, Size);
|
||||
emitClassDefinition(Struct);
|
||||
emitTypeDeclaration(Struct);
|
||||
}
|
||||
|
||||
Tokens.emitNewline();
|
||||
}
|
||||
|
||||
std::set<uint64_t>
|
||||
collectOpaqueByteSizes(llvm::ArrayRef<mlir::ModuleOp> Modules) {
|
||||
std::set<uint64_t> Result;
|
||||
|
||||
for (mlir::ModuleOp Module : Modules) {
|
||||
auto R = OpaqueTypeSizeCollector::visit(Module, Result);
|
||||
revng_assert(R.succeeded());
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public:
|
||||
void emitHelpers(llvm::ArrayRef<mlir::ModuleOp> Modules) {
|
||||
// TODO: emit `#include`s
|
||||
@@ -176,6 +242,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void emitCommonIncludes(ptml::CTokenEmitter &Tokens,
|
||||
const CDataModel &DataModel) {
|
||||
CHeaderEmitterImpl Emitter(Tokens, DataModel, TypeEmitterConfiguration{});
|
||||
Emitter.emitCommonIncludes();
|
||||
}
|
||||
|
||||
void emitTypes(ptml::CTokenEmitter &Tokens,
|
||||
mlir::ModuleOp Module,
|
||||
TypeEmitterConfiguration Configuration) {
|
||||
CHeaderEmitterImpl Emitter(Tokens,
|
||||
clift::getDataModel(Module),
|
||||
Configuration);
|
||||
Emitter.emitTypes(Module);
|
||||
}
|
||||
|
||||
void emitTypeAndGlobalHeader(ptml::CTokenEmitter &Tokens,
|
||||
mlir::ModuleOp Module,
|
||||
TypeEmitterConfiguration Configuration) {
|
||||
@@ -191,10 +272,13 @@ void emitTypeAndGlobalHeader(ptml::CTokenEmitter &Tokens,
|
||||
Emitter.emitFunctions(Module);
|
||||
Emitter.emitDynamicFunctions(Module);
|
||||
Emitter.emitSegments(Module);
|
||||
Emitter.emitOpaqueTypes(*Module.getContext(),
|
||||
Emitter.collectOpaqueByteSizes({ Module }));
|
||||
}
|
||||
|
||||
void emitHelperHeader(ptml::CTokenEmitter &Tokens,
|
||||
llvm::ArrayRef<mlir::ModuleOp> Modules) {
|
||||
llvm::ArrayRef<mlir::ModuleOp> Modules,
|
||||
const model::Binary &Binary) {
|
||||
revng_check(not Modules.empty());
|
||||
|
||||
const CDataModel &DataModel = clift::getDataModel(Modules.front());
|
||||
@@ -211,4 +295,25 @@ void emitHelperHeader(ptml::CTokenEmitter &Tokens,
|
||||
|
||||
Emitter.emitHeaderPrologue();
|
||||
Emitter.emitHelpers(Modules);
|
||||
|
||||
// This trick is practically a `const_cast`.
|
||||
mlir::MLIRContext &Context = *mlir::ModuleOp(Modules.front()).getContext();
|
||||
|
||||
std::set<std::uint64_t> NonModelOpaqueTypes;
|
||||
std::ranges::set_difference(Emitter.collectOpaqueByteSizes(Modules),
|
||||
Binary.collectAllTypeSizes(),
|
||||
std::inserter(NonModelOpaqueTypes,
|
||||
NonModelOpaqueTypes.end()));
|
||||
|
||||
Emitter.emitOpaqueTypes(Context, NonModelOpaqueTypes);
|
||||
}
|
||||
|
||||
void emitSingleTypeDefinition(ptml::CTokenEmitter &Tokens,
|
||||
const CDataModel &DataModel,
|
||||
clift::DefinedType Type,
|
||||
TypeEmitterConfiguration Config) {
|
||||
TypeDefinitionEmitter Emitter(Tokens, DataModel, Config);
|
||||
|
||||
Emitter.emitTypeDefinition(Type);
|
||||
Tokens.emitNewline();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "llvm/ADT/DepthFirstIterator.h"
|
||||
#include "llvm/ADT/PostOrderIterator.h"
|
||||
|
||||
#include "revng/Clift/CliftAttributes.h"
|
||||
@@ -30,6 +31,9 @@ void TypeDefinitionEmitter::emitTypeKeyword(clift::DefinedType Type) {
|
||||
}
|
||||
|
||||
void TypeDefinitionEmitter::emitDeclarationTypedef(clift::DefinedType Type) {
|
||||
auto Guard = Tokens.enterRegion(ptml::CTokenEmitter::RegionKind::Commentable,
|
||||
Type.getHandle());
|
||||
|
||||
Tokens.emitKeyword(ptml::CTokenEmitter::Keyword::Typedef);
|
||||
Tokens.emitSpace();
|
||||
|
||||
@@ -103,9 +107,6 @@ void TypeDefinitionEmitter::emitTypeDeclaration(clift::DefinedType Type) {
|
||||
} else if (auto Typedef = mlir::dyn_cast<clift::TypedefType>(Type)) {
|
||||
emitTypedefDefinition(Typedef);
|
||||
|
||||
} else if (auto Enum = mlir::dyn_cast<clift::EnumType>(Type)) {
|
||||
emitEnumDefinition(Enum);
|
||||
|
||||
} else if (auto Function = mlir::dyn_cast<clift::FunctionType>(Type)) {
|
||||
emitFunctionTypedef(Function);
|
||||
|
||||
@@ -120,8 +121,9 @@ static std::string paddingFieldName(uint64_t CurrentOffset) {
|
||||
// We should fix this after the configuration is separate from the
|
||||
// model
|
||||
|
||||
model::CNameBuilder Builder(model::Binary{});
|
||||
return Builder.paddingFieldName(CurrentOffset);
|
||||
model::Binary EmptyBinary{};
|
||||
model::CNameBuilder UnconfiguredNB(EmptyBinary);
|
||||
return UnconfiguredNB.paddingFieldName(CurrentOffset);
|
||||
}
|
||||
|
||||
void TypeDefinitionEmitter::emitPaddingField(clift::ClassType Class,
|
||||
@@ -204,7 +206,8 @@ void TypeDefinitionEmitter::emitClassDefinition(clift::ClassType Class) {
|
||||
//
|
||||
// TODO: fix this once the configuration is obtained from the pipe
|
||||
// (new pipeline only).
|
||||
model::CNameBuilder UnconfiguredNB(model::Binary{});
|
||||
model::Binary EmptyBinary{};
|
||||
model::CNameBuilder UnconfiguredNB(EmptyBinary);
|
||||
model::StructDefinition FakeStruct; // No model fields are read here.
|
||||
model::StructField FakeField(Field.getOffset()); // Only `Offset` read.
|
||||
if (not UnconfiguredNB.isAutomaticName(FakeStruct,
|
||||
@@ -224,6 +227,15 @@ void TypeDefinitionEmitter::emitClassDefinition(clift::ClassType Class) {
|
||||
PreviousOffset = Field.getOffset()
|
||||
+ clift::getObjectSize(Field.getType());
|
||||
}
|
||||
|
||||
// Since the all types are packed, without the trailing padding any struct
|
||||
// whose size extends past the end of its last field would end up with
|
||||
// a wrong `sizeof`.
|
||||
// Print the trailing padding unless `ExplicitPadding` is set to off, in
|
||||
// which case users are explicitly opting out of semantic equivalence
|
||||
// anyway, so breaking `sizeof` is expected.
|
||||
if (IsStruct and Configuration.ExplicitPadding)
|
||||
emitPaddingField(Class, PreviousOffset, Class.getObjectSize());
|
||||
}
|
||||
|
||||
Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon);
|
||||
@@ -238,7 +250,7 @@ void TypeDefinitionEmitter::emitEnumDefinition(clift::EnumType Enum) {
|
||||
emitDoxygenComment(Enum);
|
||||
Tokens.emitKeyword(ptml::CTokenEmitter::Keyword::Enum);
|
||||
|
||||
clift::ValueType Type = Enum.getUnderlyingType();
|
||||
mlir::Type Type = Enum.getUnderlyingType();
|
||||
emitCAttributes(clift::CAttributeListBuilder(Enum.getContext())
|
||||
.setOrUpdate<"_ENUM_UNDERLYING">(Type)
|
||||
.setOrUpdate<"_PACKED">()
|
||||
@@ -312,9 +324,6 @@ void TypeDefinitionEmitter::emitEnumDefinition(clift::EnumType Enum) {
|
||||
|
||||
Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon);
|
||||
Tokens.emitNewline();
|
||||
|
||||
// Allow using `MyEnum` instead of `enum MyEnum`.
|
||||
emitDeclarationTypedef(Enum);
|
||||
}
|
||||
|
||||
void TypeDefinitionEmitter::emitTypeDefinition(clift::DefinedType Type) {
|
||||
@@ -322,6 +331,10 @@ void TypeDefinitionEmitter::emitTypeDefinition(clift::DefinedType Type) {
|
||||
emitClassDefinition(Class);
|
||||
return;
|
||||
|
||||
} else if (auto Enum = mlir::dyn_cast<clift::EnumType>(Type)) {
|
||||
emitEnumDefinition(Enum);
|
||||
return;
|
||||
|
||||
} else if (not isSeparateDeclarationAllowed(Type)) {
|
||||
emitTypeDeclaration(Type);
|
||||
Tokens.emitNewline();
|
||||
@@ -337,34 +350,38 @@ void TypeDefinitionEmitter::emitTypeTree(const TypeDependencyNode &Root,
|
||||
revng_log(TypePrinterLog,
|
||||
"Starting a post order visit from:" << Root.label());
|
||||
|
||||
bool SkipTheRest = false;
|
||||
// Check that all the nodes have valid handles and, optionally, dedicate
|
||||
// at most one node to be skipped.
|
||||
const TypeDependencyNode *NodeToSkip = nullptr;
|
||||
for (const auto *Node : llvm::depth_first(&Root)) {
|
||||
revng_assert(not Node->T.getHandle().empty());
|
||||
if (Node->IsDefinition && Node->T.getHandle() == Configuration.TypeToOmit) {
|
||||
revng_assert(NodeToSkip == nullptr,
|
||||
"Multiple nodes with the same handle.");
|
||||
NodeToSkip = Node;
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeToSkip) {
|
||||
// Skip everything that depends on the edited node.
|
||||
for (const auto *DependsOnSkipped : llvm::inverse_depth_first(NodeToSkip))
|
||||
Emitted.emplace(DependsOnSkipped);
|
||||
}
|
||||
|
||||
size_t NodesEmittedAlready = Emitted.size();
|
||||
for (const auto *Node : llvm::post_order_ext(&Root, Emitted)) {
|
||||
LoggerIndent PostOrderIndent{ TypePrinterLog };
|
||||
|
||||
clift::DefinedType Type = Node->T;
|
||||
revng_assert(not Type.getHandle().empty());
|
||||
if (Type.getHandle() == Configuration.TypeToOmit)
|
||||
SkipTheRest = true;
|
||||
|
||||
if (SkipTheRest) {
|
||||
revng_log(TypePrinterLog, "skipping (TypeToOmit): " << Node->label());
|
||||
continue;
|
||||
} else {
|
||||
revng_log(TypePrinterLog, "visiting: " << Node->label());
|
||||
}
|
||||
|
||||
if (Node->IsDefinition) {
|
||||
revng_assert(Node->IsDefinition);
|
||||
revng_assert(isSeparateDeclarationAllowed(Type));
|
||||
revng_assert(isSeparateDeclarationAllowed(Node->T));
|
||||
|
||||
revng_log(TypePrinterLog, "Definition");
|
||||
emitTypeDefinition(Type);
|
||||
emitTypeDefinition(Node->T);
|
||||
|
||||
} else {
|
||||
revng_log(TypePrinterLog, "Declaration");
|
||||
emitTypeDeclaration(Type);
|
||||
emitTypeDeclaration(Node->T);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -293,6 +293,10 @@ mlir::LogicalResult Builder<ModelMode>::visitType(mlir::Type T) {
|
||||
if constexpr (not ModelMode)
|
||||
ShouldAdd = !ShouldAdd;
|
||||
|
||||
// Explicitly skip opaque types, as we print them separately.
|
||||
if (pipeline::locationFromString(rr::OpaqueType, Type.getHandle()))
|
||||
ShouldAdd = false;
|
||||
|
||||
// Never add helper prototypes, as we don't want to print those.
|
||||
if (pipeline::locationFromString(rr::HelperFunction, Type.getHandle()))
|
||||
ShouldAdd = false;
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
revng_add_library_internal(revngCliftImportModel SHARED
|
||||
ImportDescriptiveInfo.cpp ImportTypes.cpp Verify.cpp)
|
||||
|
||||
target_link_libraries(revngCliftImportModel revngClift revngModel)
|
||||
target_link_libraries(revngCliftImportModel revngABI revngClift revngModel)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <ranges>
|
||||
#include <type_traits>
|
||||
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "mlir/IR/RegionGraphTraits.h"
|
||||
@@ -110,7 +111,24 @@ public:
|
||||
class SymbolRenamer {
|
||||
llvm::DenseMap<llvm::StringRef, std::string> Map;
|
||||
|
||||
// Set of module-level ops whose descriptive info has already been
|
||||
// recorded. Lives here (and not on the Importer) because a single
|
||||
// importDescriptiveInfo invocation may construct several Importer
|
||||
// instances (e.g. the per-function overload visits the current
|
||||
// function, then each helper/global, then any callee reached through a
|
||||
// clift::UseOp); the dedup gate must be shared across all of them so
|
||||
// that the same target is not recorded twice.
|
||||
llvm::DenseSet<mlir::Operation *> RecordedTargets;
|
||||
|
||||
public:
|
||||
// Mark `Op` as recorded; return true if this is the first time it is
|
||||
// seen during the current importDescriptiveInfo call, false otherwise.
|
||||
// Callers that perform per-op work (prototype attributes, comments,
|
||||
// ...) should use this to gate that work so it only happens once.
|
||||
bool markRecorded(clift::GlobalOpInterface Op) {
|
||||
return RecordedTargets.insert(Op.getOperation()).second;
|
||||
}
|
||||
|
||||
void record(clift::GlobalOpInterface Op, llvm::StringRef NewName) {
|
||||
auto [Iterator, Inserted] = Map.try_emplace(Op.getName(), NewName.str());
|
||||
revng_assert(Inserted);
|
||||
@@ -235,6 +253,33 @@ public:
|
||||
if (auto S = mlir::dyn_cast<clift::StatementOpInterface>(Op))
|
||||
return visitStatementOp(S);
|
||||
|
||||
if (auto U = mlir::dyn_cast<clift::UseOp>(Op))
|
||||
return visitUseOp(U);
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// Resolve the symbol referenced by `Use` to its definition in the
|
||||
// enclosing module and record the descriptive info for that target.
|
||||
//
|
||||
// This is what guarantees that a function whose body contains
|
||||
// references to other functions or to global variables also causes
|
||||
// those callees to be renamed by the SymbolRenamer at the end of
|
||||
// importDescriptiveInfo, without the per-function variant having to
|
||||
// pre-walk every sibling module-level op.
|
||||
mlir::LogicalResult visitUseOp(clift::UseOp Use) {
|
||||
auto Module = Use->getParentOfType<mlir::ModuleOp>();
|
||||
revng_assert(Module);
|
||||
|
||||
mlir::Operation *Target = //
|
||||
mlir::SymbolTable::lookupSymbolIn(Module, Use.getSymbolNameAttr());
|
||||
|
||||
if (auto F = mlir::dyn_cast_or_null<clift::FunctionOp>(Target))
|
||||
return recordFunctionOpName(F);
|
||||
|
||||
if (auto G = mlir::dyn_cast_or_null<clift::GlobalVariableOp>(Target))
|
||||
return recordGlobalVariableOpName(G);
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
@@ -347,14 +392,14 @@ private:
|
||||
const model::RawFunctionDefinition &FMT) {
|
||||
revng_assert(ST.getFields().size() == FMT.ReturnValues().size());
|
||||
|
||||
std::string
|
||||
Name = (Model.Configuration().Naming().ArtificialReturnValuePrefix()
|
||||
+ NameBuilder.name(FMT));
|
||||
std::string Name = NameBuilder.artificialReturnValueWrapperName(FMT);
|
||||
|
||||
ST.getMutableName().setValue(Name);
|
||||
|
||||
for (auto [F, R] : llvm::zip(ST.getFields(), FMT.ReturnValues()))
|
||||
for (auto [F, R] : llvm::zip(ST.getFields(), FMT.ReturnValues())) {
|
||||
F.getMutableName().setValue(NameBuilder.name(FMT, R));
|
||||
F.getMutableComment().setValue(R.Comment());
|
||||
}
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
@@ -571,8 +616,16 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
mlir::LogicalResult visitFunctionOp(clift::FunctionOp Op) {
|
||||
CurrentFunction.reset();
|
||||
// Record the descriptive info for the given function op, without touching
|
||||
// the `CurrentFunction` state used by the body walk. This is the part of
|
||||
// the work that can be performed for any function op the importer comes
|
||||
// across (the current function, a sibling declaration, a callee reached
|
||||
// through a `clift::UseOp`, ...).
|
||||
//
|
||||
// Idempotent: a second call on the same op is a no-op.
|
||||
mlir::LogicalResult recordFunctionOpName(clift::FunctionOp Op) {
|
||||
if (not Symbols.markRecorded(Op))
|
||||
return mlir::success();
|
||||
|
||||
if (auto Pair = getModelFunction(Op.getHandle())) {
|
||||
auto &[L, MF] = *Pair;
|
||||
@@ -583,9 +636,6 @@ public:
|
||||
NameBuilder.name(MF),
|
||||
MF.Comment());
|
||||
|
||||
if (not Op.getBody().empty())
|
||||
CurrentFunction.emplace(*this, Op, std::move(L), MF);
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
@@ -610,7 +660,31 @@ public:
|
||||
revng_abort("Invalid function handle");
|
||||
}
|
||||
|
||||
mlir::LogicalResult visitGlobalVariableOp(clift::GlobalVariableOp Op) {
|
||||
mlir::LogicalResult visitFunctionOp(clift::FunctionOp Op) {
|
||||
CurrentFunction.reset();
|
||||
|
||||
if (recordFunctionOpName(Op).failed())
|
||||
return mlir::failure();
|
||||
|
||||
if (auto Pair = getModelFunction(Op.getHandle())) {
|
||||
auto &[L, MF] = *Pair;
|
||||
if (not Op.getBody().empty())
|
||||
CurrentFunction.emplace(*this, Op, std::move(L), MF);
|
||||
}
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// Record the descriptive info for the given global variable op. Mirrors
|
||||
// recordFunctionOpName: contains no per-function state and is therefore
|
||||
// safe to call from any visit context (module level or while walking a
|
||||
// function body following a clift::UseOp).
|
||||
//
|
||||
// Idempotent: a second call on the same op is a no-op.
|
||||
mlir::LogicalResult recordGlobalVariableOpName(clift::GlobalVariableOp Op) {
|
||||
if (not Symbols.markRecorded(Op))
|
||||
return mlir::success();
|
||||
|
||||
if (const model::Segment *Segment = getModelSegment(Op)) {
|
||||
Symbols.record(Op, NameBuilder.name(Model, *Segment));
|
||||
|
||||
@@ -625,6 +699,10 @@ public:
|
||||
revng_abort("Invalid global variable handle");
|
||||
}
|
||||
|
||||
mlir::LogicalResult visitGlobalVariableOp(clift::GlobalVariableOp Op) {
|
||||
return recordGlobalVariableOpName(Op);
|
||||
}
|
||||
|
||||
//===-------------------------- Comment import --------------------------===//
|
||||
|
||||
mlir::LogicalResult visitStatementOp(clift::StatementOpInterface Op) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
|
||||
#include "revng/ABI/Definition.h"
|
||||
#include "revng/ADT/RecursiveCoroutine.h"
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/Clift/CliftAttributes.h"
|
||||
@@ -15,6 +16,8 @@
|
||||
#include "revng/Clift/CliftTypes.h"
|
||||
#include "revng/CliftImportModel/CAttributeListBuilder.h"
|
||||
#include "revng/CliftImportModel/ImportModel.h"
|
||||
#include "revng/Model/NameBuilder.h"
|
||||
#include "revng/Model/Segment.h"
|
||||
#include "revng/Pipeline/Location.h"
|
||||
#include "revng/Pipes/Ranks.h"
|
||||
|
||||
@@ -60,7 +63,6 @@ class CliftConverter {
|
||||
|
||||
public:
|
||||
explicit CliftConverter(mlir::MLIRContext *Context,
|
||||
const model::Binary &Binary,
|
||||
llvm::function_ref<mlir::InFlightDiagnostic()>
|
||||
EmitError) :
|
||||
Context(Context), EmitError(EmitError) {}
|
||||
@@ -91,14 +93,12 @@ private:
|
||||
}
|
||||
|
||||
template<typename KeyT>
|
||||
clift::MutableStringAttr
|
||||
makeNameAttr(llvm::StringRef Handle, llvm::StringRef Name = {}) {
|
||||
return clift::makeNameAttr<KeyT>(Context, Handle, Name);
|
||||
clift::MutableStringAttr makeNameAttr(llvm::StringRef Handle) {
|
||||
return clift::makeNameAttr<KeyT>(Context, Handle);
|
||||
}
|
||||
template<typename KeyT>
|
||||
clift::MutableStringAttr
|
||||
makeCommentAttr(llvm::StringRef Handle, llvm::StringRef Comment = {}) {
|
||||
return clift::makeCommentAttr<KeyT>(Context, Handle, Comment);
|
||||
clift::MutableStringAttr makeCommentAttr(llvm::StringRef Handle) {
|
||||
return clift::makeCommentAttr<KeyT>(Context, Handle);
|
||||
}
|
||||
|
||||
static clift::IntegerKind
|
||||
@@ -655,18 +655,31 @@ private:
|
||||
mlir::Type
|
||||
clift::importType(llvm::function_ref<mlir::InFlightDiagnostic()> EmitError,
|
||||
mlir::MLIRContext *Context,
|
||||
const model::TypeDefinition &ModelType,
|
||||
const model::Binary &Binary) {
|
||||
return CliftConverter(Context, Binary, EmitError)
|
||||
.convertTypeDefinition(ModelType);
|
||||
const model::TypeDefinition &ModelType) {
|
||||
return CliftConverter(Context, EmitError).convertTypeDefinition(ModelType);
|
||||
}
|
||||
mlir::Type clift::importType(mlir::MLIRContext *Context,
|
||||
const model::TypeDefinition &ModelType) {
|
||||
auto EmitError = [Context]() -> mlir::InFlightDiagnostic {
|
||||
return Context->getDiagEngine().emit(mlir::UnknownLoc::get(Context),
|
||||
mlir::DiagnosticSeverity::Error);
|
||||
};
|
||||
return CliftConverter(Context, EmitError).convertTypeDefinition(ModelType);
|
||||
}
|
||||
|
||||
mlir::Type
|
||||
clift::importType(llvm::function_ref<mlir::InFlightDiagnostic()> EmitError,
|
||||
mlir::MLIRContext *Context,
|
||||
const model::Type &ModelType,
|
||||
const model::Binary &Binary) {
|
||||
return CliftConverter(Context, Binary, EmitError).convertType(ModelType);
|
||||
const model::Type &ModelType) {
|
||||
return CliftConverter(Context, EmitError).convertType(ModelType);
|
||||
}
|
||||
mlir::Type clift::importType(mlir::MLIRContext *Context,
|
||||
const model::Type &ModelType) {
|
||||
auto EmitError = [Context]() -> mlir::InFlightDiagnostic {
|
||||
return Context->getDiagEngine().emit(mlir::UnknownLoc::get(Context),
|
||||
mlir::DiagnosticSeverity::Error);
|
||||
};
|
||||
return CliftConverter(Context, EmitError).convertType(ModelType);
|
||||
}
|
||||
|
||||
using AttributeSet = MutableSet<model::FunctionAttribute::Values>;
|
||||
@@ -727,17 +740,112 @@ clift::importSegmentDeclaration(mlir::ModuleOp Module,
|
||||
|
||||
void clift::importAllModelTypes(const model::Binary &Model,
|
||||
mlir::ModuleOp Module) {
|
||||
mlir::MLIRContext *Context = Module.getContext();
|
||||
mlir::Location Loc = mlir::UnknownLoc::get(Context);
|
||||
auto EmitError = [&]() -> mlir::InFlightDiagnostic {
|
||||
return Context->getDiagEngine().emit(Loc, mlir::DiagnosticSeverity::Error);
|
||||
};
|
||||
clift::setDataModel(Module, abi::getDataModel(Model));
|
||||
|
||||
llvm::SmallVector<mlir::Attribute> TypeAttrs;
|
||||
for (const auto &ModelType : Model.TypeDefinitions()) {
|
||||
auto CliftType = clift::importType(EmitError, Context, *ModelType, Model);
|
||||
auto CliftType = clift::importType(Module.getContext(), *ModelType);
|
||||
TypeAttrs.push_back(mlir::TypeAttr::get(CliftType));
|
||||
}
|
||||
|
||||
Module->setAttr("clift.test", mlir::ArrayAttr::get(Context, TypeAttrs));
|
||||
Module->setAttr("clift.types",
|
||||
mlir::ArrayAttr::get(Module.getContext(), TypeAttrs));
|
||||
}
|
||||
|
||||
template<typename FunctionT, typename RankT>
|
||||
clift::FunctionOp importAnyFunctionDeclaration(const FunctionT &MF,
|
||||
const RankT &Rank,
|
||||
mlir::ModuleOp Module,
|
||||
const model::Binary &Binary) {
|
||||
auto ModelPrototype = Binary.prototypeOrDefault(MF.prototype());
|
||||
revng_check(ModelPrototype);
|
||||
|
||||
auto CliftType = clift::importType(Module.getContext(), *ModelPrototype);
|
||||
auto CliftPrototype = mlir::cast<clift::FunctionType>(CliftType);
|
||||
|
||||
std::string Handle = pipeline::locationString(Rank, MF.key());
|
||||
auto UnknownLocation = mlir::UnknownLoc::get(Module.getContext());
|
||||
return clift::importFunctionDeclaration(Module,
|
||||
UnknownLocation,
|
||||
toString(MF.key()),
|
||||
Handle,
|
||||
CliftPrototype,
|
||||
MF.Attributes().unwrap());
|
||||
}
|
||||
|
||||
void clift::importAllModelFunctionDeclarations(const model::Binary &Model,
|
||||
mlir::ModuleOp Module) {
|
||||
clift::setDataModel(Module, abi::getDataModel(Model));
|
||||
|
||||
for (const auto &ModelFunction : Model.Functions()) {
|
||||
importAnyFunctionDeclaration(ModelFunction,
|
||||
revng::ranks::Function,
|
||||
Module,
|
||||
Model);
|
||||
}
|
||||
|
||||
for (const auto &ModelFunction : Model.ImportedDynamicFunctions()) {
|
||||
importAnyFunctionDeclaration(ModelFunction,
|
||||
revng::ranks::DynamicFunction,
|
||||
Module,
|
||||
Model);
|
||||
}
|
||||
}
|
||||
|
||||
static mlir::Type importSegmentType(const model::Segment &Segment,
|
||||
mlir::ModuleOp Module) {
|
||||
if (const model::StructDefinition *SegmentStruct = Segment.type()) {
|
||||
return clift::importType(Module.getContext(), *SegmentStruct);
|
||||
|
||||
} else {
|
||||
auto Char = clift::IntegerType::get(Module.getContext(),
|
||||
clift::IntegerKind::Unsigned,
|
||||
1);
|
||||
return clift::ArrayType::get(Char, Segment.VirtualSize());
|
||||
}
|
||||
}
|
||||
|
||||
void clift::importAllModelSegmentDeclarations(const model::Binary &Model,
|
||||
mlir::ModuleOp Module) {
|
||||
clift::setDataModel(Module, abi::getDataModel(Model));
|
||||
|
||||
for (const auto &Segment : Model.Segments()) {
|
||||
std::string Handle = pipeline::locationString(revng::ranks::Segment,
|
||||
Segment.key());
|
||||
auto UnknownLocation = mlir::UnknownLoc::get(Module.getContext());
|
||||
clift::importSegmentDeclaration(Module,
|
||||
UnknownLocation,
|
||||
toString(Segment.key()),
|
||||
Handle,
|
||||
importSegmentType(Segment, Module));
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getOpaqueTypeHandle(uint64_t ByteSize) {
|
||||
return pipeline::locationString(revng::ranks::OpaqueType, ByteSize);
|
||||
}
|
||||
|
||||
clift::StructType clift::makeOpaqueStruct(mlir::MLIRContext &Context,
|
||||
uint64_t ByteSize) {
|
||||
std::string Handle = getOpaqueTypeHandle(ByteSize);
|
||||
|
||||
auto NameAttr = makeNameAttr<StructAttr>(&Context, Handle);
|
||||
auto CommentAttr = makeCommentAttr<StructAttr>(&Context, Handle);
|
||||
auto Attrs = llvm::ArrayRef<clift::CAttributeAttr>{};
|
||||
auto Def = clift::StructAttr::get(&Context,
|
||||
Handle,
|
||||
NameAttr,
|
||||
CommentAttr,
|
||||
ByteSize,
|
||||
{},
|
||||
Attrs);
|
||||
|
||||
// TODO: this discards the prefix configuration option.
|
||||
// We should fix this after the configuration is separate from the
|
||||
// model
|
||||
model::Binary EmptyBinary{};
|
||||
model::CNameBuilder UnconfiguredNB(EmptyBinary);
|
||||
Def.getMutableName().setValue(UnconfiguredNB.opaqueTypeName(ByteSize));
|
||||
|
||||
return clift::StructType::get(Def);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ revng_add_analyses_library_internal(
|
||||
CliftContainer.cpp
|
||||
Clifter.cpp
|
||||
EmitC.cpp
|
||||
EmitCAsDirectory.cpp
|
||||
EmitCAsSingleFile.cpp
|
||||
Headers.cpp
|
||||
ImportDataModel.cpp
|
||||
ImportDescriptiveInfo.cpp
|
||||
ImportTypes.cpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "mlir/IR/SymbolTable.h"
|
||||
#include "mlir/Parser/Parser.h"
|
||||
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/Clift/CliftDialect.h"
|
||||
#include "revng/Clift/Helpers.h"
|
||||
#include "revng/CliftPipes/CliftContainer.h"
|
||||
@@ -78,20 +79,6 @@ static void visit(ModuleOp Module, Visitor visitor) {
|
||||
}
|
||||
}
|
||||
|
||||
static const mlir::DialectRegistry &getDialectRegistry() {
|
||||
static const mlir::DialectRegistry Registry = []() -> mlir::DialectRegistry {
|
||||
mlir::DialectRegistry Registry;
|
||||
Registry.insert<clift::CliftDialect>();
|
||||
return Registry;
|
||||
}();
|
||||
return Registry;
|
||||
}
|
||||
|
||||
static ContextPtr makeContext() {
|
||||
const auto Threading = MLIRContext::Threading::DISABLED;
|
||||
return std::make_unique<MLIRContext>(getDialectRegistry(), Threading);
|
||||
}
|
||||
|
||||
// Cloning MLIR from one module into another requires first serialising the
|
||||
// source and then deserialising it again into the target module. We do this
|
||||
// a) when cloning operations from one container to another, and
|
||||
@@ -183,7 +170,7 @@ const char CliftFunctionContainer::ID = 0;
|
||||
|
||||
void CliftFunctionContainer::setModule(OwningModuleRef &&NewModule) {
|
||||
revng_assert(NewModule);
|
||||
revng_assert(clift::hasModuleAttr(NewModule.get()));
|
||||
revng_assert(clift::isCliftModule(NewModule.get()));
|
||||
|
||||
// Make any non-target functions external.
|
||||
visit(*NewModule, [&](clift::FunctionOp F) {
|
||||
@@ -230,6 +217,7 @@ CliftFunctionContainer::cloneFiltered(const pipeline::TargetsList &Filter)
|
||||
|
||||
MLIRContext &DestinationContext = *DestinationContainer->Context;
|
||||
DestinationContext.appendDialectRegistry(Context->getDialectRegistry());
|
||||
DestinationContext.loadDialect<clift::CliftDialect>();
|
||||
|
||||
OwningModuleRef &DestinationModule = DestinationContainer->Module;
|
||||
DestinationModule = cloneModuleInto(*TemporaryModule,
|
||||
@@ -251,6 +239,7 @@ void CliftFunctionContainer::mergeBackImpl(CliftFunctionContainer
|
||||
|
||||
// Register the dialects of the other container in this container.
|
||||
Context->appendDialectRegistry(SourceContainer.Context->getDialectRegistry());
|
||||
Context->loadDialect<clift::CliftDialect>();
|
||||
|
||||
// Clone the other container's module into this container's context.
|
||||
// This module is automatically erased at the end of scope.
|
||||
@@ -317,7 +306,7 @@ bool CliftFunctionContainer::removeImpl(const pipeline::TargetsList &List) {
|
||||
|
||||
pruneUnusedSymbols(*Module);
|
||||
|
||||
auto NewContext = makeContext();
|
||||
auto NewContext = clift::makeContext();
|
||||
auto NewModule = cloneModuleInto(*Module, *NewContext);
|
||||
|
||||
Module = std::move(NewModule);
|
||||
@@ -328,12 +317,10 @@ bool CliftFunctionContainer::removeImpl(const pipeline::TargetsList &List) {
|
||||
}
|
||||
|
||||
void CliftFunctionContainer::clearImpl() {
|
||||
auto NewContext = makeContext();
|
||||
auto NewContext = clift::makeContext();
|
||||
Module = clift::makeModule(*NewContext);
|
||||
|
||||
Module = ModuleOp::create(mlir::UnknownLoc::get(NewContext.get()));
|
||||
Context = std::move(NewContext);
|
||||
|
||||
clift::setModuleAttr(Module.get());
|
||||
}
|
||||
|
||||
llvm::Error CliftFunctionContainer::serialize(llvm::raw_ostream &OS) const {
|
||||
@@ -343,12 +330,12 @@ llvm::Error CliftFunctionContainer::serialize(llvm::raw_ostream &OS) const {
|
||||
|
||||
llvm::Error
|
||||
CliftFunctionContainer::deserializeImpl(const llvm::MemoryBuffer &Buffer) {
|
||||
auto NewContext = makeContext();
|
||||
OwningModuleRef NewModule;
|
||||
auto NewContext = clift::makeContext();
|
||||
|
||||
OwningModuleRef NewModule;
|
||||
if (Buffer.getBufferSize() == 0) {
|
||||
NewModule = ModuleOp::create(mlir::UnknownLoc::get(NewContext.get()));
|
||||
clift::setModuleAttr(NewModule.get());
|
||||
NewModule = clift::makeModule(*NewContext);
|
||||
|
||||
} else {
|
||||
const mlir::ParserConfig Config(NewContext.get());
|
||||
NewModule = mlir::parseSourceString<ModuleOp>(Buffer.getBuffer(), Config);
|
||||
@@ -356,7 +343,7 @@ CliftFunctionContainer::deserializeImpl(const llvm::MemoryBuffer &Buffer) {
|
||||
if (not NewModule)
|
||||
return revng::createError("Cannot load MLIR module.");
|
||||
|
||||
if (not clift::hasModuleAttr(NewModule.get()))
|
||||
if (not clift::isCliftModule(NewModule.get()))
|
||||
return revng::createError("MLIR module is not a Clift module.");
|
||||
}
|
||||
|
||||
@@ -381,6 +368,7 @@ CliftContainer::cloneFiltered(const pipeline::TargetsList &Targets) const {
|
||||
|
||||
MLIRContext &DestinationContext = *DestinationContainer->Context;
|
||||
DestinationContext.appendDialectRegistry(Context->getDialectRegistry());
|
||||
DestinationContext.loadDialect<clift::CliftDialect>();
|
||||
|
||||
OwningModuleRef &DestinationModule = DestinationContainer->Module;
|
||||
DestinationModule = cloneModuleInto(*Module, *DestinationContainer->Context);
|
||||
@@ -417,6 +405,7 @@ void CliftContainer::mergeBackImpl(CliftContainer &&SourceContainer) {
|
||||
|
||||
// Register the dialects of the other container in this container.
|
||||
Context->appendDialectRegistry(SourceContainer.Context->getDialectRegistry());
|
||||
Context->loadDialect<clift::CliftDialect>();
|
||||
|
||||
// Clone the other container's module into this container's context.
|
||||
// This module is automatically erased at the end of scope.
|
||||
@@ -442,7 +431,8 @@ void CliftContainer::mergeBackImpl(CliftContainer &&SourceContainer) {
|
||||
for (mlir::NamedAttribute Attribute : (*SourceContainer.Module)->getAttrs()) {
|
||||
// TODO: something better to do then just override existing with incoming
|
||||
// when there's a name clash?
|
||||
(*Module)->setAttr(Attribute.getName(), Attribute.getValue());
|
||||
(*Module)->setAttr(llvm::StringRef(Attribute.getName()),
|
||||
Attribute.getValue());
|
||||
}
|
||||
|
||||
// Assume that at least some symbols were copied over and always prune.
|
||||
@@ -461,12 +451,10 @@ pipeline::TargetsList CliftContainer::enumerate() const {
|
||||
}
|
||||
|
||||
void CliftContainer::clearImpl() {
|
||||
auto NewContext = makeContext();
|
||||
auto NewContext = clift::makeContext();
|
||||
Module = clift::makeModule(*NewContext);
|
||||
|
||||
Module = ModuleOp::create(mlir::UnknownLoc::get(NewContext.get()));
|
||||
Context = std::move(NewContext);
|
||||
|
||||
clift::setModuleAttr(Module.get());
|
||||
}
|
||||
|
||||
llvm::Error CliftContainer::serialize(llvm::raw_ostream &OS) const {
|
||||
@@ -475,12 +463,12 @@ llvm::Error CliftContainer::serialize(llvm::raw_ostream &OS) const {
|
||||
}
|
||||
|
||||
llvm::Error CliftContainer::deserializeImpl(const llvm::MemoryBuffer &Buffer) {
|
||||
auto NewContext = makeContext();
|
||||
OwningModuleRef NewModule;
|
||||
auto NewContext = clift::makeContext();
|
||||
|
||||
OwningModuleRef NewModule;
|
||||
if (Buffer.getBufferSize() == 0) {
|
||||
NewModule = ModuleOp::create(mlir::UnknownLoc::get(NewContext.get()));
|
||||
clift::setModuleAttr(NewModule.get());
|
||||
NewModule = clift::makeModule(*NewContext);
|
||||
|
||||
} else {
|
||||
const mlir::ParserConfig Config(NewContext.get());
|
||||
NewModule = mlir::parseSourceString<ModuleOp>(Buffer.getBuffer(), Config);
|
||||
@@ -488,7 +476,7 @@ llvm::Error CliftContainer::deserializeImpl(const llvm::MemoryBuffer &Buffer) {
|
||||
if (not NewModule)
|
||||
return revng::createError("Cannot load MLIR module.");
|
||||
|
||||
if (not clift::hasModuleAttr(NewModule.get()))
|
||||
if (not clift::isCliftModule(NewModule.get()))
|
||||
return revng::createError("MLIR module is not a Clift module.");
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ Clifter::Clifter(const class Model &Model,
|
||||
const LLVMFunctionContainer &Input,
|
||||
CliftFunctionContainer &Output) :
|
||||
Binary(*Model.get().get()), Input(Input), Output(Output) {
|
||||
Output.getContext()->loadDialect<clift::CliftDialect>();
|
||||
}
|
||||
|
||||
void Clifter::runOnFunction(const model::Function &Function) {
|
||||
@@ -76,13 +75,12 @@ void Clifter::runOnFunction(const model::Function &Function) {
|
||||
&LLVMFunction = getUniqueIsolatedFunction(Module, Function.Entry());
|
||||
|
||||
mlir::MLIRContext *Context = Output.getContext();
|
||||
auto ModuleOpObject = mlir::ModuleOp::create(mlir::UnknownLoc::get(Context));
|
||||
clift::setModuleAttr(ModuleOpObject);
|
||||
auto ModuleOpObject = clift::makeModule(*Context);
|
||||
|
||||
auto Importer = clift::Clifter::make(ModuleOpObject, Binary);
|
||||
auto Importer = clift::Clifter::make(ModuleOpObject.get(), Binary);
|
||||
Importer->import(&LLVMFunction);
|
||||
|
||||
Output.assign(Object, ModuleOpObject);
|
||||
Output.assign(Object, std::move(ModuleOpObject));
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#include "revng/Clift/Helpers.h"
|
||||
#include "revng/CliftEmitC/CBackend.h"
|
||||
#include "revng/CliftEmitC/CSemantics.h"
|
||||
#include "revng/CliftEmitC/Configuration.h"
|
||||
#include "revng/CliftImportModel/Verify.h"
|
||||
#include "revng/CliftPipes/CliftContainer.h"
|
||||
#include "revng/CliftPipes/Configuration.h"
|
||||
#include "revng/CliftPipes/EmitC.h"
|
||||
#include "revng/Pipeline/RegisterPipe.h"
|
||||
#include "revng/Pipes/Containers.h"
|
||||
@@ -76,11 +78,13 @@ static pipeline::RegisterPipe<CBackendPipe> X;
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
EmitC::EmitC(const Model &Model,
|
||||
llvm::StringRef Config,
|
||||
llvm::StringRef Configuration,
|
||||
llvm::StringRef DynamicConfig,
|
||||
CliftFunctionContainer &Input,
|
||||
PTMLCFunctionBytesContainer &Output) :
|
||||
Input(Input), Output(Output) {
|
||||
Input(Input),
|
||||
Output(Output),
|
||||
Configuration(parseCEmissionPipeConfiguration(Configuration)) {
|
||||
}
|
||||
|
||||
void EmitC::runOnFunction(const model::Function &Function) {
|
||||
@@ -93,8 +97,35 @@ void EmitC::runOnFunction(const model::Function &Function) {
|
||||
revng_assert(verifyCSemantics(Module).succeeded());
|
||||
FunctionOp MLIRFunction = getUniqueIsolatedFunction(Module, Function.Entry());
|
||||
|
||||
// TODO: once we emit any type definitions, in the decompiled code, we should
|
||||
// carry a `TypeEmitterConfiguration` set from `Options` from here
|
||||
// all the way to wherever the TypeDefinitionEmitter is constructed.
|
||||
TypeEmitterConfiguration TEConfiguration = {
|
||||
.TypeToOmit = {},
|
||||
.EmitMaximumEnumValue = false,
|
||||
.ExplicitPadding = true,
|
||||
};
|
||||
|
||||
switch (Configuration.Mode) {
|
||||
case EmissionMode::Editable:
|
||||
TEConfiguration.EmitMaximumEnumValue = true;
|
||||
TEConfiguration.ExplicitPadding = false;
|
||||
break;
|
||||
|
||||
case EmissionMode::Recompilable:
|
||||
TEConfiguration.EmitMaximumEnumValue = false;
|
||||
TEConfiguration.ExplicitPadding = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
revng_abort("Unsupported emission style.");
|
||||
};
|
||||
|
||||
auto OS = Output.getOStream(Object);
|
||||
ptml::CTokenEmitter Emitter(*OS, ptml::Tagging::Enabled);
|
||||
ptml::CTokenEmitter Emitter(*OS,
|
||||
Configuration.DisableMarkup ?
|
||||
ptml::Tagging::Disabled :
|
||||
ptml::Tagging::Enabled);
|
||||
decompile(MLIRFunction, Emitter);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include "revng/CliftEmitC/Configuration.h"
|
||||
#include "revng/CliftEmitC/Headers.h"
|
||||
#include "revng/CliftPipes/EmitCAsDirectory.h"
|
||||
#include "revng/Support/GzipTarFile.h"
|
||||
#include "revng/Support/ResourceFinder.h"
|
||||
|
||||
void revng::pypeline::piperuns::EmitCAsDirectory::run() {
|
||||
std::unique_ptr<llvm::raw_pwrite_stream> Out = Output.getOStream(ObjectID());
|
||||
revng_assert(Out);
|
||||
|
||||
GzipTarWriter TarWriter{ *Out };
|
||||
|
||||
llvm::StringRef Buffer = InputC.getMemoryBuffer(ObjectID{})->getBuffer();
|
||||
TarWriter.append("decompiled/functions.c",
|
||||
llvm::ArrayRef<char>(Buffer.data(), Buffer.size()));
|
||||
|
||||
Buffer = InputTypesAndGlobals.getMemoryBuffer(ObjectID{})->getBuffer();
|
||||
TarWriter.append("decompiled/types-and-globals.h",
|
||||
llvm::ArrayRef<char>(Buffer.data(), Buffer.size()));
|
||||
|
||||
Buffer = InputHelpers.getMemoryBuffer(ObjectID{})->getBuffer();
|
||||
TarWriter.append("decompiled/helpers.h",
|
||||
llvm::ArrayRef<char>(Buffer.data(), Buffer.size()));
|
||||
|
||||
{
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/CliftPipes/EmitCAsSingleFile.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/PTML/Constants.h"
|
||||
#include "revng/PTML/PTMLEmitter.h"
|
||||
#include "revng/Pipeline/AllRegistries.h"
|
||||
#include "revng/Pipes/Containers.h"
|
||||
#include "revng/Pipes/FileContainer.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
#include "revng/Pipes/StringBufferContainer.h"
|
||||
|
||||
static void printIncludes(ptml::CTokenEmitter &Tokens) {
|
||||
|
||||
Tokens.emitIncludeDirective("types-and-globals.h",
|
||||
"",
|
||||
ptml::CTokenEmitter::IncludeMode::Quote);
|
||||
Tokens.emitIncludeDirective("helpers.h",
|
||||
"",
|
||||
ptml::CTokenEmitter::IncludeMode::Quote);
|
||||
Tokens.emitNewline();
|
||||
}
|
||||
|
||||
namespace revng::pipes {
|
||||
|
||||
inline constexpr char DecompiledMIMEType[] = "text/x.c+ptml";
|
||||
inline constexpr char DecompiledSuffix[] = ".c";
|
||||
inline constexpr char DecompiledName[] = "decompiled-c-code";
|
||||
using DecompiledFileContainer = StringBufferContainer<&kinds::DecompiledToC,
|
||||
DecompiledName,
|
||||
DecompiledMIMEType,
|
||||
DecompiledSuffix>;
|
||||
|
||||
static pipeline::RegisterDefaultConstructibleContainer<DecompiledFileContainer>
|
||||
Reg;
|
||||
|
||||
} // namespace revng::pipes
|
||||
|
||||
class EmitCAsSingleFile {
|
||||
public:
|
||||
static constexpr auto Name = "emit-c-as-single-file";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(Decompiled,
|
||||
0,
|
||||
DecompiledToC,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const revng::pipes::DecompileStringMap &DecompiledFunctions,
|
||||
revng::pipes::DecompiledFileContainer &OutCFile) {
|
||||
{
|
||||
llvm::raw_string_ostream Out = OutCFile.asStream();
|
||||
static constexpr ptml::Tagging Tags = ptml::Tagging::Enabled;
|
||||
|
||||
ptml::CTokenEmitter Tokens(Out, Tags);
|
||||
printIncludes(Tokens);
|
||||
|
||||
ptml::StreamEmitter RawEmitter(Out);
|
||||
for (const auto &[MetaAddress, CFunction] : DecompiledFunctions)
|
||||
RawEmitter.emit(CFunction + "\n");
|
||||
}
|
||||
|
||||
EC.commitUniqueTarget(OutCFile);
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<EmitCAsSingleFile> Y;
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
EmitCAsSingleFile::EmitCAsSingleFile(const class Model &Model,
|
||||
llvm::StringRef Configuration,
|
||||
llvm::StringRef DynamicConfig,
|
||||
const PTMLCFunctionBytesContainer &Input,
|
||||
PTMLCBytesContainer &Output) :
|
||||
Binary(*Model.get().get()),
|
||||
Input(Input),
|
||||
Output(Output),
|
||||
Configuration(parseCEmissionPipeConfiguration(Configuration)) {
|
||||
}
|
||||
|
||||
void EmitCAsSingleFile::run() {
|
||||
std::unique_ptr<llvm::raw_pwrite_stream> Out = Output.getOStream(ObjectID());
|
||||
|
||||
ptml::CTokenEmitter Tokens(*Out,
|
||||
Configuration.DisableMarkup ?
|
||||
ptml::Tagging::Disabled :
|
||||
ptml::Tagging::Enabled);
|
||||
printIncludes(Tokens);
|
||||
|
||||
ptml::StreamEmitter RawEmitter(*Out);
|
||||
for (const auto &Object : Input.objects()) {
|
||||
auto Buffer = Input.getMemoryBuffer(Object);
|
||||
RawEmitter.emit(Buffer->getBuffer().str() + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/Pipeline/Kind.h"
|
||||
#include "revng/Pipeline/RegisterContainerFactory.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
#include "revng/Pipes/Ranks.h"
|
||||
#include "revng/Pipes/StringBufferContainer.h"
|
||||
#include "revng/Pipes/StringMap.h"
|
||||
|
||||
namespace revng::kinds {
|
||||
|
||||
inline pipeline::SingleElementKind
|
||||
TypeAndGlobalHeader("type-and-global-header",
|
||||
Binary,
|
||||
revng::ranks::Binary,
|
||||
fat(revng::ranks::TypeDefinition,
|
||||
revng::ranks::StructField,
|
||||
revng::ranks::UnionField,
|
||||
revng::ranks::EnumEntry,
|
||||
revng::ranks::DynamicFunction,
|
||||
revng::ranks::Segment,
|
||||
revng::ranks::ArtificialStruct,
|
||||
revng::ranks::OpaqueType),
|
||||
{ &Decompiled });
|
||||
|
||||
inline pipeline::SingleElementKind
|
||||
HelperHeader("helper-header", Binary, revng::ranks::Binary, {}, {});
|
||||
|
||||
inline TypeKind SingleTypeDefinition("single-type-definition",
|
||||
TypeAndGlobalHeader,
|
||||
ranks::TypeDefinition,
|
||||
{},
|
||||
{});
|
||||
|
||||
} // namespace revng::kinds
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline constexpr char TypeAndGlobalHeaderName[] = "type-and-global-header";
|
||||
inline constexpr char HelperHeaderName[] = "helper-header";
|
||||
|
||||
inline constexpr char HeaderMIMEType[] = "text/x.h+ptml";
|
||||
inline constexpr char HeaderSuffix[] = ".h";
|
||||
|
||||
inline constexpr char TypeDefinitionName[] = "single-type-definition";
|
||||
inline constexpr char TypeDefinitionMimeType[] = "text/x.c+tar+gz";
|
||||
inline constexpr char TypeDefinitionSuffix[] = ".c";
|
||||
|
||||
template<auto... Values>
|
||||
using SBF = revng::pipes::StringBufferContainer<Values...>;
|
||||
|
||||
// The real class is used here because aliasing an alias is not allowed.
|
||||
namespace RPD = revng::pipes::detail;
|
||||
template<auto... Values>
|
||||
using TSM = RPD::GenericStringMap<&revng::ranks::TypeDefinition, Values...>;
|
||||
|
||||
template<typename T>
|
||||
using RegisterDCC = pipeline::RegisterDefaultConstructibleContainer<T>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using TypeAndGlobalHeaderContainer = detail::SBF<
|
||||
&revng::kinds::TypeAndGlobalHeader,
|
||||
detail::TypeAndGlobalHeaderName,
|
||||
detail::HeaderMIMEType,
|
||||
detail::HeaderSuffix>;
|
||||
inline detail::RegisterDCC<TypeAndGlobalHeaderContainer> RegisteredMHC;
|
||||
|
||||
using HelperHeaderContainer = detail::SBF<&revng::kinds::HelperHeader,
|
||||
detail::HelperHeaderName,
|
||||
detail::HeaderMIMEType,
|
||||
detail::HeaderSuffix>;
|
||||
inline detail::RegisterDCC<HelperHeaderContainer> RegisteredHHC;
|
||||
|
||||
using TypeDefinitionContainer = detail::TSM<&revng::kinds::SingleTypeDefinition,
|
||||
detail::TypeDefinitionName,
|
||||
detail::TypeDefinitionMimeType,
|
||||
detail::TypeDefinitionSuffix>;
|
||||
inline detail::RegisterDCC<TypeDefinitionContainer> RegisteredTDC;
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "mlir/IR/BuiltinOps.h"
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
|
||||
#include "revng/ABI/Definition.h"
|
||||
#include "revng/Clift/CliftTypeInterfaces.h"
|
||||
#include "revng/CliftEmitC/CEmitter.h"
|
||||
#include "revng/CliftEmitC/CSemantics.h"
|
||||
#include "revng/CliftEmitC/Headers.h"
|
||||
#include "revng/CliftImportModel/ImportModel.h"
|
||||
#include "revng/CliftPipes/CliftContainer.h"
|
||||
#include "revng/CliftPipes/Headers.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/PTML/PTMLEmitter.h"
|
||||
#include "revng/Pipeline/RegisterPipe.h"
|
||||
|
||||
#include "HeaderContainers.h"
|
||||
|
||||
//
|
||||
// Shared logic
|
||||
//
|
||||
|
||||
using EmissionMode = revng::pypeline::piperuns::EmissionMode;
|
||||
using PipeConfiguration = revng::pypeline::piperuns::CEmissionPipeConfiguration;
|
||||
|
||||
static void emitTypeAndGlobalHeaderImpl(llvm::raw_ostream &Out,
|
||||
mlir::ModuleOp Module,
|
||||
PipeConfiguration *PipeCfg = nullptr) {
|
||||
TypeEmitterConfiguration Configuration = {
|
||||
.TypeToOmit = {},
|
||||
.EmitMaximumEnumValue = false,
|
||||
.ExplicitPadding = true,
|
||||
};
|
||||
|
||||
if (PipeCfg) {
|
||||
switch (PipeCfg->Mode) {
|
||||
case EmissionMode::Editable:
|
||||
Configuration.EmitMaximumEnumValue = true;
|
||||
Configuration.ExplicitPadding = false;
|
||||
break;
|
||||
|
||||
case EmissionMode::Recompilable:
|
||||
Configuration.EmitMaximumEnumValue = false;
|
||||
Configuration.ExplicitPadding = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
revng_abort("Unsupported emission style.");
|
||||
};
|
||||
}
|
||||
|
||||
ptml::CTokenEmitter Tokens(Out,
|
||||
PipeCfg && PipeCfg->DisableMarkup ?
|
||||
ptml::Tagging::Disabled :
|
||||
ptml::Tagging::Enabled);
|
||||
emitTypeAndGlobalHeader(Tokens, Module, Configuration);
|
||||
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
static void
|
||||
emitHelperHeaderImpl(llvm::raw_ostream &Out,
|
||||
std::vector<mlir::ModuleOp> Modules,
|
||||
const model::Binary &Binary,
|
||||
ptml::Tagging Tagging = ptml::Tagging::Enabled) {
|
||||
ptml::CTokenEmitter Tokens(Out, Tagging);
|
||||
emitHelperHeader(Tokens, Modules, Binary);
|
||||
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
static void emitTypeDefinitionImpl(llvm::raw_ostream &Out,
|
||||
mlir::ModuleOp Module,
|
||||
const CDataModel &DataModel,
|
||||
const model::TypeDefinition &Type,
|
||||
PipeConfiguration *PipeCfg = nullptr) {
|
||||
TypeEmitterConfiguration Configuration = {
|
||||
.TypeToOmit = {},
|
||||
.EmitMaximumEnumValue = true,
|
||||
.ExplicitPadding = false,
|
||||
};
|
||||
|
||||
if (PipeCfg) {
|
||||
switch (PipeCfg->Mode) {
|
||||
case EmissionMode::Editable:
|
||||
Configuration.EmitMaximumEnumValue = true;
|
||||
Configuration.ExplicitPadding = false;
|
||||
break;
|
||||
|
||||
case EmissionMode::Recompilable:
|
||||
Configuration.EmitMaximumEnumValue = false;
|
||||
Configuration.ExplicitPadding = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
revng_abort("Unsupported emission style.");
|
||||
};
|
||||
}
|
||||
|
||||
ptml::CTokenEmitter Tokens(Out,
|
||||
not PipeCfg || PipeCfg->DisableMarkup ?
|
||||
ptml::Tagging::Disabled :
|
||||
ptml::Tagging::Enabled);
|
||||
|
||||
// TODO: Extend `importType` to be able to signal whether a type already
|
||||
// exists or if it was reimported.
|
||||
auto CliftType = clift::importType(Module.getContext(), Type);
|
||||
revng_check(CliftType != nullptr);
|
||||
|
||||
emitSingleTypeDefinition(Tokens, DataModel, CliftType, Configuration);
|
||||
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
//
|
||||
// Old style pipes
|
||||
//
|
||||
|
||||
namespace {
|
||||
|
||||
class TypeAndGlobalHeaderPipe {
|
||||
public:
|
||||
static constexpr auto Name = "emit-type-and-global-header";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(CliftModule,
|
||||
0,
|
||||
TypeAndGlobalHeader,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const revng::pipes::CliftContainer &CliftContainer,
|
||||
TypeAndGlobalHeaderContainer &HeaderFile) {
|
||||
llvm::raw_string_ostream Stream = HeaderFile.asStream();
|
||||
emitTypeAndGlobalHeaderImpl(Stream, CliftContainer.getModule());
|
||||
EC.commitUniqueTarget(HeaderFile);
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<TypeAndGlobalHeaderPipe> TypeAndGlobalHeader;
|
||||
|
||||
class HelperHeaderPipe {
|
||||
public:
|
||||
static constexpr auto Name = "emit-helper-header";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(CliftFunction,
|
||||
0,
|
||||
HelperHeader,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const revng::pipes::CliftFunctionContainer &CliftContainer,
|
||||
HelperHeaderContainer &HeaderFile) {
|
||||
llvm::raw_string_ostream Stream = HeaderFile.asStream();
|
||||
emitHelperHeaderImpl(Stream,
|
||||
{ CliftContainer.getModule() },
|
||||
*revng::getModelFromContext(EC));
|
||||
EC.commitUniqueTarget(HeaderFile);
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<HelperHeaderPipe> HelperHeader;
|
||||
|
||||
class SingleTypeDefinitionPipe {
|
||||
public:
|
||||
static constexpr auto Name = "emit-single-type-definition";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(CliftModule,
|
||||
0,
|
||||
SingleTypeDefinition,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const revng::pipes::CliftContainer &CliftContainer,
|
||||
TypeDefinitionContainer &ModelTypesContainer) {
|
||||
mlir::ModuleOp Module = CliftContainer.getModule();
|
||||
auto DataModel = abi::getDataModel(*revng::getModelFromContext(EC));
|
||||
|
||||
for (const model::TypeDefinition &Type :
|
||||
revng::getTypeDefinitionsAndCommit(EC, ModelTypesContainer.name())) {
|
||||
std::string &Result = ModelTypesContainer[Type.key()];
|
||||
llvm::raw_string_ostream Out(Result);
|
||||
emitTypeDefinitionImpl(Out, Module, DataModel, Type);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<SingleTypeDefinitionPipe> TypeDefinition;
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// New style pipes
|
||||
//
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
void EmitTypeAndGlobalHeader::run() {
|
||||
std::unique_ptr<llvm::raw_ostream> Out = Output.getOStream(ObjectID());
|
||||
emitTypeAndGlobalHeaderImpl(*Out, Input.getModule(), &Configuration);
|
||||
}
|
||||
|
||||
void EmitHelperHeader::run() {
|
||||
std::unique_ptr<llvm::raw_ostream> Out = Output.getOStream(ObjectID());
|
||||
|
||||
std::vector<mlir::ModuleOp> FunctionModules;
|
||||
for (const auto &Object : Input.objects())
|
||||
FunctionModules.emplace_back(Input.getModule(Object));
|
||||
|
||||
emitHelperHeaderImpl(*Out,
|
||||
FunctionModules,
|
||||
Binary,
|
||||
Configuration.DisableMarkup ? ptml::Tagging::Disabled :
|
||||
ptml::Tagging::Enabled);
|
||||
}
|
||||
|
||||
using ESTD = EmitSingleTypeDefinition;
|
||||
void ESTD::runOnTypeDefinition(const model::UpcastableTypeDefinition &Type) {
|
||||
revng_assert(Type);
|
||||
auto Stream = Output.getOStream(ObjectID(Type->key()));
|
||||
|
||||
auto DataModel = abi::getDataModel(Binary);
|
||||
emitTypeDefinitionImpl(*Stream,
|
||||
Input.getModule(),
|
||||
DataModel,
|
||||
*Type,
|
||||
&Configuration);
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
@@ -8,8 +8,7 @@
|
||||
#include "revng/Pipeline/RegisterPipe.h"
|
||||
|
||||
static void importDataModel(mlir::ModuleOp Module, const model::Binary &Model) {
|
||||
const auto &Definition = abi::Definition::get(Model.targetABI());
|
||||
clift::setDataModel(Module, Definition.getDataModel());
|
||||
clift::setDataModel(Module, abi::getDataModel(Model));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -6,26 +6,29 @@
|
||||
#include "revng/Clift/Helpers.h"
|
||||
#include "revng/CliftImportModel/ImportModel.h"
|
||||
#include "revng/CliftPipes/CliftContainer.h"
|
||||
#include "revng/CliftPipes/ImportTypes.h"
|
||||
#include "revng/Pipeline/Location.h"
|
||||
#include "revng/Pipeline/RegisterPipe.h"
|
||||
#include "revng/Pipes/FileContainer.h"
|
||||
#include "revng/Pipes/Kinds.h"
|
||||
|
||||
//
|
||||
// Old style pipes
|
||||
//
|
||||
|
||||
class ImportTypesPipe {
|
||||
public:
|
||||
static constexpr auto Name = "import-types";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
return { ContractGroup({ Contract(CliftModule,
|
||||
0,
|
||||
CliftModule,
|
||||
0,
|
||||
InputPreservation::Preserve) }) };
|
||||
return { pipeline::ContractGroup(revng::kinds::Binary,
|
||||
0,
|
||||
revng::kinds::CliftModule,
|
||||
1) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
const revng::pipes::BinaryFileContainer &,
|
||||
revng::pipes::CliftContainer &CliftContainer) {
|
||||
mlir::MLIRContext *Context = CliftContainer.getContext();
|
||||
Context->loadDialect<clift::CliftDialect>();
|
||||
@@ -38,3 +41,67 @@ public:
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<ImportTypesPipe> Y;
|
||||
|
||||
class ImportFunctionDeclarationsPipe {
|
||||
public:
|
||||
static constexpr auto Name = "import-function-declarations";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
return { pipeline::ContractGroup(revng::kinds::CliftModule, 0) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
revng::pipes::CliftContainer &CliftContainer) {
|
||||
mlir::MLIRContext *Context = CliftContainer.getContext();
|
||||
Context->loadDialect<clift::CliftDialect>();
|
||||
|
||||
clift::importAllModelFunctionDeclarations(*revng::getModelFromContext(EC),
|
||||
CliftContainer.getModule());
|
||||
|
||||
EC.commitAllFor(CliftContainer);
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<ImportFunctionDeclarationsPipe> IFD;
|
||||
|
||||
class ImportSegmentDeclarationsPipe {
|
||||
public:
|
||||
static constexpr auto Name = "import-segment-declarations";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
return { pipeline::ContractGroup(revng::kinds::CliftModule, 0) };
|
||||
}
|
||||
|
||||
void run(pipeline::ExecutionContext &EC,
|
||||
revng::pipes::CliftContainer &CliftContainer) {
|
||||
mlir::MLIRContext *Context = CliftContainer.getContext();
|
||||
Context->loadDialect<clift::CliftDialect>();
|
||||
|
||||
clift::importAllModelSegmentDeclarations(*revng::getModelFromContext(EC),
|
||||
CliftContainer.getModule());
|
||||
|
||||
EC.commitAllFor(CliftContainer);
|
||||
}
|
||||
};
|
||||
|
||||
static pipeline::RegisterPipe<ImportSegmentDeclarationsPipe> ISD;
|
||||
|
||||
//
|
||||
// New style pipes
|
||||
//
|
||||
|
||||
namespace revng::pypeline::piperuns {
|
||||
|
||||
void ImportTypes::run() {
|
||||
clift::importAllModelTypes(Binary, Output.getModule());
|
||||
}
|
||||
|
||||
void ImportFunctionDeclarations::run() {
|
||||
clift::importAllModelFunctionDeclarations(Binary, Module.getModule());
|
||||
}
|
||||
|
||||
void ImportSegmentDeclarations::run() {
|
||||
clift::importAllModelSegmentDeclarations(Binary, Module.getModule());
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::piperuns
|
||||
|
||||
+14
-53
@@ -24,6 +24,7 @@
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/Model/FunctionTags.h"
|
||||
#include "revng/Model/IRHelpers.h"
|
||||
#include "revng/Model/NameBuilder.h"
|
||||
#include "revng/Pipeline/Location.h"
|
||||
#include "revng/Pipes/Ranks.h"
|
||||
#include "revng/RestructureCFG/ScopeGraphGraphTraits.h"
|
||||
@@ -126,7 +127,7 @@ public:
|
||||
Model(Model),
|
||||
Builder(Context),
|
||||
DataLayout(nullptr) {
|
||||
revng_assert(clift::hasModuleAttr(Module));
|
||||
revng_assert(clift::isCliftModule(Module));
|
||||
Builder.setInsertionPointToEnd(Module.getBody());
|
||||
}
|
||||
|
||||
@@ -230,25 +231,9 @@ private:
|
||||
|
||||
//===------------------------- Model type import ------------------------===//
|
||||
|
||||
mlir::Type importType(const model::Type &Type) {
|
||||
auto EmitError = [&]() -> mlir::InFlightDiagnostic {
|
||||
return Context->getDiagEngine().emit(mlir::UnknownLoc::get(Context),
|
||||
mlir::DiagnosticSeverity::Error);
|
||||
};
|
||||
return clift::importType(EmitError, Context, Type, Model);
|
||||
}
|
||||
|
||||
mlir::Type importType(const model::TypeDefinition &Type) {
|
||||
auto EmitError = [&]() -> mlir::InFlightDiagnostic {
|
||||
return Context->getDiagEngine().emit(mlir::UnknownLoc::get(Context),
|
||||
mlir::DiagnosticSeverity::Error);
|
||||
};
|
||||
return clift::importType(EmitError, Context, Type, Model);
|
||||
}
|
||||
|
||||
template<typename TypeT, typename ModelTypeT>
|
||||
TypeT importType(const ModelTypeT &Type) {
|
||||
return mlir::cast<TypeT>(importType(Type));
|
||||
return mlir::cast<TypeT>(clift::importType(Context, Type));
|
||||
}
|
||||
|
||||
//===------------------------- LLVM type import -------------------------===//
|
||||
@@ -263,37 +248,6 @@ private:
|
||||
return getVoidPointerType();
|
||||
}
|
||||
|
||||
std::string getOpaqueTypeHandle(uint64_t ByteSize) {
|
||||
return pipeline::locationString(revng::ranks::OpaqueType, ByteSize);
|
||||
}
|
||||
|
||||
clift::StructType importOpaqueStruct(uint64_t NumBytes) {
|
||||
std::string Handle = getOpaqueTypeHandle(NumBytes);
|
||||
|
||||
auto NameAttr = makeNameAttr<StructAttr>(Context, Handle);
|
||||
auto CommentAttr = makeCommentAttr<StructAttr>(Context, Handle);
|
||||
auto Attrs = llvm::ArrayRef<clift::CAttributeAttr>{};
|
||||
auto Def = clift::StructAttr::get(Context,
|
||||
Handle,
|
||||
NameAttr,
|
||||
CommentAttr,
|
||||
NumBytes,
|
||||
{},
|
||||
Attrs);
|
||||
|
||||
return clift::StructType::get(Def);
|
||||
}
|
||||
|
||||
clift::StructType importOpaqueStruct(const llvm::ArrayType *Array) {
|
||||
const auto *ElementType = Array->getElementType();
|
||||
revng_assert(ElementType->isIntegerTy());
|
||||
revng_assert(ElementType->getIntegerBitWidth() == 8);
|
||||
uint64_t NumBytes = Array->getNumElements()
|
||||
* (ElementType->getIntegerBitWidth() / 8);
|
||||
|
||||
return importOpaqueStruct(NumBytes);
|
||||
}
|
||||
|
||||
mlir::Type importLLVMType(const llvm::Type *Type) {
|
||||
if (Type->isVoidTy())
|
||||
return getVoidType();
|
||||
@@ -304,8 +258,15 @@ private:
|
||||
if (auto *T = llvm::dyn_cast<llvm::PointerType>(Type))
|
||||
return importLLVMPointerType(T);
|
||||
|
||||
if (auto *T = llvm::dyn_cast<llvm::ArrayType>(Type))
|
||||
return importOpaqueStruct(T);
|
||||
if (auto *T = llvm::dyn_cast<llvm::ArrayType>(Type)) {
|
||||
const auto *ElementType = T->getElementType();
|
||||
revng_assert(ElementType->isIntegerTy());
|
||||
revng_assert(ElementType->getIntegerBitWidth() == 8);
|
||||
uint64_t NumBytes = T->getNumElements()
|
||||
* (ElementType->getIntegerBitWidth() / 8);
|
||||
|
||||
return clift::makeOpaqueStruct(*Context, NumBytes);
|
||||
}
|
||||
|
||||
if (auto *S = llvm::dyn_cast<llvm::StructType>(Type)) {
|
||||
const auto *StructLayout = DataLayout->getStructLayout(S);
|
||||
@@ -334,7 +295,7 @@ private:
|
||||
uint64_t ByteSize = PreviousOffsetInBits / 8;
|
||||
revng_assert(llvm::alignTo(ByteSize, Alignment) == StructByteSize);
|
||||
revng_assert(StructByteSize);
|
||||
return importOpaqueStruct(StructByteSize);
|
||||
return clift::makeOpaqueStruct(*Context, StructByteSize);
|
||||
}
|
||||
|
||||
revng_abort("Unsupported LLVM type");
|
||||
@@ -1684,7 +1645,7 @@ private:
|
||||
if (hasStackFrameMetadata(A)) {
|
||||
auto StackType = ModelFunction.stackFrameType();
|
||||
revng_assert(StackType);
|
||||
Type = cast<clift::StructType>(C.importType(*StackType));
|
||||
Type = C.importType<clift::StructType>(*StackType);
|
||||
Handle = pipeline::locationString(revng::ranks::StackFrameVariable,
|
||||
ModelFunction.Entry());
|
||||
} else {
|
||||
|
||||
@@ -781,6 +781,19 @@ private:
|
||||
CalledFunctions.insert(CalledFunction);
|
||||
}
|
||||
|
||||
// Preserve all the CSVs used by helpers as well, this saves us from
|
||||
// having "surprises" later, which can be a problem since sometimes we
|
||||
// ignore non-existing CSVs.
|
||||
if (auto *Call = getCallToHelper(&Instruction)) {
|
||||
auto MaybeUsedCSVs = tryGetCSVUsedByHelperCall(Call);
|
||||
if (MaybeUsedCSVs) {
|
||||
for (auto *CSV : MaybeUsedCSVs->Read)
|
||||
UsedGVs.insert(CSV);
|
||||
for (auto *CSV : MaybeUsedCSVs->Written)
|
||||
UsedGVs.insert(CSV);
|
||||
}
|
||||
}
|
||||
|
||||
std::queue<llvm::User *> Users;
|
||||
Users.push(&Instruction);
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
using namespace llvm;
|
||||
using namespace mfp;
|
||||
|
||||
static Logger Log("promote-csvs");
|
||||
|
||||
// TODO: switch from CallInst to CallBase
|
||||
|
||||
struct CSVsUsageMap {
|
||||
@@ -462,6 +464,8 @@ struct UsedRegistersMFI : public SetUnionLattice<FunctionNodeData::UsedCSVSet> {
|
||||
CSVsUsageMap PromoteCSVs::getUsedCSVs(ArrayRef<CallInst *> CallsRange) {
|
||||
CSVsUsageMap Result;
|
||||
|
||||
revng_log(Log, "getUsedCSVs");
|
||||
|
||||
// Note: this graph goes from callee to callers
|
||||
GenericCallGraph CallGraph;
|
||||
|
||||
@@ -484,6 +488,20 @@ CSVsUsageMap PromoteCSVs::getUsedCSVs(ArrayRef<CallInst *> CallsRange) {
|
||||
and Callee->getName() != AbortFunctionName) {
|
||||
CSVsUsage &Usage = Result.Calls[Call];
|
||||
auto UsedCSVs = getCSVUsedByHelperCall(Call);
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
Log << "Call " << getName(Call) << " to " << Callee->getName() << ":\n";
|
||||
Log << " Reads:\n";
|
||||
for (auto *CSV : UsedCSVs.Read)
|
||||
Log << " " << CSV->getName() << "\n";
|
||||
|
||||
Log << " Written:\n";
|
||||
for (auto *CSV : UsedCSVs.Written)
|
||||
Log << " " << CSV->getName() << "\n";
|
||||
Log << DoLog;
|
||||
}
|
||||
|
||||
revng_log(Log, "Call " << getName(Call));
|
||||
Usage.Read = UsedCSVs.Read;
|
||||
Usage.Written = UsedCSVs.Written;
|
||||
} else {
|
||||
@@ -567,17 +585,25 @@ ArrayRef<T> oneElement(T &Element) {
|
||||
}
|
||||
|
||||
void PromoteCSVs::wrapCallsToHelpers(Function *F) {
|
||||
revng_log(Log, "wrapCallsToHelpers: " << F->getName().str());
|
||||
std::vector<CallInst *> ToWrap;
|
||||
for (BasicBlock &BB : *F) {
|
||||
for (Instruction &I : BB) {
|
||||
if (auto *Call = dyn_cast<CallInst>(&I)) {
|
||||
Function *Callee = getCallee(Call);
|
||||
|
||||
// Ignore calls to isolated functions
|
||||
if (Callee == nullptr or not needsWrapper(Callee))
|
||||
continue;
|
||||
{
|
||||
LoggerIndent Indent(Log);
|
||||
for (BasicBlock &BB : *F) {
|
||||
for (Instruction &I : BB) {
|
||||
if (auto *Call = dyn_cast<CallInst>(&I)) {
|
||||
Function *Callee = getCallee(Call);
|
||||
|
||||
ToWrap.emplace_back(Call);
|
||||
// Ignore calls to isolated functions
|
||||
if (Callee == nullptr or not needsWrapper(Callee))
|
||||
continue;
|
||||
|
||||
revng_log(Log,
|
||||
"Call to " << Callee->getName().str()
|
||||
<< " needs a wrapper");
|
||||
ToWrap.emplace_back(Call);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,15 @@ namespace revng::pipes {
|
||||
|
||||
inline constexpr char HelpersHeaderFactoryMIMEType[] = "text/x.c+ptml";
|
||||
inline constexpr char HelpersHeaderFactorySuffix[] = ".h";
|
||||
inline constexpr char HelpersHeaderFactoryName[] = "helpers-header";
|
||||
using HelpersHeaderFileContainer = FileContainer<&kinds::HelpersHeader,
|
||||
inline constexpr char HelpersHeaderFactoryName[] = "legacy-helpers-header";
|
||||
using HelpersHeaderFileContainer = FileContainer<&kinds::LegacyHelpersHeader,
|
||||
HelpersHeaderFactoryName,
|
||||
HelpersHeaderFactoryMIMEType,
|
||||
HelpersHeaderFactorySuffix>;
|
||||
|
||||
class HelpersToHeader {
|
||||
public:
|
||||
static constexpr auto Name = "helpers-to-header";
|
||||
static constexpr auto Name = "legacy-helpers-to-header";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
return { ContractGroup{ Contract(StackAccessesSegregated,
|
||||
0,
|
||||
HelpersHeader,
|
||||
LegacyHelpersHeader,
|
||||
1,
|
||||
InputPreservation::Preserve) } };
|
||||
}
|
||||
|
||||
@@ -16,21 +16,21 @@ namespace revng::pipes {
|
||||
|
||||
inline constexpr char ModelHeaderFileContainerMIMEType[] = "text/x.c+ptml";
|
||||
inline constexpr char ModelHeaderFileContainerSuffix[] = ".h";
|
||||
inline constexpr char ModelHeaderFileContainerName[] = "model-header";
|
||||
using ModelHeaderFileContainer = FileContainer<&kinds::ModelHeader,
|
||||
inline constexpr char ModelHeaderFileContainerName[] = "legacy-model-header";
|
||||
using ModelHeaderFileContainer = FileContainer<&kinds::LegacyModelHeader,
|
||||
ModelHeaderFileContainerName,
|
||||
ModelHeaderFileContainerMIMEType,
|
||||
ModelHeaderFileContainerSuffix>;
|
||||
|
||||
class ModelToHeader {
|
||||
public:
|
||||
static constexpr auto Name = "model-to-header";
|
||||
static constexpr auto Name = "legacy-model-to-header";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
using namespace revng::kinds;
|
||||
|
||||
Contract C1(Binary, 0, ModelHeader, 1, InputPreservation::Preserve);
|
||||
Contract C1(Binary, 0, LegacyModelHeader, 1, InputPreservation::Preserve);
|
||||
return { ContractGroup({ C1 }) };
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,17 @@
|
||||
namespace revng::pipes {
|
||||
|
||||
inline constexpr char ModelTypeDefinitionMime[] = "text/x.c+tar+gz";
|
||||
inline constexpr char ModelTypeDefinitionName[] = "model-type-definitions";
|
||||
inline constexpr char ModelTypeDefinitionName[] = "legacy-model-type-"
|
||||
"definitions";
|
||||
inline constexpr char ModelTypeDefinitionExtension[] = ".h";
|
||||
using TypeDefinitionStringMap = TypeStringMap<&kinds::ModelTypeDefinition,
|
||||
using TypeDefinitionStringMap = TypeStringMap<&kinds::LegacyModelTypeDefinition,
|
||||
ModelTypeDefinitionName,
|
||||
ModelTypeDefinitionMime,
|
||||
ModelTypeDefinitionExtension>;
|
||||
|
||||
class GenerateModelTypeDefinition {
|
||||
public:
|
||||
static constexpr auto Name = "generate-model-type-definition";
|
||||
static constexpr auto Name = "legacy-generate-model-type-definition";
|
||||
|
||||
std::array<pipeline::ContractGroup, 1> getContract() const {
|
||||
using namespace pipeline;
|
||||
@@ -31,7 +32,7 @@ public:
|
||||
|
||||
return { ContractGroup({ Contract(kinds::Binary,
|
||||
0,
|
||||
ModelTypeDefinition,
|
||||
LegacyModelTypeDefinition,
|
||||
1,
|
||||
InputPreservation::Preserve) }) };
|
||||
}
|
||||
|
||||
@@ -29,11 +29,14 @@ public:
|
||||
}
|
||||
|
||||
// The global variables are not needed in this module. Drop their debug info
|
||||
// and their initializer. GlobalDCE will be run later to remove them. This
|
||||
// is except a couple of special ones that are always needed.
|
||||
// and their initializer. GlobalDCE will be run later to remove them.
|
||||
// Note that we preserve CSVs and some other special variables. In
|
||||
// particular, we need CSVs to avoid having new CSVs popping up
|
||||
// post-inlining, which is problematic since sometimes we handle
|
||||
// non-existing CSVs in a special way.
|
||||
for (GlobalVariable &GV : M.globals()) {
|
||||
GV.eraseMetadata(LLVMContext::MD_dbg);
|
||||
if (not isSpecialGV(GV))
|
||||
if (not isSpecialGV(GV) and not FunctionTags::CSV.isTagOf(&GV))
|
||||
GV.setInitializer(nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,20 @@ revng_add_analyses_library_internal(revngImportFromCAnalysis HeaderToModel.cpp
|
||||
|
||||
target_link_libraries(
|
||||
revngImportFromCAnalysis
|
||||
revngTypeNames
|
||||
revngABI
|
||||
revngBasicAnalyses
|
||||
revngCliftEmitC
|
||||
revngCliftImportModel
|
||||
revngModel
|
||||
revngSupport
|
||||
revngPTML
|
||||
revngPipeline
|
||||
revngPipes
|
||||
revngPTML
|
||||
revngBasicAnalyses
|
||||
revngModelToHeader
|
||||
clangBasic
|
||||
revngSupport
|
||||
revngTypeNames
|
||||
clangAST
|
||||
clangBasic
|
||||
clangDriver
|
||||
clangSerialization
|
||||
clangFrontend
|
||||
clangSerialization
|
||||
clangTooling
|
||||
${LLVM_LIBRARIES})
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "clang/Frontend/TextDiagnostic.h"
|
||||
|
||||
#include "revng/ABI/ModelHelpers.h"
|
||||
#include "revng/Model/FunctionAttribute.h"
|
||||
#include "revng/Model/Processing.h"
|
||||
#include "revng/PTML/CAttributes.h"
|
||||
#include "revng/PTML/CBuilder.h"
|
||||
@@ -552,6 +553,9 @@ bool DeclVisitor::VisitFunctionDecl(const clang::FunctionDecl *FD) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
NewArgument.Name() = FD->getParamDecl(I)->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original argument.
|
||||
|
||||
NewArgument.Type() = std::move(ParamType);
|
||||
++Index;
|
||||
}
|
||||
@@ -698,6 +702,9 @@ bool DeclVisitor::VisitFunctionDecl(const clang::FunctionDecl *FD) {
|
||||
ParamReg.Type() = std::move(ParamType);
|
||||
if (not ParamDecl->getName().empty())
|
||||
ParamReg.Name() = ParamDecl->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original register.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,9 +712,20 @@ bool DeclVisitor::VisitFunctionDecl(const clang::FunctionDecl *FD) {
|
||||
// Update the name in case it changed.
|
||||
auto &ModelFunction = Model->Functions()[FunctionEntry];
|
||||
|
||||
if (FD->hasAttr<clang::NoReturnAttr>()
|
||||
|| FD->hasAttr<clang::C11NoReturnAttr>()) {
|
||||
ModelFunction.Attributes().emplace(model::FunctionAttribute::NoReturn);
|
||||
}
|
||||
|
||||
if (FD->hasAttr<clang::AlwaysInlineAttr>())
|
||||
ModelFunction.Attributes().emplace(model::FunctionAttribute::AlwaysInline);
|
||||
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
ModelFunction.Name() = FD->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original function.
|
||||
|
||||
// TODO: remember/clone StackFrameType as well.
|
||||
|
||||
auto &&[_, Prototype] = Model->recordNewType(std::move(NewType));
|
||||
@@ -767,6 +785,9 @@ bool DeclVisitor::VisitTypedefDecl(const TypedefDecl *D) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
TheTypeTypeDef->Name() = D->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original type.
|
||||
|
||||
if (AnalysisOption == ImportFromCOption::EditType) {
|
||||
revng_assert(*Type == NewTypedef->key());
|
||||
Model->TypeDefinitions().erase(*Type);
|
||||
@@ -874,6 +895,9 @@ bool DeclVisitor::handleStructType(const clang::RecordDecl *RD) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
NewType->Name() = RD->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original type.
|
||||
|
||||
auto *Struct = cast<model::StructDefinition>(NewType.get());
|
||||
uint64_t CurrentOffset = 0;
|
||||
|
||||
@@ -982,6 +1006,9 @@ bool DeclVisitor::handleStructType(const clang::RecordDecl *RD) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
FieldModelType.Name() = Field->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original field.
|
||||
|
||||
FieldModelType.Type() = std::move(ModelField);
|
||||
} else {
|
||||
// Do not create fields for padding
|
||||
@@ -1047,6 +1074,9 @@ bool DeclVisitor::handleUnionType(const clang::RecordDecl *RD) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
NewType->Name() = RD->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original type.
|
||||
|
||||
auto Union = cast<model::UnionDefinition>(NewType.get());
|
||||
|
||||
uint64_t CurrentIndex = 0;
|
||||
@@ -1074,6 +1104,9 @@ bool DeclVisitor::handleUnionType(const clang::RecordDecl *RD) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
FieldModelType.Name() = Field->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original field.
|
||||
|
||||
FieldModelType.Type() = std::move(TheFieldType);
|
||||
|
||||
++CurrentIndex;
|
||||
@@ -1181,10 +1214,16 @@ bool DeclVisitor::VisitEnumDecl(const EnumDecl *D) {
|
||||
// TODO: we shouldn't write generated names into the model.
|
||||
NewType->Name() = Definition->getName();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original type.
|
||||
|
||||
for (const auto *Enum : Definition->enumerators()) {
|
||||
auto Value = Enum->getInitVal().getExtValue();
|
||||
auto NewIterator = NewType->Entries().insert(Value).first;
|
||||
NewIterator->Name() = Enum->getName().str();
|
||||
|
||||
// TODO: This discard whatever comments might have been attached to
|
||||
// the original entry.
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -20,10 +20,18 @@
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
#include "revng/HeadersGeneration/PTMLHeaderBuilder.h"
|
||||
#include "revng/ABI/Definition.h"
|
||||
#include "revng/Clift/Clift.h"
|
||||
#include "revng/CliftEmitC/CEmitter.h"
|
||||
#include "revng/CliftEmitC/Configuration.h"
|
||||
#include "revng/CliftEmitC/Headers.h"
|
||||
#include "revng/CliftEmitC/TypeDefinitionEmitter.h"
|
||||
#include "revng/CliftImportModel/ImportModel.h"
|
||||
#include "revng/ImportFromC/ImportFromCAnalysis.h"
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/Model/EnumDefinition.h"
|
||||
#include "revng/Model/VerifyHelper.h"
|
||||
#include "revng/PTML/CTokenEmitter.h"
|
||||
#include "revng/Pipeline/Context.h"
|
||||
#include "revng/Pipeline/Kind.h"
|
||||
#include "revng/Pipeline/Option.h"
|
||||
@@ -36,7 +44,6 @@
|
||||
|
||||
#include "HeaderToModel.h"
|
||||
#include "ImportFromCAnalysis.h"
|
||||
#include "ImportFromCHelpers.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
@@ -72,7 +79,28 @@ static std::optional<std::string> findHeaderFile(const std::string &File) {
|
||||
return (*MaybeHeaderPath).substr(0, Index);
|
||||
}
|
||||
|
||||
static Logger Log("header-to-model-errors");
|
||||
static bool isSeparateDeclarationAllowed(const model::TypeDefinition &T) {
|
||||
return llvm::isa<model::StructDefinition>(&T)
|
||||
or llvm::isa<model::UnionDefinition>(&T)
|
||||
or llvm::isa<model::EnumDefinition>(&T);
|
||||
}
|
||||
|
||||
static std::pair<std::unique_ptr<mlir::MLIRContext>,
|
||||
mlir::OwningOpRef<mlir::ModuleOp>>
|
||||
makeHeaderModule(const model::Binary &Model) {
|
||||
std::unique_ptr<mlir::MLIRContext> Context = clift::makeContext();
|
||||
mlir::OwningOpRef<mlir::ModuleOp> Module = clift::makeModule(*Context);
|
||||
|
||||
clift::importAllModelTypes(Model, Module.get());
|
||||
clift::importAllModelFunctionDeclarations(Model, Module.get());
|
||||
clift::importAllModelSegmentDeclarations(Model, Module.get());
|
||||
|
||||
clift::importDescriptiveInfo(Model, Module.get());
|
||||
|
||||
return std::make_pair(std::move(Context), std::move(Module));
|
||||
}
|
||||
|
||||
static Logger Log("import-from-c-clang-input");
|
||||
|
||||
struct ImportFromCAnalysis {
|
||||
static constexpr auto Name = "import-from-c";
|
||||
@@ -148,44 +176,51 @@ struct ImportFromCAnalysis {
|
||||
+ ErrorCode.message());
|
||||
}
|
||||
|
||||
ptml::ModelCBuilder::ConfigurationOptions Configuration = {
|
||||
.EnableStackFrameInlining = false
|
||||
auto [Context, HeaderModule] = makeHeaderModule(*Model);
|
||||
|
||||
TypeEmitterConfiguration Configuration = {
|
||||
.TypeToOmit = {},
|
||||
.EmitMaximumEnumValue = true,
|
||||
.ExplicitPadding = false,
|
||||
};
|
||||
ptml::HeaderBuilder::ConfigurationOptions HeaderConfiguration = {};
|
||||
|
||||
// Extra variable is here to extend the lifetime of the string.
|
||||
std::string EditedTypeHandle;
|
||||
|
||||
if (TheOption == ImportFromCOption::EditType) {
|
||||
// For all the types other than functions and typedefs, generate forward
|
||||
// declarations.
|
||||
if (!ptml::ModelCBuilder::isDeclarationTheSameAsDefinition(*TypeToEdit)) {
|
||||
llvm::raw_string_ostream Stream(HeaderConfiguration.PostIncludeSnippet);
|
||||
ptml::ModelCBuilder PI(Stream,
|
||||
*Model,
|
||||
/* GenerateTaglessPTML = */ true);
|
||||
PI.appendLineComment("The type we are editing");
|
||||
// The declaration of this type will be near the top of the file.
|
||||
PI.printForwardDeclaration(*TypeToEdit);
|
||||
PI.append("\n");
|
||||
}
|
||||
|
||||
// Find all types whose definition depends on the type we are editing.
|
||||
Configuration.TypesToOmit = collectDependentTypes(*TypeToEdit, Model);
|
||||
|
||||
} else if (TheOption == ImportFromCOption::EditFunctionPrototype) {
|
||||
HeaderConfiguration.FunctionsToOmit.insert(FunctionToEdit->Entry());
|
||||
|
||||
} else if (TheOption == ImportFromCOption::AddType) {
|
||||
// Nothing special to do when adding types
|
||||
|
||||
} else {
|
||||
revng_abort("Unknown action requested.");
|
||||
EditedTypeHandle = pipeline::locationString(revng::ranks::TypeDefinition,
|
||||
TypeToEdit->key());
|
||||
Configuration.TypeToOmit = EditedTypeHandle;
|
||||
}
|
||||
|
||||
{
|
||||
ptml::CTokenEmitter Tokens(Out, ptml::Tagging::Disabled);
|
||||
emitCommonIncludes(Tokens, abi::getDataModel(*Model));
|
||||
|
||||
if (TheOption == ImportFromCOption::EditType
|
||||
and isSeparateDeclarationAllowed(*TypeToEdit)) {
|
||||
auto Current = clift::importType(Context.get(), *TypeToEdit);
|
||||
|
||||
// TODO: we only need information about one type, don't import them all!
|
||||
clift::importDescriptiveInfo(*Model, *HeaderModule);
|
||||
|
||||
TypeDefinitionEmitter TDE(Tokens,
|
||||
abi::getDataModel(*Model),
|
||||
Configuration);
|
||||
TDE.emitForwardDeclaration(Current);
|
||||
Tokens.emitNewline();
|
||||
}
|
||||
|
||||
emitTypes(Tokens, *HeaderModule, Configuration);
|
||||
}
|
||||
|
||||
ptml::ModelCBuilder B(Out,
|
||||
*Model,
|
||||
/* EnableTaglessMode = */ true,
|
||||
std::move(Configuration));
|
||||
ptml::HeaderBuilder(B, std::move(HeaderConfiguration)).printModelHeader();
|
||||
Out.close();
|
||||
|
||||
if (Log.isEnabled()) {
|
||||
std::ifstream FilteredHeader(FilterModelPath.path().str());
|
||||
Log << "Filtered header:\n" << FilteredHeader.rdbuf() << "\n" << DoLog;
|
||||
}
|
||||
|
||||
std::string FilteredHeader = std::string("#include \"")
|
||||
+ FilterModelPath.path().str()
|
||||
+ std::string("\"");
|
||||
@@ -247,6 +282,8 @@ struct ImportFromCAnalysis {
|
||||
FilteredHeader += "\n";
|
||||
FilteredHeader += CCode;
|
||||
|
||||
revng_log(Log, "Real input:\n" << FilteredHeader << "\n");
|
||||
|
||||
if (not clang::tooling::runToolOnCodeWithArgs(std::move(Action),
|
||||
FilteredHeader,
|
||||
Compilation,
|
||||
@@ -260,8 +297,6 @@ struct ImportFromCAnalysis {
|
||||
std::string Result;
|
||||
for (auto &Error : Errors)
|
||||
Result += std::move(Error);
|
||||
|
||||
revng_log(Log, Result.c_str());
|
||||
return revng::createError(Result);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <queue>
|
||||
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/raw_os_ostream.h"
|
||||
@@ -12,6 +13,7 @@
|
||||
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/Model/BinaryIdentifier.h"
|
||||
#include "revng/Model/PrimitiveType.h"
|
||||
#include "revng/Model/TypeSystemPrinter.h"
|
||||
#include "revng/Model/VerifyHelper.h"
|
||||
#include "revng/Support/CommandLine.h"
|
||||
@@ -453,6 +455,60 @@ Values formCOFFRelocation(model::Architecture::Values Architecture) {
|
||||
} // namespace RelocationType
|
||||
} // namespace model
|
||||
|
||||
std::set<uint64_t> model::Binary::collectAllTypeSizes() const {
|
||||
// TODO: don't hardcode this set here. Share it with the other users!
|
||||
// Important: this should already contain all the primitive sizes we
|
||||
// support (which includes all the pointers sizes).
|
||||
std::set<uint64_t> ByteSizes = { 1, 2, 4, 8, 10, 12, 16 };
|
||||
|
||||
VerifyHelper SizeCache;
|
||||
for (const model::UpcastableTypeDefinition &Type : this->TypeDefinitions()) {
|
||||
// This takes care of all the type definitions, meaning we don't have to
|
||||
// look at defined types anymore.
|
||||
if (std::optional<uint64_t> MaybeSize = Type->size(SizeCache))
|
||||
ByteSizes.insert(MaybeSize.value());
|
||||
|
||||
for (const model::Type *Edge : Type->edges()) {
|
||||
// Since primitives, pointers and defined types are already taken care of,
|
||||
// we are only interested in arrays here.
|
||||
|
||||
// IMPORTANT: do not forget to update this after new type kinds are added!
|
||||
|
||||
while (!llvm::isa<model::PrimitiveType>(Edge)
|
||||
&& !llvm::isa<model::DefinedType>(Edge)) {
|
||||
if (const auto *Pointer = llvm::dyn_cast<model::PointerType>(Edge)) {
|
||||
// Keep going deeper on a pointer in case it's a pointer to an array.
|
||||
Edge = Pointer->PointeeType().get();
|
||||
|
||||
} else if (const auto *Array = llvm::dyn_cast<model::ArrayType>(Edge)) {
|
||||
if (std::optional<uint64_t> MaybeSize = Edge->trySize(SizeCache))
|
||||
ByteSizes.insert(MaybeSize.value());
|
||||
|
||||
// Keep going deeper on an array in case it's a nested one.
|
||||
Edge = Array->ElementType().get();
|
||||
|
||||
} else {
|
||||
revng_abort("Unsupported type kind!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *RFT = Type->getRawFunction()) {
|
||||
uint64_t ReturnTypeSize = 0;
|
||||
for (const auto &RV : RFT->ReturnValues()) {
|
||||
std::optional<uint64_t> MaybeSize = RV.Type()->trySize(SizeCache);
|
||||
revng_assert(MaybeSize.has_value());
|
||||
ReturnTypeSize += MaybeSize.value();
|
||||
}
|
||||
|
||||
if (ReturnTypeSize)
|
||||
ByteSizes.insert(ReturnTypeSize);
|
||||
}
|
||||
}
|
||||
|
||||
return ByteSizes;
|
||||
}
|
||||
|
||||
void model::Binary::dumpTypeGraph(const char *Path) const {
|
||||
DisableTracking Guard(*this);
|
||||
|
||||
|
||||
+20
-56
@@ -2,10 +2,13 @@
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/Model/NameBuilder.h"
|
||||
#include "revng/Model/NamingConfiguration.h"
|
||||
#include "revng/Model/VerifyHelper.h"
|
||||
#include "revng/Support/Configuration.h"
|
||||
#include "revng/Support/Error.h"
|
||||
@@ -794,66 +797,27 @@ bool Binary::verifyTypeDefinitions(VerifyHelper &VH) const {
|
||||
//
|
||||
|
||||
bool Configuration::verify(VerifyHelper &VH) const {
|
||||
// TODO: as this helper grows, split it up.
|
||||
using TT = TupleLikeTraits<model::NamingConfiguration>;
|
||||
return compile_time::repeatAnd<std::tuple_size_v<TT::tuple>>([&VH]<size_t I> {
|
||||
using Current = std::tuple_element_t<I, TT::tuple>;
|
||||
|
||||
// These checks are not necessary for now since the can't return an empty
|
||||
// string but they will be needed after the have a way to specify the default
|
||||
// value of a TTG field (since the default value helpers will go away).
|
||||
//
|
||||
// As such, let's add them now so that they don't end up forgotten.
|
||||
if constexpr (size_t(TT::Fields::UnnamedTypeDefinitionPrefix) == I) {
|
||||
// This is the only field we explicitly allow to be empty.
|
||||
return true;
|
||||
|
||||
if (Configuration().Naming().UnnamedSegmentPrefix().empty())
|
||||
return VH.fail("Segment prefix must not be empty.");
|
||||
} else if constexpr (not std::is_same_v<Current, std::string>) {
|
||||
// Do not check non-string fields.
|
||||
return true;
|
||||
|
||||
if (Configuration().Naming().UnnamedFunctionPrefix().empty())
|
||||
return VH.fail("Function prefix must not be empty.");
|
||||
} else if (not model::get<I>(Configuration().Naming()).empty()) {
|
||||
// The value is not empty, we're good.
|
||||
return true;
|
||||
|
||||
if (Configuration().Naming().UnnamedDynamicFunctionPrefix().empty())
|
||||
return VH.fail("Dynamic function prefix must not be empty.");
|
||||
|
||||
// `UnnamedTypeDefinitionPrefix` can be empty.
|
||||
|
||||
if (Configuration().Naming().UnnamedEnumEntryPrefix().empty())
|
||||
return VH.fail("Enum entry prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedStructFieldPrefix().empty())
|
||||
return VH.fail("Struct field prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedUnionFieldPrefix().empty())
|
||||
return VH.fail("Union field prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedFunctionArgumentPrefix().empty())
|
||||
return VH.fail("Argument prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedFunctionRegisterPrefix().empty())
|
||||
return VH.fail("Register prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedLocalVariablePrefix().empty())
|
||||
return VH.fail("Local variable prefix must not be empty.");
|
||||
if (Configuration().Naming().UnnamedBreakFromLoopVariablePrefix().empty())
|
||||
return VH.fail("\"Break from loop\" variable prefix must not be empty.");
|
||||
if (Configuration().Naming().UnnamedGotoLabelPrefix().empty())
|
||||
return VH.fail("Goto label prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().StructPaddingPrefix().empty())
|
||||
return VH.fail("Padding prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().OpaqueCSVValuePrefix().empty())
|
||||
return VH.fail("Opaque CSV prefix must not be empty.");
|
||||
if (Configuration().Naming().MaximumEnumValuePrefix().empty())
|
||||
return VH.fail("Maximum enum value prefix must not be empty.");
|
||||
|
||||
if (Configuration().Naming().UnnamedStackFrameVariableName().empty())
|
||||
return VH.fail("Stack frame variable name must not be empty.");
|
||||
if (Configuration().Naming().RawStackArgumentName().empty())
|
||||
return VH.fail("Raw stack argument name must not be empty.");
|
||||
if (Configuration().Naming().LoopStateVariableName().empty())
|
||||
return VH.fail("Loop state variable name must not be empty.");
|
||||
|
||||
if (Configuration().Naming().ArtificialReturnValuePrefix().empty())
|
||||
return VH.fail("Artificial return value prefix must not be empty.");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return VH.fail("`NamingConfiguration::" + TT::FieldNames[I]
|
||||
+ "` field must not be empty.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -539,8 +539,7 @@ void CTokenEmitter::emitIdentifier(llvm::StringRef Identifier,
|
||||
|
||||
revng_check(!Identifier.empty());
|
||||
if (not validateIdentifier(Identifier)) {
|
||||
revng_abort(("The specified identifier is not a valid C identifier: `"
|
||||
+ Identifier + "`")
|
||||
revng_abort(("`" + Identifier + "` is not a valid C identifier.")
|
||||
.str()
|
||||
.c_str());
|
||||
}
|
||||
|
||||
+95
-77
@@ -4,13 +4,16 @@
|
||||
|
||||
#include "revng/ABI/Analyses/ConvertFunctionsToCABI.h"
|
||||
#include "revng/ABI/Analyses/ConvertFunctionsToRaw.h"
|
||||
#include "revng/Backend/DecompileToSingleFile.h"
|
||||
#include "revng/Canonicalize/SimplifySwitch.h"
|
||||
#include "revng/Canonicalize/SwitchToStatements.h"
|
||||
#include "revng/CliftPipes/Clifter.h"
|
||||
#include "revng/CliftPipes/EmitC.h"
|
||||
#include "revng/CliftPipes/EmitCAsDirectory.h"
|
||||
#include "revng/CliftPipes/EmitCAsSingleFile.h"
|
||||
#include "revng/CliftPipes/Headers.h"
|
||||
#include "revng/CliftPipes/ImportDataModel.h"
|
||||
#include "revng/CliftPipes/ImportDescriptiveInfo.h"
|
||||
#include "revng/CliftPipes/ImportTypes.h"
|
||||
#include "revng/CliftPipes/VerifyAgainstModel.h"
|
||||
#include "revng/DataLayoutAnalysis/DLA.h"
|
||||
#include "revng/EarlyFunctionAnalysis/AttachDebugInfo.h"
|
||||
@@ -53,81 +56,96 @@
|
||||
#include "revng/Yield/Pipes/YieldCallGraph.h"
|
||||
#include "revng/Yield/Pipes/YieldCallGraphSlice.h"
|
||||
|
||||
using namespace revng::pypeline;
|
||||
#define REGISTER(TYPE, NAME) \
|
||||
static Register##TYPE<NAME> CONCAT3(TYPE, _, __COUNTER__)
|
||||
|
||||
//
|
||||
// Containers
|
||||
//
|
||||
|
||||
static RegisterContainer<LLVMRootContainer> C1;
|
||||
static RegisterContainer<LLVMFunctionContainer> C3;
|
||||
static RegisterContainer<PTMLCBytesContainer> C4;
|
||||
static RegisterContainer<BinariesContainer> C5;
|
||||
static RegisterContainer<PTMLCTypeContainer> C6;
|
||||
static RegisterContainer<CFGMap> C7;
|
||||
static RegisterContainer<HexDumpContainer> C8;
|
||||
static RegisterContainer<AssemblyInternalContainer> C9;
|
||||
static RegisterContainer<AssemblyContainer> C10;
|
||||
static RegisterContainer<ObjectFileContainer> C11;
|
||||
static RegisterContainer<TranslatedContainer> C12;
|
||||
static RegisterContainer<CliftFunctionContainer> C13;
|
||||
static RegisterContainer<PTMLCFunctionBytesContainer> C14;
|
||||
static RegisterContainer<CrossRelationsContainer> C15;
|
||||
static RegisterContainer<CallGraphContainer> C16;
|
||||
static RegisterContainer<CallGraphSliceContainer> C17;
|
||||
static RegisterContainer<FunctionControlFlowContainer> C18;
|
||||
static RegisterContainer<CliftModuleContainer> C19;
|
||||
static RegisterContainer<CliftSingleTypeContainer> C20;
|
||||
using namespace revng::pypeline;
|
||||
|
||||
REGISTER(Container, AssemblyContainer);
|
||||
REGISTER(Container, AssemblyInternalContainer);
|
||||
REGISTER(Container, BinariesContainer);
|
||||
REGISTER(Container, CallGraphContainer);
|
||||
REGISTER(Container, CallGraphSliceContainer);
|
||||
REGISTER(Container, CFGMap);
|
||||
REGISTER(Container, CliftFunctionContainer);
|
||||
REGISTER(Container, CliftModuleContainer);
|
||||
REGISTER(Container, CliftSingleTypeContainer);
|
||||
REGISTER(Container, CrossRelationsContainer);
|
||||
REGISTER(Container, FunctionControlFlowContainer);
|
||||
REGISTER(Container, HexDumpContainer);
|
||||
REGISTER(Container, LLVMFunctionContainer);
|
||||
REGISTER(Container, LLVMRootContainer);
|
||||
REGISTER(Container, ObjectFileContainer);
|
||||
REGISTER(Container, PTMLCBytesContainer);
|
||||
REGISTER(Container, PTMLCFunctionBytesContainer);
|
||||
REGISTER(Container, PTMLCTypeBytesContainer);
|
||||
REGISTER(Container, PTMLCTypeContainer);
|
||||
REGISTER(Container, RecompilableArchiveContainer);
|
||||
REGISTER(Container, TranslatedContainer);
|
||||
|
||||
//
|
||||
// Pipes
|
||||
//
|
||||
|
||||
using namespace revng::pypeline::pipes;
|
||||
using namespace revng::pypeline::piperuns;
|
||||
namespace piperuns = revng::pypeline::piperuns;
|
||||
|
||||
static RegisterSingleOutputPipeRun<Lift> P1;
|
||||
static RegisterPipe<PureLLVMPassesRootPipe> P2;
|
||||
static RegisterPipe<PureLLVMPassesPipe> P3;
|
||||
static RegisterSingleOutputPipeRun<ModelToHeader> P4;
|
||||
static RegisterTypeDefinitionPipeRun<GenerateModelTypeDefinition> P5;
|
||||
static RegisterFunctionPipeRun<CollectCFG> P6;
|
||||
static RegisterFunctionPipeRun<Isolate> P7;
|
||||
static RegisterFunctionPipeRun<AttachDebugInfo> P8;
|
||||
static RegisterFunctionPipeRun<piperuns::EnforceABI> P9;
|
||||
static RegisterFunctionPipeRun<PromoteCSVs> P10;
|
||||
static RegisterSingleOutputPipeRun<HexDump> P11;
|
||||
static RegisterFunctionPipeRun<ProcessAssembly> P12;
|
||||
static RegisterFunctionPipeRun<YieldAssembly> P13;
|
||||
static RegisterSingleOutputPipeRun<LinkSupport> P14;
|
||||
static RegisterSingleOutputPipeRun<CompileRootModule> P15;
|
||||
static RegisterSingleOutputPipeRun<LinkForTranslation> P16;
|
||||
static RegisterSingleOutputPipeRun<InvokeIsolatedFunctions> P17;
|
||||
static RegisterFunctionPipeRun<RemoveLiftingArtifacts> P18;
|
||||
static RegisterFunctionPipeRun<PromoteInitCSVToUndef> P19;
|
||||
static RegisterFunctionPipeRun<InjectStackSizeProbesAtCallSites> P20;
|
||||
static RegisterFunctionPipeRun<PromoteStackPointer> P21;
|
||||
static RegisterFunctionPipeRun<SimplifySwitch> P22;
|
||||
static RegisterFunctionPipeRun<LegacySegregateStackAccesses> P23;
|
||||
static RegisterFunctionPipeRun<MakeSegmentRef> P24;
|
||||
static RegisterFunctionPipeRun<SegregateStackAccesses> P25;
|
||||
static RegisterFunctionPipeRun<SwitchToStatements> P26;
|
||||
static RegisterFunctionPipeRun<Clifter> P27;
|
||||
static RegisterPipe<PureMLIRPassesPipe> P28;
|
||||
static RegisterSingleOutputPipeRun<VerifyAgainstModel> P29;
|
||||
static RegisterSingleOutputPipeRun<ImportDescriptiveInfo> P30;
|
||||
static RegisterFunctionPipeRun<EmitC> P31;
|
||||
static RegisterSingleOutputPipeRun<DecompileToSingleFile> P32;
|
||||
static RegisterSingleOutputPipeRun<MergeLLVMModules> P33;
|
||||
static RegisterSingleOutputPipeRun<CleanupIR> P34;
|
||||
static RegisterSingleOutputPipeRun<ProcessCallGraph> P35;
|
||||
static RegisterSingleOutputPipeRun<YieldCallGraph> P36;
|
||||
static RegisterFunctionPipeRun<YieldCallGraphSlice> P37;
|
||||
static RegisterFunctionPipeRun<YieldCFG> P38;
|
||||
static RegisterFunctionPipeRun<ImportDescriptiveFunctionInfo> P39;
|
||||
static RegisterFunctionPipeRun<VerifyFunctionAgainstModel> P40;
|
||||
static RegisterFunctionPipeRun<ImportFunctionDataModel> P41;
|
||||
REGISTER(Pipe, PureLLVMPassesPipe);
|
||||
REGISTER(Pipe, PureLLVMPassesRootPipe);
|
||||
REGISTER(Pipe, PureMLIRPassesPipe);
|
||||
|
||||
using namespace revng::pypeline::piperuns;
|
||||
|
||||
REGISTER(FunctionPipeRun, AttachDebugInfo);
|
||||
REGISTER(FunctionPipeRun, Clifter);
|
||||
REGISTER(FunctionPipeRun, CollectCFG);
|
||||
REGISTER(FunctionPipeRun, EmitC);
|
||||
REGISTER(FunctionPipeRun, EnforceABI);
|
||||
REGISTER(FunctionPipeRun, ImportDescriptiveFunctionInfo);
|
||||
REGISTER(FunctionPipeRun, ImportFunctionDataModel);
|
||||
REGISTER(FunctionPipeRun, InjectStackSizeProbesAtCallSites);
|
||||
REGISTER(FunctionPipeRun, Isolate);
|
||||
REGISTER(FunctionPipeRun, LegacySegregateStackAccesses);
|
||||
REGISTER(FunctionPipeRun, MakeSegmentRef);
|
||||
REGISTER(FunctionPipeRun, ProcessAssembly);
|
||||
REGISTER(FunctionPipeRun, PromoteCSVs);
|
||||
REGISTER(FunctionPipeRun, PromoteInitCSVToUndef);
|
||||
REGISTER(FunctionPipeRun, PromoteStackPointer);
|
||||
REGISTER(FunctionPipeRun, RemoveLiftingArtifacts);
|
||||
REGISTER(FunctionPipeRun, SegregateStackAccesses);
|
||||
REGISTER(FunctionPipeRun, SimplifySwitch);
|
||||
REGISTER(FunctionPipeRun, SwitchToStatements);
|
||||
REGISTER(FunctionPipeRun, VerifyFunctionAgainstModel);
|
||||
REGISTER(FunctionPipeRun, YieldAssembly);
|
||||
REGISTER(FunctionPipeRun, YieldCallGraphSlice);
|
||||
REGISTER(FunctionPipeRun, YieldCFG);
|
||||
|
||||
REGISTER(SingleOutputPipeRun, CleanupIR);
|
||||
REGISTER(SingleOutputPipeRun, CompileRootModule);
|
||||
REGISTER(SingleOutputPipeRun, EmitCAsDirectory);
|
||||
REGISTER(SingleOutputPipeRun, EmitCAsSingleFile);
|
||||
REGISTER(SingleOutputPipeRun, EmitHelperHeader);
|
||||
REGISTER(SingleOutputPipeRun, EmitTypeAndGlobalHeader);
|
||||
REGISTER(SingleOutputPipeRun, HexDump);
|
||||
REGISTER(SingleOutputPipeRun, ImportDescriptiveInfo);
|
||||
REGISTER(SingleOutputPipeRun, ImportFunctionDeclarations);
|
||||
REGISTER(SingleOutputPipeRun, ImportSegmentDeclarations);
|
||||
REGISTER(SingleOutputPipeRun, ImportTypes);
|
||||
REGISTER(SingleOutputPipeRun, InvokeIsolatedFunctions);
|
||||
REGISTER(SingleOutputPipeRun, Lift);
|
||||
REGISTER(SingleOutputPipeRun, LinkForTranslation);
|
||||
REGISTER(SingleOutputPipeRun, LinkSupport);
|
||||
REGISTER(SingleOutputPipeRun, MergeLLVMModules);
|
||||
REGISTER(SingleOutputPipeRun, ModelToHeader);
|
||||
REGISTER(SingleOutputPipeRun, ProcessCallGraph);
|
||||
REGISTER(SingleOutputPipeRun, VerifyAgainstModel);
|
||||
REGISTER(SingleOutputPipeRun, YieldCallGraph);
|
||||
|
||||
REGISTER(TypeDefinitionPipeRun, EmitSingleTypeDefinition);
|
||||
REGISTER(TypeDefinitionPipeRun, GenerateModelTypeDefinition);
|
||||
|
||||
//
|
||||
// Analyses
|
||||
@@ -135,16 +153,16 @@ static RegisterFunctionPipeRun<ImportFunctionDataModel> P41;
|
||||
|
||||
using namespace revng::pypeline::analyses;
|
||||
|
||||
static RegisterAnalysis<ApplyDiff> A1;
|
||||
static RegisterAnalysis<VerifyDiff> A2;
|
||||
static RegisterAnalysis<SetModel> A3;
|
||||
static RegisterAnalysis<VerifyModel> A4;
|
||||
static RegisterAnalysis<ParseBinaryAnalysis> A5;
|
||||
static RegisterAnalysis<ImportPrototypesFromDatabase> A6;
|
||||
static RegisterAnalysis<DetectABI> A7;
|
||||
static RegisterAnalysis<DetectStackSize> A8;
|
||||
static RegisterAnalysis<AnalyzeDataLayout> A9;
|
||||
static RegisterAnalysis<ConvertFunctionsToCABI> A10;
|
||||
static RegisterAnalysis<ConvertFunctionsToRaw> A11;
|
||||
static RegisterAnalysis<ImportFromC> A12;
|
||||
static RegisterAnalysis<LLMRename> A13;
|
||||
REGISTER(Analysis, AnalyzeDataLayout);
|
||||
REGISTER(Analysis, ApplyDiff);
|
||||
REGISTER(Analysis, ConvertFunctionsToCABI);
|
||||
REGISTER(Analysis, ConvertFunctionsToRaw);
|
||||
REGISTER(Analysis, DetectABI);
|
||||
REGISTER(Analysis, DetectStackSize);
|
||||
REGISTER(Analysis, ImportFromC);
|
||||
REGISTER(Analysis, ImportPrototypesFromDatabase);
|
||||
REGISTER(Analysis, LLMRename);
|
||||
REGISTER(Analysis, ParseBinaryAnalysis);
|
||||
REGISTER(Analysis, SetModel);
|
||||
REGISTER(Analysis, VerifyDiff);
|
||||
REGISTER(Analysis, VerifyModel);
|
||||
|
||||
@@ -347,62 +347,8 @@ void ptml::ModelCBuilder::printOpaqueTypeDefinition(uint64_t 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();
|
||||
std::set<uint64_t> ByteSizes = Binary.collectAllTypeSizes();
|
||||
for (uint64_t ByteSize : ByteSizes) {
|
||||
*Out << "\n";
|
||||
printOpaqueTypeDefinition(ByteSize);
|
||||
@@ -412,7 +358,7 @@ void ptml::ModelCBuilder::printModelOpaqueTypeDefinitions() {
|
||||
using MCB = ptml::ModelCBuilder;
|
||||
using SizeSet = std::set<uint64_t>;
|
||||
void MCB::printHelperOpaqueTypeDefinitions(const SizeSet &HelperByteSizes) {
|
||||
std::set<uint64_t> ModelByteSizes = getModelOpaqueByteSizes();
|
||||
std::set<uint64_t> ModelByteSizes = Binary.collectAllTypeSizes();
|
||||
for (uint64_t ByteSize : HelperByteSizes) {
|
||||
if (not ModelByteSizes.contains(ByteSize)) {
|
||||
*Out << "\n";
|
||||
|
||||
@@ -23,9 +23,9 @@ containers:
|
||||
type: BinariesContainer
|
||||
- name: llvm-root
|
||||
type: LLVMRootContainer
|
||||
- name: model-header
|
||||
- name: legacy-model-header
|
||||
type: PTMLCBytesContainer
|
||||
- name: model-types
|
||||
- name: legacy-model-types
|
||||
type: PTMLCTypeContainer
|
||||
- name: cfg-map
|
||||
type: CFGMap
|
||||
@@ -49,6 +49,12 @@ containers:
|
||||
type: PTMLCFunctionBytesContainer
|
||||
- name: decompiled-single-file
|
||||
type: PTMLCBytesContainer
|
||||
- name: tagless-decompile-c
|
||||
type: PTMLCFunctionBytesContainer
|
||||
- name: tagless-decompiled-single-file
|
||||
type: PTMLCBytesContainer
|
||||
- name: recompilable-archive
|
||||
type: RecompilableArchiveContainer
|
||||
- name: llvm-root-merged
|
||||
type: LLVMRootContainer
|
||||
- name: cross-relations
|
||||
@@ -59,6 +65,18 @@ containers:
|
||||
type: CallGraphSliceContainer
|
||||
- name: function-cfg
|
||||
type: FunctionControlFlowContainer
|
||||
- name: clift-types-and-globals
|
||||
type: CliftModuleContainer
|
||||
- name: type-and-global-header
|
||||
type: PTMLCBytesContainer
|
||||
- name: helper-header
|
||||
type: PTMLCBytesContainer
|
||||
- name: single-type
|
||||
type: PTMLCTypeBytesContainer
|
||||
- name: tagless-type-and-global-header
|
||||
type: PTMLCBytesContainer
|
||||
- name: tagless-helper-header
|
||||
type: PTMLCBytesContainer
|
||||
|
||||
analyses:
|
||||
- apply-diff
|
||||
@@ -305,7 +323,7 @@ branches:
|
||||
- analysis: analyze-data-layout
|
||||
containers: [llvm-functions]
|
||||
|
||||
backend:
|
||||
clift-functions:
|
||||
from: detect-stack-size
|
||||
tasks:
|
||||
- pipe: pure-llvm-passes-pipe
|
||||
@@ -421,6 +439,17 @@ branches:
|
||||
containers: [clift-functions]
|
||||
- pipe: import-descriptive-function-info
|
||||
arguments: [clift-functions]
|
||||
- savepoint: pre-backend-clift
|
||||
containers: [clift-functions]
|
||||
artifacts:
|
||||
- name: pre-backend-clift
|
||||
container: clift-functions
|
||||
category: debug
|
||||
filename: pre_backend_clift.mlir.bc
|
||||
|
||||
decompiled:
|
||||
from: clift-functions
|
||||
tasks:
|
||||
- pipe: emit-c
|
||||
arguments: [clift-functions, decompile-c]
|
||||
- savepoint: emit-c
|
||||
@@ -433,12 +462,32 @@ branches:
|
||||
defined_locations:
|
||||
- function
|
||||
preferred_artifacts:
|
||||
- emit-model-header
|
||||
- emit-type-and-global-header
|
||||
analyses:
|
||||
- analysis: llm-rename
|
||||
containers: [decompile-c]
|
||||
|
||||
- pipe: decompile-to-single-file
|
||||
helpers:
|
||||
from: clift-functions
|
||||
tasks:
|
||||
- pipe: emit-helper-header
|
||||
arguments: [clift-functions, helper-header]
|
||||
- savepoint: helper-header
|
||||
containers: [helper-header]
|
||||
artifacts:
|
||||
- name: emit-helper-header
|
||||
container: helper-header
|
||||
category: user
|
||||
filename: helpers.h
|
||||
defined_locations:
|
||||
- helper-function
|
||||
- helper-struct-type
|
||||
- helper-struct-field
|
||||
|
||||
single-file:
|
||||
from: decompiled
|
||||
tasks:
|
||||
- pipe: emit-c-as-single-file
|
||||
arguments: [decompile-c, decompiled-single-file]
|
||||
- savepoint: emit-c-as-single-file
|
||||
containers: [decompiled-single-file]
|
||||
@@ -450,17 +499,101 @@ branches:
|
||||
defined_locations:
|
||||
- function
|
||||
preferred_artifacts:
|
||||
- emit-model-header
|
||||
- emit-type-and-global-header
|
||||
|
||||
emit-model-header:
|
||||
tagless-decompiled:
|
||||
from: clift-functions
|
||||
tasks:
|
||||
- pipe: model-to-header
|
||||
arguments: [model-header]
|
||||
- savepoint: model-header
|
||||
containers: [model-header]
|
||||
- pipe: emit-c
|
||||
configuration:
|
||||
disable-markup: true
|
||||
emission-mode: recompilable
|
||||
arguments: [clift-functions, tagless-decompile-c]
|
||||
- savepoint: emit-tagless-c
|
||||
containers: [tagless-decompile-c]
|
||||
artifacts:
|
||||
- name: emit-model-header
|
||||
container: model-header
|
||||
- name: emit-tagless-c
|
||||
container: tagless-decompile-c
|
||||
category: metadata
|
||||
filename: decompiled.c
|
||||
defined_locations:
|
||||
- function
|
||||
preferred_artifacts:
|
||||
- emit-type-and-global-header
|
||||
|
||||
tagless-single-file:
|
||||
from: tagless-decompiled
|
||||
tasks:
|
||||
- pipe: emit-c-as-single-file
|
||||
arguments: [tagless-decompile-c, tagless-decompiled-single-file]
|
||||
configuration:
|
||||
disable-markup: true
|
||||
emission-mode: recompilable
|
||||
- savepoint: emit-tagless-c-as-single-file
|
||||
containers: [tagless-decompiled-single-file]
|
||||
artifacts:
|
||||
- name: emit-tagless-c-as-single-file
|
||||
container: tagless-decompiled-single-file
|
||||
category: metadata
|
||||
filename: binary_decompiled.c
|
||||
defined_locations:
|
||||
- function
|
||||
preferred_artifacts:
|
||||
- emit-type-and-global-header
|
||||
|
||||
- pipe: emit-helper-header
|
||||
arguments: [clift-functions, tagless-helper-header]
|
||||
configuration:
|
||||
disable-markup: true
|
||||
emission-mode: recompilable
|
||||
artifacts:
|
||||
- name: emit-tagless-helper-header
|
||||
container: tagless-helper-header
|
||||
category: metadata
|
||||
filename: helpers.h
|
||||
|
||||
- pipe: import-types
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: import-function-declarations
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: import-segment-declarations
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: import-descriptive-info
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: emit-type-and-global-header
|
||||
arguments: [clift-types-and-globals, tagless-type-and-global-header]
|
||||
configuration:
|
||||
disable-markup: true
|
||||
emission-mode: recompilable
|
||||
artifacts:
|
||||
- name: emit-tagless-type-and-global-header
|
||||
container: tagless-type-and-global-header
|
||||
category: metadata
|
||||
filename: types-and-globals.h
|
||||
|
||||
- pipe: emit-c-as-directory
|
||||
arguments:
|
||||
- tagless-decompiled-single-file
|
||||
- tagless-type-and-global-header
|
||||
- tagless-helper-header
|
||||
- recompilable-archive
|
||||
- savepoint: recompilable-archive
|
||||
containers: [recompilable-archive]
|
||||
artifacts:
|
||||
- name: emit-recompilable-archive
|
||||
container: recompilable-archive
|
||||
category: metadata
|
||||
filename: recompilable_archive.tar
|
||||
|
||||
legacy-emit-model-header:
|
||||
tasks:
|
||||
- pipe: legacy-model-to-header
|
||||
arguments: [legacy-model-header]
|
||||
- savepoint: legacy-model-header
|
||||
containers: [legacy-model-header]
|
||||
artifacts:
|
||||
- name: legacy-emit-model-header
|
||||
container: legacy-model-header
|
||||
category: user
|
||||
filename: types-and-globals.h
|
||||
defined_locations:
|
||||
@@ -474,15 +607,15 @@ branches:
|
||||
preferred_artifacts:
|
||||
- emit-c
|
||||
|
||||
emit-type-definition:
|
||||
legacy-emit-type-definition:
|
||||
tasks:
|
||||
- pipe: generate-model-type-definition
|
||||
arguments: [model-types]
|
||||
- savepoint: model-types
|
||||
containers: [model-types]
|
||||
- pipe: legacy-generate-model-type-definition
|
||||
arguments: [legacy-model-types]
|
||||
- savepoint: legacy-model-types
|
||||
containers: [legacy-model-types]
|
||||
artifacts:
|
||||
- name: emit-type-definitions
|
||||
container: model-types
|
||||
- name: legacy-emit-type-definitions
|
||||
container: legacy-model-types
|
||||
category: user
|
||||
filename: type.h
|
||||
|
||||
@@ -642,3 +775,68 @@ branches:
|
||||
container: function-cfg
|
||||
category: user
|
||||
filename: cfg.svg
|
||||
|
||||
import-types:
|
||||
tasks:
|
||||
- pipe: import-types
|
||||
arguments: [clift-types-and-globals]
|
||||
artifacts:
|
||||
- name: import-types
|
||||
container: clift-types-and-globals
|
||||
category: debug
|
||||
filename: types.mlir
|
||||
- pipe: import-function-declarations
|
||||
arguments: [clift-types-and-globals]
|
||||
|
||||
emit-type-and-global-header:
|
||||
from: import-types
|
||||
tasks:
|
||||
- pipe: import-segment-declarations
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: import-descriptive-info
|
||||
arguments: [clift-types-and-globals]
|
||||
artifacts:
|
||||
- name: type-and-global-header-clift
|
||||
container: clift-types-and-globals
|
||||
category: debug
|
||||
filename: type-and-global-header.mlir.bc
|
||||
- pipe: emit-type-and-global-header
|
||||
arguments: [clift-types-and-globals, type-and-global-header]
|
||||
configuration:
|
||||
disable-markup: false
|
||||
emission-mode: recompilable
|
||||
- savepoint: type-and-global-header
|
||||
containers: [type-and-global-header]
|
||||
artifacts:
|
||||
- name: emit-type-and-global-header
|
||||
container: type-and-global-header
|
||||
category: user
|
||||
filename: types-and-globals.h
|
||||
defined_locations:
|
||||
- type-definition
|
||||
- struct-field
|
||||
- union-field
|
||||
- enum-entry
|
||||
- dynamic-function
|
||||
- segment
|
||||
- artificial-struct
|
||||
preferred_artifacts:
|
||||
- emit-c
|
||||
|
||||
emit-single-type-definition:
|
||||
from: import-types
|
||||
tasks:
|
||||
- pipe: import-descriptive-info
|
||||
arguments: [clift-types-and-globals]
|
||||
- pipe: emit-single-type-definition
|
||||
arguments: [clift-types-and-globals, single-type]
|
||||
configuration:
|
||||
disable-markup: true
|
||||
emission-mode: editable
|
||||
- savepoint: single-type-definition
|
||||
containers: [single-type]
|
||||
artifacts:
|
||||
- name: emit-single-type-definition
|
||||
container: single-type
|
||||
category: user
|
||||
filename: type.h
|
||||
|
||||
@@ -100,7 +100,7 @@ class PipelineParser:
|
||||
for preferred_artifact in artifact.preferred_artifacts:
|
||||
if preferred_artifact not in artifact_names:
|
||||
raise ValueError(
|
||||
f"Artifact {artifact.name} defines {preferred_artifact}"
|
||||
f"Artifact {artifact.name} defines {preferred_artifact} "
|
||||
"as a preferred artifact but it does not exist"
|
||||
)
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ class LocalStorageProviderFactory(_LocalStorageProviderCommon, StorageProviderFa
|
||||
if (directory / model_name).exists():
|
||||
break
|
||||
if directory == directory.parent:
|
||||
raise FileNotFoundError(f'Model "{model_name}" not found')
|
||||
raise FileNotFoundError(f'Model "{str(directory / model_name)}" not found')
|
||||
directory = directory.parent
|
||||
|
||||
model_path = directory / model_name
|
||||
|
||||
@@ -42,8 +42,8 @@ using namespace std::string_view_literals;
|
||||
|
||||
/*# --- TupleLikeTraits --- -#*/
|
||||
template <> struct TupleLikeTraits</*=- struct | user_fullname =*/> {
|
||||
static constexpr const llvm::StringRef Name = "/*=- struct.name =*/";
|
||||
static constexpr const llvm::StringRef FullName = "/*=- struct | user_fullname =*/";
|
||||
static constexpr llvm::StringRef Name = "/*=- struct.name =*/";
|
||||
static constexpr llvm::StringRef FullName = "/*=- struct | user_fullname =*/";
|
||||
using tuple = std::tuple<
|
||||
/**- for field in struct.all_fields -**/
|
||||
/*=- struct | user_fullname =*/::TypeOf/*=- field.name =*/
|
||||
|
||||
@@ -135,6 +135,8 @@ $ ROOT="$(dirname "$(dirname "$(which revng)")")"
|
||||
$ pretty() { sed "s/ #[0-9]*//; s/ ![^ ]*//g; s/;.*//"; }
|
||||
$ revng opt -strip-debug -sroa -instcombine -dce -S \
|
||||
"$ROOT/share/libtcg/libtcg-helpers-x86_64.bc" \
|
||||
-o libtcg-helpers-x86_64-optimized.S
|
||||
$ cat libtcg-helpers-x86_64-optimized.S \
|
||||
| sed -n "/^define void @helper_clts/,/^}/p" \
|
||||
| pretty
|
||||
define void @helper_clts(ptr noundef %0) section "revng_inline" {
|
||||
@@ -158,8 +160,7 @@ The function takes a `%struct.CPUArchState` pointer `%0` (the `env` argument) an
|
||||
Now let's look at `helper_divb_AL`:
|
||||
|
||||
```bash
|
||||
$ revng opt -strip-debug -sroa -instcombine -dce -S \
|
||||
"$ROOT/share/libtcg/libtcg-helpers-x86_64.bc" \
|
||||
$ cat libtcg-helpers-x86_64-optimized.S \
|
||||
| sed -n "/^define void @helper_divb_AL/,/^}/p" \
|
||||
| pretty \
|
||||
| head -16
|
||||
@@ -217,6 +218,8 @@ Let's look at `helper_clts` after the `fix-helpers` transformation:
|
||||
```bash
|
||||
$ revng opt -strip-debug -instcombine -dce -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-full-x86_64.bc" \
|
||||
-o libtcg-helpers-full-x86_64-optimized.S
|
||||
$ cat libtcg-helpers-full-x86_64-optimized.S \
|
||||
| sed -n "/^define void @helper_clts/,/^}/p" \
|
||||
| pretty
|
||||
define void @helper_clts(ptr noundef %0) section "revng_inline" {
|
||||
@@ -238,8 +241,7 @@ Well-known registers get human-readable names instead.
|
||||
For instance, `helper_divb_AL` after annotation:
|
||||
|
||||
```bash
|
||||
$ revng opt -strip-debug -instcombine -dce -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-full-x86_64.bc" \
|
||||
$ cat libtcg-helpers-full-x86_64-optimized.S \
|
||||
| sed -n "/^define void @helper_divb_AL/,/^}/p" \
|
||||
| pretty \
|
||||
| head -10
|
||||
@@ -279,8 +281,7 @@ But in the full module, `fix-helpers` cannot replace them with a single CSV —
|
||||
Instead, it emits a `switch` on the pointer value (which is the byte offset of the register within `CPUState`) to dispatch to the correct CSV:
|
||||
|
||||
```bash
|
||||
$ revng opt -strip-debug -instcombine -dce -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-full-x86_64.bc" \
|
||||
$ cat libtcg-helpers-full-x86_64-optimized.S \
|
||||
| sed -n "/^define void @helper_addsd/,/^}/p" \
|
||||
| pretty \
|
||||
| sed -n '1,12p'
|
||||
@@ -311,6 +312,8 @@ For example, `helper_clts` (which is `REVNG_INLINE`) still has its full definiti
|
||||
```bash
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-to-inline-x86_64.bc" \
|
||||
-o libtcg-helpers-to-inline-x86_64-optimized.S
|
||||
$ cat libtcg-helpers-to-inline-x86_64-optimized.S \
|
||||
| grep "^define.*@helper_clts" \
|
||||
| pretty
|
||||
define void @helper_clts(ptr noundef %0) section "revng_inline" {
|
||||
@@ -320,8 +323,7 @@ We can see the counts: the *to-inline* module has a small number of definitions
|
||||
|
||||
```bash
|
||||
$ echo "Definitions:"
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-to-inline-x86_64.bc" \
|
||||
$ cat libtcg-helpers-to-inline-x86_64-optimized.S \
|
||||
| grep -c "^define"
|
||||
Definitions:
|
||||
363
|
||||
@@ -335,6 +337,8 @@ Every function, including `REVNG_INLINE` ones, is a bare declaration.
|
||||
```bash
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-declarations-only-x86_64.bc" \
|
||||
-o libtcg-helpers-declarations-only-x86_64-optimized.S
|
||||
$ cat libtcg-helpers-declarations-only-x86_64-optimized.S \
|
||||
| grep "^declare.*@helper_clts" \
|
||||
| pretty
|
||||
declare void @helper_clts(ptr noundef) section "revng_inline"
|
||||
@@ -344,14 +348,12 @@ The module has 0 definitions and 1089 declarations:
|
||||
|
||||
```bash
|
||||
$ echo "Definitions:"
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-declarations-only-x86_64.bc" \
|
||||
$ cat libtcg-helpers-declarations-only-x86_64-optimized.S \
|
||||
| { grep -c "^define" || true; }
|
||||
Definitions:
|
||||
0
|
||||
$ echo "Declarations:"
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-declarations-only-x86_64.bc" \
|
||||
$ cat libtcg-helpers-declarations-only-x86_64-optimized.S \
|
||||
| grep "^declare" | grep -c "helper_"
|
||||
Declarations:
|
||||
1089
|
||||
@@ -365,23 +367,21 @@ This metadata records which CSVs a helper reads and which it writes, *even when
|
||||
For instance, in the *declarations-only* module, `helper_write_eflags` has no body, yet its declaration carries the metadata:
|
||||
|
||||
```bash
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-declarations-only-x86_64.bc" \
|
||||
$ cat libtcg-helpers-declarations-only-x86_64-optimized.S \
|
||||
| grep "^declare.*@helper_write_eflags"
|
||||
declare !revng.csua !298 !revng.csvaccess.offsets.load !302 !revng.csvaccess.offsets.store !304 !revng.tags !12 void @helper_write_eflags(ptr noundef, i64 noundef, i32 noundef) #0
|
||||
declare !revng.csua !299 !revng.csvaccess.offsets.load !303 !revng.csvaccess.offsets.store !305 !revng.tags !13 void @helper_write_eflags(ptr noundef, i64 noundef, i32 noundef) #0
|
||||
```
|
||||
|
||||
The `!302` and `!304` are references to metadata nodes defined at the end of the module.
|
||||
The `!303` and `!305` are references to metadata nodes defined at the end of the module.
|
||||
Resolving them reveals the actual CSV lists:
|
||||
|
||||
```bash
|
||||
$ revng opt -strip-debug -S \
|
||||
"$ROOT/share/revng/libtcg-helpers-declarations-only-x86_64.bc" \
|
||||
| grep -E "^!(302|303|304|305) ="
|
||||
!302 = !{i32 0, !303}
|
||||
!303 = !{!"_state_0x2848"}
|
||||
!304 = !{i32 0, !305}
|
||||
!305 = !{!"_cc_src", !"_state_0x286c", !"_state_0x2848", !"_cc_op"}
|
||||
$ cat libtcg-helpers-declarations-only-x86_64-optimized.S \
|
||||
| grep -E "^!(303|304|305|306) ="
|
||||
!303 = !{i32 0, !304}
|
||||
!304 = !{!"_state_0x2848"}
|
||||
!305 = !{i32 0, !306}
|
||||
!306 = !{!"_cc_src", !"_state_0x286c", !"_state_0x2848", !"_cc_op"}
|
||||
```
|
||||
|
||||
Each CSV access metadata node is a tuple `!{i32 0, !<csv-list>}` where the second element lists the CSV names.
|
||||
@@ -510,42 +510,42 @@ $ revng opt -strip-debug -S enforced.bc \
|
||||
| pretty \
|
||||
| sed -n "1p; /and i64.*u0xffff$/,/helper_divb_AL.exit:/p"
|
||||
define i64 @local_0x400000_Code_x86_64(i64 %rdi_x86_64, i64 %rsi_x86_64) {
|
||||
%7 = and i64 %6, u0xffff
|
||||
%8 = trunc i64 %7 to i32
|
||||
%9 = and i64 %5, 255
|
||||
%10 = trunc i64 %9 to i32
|
||||
%11 = icmp eq i32 %10, 0
|
||||
br i1 %11, label %12, label %13
|
||||
%27 = and i64 %26, u0xffff
|
||||
%28 = trunc i64 %27 to i32
|
||||
%29 = and i64 %25, 255
|
||||
%30 = trunc i64 %29 to i32
|
||||
%31 = icmp eq i32 %30, 0
|
||||
br i1 %31, label %32, label %33
|
||||
|
||||
12:
|
||||
32:
|
||||
unreachable
|
||||
|
||||
13:
|
||||
%14 = udiv i32 %8, %10
|
||||
%15 = icmp ugt i32 %14, 255
|
||||
br i1 %15, label %16, label %17
|
||||
33:
|
||||
%34 = udiv i32 %28, %30
|
||||
%35 = icmp ugt i32 %34, 255
|
||||
br i1 %35, label %36, label %37
|
||||
|
||||
16:
|
||||
36:
|
||||
unreachable
|
||||
|
||||
17:
|
||||
%18 = and i32 %14, 255
|
||||
%19 = urem i32 %8, %10
|
||||
%20 = and i32 %19, 255
|
||||
%21 = load i64, ptr %_rax, align 8
|
||||
%22 = and i64 %21, u0xffffffffffff0000
|
||||
%23 = shl i32 %20, 8
|
||||
%24 = zext i32 %23 to i64
|
||||
%25 = or i64 %22, %24
|
||||
%26 = zext i32 %18 to i64
|
||||
%27 = or i64 %25, %26
|
||||
store i64 %27, ptr %_rax, align 8
|
||||
37:
|
||||
%38 = and i32 %34, 255
|
||||
%39 = urem i32 %28, %30
|
||||
%40 = and i32 %39, 255
|
||||
%41 = load i64, ptr %_rax, align 8
|
||||
%42 = and i64 %41, u0xffffffffffff0000
|
||||
%43 = shl i32 %40, 8
|
||||
%44 = zext i32 %43 to i64
|
||||
%45 = or i64 %42, %44
|
||||
%46 = zext i32 %38 to i64
|
||||
%47 = or i64 %45, %46
|
||||
store i64 %47, ptr %_rax, align 8
|
||||
br label %helper_divb_AL.exit
|
||||
|
||||
helper_divb_AL.exit:
|
||||
```
|
||||
|
||||
The `call void @helper_divb_AL(...)` is gone — its body has been inlined.
|
||||
The `call void @helper_divb_AL(...)` is gone, its body has been inlined.
|
||||
Since `remove-exceptional-functions` is part of the `enforce-abi` pipeline, the `raise_exception_ra` calls (which are `REVNG_EXCEPTIONAL`) have already been replaced with `unreachable`.
|
||||
|
||||
Compare this with the C source: the `udiv`/`urem` implement the division, `%_rax` is the accumulator, and the two `unreachable` blocks (labels 12 and 16) are where `raise_exception_ra` used to be (division-by-zero and quotient-overflow checks).
|
||||
@@ -560,22 +560,22 @@ $ revng opt -strip-debug -simplifycfg -remove-llvmassume-calls -dce -S enforced.
|
||||
| pretty \
|
||||
| sed -n "1p; /and i64.*u0xffff$/,/store i64.*%_rax/p"
|
||||
define i64 @local_0x400000_Code_x86_64(i64 %rdi_x86_64, i64 %rsi_x86_64) {
|
||||
%7 = and i64 %6, u0xffff
|
||||
%8 = trunc i64 %7 to i32
|
||||
%9 = and i64 %5, 255
|
||||
%10 = trunc i64 %9 to i32
|
||||
%11 = udiv i32 %8, %10
|
||||
%12 = and i32 %11, 255
|
||||
%13 = urem i32 %8, %10
|
||||
%14 = and i32 %13, 255
|
||||
%15 = load i64, ptr %_rax, align 8
|
||||
%16 = and i64 %15, u0xffffffffffff0000
|
||||
%17 = shl i32 %14, 8
|
||||
%18 = zext i32 %17 to i64
|
||||
%19 = or i64 %16, %18
|
||||
%20 = zext i32 %12 to i64
|
||||
%21 = or i64 %19, %20
|
||||
store i64 %21, ptr %_rax, align 8
|
||||
%27 = and i64 %26, u0xffff
|
||||
%28 = trunc i64 %27 to i32
|
||||
%29 = and i64 %25, 255
|
||||
%30 = trunc i64 %29 to i32
|
||||
%31 = udiv i32 %28, %30
|
||||
%32 = and i32 %31, 255
|
||||
%33 = urem i32 %28, %30
|
||||
%34 = and i32 %33, 255
|
||||
%35 = load i64, ptr %_rax, align 8
|
||||
%36 = and i64 %35, u0xffffffffffff0000
|
||||
%37 = shl i32 %34, 8
|
||||
%38 = zext i32 %37 to i64
|
||||
%39 = or i64 %36, %38
|
||||
%40 = zext i32 %32 to i64
|
||||
%41 = or i64 %39, %40
|
||||
store i64 %41, ptr %_rax, align 8
|
||||
```
|
||||
|
||||
The exceptional calls and dead code are completely gone.
|
||||
|
||||
@@ -33,26 +33,26 @@ EXAMPLES
|
||||
A single command to produce the decompiled code saving the result to `decompiled.c`:
|
||||
|
||||
```
|
||||
revng artifact --analyze decompile-to-single-file /usr/bin/hostname -o decompiled.c
|
||||
revng artifact --analyze emit-c-as-single-file /usr/bin/hostname -o decompiled.c
|
||||
```
|
||||
|
||||
An equivalent command using `--analyses`:
|
||||
|
||||
```
|
||||
revng artifact --analyses=revng-initial-auto-analysis decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --analyses=revng-initial-auto-analysis emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
An equivalent set of commands using [`revng-analyze`](revng-analyze.md) and `--resume`:
|
||||
|
||||
```{bash notest}
|
||||
revng analyze --resume project-dir/ revng-initial-auto-analysis /usr/bin/hostname
|
||||
revng artifact --resume project-dir/ decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --resume project-dir/ emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
Decompile a program using `mymodel.yml` as the model:
|
||||
|
||||
```{bash notest}
|
||||
revng artifact --model mymodel.yml decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --model mymodel.yml emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
SEE ALSO
|
||||
|
||||
@@ -40,26 +40,26 @@ EXAMPLES
|
||||
A single command to produce the decompiled code saving the result to `decompiled.c`:
|
||||
|
||||
```
|
||||
revng artifact --analyze decompile-to-single-file /usr/bin/hostname -o decompiled.c
|
||||
revng artifact --analyze emit-c-as-single-file /usr/bin/hostname -o decompiled.c
|
||||
```
|
||||
|
||||
An equivalent command using `--analyses`:
|
||||
|
||||
```
|
||||
revng artifact --analyses=revng-initial-auto-analysis decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --analyses=revng-initial-auto-analysis emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
An equivalent set of commands using [`revng-analyze`](revng-analyze.md) and `--resume`:
|
||||
|
||||
```{bash notest}
|
||||
revng analyze --resume project-dir/ revng-initial-auto-analysis /usr/bin/hostname
|
||||
revng artifact --resume project-dir/ decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --resume project-dir/ emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
Decompile a program using `mymodel.yml` as the model:
|
||||
|
||||
```{bash notest}
|
||||
revng artifact --model mymodel.yml decompile-to-single-file /usr/bin/hostname
|
||||
revng artifact --model mymodel.yml emit-c-as-single-file /usr/bin/hostname
|
||||
```
|
||||
|
||||
SEE ALSO
|
||||
|
||||
@@ -6,29 +6,32 @@ USAGE: revng-artifact [options] <artifact> <binary>
|
||||
|
||||
<artifact> can be one of:
|
||||
|
||||
lift - application/x.llvm.bc+zstd
|
||||
isolate - application/x.llvm.bc+zstd
|
||||
enforce-abi - application/x.llvm.bc+zstd
|
||||
emit-cfg - text/yaml+tar+gz
|
||||
hexdump - text/x.hexdump+ptml
|
||||
render-svg-call-graph - image/svg
|
||||
render-svg-call-graph-slice - image/svg
|
||||
disassemble - text/x.asm+ptml+tar+gz
|
||||
render-svg-cfg - image/svg
|
||||
recompile - application/x-executable
|
||||
recompile-isolated - application/x-executable
|
||||
simplify-switch - application/x.llvm.bc+zstd
|
||||
make-segment-ref - application/x.llvm.bc+zstd
|
||||
decompile - text/x.c+ptml+tar+gz
|
||||
decompile-to-single-file - text/x.c+ptml
|
||||
emit-recompilable-archive - application/x.recompilable-archive
|
||||
emit-helpers-header - text/x.c+ptml
|
||||
emit-model-header - text/x.c+ptml
|
||||
emit-type-definitions - text/x.c+tar+gz
|
||||
cleanup-ir - application/x.llvm.bc+zstd
|
||||
segregate-stack-accesses - application/x.llvm.bc+zstd
|
||||
emit-c - text/x.c+ptml+tar+gz
|
||||
emit-c-as-single-file - text/x.c+ptml
|
||||
lift - application/x.llvm.bc+zstd
|
||||
isolate - application/x.llvm.bc+zstd
|
||||
enforce-abi - application/x.llvm.bc+zstd
|
||||
emit-cfg - text/yaml+tar+gz
|
||||
hexdump - text/x.hexdump+ptml
|
||||
render-svg-call-graph - image/svg
|
||||
render-svg-call-graph-slice - image/svg
|
||||
disassemble - text/x.asm+ptml+tar+gz
|
||||
render-svg-cfg - image/svg
|
||||
recompile - application/x-executable
|
||||
recompile-isolated - application/x-executable
|
||||
simplify-switch - application/x.llvm.bc+zstd
|
||||
make-segment-ref - application/x.llvm.bc+zstd
|
||||
legacy-decompile - text/x.c+ptml+tar+gz
|
||||
legacy-decompile-to-single-file - text/x.c+ptml
|
||||
legacy-emit-helpers-header - text/x.c+ptml
|
||||
legacy-emit-model-header - text/x.c+ptml
|
||||
legacy-emit-type-definitions - text/x.c+tar+gz
|
||||
cleanup-ir - application/x.llvm.bc+zstd
|
||||
segregate-stack-accesses - application/x.llvm.bc+zstd
|
||||
emit-c - text/x.c+ptml+tar+gz
|
||||
emit-c-as-single-file - text/x.c+ptml
|
||||
import-types - application/x.mlir.bc
|
||||
emit-type-and-global-header - text/x.h+ptml
|
||||
emit-helper-header - text/x.h+ptml
|
||||
emit-single-type-definition - text/x.c+tar+gz
|
||||
```
|
||||
|
||||
## MIME types
|
||||
|
||||
@@ -53,7 +53,7 @@ Once you have successfully loaded a binary, you can obtain the available artifac
|
||||
>>> project.model.Functions[0].disassemble
|
||||
|
||||
# You can also get the artifact for `TypeDefinitions`
|
||||
>>> project.model.TypeDefinitions[1].get_artifact("emit-type-definitions")
|
||||
>>> project.model.TypeDefinitions[1].get_artifact("emit-single-type-definition")
|
||||
```
|
||||
|
||||
You can also `parse` the result with `ptml`:
|
||||
|
||||
@@ -40,12 +40,11 @@ $ revng analyze \
|
||||
-o /dev/null
|
||||
$ revng artifact \
|
||||
--resume=working-directory \
|
||||
decompile-to-single-file \
|
||||
emit-c-as-single-file \
|
||||
example \
|
||||
| revng ptml | grep -A2 -B1 -F ' main('
|
||||
_ABI(SystemV_x86_64)
|
||||
generic64_t main(generic64_t argument_0) {
|
||||
return (argument_0 * 3) & 0xFFFFFFFF;
|
||||
_ABI(SystemV_x86_64) generic64_t main(generic64_t argument_0) {
|
||||
return argument_0 * 3UL & 0xFFFFFFFFUL;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,12 +56,11 @@ Alternatively, you can run `revng-initial-auto-analysis` *and* produce the artif
|
||||
```bash
|
||||
$ revng artifact \
|
||||
--analyze \
|
||||
decompile-to-single-file \
|
||||
emit-c-as-single-file \
|
||||
example \
|
||||
| revng ptml \
|
||||
| grep -A2 -B1 -F ' main('
|
||||
_ABI(SystemV_x86_64)
|
||||
generic64_t main(generic64_t argument_0) {
|
||||
return (argument_0 * 3) & 0xFFFFFFFF;
|
||||
_ABI(SystemV_x86_64) generic64_t main(generic64_t argument_0) {
|
||||
return argument_0 * 3UL & 0xFFFFFFFFUL;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -33,20 +33,26 @@ Containers:
|
||||
Type: function-control-flow-graph-svg
|
||||
- Name: cfg.yml.tar.gz
|
||||
Type: cfg
|
||||
- Name: types-and-globals.h
|
||||
Type: model-header
|
||||
- Name: helpers.h
|
||||
Type: helpers-header
|
||||
- Name: legacy-types-and-globals.h
|
||||
Type: legacy-model-header
|
||||
- Name: legacy-helpers.h
|
||||
Type: legacy-helpers-header
|
||||
- Name: decompiled.c
|
||||
Type: decompiled-c-code
|
||||
- Name: decompiled.tar.gz
|
||||
Type: decompile
|
||||
- Name: recompilable-archive.tar.gz
|
||||
Type: recompilable-archive
|
||||
- Name: functions.mlir
|
||||
Type: clift-functions
|
||||
- Name: model-type-definitions.tar.gz
|
||||
Type: model-type-definitions
|
||||
- Name: types-and-globals.mlir
|
||||
Type: clift-module
|
||||
- Name: legacy-model-type-definitions.tar.gz
|
||||
Type: legacy-model-type-definitions
|
||||
- Name: types-and-globals.h
|
||||
Type: type-and-global-header
|
||||
- Name: helpers.h
|
||||
Type: helper-header
|
||||
- Name: single-type-definition.tar.gz
|
||||
Type: single-type-definition
|
||||
Branches:
|
||||
- Steps:
|
||||
- Name: initial
|
||||
@@ -478,7 +484,7 @@ Branches:
|
||||
program and detects the layout of data structures.
|
||||
The produced data structures are the result of merging the
|
||||
information obtained from each function interprocedurally.
|
||||
- Name: canonicalize
|
||||
- Name: legacy-canonicalize
|
||||
Pipes:
|
||||
- Type: llvm-pipe
|
||||
UsedContainers: [functions.bc.zstd]
|
||||
@@ -504,16 +510,16 @@ Branches:
|
||||
- pretty-int-formatting
|
||||
- discard-broken-debug-information
|
||||
- strip-dead-prototypes
|
||||
- Name: embed-statement-comments
|
||||
- Name: legacy-embed-statement-comments
|
||||
Pipes:
|
||||
- Type: embed-statement-comments
|
||||
- Type: legacy-embed-statement-comments
|
||||
UsedContainers: [functions.bc.zstd]
|
||||
- Name: decompile
|
||||
- Name: legacy-decompile
|
||||
Pipes:
|
||||
- Type: helpers-to-header
|
||||
UsedContainers: [functions.bc.zstd, helpers.h]
|
||||
- Type: model-to-header
|
||||
UsedContainers: [input, types-and-globals.h]
|
||||
- Type: legacy-helpers-to-header
|
||||
UsedContainers: [functions.bc.zstd, legacy-helpers.h]
|
||||
- Type: legacy-model-to-header
|
||||
UsedContainers: [input, legacy-types-and-globals.h]
|
||||
- Type: decompile
|
||||
UsedContainers: [functions.bc.zstd, cfg.yml.tar.gz, decompiled.tar.gz]
|
||||
Artifacts:
|
||||
@@ -536,9 +542,9 @@ Branches:
|
||||
UsedContainers: [decompiled.tar.gz]
|
||||
Docs: |-
|
||||
Rename the specified function(s) bodies using an LLM
|
||||
- Name: decompile-to-single-file
|
||||
- Name: legacy-decompile-to-single-file
|
||||
Pipes:
|
||||
- Type: decompile-to-single-file
|
||||
- Type: emit-c-as-single-file
|
||||
UsedContainers: [decompiled.tar.gz, decompiled.c]
|
||||
Artifacts:
|
||||
Container: decompiled.c
|
||||
@@ -548,64 +554,41 @@ Branches:
|
||||
This artifact is a single PTML file representing the decompiled C
|
||||
code of the whole program, including the body of all of program's
|
||||
functions.
|
||||
- From: embed-statement-comments
|
||||
- From: legacy-canonicalize
|
||||
Steps:
|
||||
- Name: emit-recompilable-archive
|
||||
- Name: legacy-emit-helpers-header
|
||||
Pipes:
|
||||
- Type: decompile-to-directory
|
||||
UsedContainers: [functions.bc.zstd, cfg.yml.tar.gz, recompilable-archive.tar.gz]
|
||||
- Type: legacy-helpers-to-header
|
||||
UsedContainers: [functions.bc.zstd, legacy-helpers.h]
|
||||
Artifacts:
|
||||
Container: recompilable-archive.tar.gz
|
||||
Kind: recompilable-archive
|
||||
SingleTargetFilename: recompilable-archive.tar.gz
|
||||
Docs: |-
|
||||
This artifact is an archive containing all the files necessary to
|
||||
recompile the decompiled C code of the input program.
|
||||
These files are not in PTML, they are plain C.
|
||||
|
||||
It contains:
|
||||
|
||||
- `functions.c`: the `decompile-to-single-file` artifact;
|
||||
- `types-and-globals.h`: see the `emit-model-header` artifact;
|
||||
- `helpers.h`: see the `emit-helpers-header` artifact;
|
||||
- `attributes.h`: an helper header file defining a set of
|
||||
annotations used by the decompiled C source files;
|
||||
- `primitive-types.h`: a header defining all the primitive types.
|
||||
- From: canonicalize
|
||||
Steps:
|
||||
- Name: emit-helpers-header
|
||||
Pipes:
|
||||
- Type: helpers-to-header
|
||||
UsedContainers: [functions.bc.zstd, helpers.h]
|
||||
Artifacts:
|
||||
Container: helpers.h
|
||||
Kind: helpers-header
|
||||
SingleTargetFilename: helpers.h
|
||||
Container: legacy-helpers.h
|
||||
Kind: legacy-helpers-header
|
||||
Docs: |-
|
||||
This artifact contains the declarations of all the helpers used
|
||||
the decompiled code.
|
||||
- From: initial
|
||||
Steps:
|
||||
- Name: emit-model-header
|
||||
- Name: legacy-emit-model-header
|
||||
Pipes:
|
||||
- Type: model-to-header
|
||||
UsedContainers: [input, types-and-globals.h]
|
||||
- Type: legacy-model-to-header
|
||||
UsedContainers: [input, legacy-types-and-globals.h]
|
||||
Artifacts:
|
||||
Container: types-and-globals.h
|
||||
Kind: model-header
|
||||
SingleTargetFilename: types-and-globals.h
|
||||
Container: legacy-types-and-globals.h
|
||||
Kind: legacy-model-header
|
||||
Docs: |-
|
||||
This artifact contains all the declaration of types, functions and
|
||||
segments defined in the binary.
|
||||
- From: canonicalize
|
||||
- From: legacy-canonicalize
|
||||
Steps:
|
||||
- Name: emit-type-definitions
|
||||
- Name: legacy-emit-type-definitions
|
||||
Pipes:
|
||||
- Type: generate-model-type-definition
|
||||
UsedContainers: [input, model-type-definitions.tar.gz]
|
||||
- Type: legacy-generate-model-type-definition
|
||||
UsedContainers: [input, legacy-model-type-definitions.tar.gz]
|
||||
Artifacts:
|
||||
Container: model-type-definitions.tar.gz
|
||||
Kind: model-type-definition
|
||||
Container: legacy-model-type-definitions.tar.gz
|
||||
Kind: legacy-model-type-definition
|
||||
SingleTargetFilename: type.h
|
||||
Docs: |-
|
||||
This artifact is an archive of plain C headers.
|
||||
@@ -614,7 +597,7 @@ Branches:
|
||||
This artifact is designed to be used as the initial input of the
|
||||
`import-from-c` analysis. In fact, this artifact is designed to be
|
||||
easily editable by the end-user; it's not designed to represent
|
||||
valid C code, unlike the `emit-model-header` artifact.
|
||||
valid C code, unlike the `legacy-emit-model-header` artifact.
|
||||
- From: make-segment-ref
|
||||
Steps:
|
||||
- Name: cleanup-ir
|
||||
@@ -726,7 +709,7 @@ Branches:
|
||||
program.
|
||||
- Name: emit-c-as-single-file
|
||||
Pipes:
|
||||
- Type: decompile-to-single-file
|
||||
- Type: emit-c-as-single-file
|
||||
UsedContainers: [decompiled.tar.gz, decompiled.c]
|
||||
Artifacts:
|
||||
Container: decompiled.c
|
||||
@@ -736,6 +719,69 @@ Branches:
|
||||
This artifact is a single PTML file representing the decompiled C
|
||||
code of the whole program, including the body of all of program's
|
||||
functions. (Clift backend)
|
||||
- From: initial
|
||||
Steps:
|
||||
- Name: import-types
|
||||
Pipes:
|
||||
- Type: import-types
|
||||
UsedContainers: [input, types-and-globals.mlir]
|
||||
Artifacts:
|
||||
Container: types-and-globals.mlir
|
||||
Kind: clift-module
|
||||
SingleTargetFilename: types-and-globals.mlir
|
||||
Docs: |-
|
||||
This artifact contains all the declarations of non-primitive types
|
||||
without any name or comment information.
|
||||
- Name: emit-type-and-global-header
|
||||
Pipes:
|
||||
- Type: import-function-declarations
|
||||
UsedContainers: [types-and-globals.mlir]
|
||||
- Type: import-segment-declarations
|
||||
UsedContainers: [types-and-globals.mlir]
|
||||
- Type: import-descriptive-info
|
||||
UsedContainers: [types-and-globals.mlir]
|
||||
- Type: emit-type-and-global-header
|
||||
UsedContainers: [types-and-globals.mlir, types-and-globals.h]
|
||||
Artifacts:
|
||||
Container: types-and-globals.h
|
||||
Kind: type-and-global-header
|
||||
SingleTargetFilename: types-and-globals.h
|
||||
Docs: |-
|
||||
This artifact is a header containing all the declarations of
|
||||
non-primitive types, functions and segments.
|
||||
- From: emit-c
|
||||
Steps:
|
||||
- Name: emit-helper-header
|
||||
Pipes:
|
||||
- Type: emit-helper-header
|
||||
UsedContainers: [functions.mlir, helpers.h]
|
||||
Artifacts:
|
||||
Container: helpers.h
|
||||
Kind: helper-header
|
||||
SingleTargetFilename: helpers.h
|
||||
Docs: |-
|
||||
This artifact contains all the declarations of llvm and qemu helpers
|
||||
that survived until the decompiled code.
|
||||
- From: import-types
|
||||
Steps:
|
||||
- Name: emit-single-type-definition
|
||||
Pipes:
|
||||
- Type: import-descriptive-info
|
||||
UsedContainers: [types-and-globals.mlir]
|
||||
- Type: emit-single-type-definition
|
||||
UsedContainers: [types-and-globals.mlir, single-type-definition.tar.gz]
|
||||
Artifacts:
|
||||
Container: single-type-definition.tar.gz
|
||||
Kind: single-type-definition
|
||||
SingleTargetFilename: type.h
|
||||
Docs: |-
|
||||
This artifact is an archive of plain C headers.
|
||||
Each file contains the declaration of a type defined for this
|
||||
binary.
|
||||
This artifact is designed to be used as the initial input of the
|
||||
`import-from-c` analysis. In fact, this artifact is designed to be
|
||||
easily editable by the end-user; it's not designed to represent
|
||||
valid C code, unlike the `emit-type-and-global-header` artifact.
|
||||
AnalysesLists:
|
||||
- Name: revng-initial-auto-analysis
|
||||
Analyses:
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
#
|
||||
|
||||
commands:
|
||||
# TODO: drop this test once the old backend is removed
|
||||
- # Run legacy decompilation pipeline up to decompile-to-single-file
|
||||
type: revng.legacy.decompile-to-single-file
|
||||
- # Run decompilation pipeline up to emit-c-as-single-file
|
||||
type: revng.emit-c-as-single-file
|
||||
from:
|
||||
- type: revng-qa.compiled-with-debug-info
|
||||
filter: for-decompilation
|
||||
suffix: /
|
||||
command: |-
|
||||
revng artifact --resume "$OUTPUT" --analyze decompile-to-single-file "$INPUT" -o /dev/null
|
||||
revng2 -C "$OUTPUT" project init "$INPUT";
|
||||
revng2 -C "$OUTPUT" project artifact emit-c-as-single-file -o /dev/null
|
||||
- # Run the Clift decompilation pipeline
|
||||
type: revng.emit-c
|
||||
from:
|
||||
@@ -27,29 +27,25 @@ commands:
|
||||
- type: revng.emit-c
|
||||
command: |-
|
||||
revng model compare "${SOURCE}".model.yml "$INPUT/model.yml"
|
||||
# TODO: convert this test to the new backend when possible
|
||||
- # Check well-formedness of decompiled C
|
||||
type: revng.legacy.decompile-to-single-file.check-decompiled-c
|
||||
type: revng.emit-c-as-single-file.check-decompiled-c
|
||||
from:
|
||||
- type: revng-qa.compiled-with-debug-info
|
||||
filter: for-decompilation
|
||||
- type: revng.legacy.decompile-to-single-file
|
||||
- type: revng.emit-c-as-single-file
|
||||
suffix: /
|
||||
command: |-
|
||||
RESUME=$$(temp -d);
|
||||
cp -Tar "$INPUT2" "$$RESUME";
|
||||
revng artifact --resume "$$RESUME" decompile-to-single-file "$INPUT1" | revng ptml > "$OUTPUT/decompiled.c";
|
||||
revng artifact --resume "$$RESUME" emit-model-header "$INPUT1" | revng ptml > "$OUTPUT/types-and-globals.h";
|
||||
revng artifact --resume "$$RESUME" emit-helpers-header "$INPUT1" | revng ptml > "$OUTPUT/helpers.h";
|
||||
revng check-decompiled-c "$OUTPUT/decompiled.c" -I "$OUTPUT" -DLEGACY_BACKEND
|
||||
# TODO: convert this test to the new backend when possible
|
||||
revng2 -C "$INPUT" project artifact emit-c-as-single-file |
|
||||
yq -r '.["/binary"]' |
|
||||
revng ptml > "$OUTPUT/decompiled.c";
|
||||
revng2 -C "$INPUT" project artifact emit-type-and-global-header |
|
||||
yq -r '.["/binary"]' |
|
||||
revng ptml > "$OUTPUT/types-and-globals.h";
|
||||
revng2 -C "$INPUT" project artifact emit-helper-header |
|
||||
yq -r '.["/binary"]' |
|
||||
revng ptml > "$OUTPUT/helpers.h";
|
||||
revng check-decompiled-c "$OUTPUT/decompiled.c" -I "$OUTPUT"
|
||||
- # Perform checks on the decompiled C code
|
||||
type: revng.legacy.decompile-to-single-file.filecheck
|
||||
type: revng.emit-c-as-single-file.filecheck
|
||||
from:
|
||||
- type: revng-qa.compiled-with-debug-info
|
||||
filter: for-decompilation
|
||||
- type: revng.legacy.decompile-to-single-file
|
||||
- type: revng.emit-c-as-single-file
|
||||
command: |-
|
||||
RESUME=$$(temp -d);
|
||||
cp -Tar "$INPUT2" "$$RESUME";
|
||||
revng artifact --resume "$$RESUME" decompile-to-single-file "$INPUT1" | revng ptml | FileCheck "${SOURCE}".filecheck
|
||||
revng2 -C "$INPUT" project artifact emit-c-as-single-file | revng ptml | FileCheck "${SOURCE}".filecheck
|
||||
|
||||
@@ -96,90 +96,32 @@ commands:
|
||||
# both should contain the same members (except the extension).
|
||||
#
|
||||
# TODO: remove this when the old pipeline is removed
|
||||
- type: revng.legacy.decompiled-c-tar
|
||||
- type: revng.decompiled-c-tar
|
||||
from:
|
||||
- type: revng-qa.compiled
|
||||
filter: one-per-architecture
|
||||
- type: revng.lifted
|
||||
suffix: /
|
||||
command: |-
|
||||
revng artifact
|
||||
--resume "$OUTPUT"
|
||||
--analyze
|
||||
decompile "$INPUT" > "$OUTPUT/decompiled.tar.gz";
|
||||
cp -Tar "$INPUT" "$OUTPUT";
|
||||
revng2 -C "$OUTPUT" project artifact emit-c > "$OUTPUT/decompiled.tar.gz";
|
||||
|
||||
diff -u <(yq -r '.Functions[].Entry' "$OUTPUT/context/model.yml" | sort)
|
||||
<(tar -tf "$OUTPUT/decompiled.tar.gz" | sed 's|\.c\.ptml$$||' | sort);
|
||||
|
||||
#
|
||||
# Produce legacy single decompiled file and headers from revng.analyzed-model
|
||||
#
|
||||
# TODO: drop this when the old backend is dropped
|
||||
- type: revng.legacy.decompiled-c
|
||||
from:
|
||||
- type: revng-qa.compiled
|
||||
filter: one-per-architecture
|
||||
- type: revng.analyzed-model
|
||||
suffix: /
|
||||
command: |-
|
||||
./emit-and-check-c decompile-to-single-file "$INPUT1" "$INPUT2/model.yml" "$OUTPUT" "$POINTER_SIZE" -Wno-shift-count-overflow
|
||||
diff -u <(yq -r '.Functions[].Entry | "/function/" + .' "$OUTPUT/model.yml" | sort)
|
||||
<(yq -r '. | keys | .[]' "$OUTPUT/decompiled.tar.gz" | sort);
|
||||
|
||||
#
|
||||
# Produce the emit-c artifact and check the resulting C code
|
||||
#
|
||||
# TODO: port this to the new pipeline once it has the `emit-helpers-header` artifact
|
||||
- type: revng.emit-and-check-c
|
||||
from:
|
||||
- type: revng-qa.compiled
|
||||
filter: one-per-architecture
|
||||
- type: revng.analyzed-model
|
||||
- type: revng.lifted
|
||||
suffix: /
|
||||
command: |-
|
||||
./emit-and-check-c emit-c-as-single-file "$INPUT1" "$INPUT2/model.yml" "$OUTPUT" "$POINTER_SIZE"
|
||||
scripts:
|
||||
emit-and-check-c: |-
|
||||
#!/usr/bin/env bash
|
||||
cp -Tar "$INPUT" "$OUTPUT";
|
||||
revng2 -C "$OUTPUT" project artifact emit-recompilable-archive |
|
||||
yq -r '.["/binary"]' | base64 -d > "$OUTPUT/recompilable-archive.tar.gz";
|
||||
|
||||
set -euo pipefail
|
||||
mkdir -p "$OUTPUT";
|
||||
tar -xzf "$OUTPUT/recompilable-archive.tar.gz" -C "$OUTPUT";
|
||||
|
||||
ARTIFACT="$1"
|
||||
shift
|
||||
BINARY="$1"
|
||||
shift
|
||||
MODEL="$1"
|
||||
shift
|
||||
RESUME="$1"
|
||||
shift
|
||||
POINTER_SIZE="$1"
|
||||
shift
|
||||
|
||||
OUTDIR="$RESUME/e2e-test-$ARTIFACT"
|
||||
mkdir -p "$OUTDIR"
|
||||
|
||||
revng artifact \
|
||||
--model "$MODEL" \
|
||||
--resume "$RESUME" \
|
||||
emit-helpers-header \
|
||||
"$BINARY" \
|
||||
| revng ptml > "$OUTDIR/helpers.h"
|
||||
|
||||
revng artifact \
|
||||
--model "$MODEL" \
|
||||
--resume "$RESUME" \
|
||||
emit-model-header \
|
||||
"$BINARY" \
|
||||
| revng ptml > "$OUTDIR/types-and-globals.h"
|
||||
|
||||
revng artifact \
|
||||
--model "$MODEL" \
|
||||
--resume "$RESUME" \
|
||||
"$ARTIFACT" \
|
||||
"$BINARY" \
|
||||
| revng ptml > "$OUTDIR/decompiled.c"
|
||||
|
||||
DEFINES=""
|
||||
if [ "$ARTIFACT" == "decompile-to-single-file" ]; then
|
||||
DEFINES="-DLEGACY_BACKEND"
|
||||
fi
|
||||
|
||||
revng check-decompiled-c "$OUTDIR/decompiled.c" -I"$OUTDIR" "$@" -m"$POINTER_SIZE" $DEFINES \
|
||||
|& tee "$OUTDIR/check-decompiled-c.log"
|
||||
revng check-decompiled-c "$OUTPUT/decompiled/functions.c"
|
||||
-I"$OUTPUT/decompiled/" "$$@" -m"$POINTER_SIZE"
|
||||
|& tee "$OUTPUT/check-decompiled-c.log";
|
||||
|
||||
@@ -13,10 +13,12 @@ sources:
|
||||
- cft-with-complex-arguments
|
||||
- cft-with-no-abi
|
||||
- enum
|
||||
- enum-type-change
|
||||
- enum-with-a-broken-annotation
|
||||
- enum-with-a-non-primitive-underlying-type
|
||||
- enum-with-a-void-underlying-type
|
||||
- enum-without-an-annotation
|
||||
- oversized-field
|
||||
- primitives
|
||||
- rft
|
||||
- rft-with-a-misplaced-stack-argument
|
||||
@@ -45,6 +47,8 @@ sources:
|
||||
- struct-without-the-packed-attribute
|
||||
- struct-with-overlapping-fields
|
||||
- typedef
|
||||
- typedef-to-pointer
|
||||
- typedef-to-value
|
||||
- union
|
||||
|
||||
commands:
|
||||
@@ -83,6 +87,12 @@ commands:
|
||||
|
||||
if [[ "$$EXIT_CODE" -eq 0 ]]; then
|
||||
|
||||
if ! [[ -e "${INPUT}/check-against.yml" ]]; then
|
||||
echo "import-from-c succeeded, but an error was expected, stderr:";
|
||||
cat "${OUTPUT}/model.yml";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
revng model compare "${INPUT}/check-against.yml" "${OUTPUT}/model.yml";
|
||||
|
||||
else
|
||||
@@ -94,23 +104,23 @@ commands:
|
||||
fi;
|
||||
|
||||
if ! [[ -e "${INPUT}/expected-error.txt" ]]; then
|
||||
echo "import-from-c failed but no error was expected, stderr:";
|
||||
echo "import-from-c returned 1 but no error was expected, stderr:";
|
||||
cat "${OUTPUT}/error.txt";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
ACTUAL_ERROR=$$(grep -Pzo '(?<=RuntimeError: )[^\x00]*' "${OUTPUT}/error.txt" | tr -d '\0' || true);
|
||||
if [[ -z "$$ACTUAL_ERROR" ]]; then
|
||||
echo "import-from-c failed without reporting a RuntimeError, stderr:";
|
||||
echo "import-from-c returned 1 without reporting a RuntimeError, stderr:";
|
||||
cat "${OUTPUT}/error.txt";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
if ! [[ "$$(tail -n +5 "${INPUT}/expected-error.txt")" == "$$ACTUAL_ERROR" ]]; then
|
||||
echo "Expected:";
|
||||
cat "${INPUT}/expected-error.txt";
|
||||
echo "$$(tail -n +5 "${INPUT}/expected-error.txt")";
|
||||
echo "Got:";
|
||||
cat "${OUTPUT}/error.txt";
|
||||
echo "$$ACTUAL_ERROR";
|
||||
exit 1;
|
||||
fi;
|
||||
fi
|
||||
|
||||
@@ -16,7 +16,7 @@ sources:
|
||||
- annotate-attibutes.model.yml
|
||||
|
||||
commands:
|
||||
- type: revng.model-to-header-test
|
||||
- type: revng.type-and-global-header-test
|
||||
from:
|
||||
- type: source
|
||||
filter: model-to-header
|
||||
@@ -24,7 +24,7 @@ commands:
|
||||
command: |-
|
||||
PROJECT_DIR=$$(temp -d);
|
||||
cp "$INPUT" "$$PROJECT_DIR/model.yml";
|
||||
revng2 -C "$$PROJECT_DIR" project artifact emit-model-header |
|
||||
revng2 -C "$$PROJECT_DIR" project artifact emit-type-and-global-header |
|
||||
yq -r '.["/binary"]' |
|
||||
revng ptml > "$OUTPUT";
|
||||
revng check-decompiled-c "$OUTPUT";
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
commands:
|
||||
# This tests decompilation of some sample on-liner function representing all possible combinations of operators from different precedence groups.
|
||||
#
|
||||
# The goal is to ensure we emit parentheses around those correctly.
|
||||
# TODO: port this to the new backend when possible
|
||||
- type: revng.check-precedence
|
||||
from:
|
||||
- type: revng-qa.compiled-with-debug-info
|
||||
filter: for-precedence-tests
|
||||
suffix: .c
|
||||
command: |-
|
||||
revng artifact --analyze decompile-to-single-file "${INPUT}"
|
||||
| revng ptml
|
||||
| tee "${OUTPUT}"
|
||||
| FileCheck "${SOURCE}".filecheck
|
||||
@@ -31,21 +31,21 @@ commands:
|
||||
cd "$INPUT";
|
||||
"${SOURCES_ROOT}/share/revng/test/tests/pypeline-comparison/compare.sh" model.yml input disassemble;
|
||||
|
||||
# Run emit-model-header comparison
|
||||
- type: revng.test-pypeline-comparison-emit-model-header
|
||||
# Run emit-type-and-global-header comparison
|
||||
- type: revng.test-pypeline-comparison-emit-type-and-global-header
|
||||
from:
|
||||
- type: revng.test-pypeline-comparison-prepare
|
||||
command: |-
|
||||
cd "$INPUT";
|
||||
"${SOURCES_ROOT}/share/revng/test/tests/pypeline-comparison/compare.sh" model.yml input emit-model-header;
|
||||
"${SOURCES_ROOT}/share/revng/test/tests/pypeline-comparison/compare.sh" model.yml input emit-type-and-global-header;
|
||||
|
||||
# Run emit-type-definitions comparison
|
||||
- type: revng.test-pypeline-comparison-emit-type-definitions
|
||||
# Run emit-single-type-definition comparison
|
||||
- type: revng.test-pypeline-comparison-emit-single-type-definition
|
||||
from:
|
||||
- type: revng.test-pypeline-comparison-prepare
|
||||
command: |-
|
||||
cd "$INPUT";
|
||||
"${SOURCES_ROOT}/share/revng/test/tests/pypeline-comparison/compare.sh" model.yml input emit-type-definitions;
|
||||
"${SOURCES_ROOT}/share/revng/test/tests/pypeline-comparison/compare.sh" model.yml input emit-single-type-definition;
|
||||
|
||||
# Run hexdump comparison
|
||||
- type: revng.test-pypeline-comparison-hexdump
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) rev.ng Labs Srl. See LICENSE.md for details.
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
# In order to understand this file, see:
|
||||
|
||||
@@ -22,21 +22,19 @@ should be indexed by a index that increments by one at each iteration.
|
||||
CHECK: {{.*}}64_t sum([[NODE_STRUCT]] *[[ARG0:.*]])
|
||||
CHECK: do {
|
||||
CHECK: [[ACCUMULATOR:var_[0-9]+]] = [[ACCUMULATOR]] + [[ARG0]]->offset_0[[[INDEX:var_[0-9]+]]];
|
||||
CHECK: [[INDEX]] = [[INDEX]] + 1;
|
||||
CHECK: } while ([[INDEX]] != 5);
|
||||
CHECK: [[INDEX]] = [[INDEX]] + 1UL;
|
||||
CHECK: } while ([[INDEX]] != 5UL);
|
||||
CHECK: return [[ACCUMULATOR]];
|
||||
|
||||
|
||||
The compute function should take a pointer to node.
|
||||
TODO: it should also return a 64-bits integer. Try to re-add it later in the
|
||||
future, possibly when EFA 4 is ready.
|
||||
The compute function should take a pointer to node and return a 64-bits integer.
|
||||
It should contain a local variable PTR that is a pointer to node, and a do-while
|
||||
loop, where PTR is updated with PTR->offset_40 (representing the next pointer
|
||||
field), and PTR->offset_40 should also be the exit condition of the do-while.
|
||||
|
||||
CHECK: compute([[NODE_STRUCT]] *{{.*}})
|
||||
CHECK: [[NODE_STRUCT]] *[[PTR:var_[0-9]+]];
|
||||
CHECK: do {
|
||||
CHECK: [[ACCUMULATOR:var_[0-9]+]] = [[ACCUMULATOR]] + [[WHATEVER:.*]]
|
||||
CHECK: [[PTR]] = [[PTR]]->offset_40;
|
||||
CHECK: while ([[PTR]]);
|
||||
TODO: don't forget to re-enable these checks after the test is fixed
|
||||
|
||||
C_disabled_HECK: {{[a-z]+64_t}} compute([[NODE_STRUCT]] *{{.*}})
|
||||
C_disabled_HECK: do {
|
||||
C_disabled_HECK: [[ACCUMULATOR:var_[0-9]+]] = [[ACCUMULATOR]] + {{[a-z0-9_]+}};
|
||||
C_disabled_HECK: [[PTR:var_[0-9]+]] = *(({{[a-z0-9_]+}} *) [[PTR]] + 5UL);
|
||||
C_disabled_HECK: while ([[PTR]]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user