diff --git a/LICENSE.mit b/LICENSE.mit index 10e1d4d86..93021c457 100644 --- a/LICENSE.mit +++ b/LICENSE.mit @@ -1,7 +1,7 @@ MIT License Copyright (c) 2015-2017 Alessandro Di Federico -Copyright (c) 2017-2024 rev.ng Labs Srl +Copyright (c) 2017-2026 rev.ng Labs Srl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/include/revng/ABI/Definition.h b/include/revng/ABI/Definition.h index 4b08e14ca..6d65ace03 100644 --- a/include/revng/ABI/Definition.h +++ b/include/revng/ABI/Definition.h @@ -249,6 +249,8 @@ public: } }; +[[nodiscard]] CDataModel getDataModel(const model::Binary &Binary); + } // namespace abi #include "revng/ABI/Generated/Late/Definition.h" diff --git a/include/revng/ABI/ModelHelpers.h b/include/revng/ABI/ModelHelpers.h index 307b41b84..d81786e34 100644 --- a/include/revng/ABI/ModelHelpers.h +++ b/include/revng/ABI/ModelHelpers.h @@ -68,10 +68,6 @@ getStrongModelInfo(const llvm::Instruction *Inst, const model::Binary &Model); extern llvm::SmallVector getExpectedModelType(const llvm::Use *U, const model::Binary &Model); -extern llvm::SmallVector -flattenReturnTypes(const abi::FunctionType::Layout &Layout, - const model::Binary &Model); - /// \note This is the final prototype, after segregate-stack-access template inline llvm::FunctionType & diff --git a/include/revng/ADT/UniquedStack.h b/include/revng/ADT/UniquedStack.h deleted file mode 100644 index d2765936d..000000000 --- a/include/revng/ADT/UniquedStack.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#include -#include -#include - -#include "revng/Support/Assert.h" - -/// Stack where an element cannot be re-inserted in it's already in the stack -template -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 Set; - std::vector Queue; -}; diff --git a/include/revng/Backend/DecompileToDirectoryPipe.h b/include/revng/Backend/DecompileToDirectoryPipe.h deleted file mode 100644 index e20add322..000000000 --- a/include/revng/Backend/DecompileToDirectoryPipe.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#include -#include - -#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 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 diff --git a/include/revng/Backend/DecompileToSingleFilePipe.h b/include/revng/Backend/DecompileToSingleFilePipe.h deleted file mode 100644 index 753bf787a..000000000 --- a/include/revng/Backend/DecompileToSingleFilePipe.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#include -#include - -#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 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 diff --git a/include/revng/Clift/Clift.h b/include/revng/Clift/Clift.h index 0beea750e..b7309d52e 100644 --- a/include/revng/Clift/Clift.h +++ b/include/revng/Clift/Clift.h @@ -7,6 +7,7 @@ #include #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 makeContext(); -/// Sets the Clift module attribute on the specified module. -void setModuleAttr(mlir::ModuleOp Module); +/// Creates a new Clift module. +mlir::OwningOpRef 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. diff --git a/include/revng/Clift/CliftAttributes.h b/include/revng/Clift/CliftAttributes.h index 92663156f..e58b07269 100644 --- a/include/revng/Clift/CliftAttributes.h +++ b/include/revng/Clift/CliftAttributes.h @@ -20,14 +20,23 @@ namespace clift { +template +MutableStringAttr +makeNameAttr(mlir::MLIRContext *Context, llvm::StringRef Handle); + template MutableStringAttr makeNameAttr(mlir::MLIRContext *Context, llvm::StringRef Handle, - llvm::StringRef Name = ""); + llvm::StringRef Name); + +template +MutableStringAttr +makeCommentAttr(mlir::MLIRContext *Context, llvm::StringRef Handle); + template 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 -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 +MutableStringAttr makeNameAttr(mlir::MLIRContext *Context, + llvm::StringRef Handle, + llvm::StringRef Name) { + auto Attr = makeNameAttr(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 +MutableStringAttr +makeCommentAttr(mlir::MLIRContext *Context, llvm::StringRef Handle) { + return MutableStringAttr::get(Context, + StringPairAttr::get(Context, + T::CommentAttrKey, + Handle)); +} + template 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(Context, Handle); + revng_assert(Attr.getValue().empty()); + Attr.setValue(Name); + return Attr; } // VERY IMPORTANT!!! diff --git a/include/revng/Clift/CliftMutableStringAttr.h b/include/revng/Clift/CliftMutableStringAttr.h index c794d8981..90ae9e8e4 100644 --- a/include/revng/Clift/CliftMutableStringAttr.h +++ b/include/revng/Clift/CliftMutableStringAttr.h @@ -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); diff --git a/include/revng/CliftEmitC/CBackend.h b/include/revng/CliftEmitC/CBackend.h index 6647bfd15..0c6d8a447 100644 --- a/include/revng/CliftEmitC/CBackend.h +++ b/include/revng/CliftEmitC/CBackend.h @@ -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); diff --git a/include/revng/CliftEmitC/CEmitter.h b/include/revng/CliftEmitC/CEmitter.h index 0ec4637c5..2615c4b17 100644 --- a/include/revng/CliftEmitC/CEmitter.h +++ b/include/revng/CliftEmitC/CEmitter.h @@ -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(Type); + return mlir::isa(Type) or mlir::isa(Type); } diff --git a/include/revng/CliftEmitC/CSemantics.h b/include/revng/CliftEmitC/CSemantics.h index d2b9b108d..dcc322198 100644 --- a/include/revng/CliftEmitC/CSemantics.h +++ b/include/revng/CliftEmitC/CSemantics.h @@ -5,7 +5,6 @@ // #include "revng/Clift/Clift.h" -#include "revng/Support/CDataModel.h" // TODO: does this really belong with the emitters? diff --git a/include/revng/CliftEmitC/Configuration.h b/include/revng/CliftEmitC/Configuration.h index 4e5d69b84..9ccae569d 100644 --- a/include/revng/CliftEmitC/Configuration.h +++ b/include/revng/CliftEmitC/Configuration.h @@ -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; }; diff --git a/include/revng/CliftEmitC/Headers.h b/include/revng/CliftEmitC/Headers.h index 0a1d7f90d..a3986600e 100644 --- a/include/revng/CliftEmitC/Headers.h +++ b/include/revng/CliftEmitC/Headers.h @@ -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 Modules); + llvm::ArrayRef Modules, + const model::Binary &Binary); + +void emitSingleTypeDefinition(ptml::CTokenEmitter &Tokens, + const CDataModel &DataModel, + clift::DefinedType Type, + TypeEmitterConfiguration Configuration = {}); diff --git a/include/revng/CliftImportModel/ImportModel.h b/include/revng/CliftImportModel/ImportModel.h index 3dddf2f1f..804c929da 100644 --- a/include/revng/CliftImportModel/ImportModel.h +++ b/include/revng/CliftImportModel/ImportModel.h @@ -21,8 +21,9 @@ namespace clift { /// \return The corresponding Clift type, or null on failure. mlir::Type importType(llvm::function_ref 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 EmitError, /// \return The corresponding Clift type, or null on failure. mlir::Type importType(llvm::function_ref 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 diff --git a/include/revng/CliftPipes/Configuration.h b/include/revng/CliftPipes/Configuration.h new file mode 100644 index 000000000..c00112832 --- /dev/null +++ b/include/revng/CliftPipes/Configuration.h @@ -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 { + 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 { + 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(Config)); +} + +} // namespace revng::pypeline::piperuns diff --git a/include/revng/CliftPipes/EmitC.h b/include/revng/CliftPipes/EmitC.h index fef4328cd..45a4f9907 100644 --- a/include/revng/CliftPipes/EmitC.h +++ b/include/revng/CliftPipes/EmitC.h @@ -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, + PipeRunArgument, + PipeRunArgument>; + + 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 diff --git a/include/revng/Backend/DecompileToSingleFile.h b/include/revng/CliftPipes/EmitCAsSingleFile.h similarity index 53% rename from include/revng/Backend/DecompileToSingleFile.h rename to include/revng/CliftPipes/EmitCAsSingleFile.h index 64f3029bf..fb38c58e9 100644 --- a/include/revng/Backend/DecompileToSingleFile.h +++ b/include/revng/CliftPipes/EmitCAsSingleFile.h @@ -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 &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, @@ -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(); }; diff --git a/include/revng/CliftPipes/Headers.h b/include/revng/CliftPipes/Headers.h new file mode 100644 index 000000000..3c4fdd665 --- /dev/null +++ b/include/revng/CliftPipes/Headers.h @@ -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>; + + 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>; + + 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>; + + 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 diff --git a/include/revng/CliftPipes/ImportTypes.h b/include/revng/CliftPipes/ImportTypes.h new file mode 100644 index 000000000..752c6b207 --- /dev/null +++ b/include/revng/CliftPipes/ImportTypes.h @@ -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>; + + 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>; + + 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>; + + 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 diff --git a/include/revng/FunctionIsolation/EnforceABI.h b/include/revng/FunctionIsolation/EnforceABI.h index 0242159e1..a992788b5 100644 --- a/include/revng/FunctionIsolation/EnforceABI.h +++ b/include/revng/FunctionIsolation/EnforceABI.h @@ -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 { diff --git a/include/revng/HeadersGeneration/ModelToHeaderPipe.h b/include/revng/HeadersGeneration/ModelToHeaderPipe.h index 9d941a899..5006455c0 100644 --- a/include/revng/HeadersGeneration/ModelToHeaderPipe.h +++ b/include/revng/HeadersGeneration/ModelToHeaderPipe.h @@ -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 collectAllTypeSizes() const; + public: void dumpTypeGraph(const char *Path) const debug_function; diff --git a/include/revng/PTML/CBuilder.h b/include/revng/PTML/CBuilder.h index 409a0f281..d7611b860 100644 --- a/include/revng/PTML/CBuilder.h +++ b/include/revng/PTML/CBuilder.h @@ -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" diff --git a/include/revng/PTML/TokenDefinitions.h b/include/revng/PTML/TokenDefinitions.h deleted file mode 100644 index 1500dcea6..000000000 --- a/include/revng/PTML/TokenDefinitions.h +++ /dev/null @@ -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 - VariableTokensWithName(const llvm::StringRef VariableName, - const T Declaration, - const T Use) : - VariableTokens(Declaration, Use), VariableName(VariableName) {} -}; diff --git a/include/revng/Pipebox/Containers.h b/include/revng/Pipebox/Containers.h index e55b79fdf..09d224227 100644 --- a/include/revng/Pipebox/Containers.h +++ b/include/revng/Pipebox/Containers.h @@ -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 diff --git a/include/revng/PipeboxCommon/CliftContainers.h b/include/revng/PipeboxCommon/CliftContainers.h index 1b684b94d..5bdccd4a3 100644 --- a/include/revng/PipeboxCommon/CliftContainers.h +++ b/include/revng/PipeboxCommon/CliftContainers.h @@ -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(); - return Registry; - })(); - private: bool Disposable = false; - std::optional Context; + std::unique_ptr Context; std::map> Modules; public: - CliftFunctionContainer() : - Context(std::in_place_t{}, MLIRDialectRegistry, Threading) {} + CliftFunctionContainer() : Context(clift::makeContext()) {} public: std::set objects() const { @@ -47,12 +39,12 @@ public: void deserialize(const std::map> 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(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 &&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(); - return Registry; - })(); - private: bool Disposable = false; - std::optional Context; + std::unique_ptr Context; std::map> Modules; public: - CliftSingleTypeContainer() : - Context(std::in_place_t{}, MLIRDialectRegistry, Threading) {} + CliftSingleTypeContainer() : Context(clift::makeContext()) {} public: std::set objects() const { @@ -137,7 +117,7 @@ public: llvm::StringRef String(Buffer.data(), Buffer.size()); auto NewModule = mlir::parseSourceString(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 &&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(); - return Registry; - })(); - private: bool Disposable = false; - std::optional Context; + std::unique_ptr Context; mlir::OwningOpRef 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 objects() const { @@ -233,7 +198,7 @@ public: llvm::StringRef String(Buffer.data(), Buffer.size()); Module = mlir::parseSourceString(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 &&NewModule) { revng_assert(&*Context == NewModule->getContext()); - Module = NewModule; + Module = std::move(NewModule); } }; diff --git a/include/revng/PipeboxCommon/Helpers/Registrars.h b/include/revng/PipeboxCommon/Helpers/Registrars.h index 09c0e1853..9ca73a7d1 100644 --- a/include/revng/PipeboxCommon/Helpers/Registrars.h +++ b/include/revng/PipeboxCommon/Helpers/Registrars.h @@ -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 { return std::make_unique>(Config); diff --git a/include/revng/Pipes/Containers.h b/include/revng/Pipes/Containers.h index 8dad3f99e..c0478ceb1 100644 --- a/include/revng/Pipes/Containers.h +++ b/include/revng/Pipes/Containers.h @@ -16,4 +16,5 @@ using DecompileStringMap = FunctionStringMap<&kinds::Decompiled, DecompileName, DecompileMime, DecompileExtension>; + } // namespace revng::pipes diff --git a/include/revng/Pipes/Kinds.h b/include/revng/Pipes/Kinds.h index 4cb7291b4..833bedfe4 100644 --- a/include/revng/Pipes/Kinds.h +++ b/include/revng/Pipes/Kinds.h @@ -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 diff --git a/include/revng/TupleTree/TupleLikeTraits.h b/include/revng/TupleTree/TupleLikeTraits.h index a110d2591..8acd418c3 100644 --- a/include/revng/TupleTree/TupleLikeTraits.h +++ b/include/revng/TupleTree/TupleLikeTraits.h @@ -44,6 +44,10 @@ concept TraitedTupleLike = requires { typename TupleLikeTraits::tuple; typename TupleLikeTraits::Fields; + + // TODO: this should also check for `get(std::declval())`, but there's + // an empty struct in `SerializableGraph.h` for which it's impossible + // to instantiate. } && detail::HasTuple && std::is_enum_v::Fields>; // diff --git a/lib/ABI/DefaultFunctionPrototype.cpp b/lib/ABI/DefaultFunctionPrototype.cpp index 86e8467bf..3316f9f1f 100644 --- a/lib/ABI/DefaultFunctionPrototype.cpp +++ b/lib/ABI/DefaultFunctionPrototype.cpp @@ -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) { diff --git a/lib/ABI/Definition.cpp b/lib/ABI/Definition.cpp index 80f3f19b3..5582f4905 100644 --- a/lib/ABI/Definition.cpp +++ b/lib/ABI/Definition.cpp @@ -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 diff --git a/lib/ABI/FunctionType/Layout.cpp b/lib/ABI/FunctionType/Layout.cpp index 663e3b0bd..df66e81b0 100644 --- a/lib/ABI/FunctionType/Layout.cpp +++ b/lib/ABI/FunctionType/Layout.cpp @@ -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 { diff --git a/lib/ABI/ModelHelpers.cpp b/lib/ABI/ModelHelpers.cpp index 010c539ed..bc116f5ac 100644 --- a/lib/ABI/ModelHelpers.cpp +++ b/lib/ABI/ModelHelpers.cpp @@ -200,7 +200,7 @@ static model::UpcastableType traverseModelGEP(const model::Binary &Model, return *Result; } -llvm::SmallVector +static llvm::SmallVector flattenReturnTypes(const abi::FunctionType::Layout &Layout, const model::Binary &Model) { diff --git a/lib/Backend/CMakeLists.txt b/lib/Backend/CMakeLists.txt index a586fa354..30003d544 100644 --- a/lib/Backend/CMakeLists.txt +++ b/lib/Backend/CMakeLists.txt @@ -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 diff --git a/lib/Backend/DecompileFunction.cpp b/lib/Backend/DecompileFunction.cpp index 97f686fa0..3cc329a19 100644 --- a/lib/Backend/DecompileFunction.cpp +++ b/lib/Backend/DecompileFunction.cpp @@ -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; using ModelTypesMap = std::map 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 Y; diff --git a/lib/Backend/DecompileToSingleFile.cpp b/lib/Backend/DecompileToSingleFile.cpp deleted file mode 100644 index 720517936..000000000 --- a/lib/Backend/DecompileToSingleFile.cpp +++ /dev/null @@ -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 &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 diff --git a/lib/Backend/DecompileToSingleFilePipe.cpp b/lib/Backend/DecompileToSingleFilePipe.cpp deleted file mode 100644 index becfdb949..000000000 --- a/lib/Backend/DecompileToSingleFilePipe.cpp +++ /dev/null @@ -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 - 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 Y; diff --git a/lib/Canonicalize/EmbedStatementComments.cpp b/lib/Canonicalize/EmbedStatementComments.cpp index 284638e1c..4f284d1a7 100644 --- a/lib/Canonicalize/EmbedStatementComments.cpp +++ b/lib/Canonicalize/EmbedStatementComments.cpp @@ -49,7 +49,7 @@ struct yield::StatementTraits { 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) { diff --git a/lib/Canonicalize/ExitSSAPass.cpp b/lib/Canonicalize/ExitSSAPass.cpp index 92b40aa6e..374490ac1 100644 --- a/lib/Canonicalize/ExitSSAPass.cpp +++ b/lib/Canonicalize/ExitSSAPass.cpp @@ -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" diff --git a/lib/Clift/Clift.cpp b/lib/Clift/Clift.cpp index 04ca31145..22a39cada 100644 --- a/lib/Clift/Clift.cpp +++ b/lib/Clift/Clift.cpp @@ -13,6 +13,31 @@ #include "revng/Clift/CliftAttributes.h" #include "revng/Clift/CliftOpHelpers.h" +std::unique_ptr clift::makeContext() { + static constexpr auto Threading = mlir::MLIRContext::Threading::DISABLED; + static const mlir::DialectRegistry Registry = []() -> mlir::DialectRegistry { + mlir::DialectRegistry Registry; + Registry.insert(); + return Registry; + }(); + + auto Result = std::make_unique(Registry, Threading); + Result->loadDialect(); + return Result; +} + +mlir::OwningOpRef +clift::makeModule(mlir::MLIRContext &Context) { + auto DebugLocation = mlir::UnknownLoc::get(&Context); + mlir::OwningOpRef + Result = mlir::ModuleOp::create(DebugLocation); + + Result.get()->setAttr(CliftDialect::getModuleAttrName(), + mlir::UnitAttr::get(&Context)); + + return Result; +} + using UnresolvedOperandsVector = // llvm::SmallVectorImpl; @@ -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(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(Attr).getDataModel(); diff --git a/lib/Clift/CliftAttributes.cpp b/lib/Clift/CliftAttributes.cpp index e72b8e70b..cad96bb75 100644 --- a/lib/Clift/CliftAttributes.cpp +++ b/lib/Clift/CliftAttributes.cpp @@ -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; } diff --git a/lib/Clift/CliftTypes.cpp b/lib/Clift/CliftTypes.cpp index 72c33bc28..8b267649a 100644 --- a/lib/Clift/CliftTypes.cpp +++ b/lib/Clift/CliftTypes.cpp @@ -158,10 +158,15 @@ AddressableType VoidType::removeConst() const { template> 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 ----------------------------===// diff --git a/lib/CliftEmitC/CEmissionPasses.cpp b/lib/CliftEmitC/CEmissionPasses.cpp index 434415614..640228e91 100644 --- a/lib/CliftEmitC/CEmissionPasses.cpp +++ b/lib/CliftEmitC/CEmissionPasses.cpp @@ -97,7 +97,7 @@ using HHBase = clift::impl::CliftEmitHelperHeaderBase; clift::PassPtr clift::createEmitHelperHeaderPass() { static constexpr auto Impl = [](mlir::ModuleOp Module, ptml::CTokenEmitter &Tokens) { - emitHelperHeader(Tokens, { Module }); + emitHelperHeader(Tokens, { Module }, model::Binary{}); return true; }; diff --git a/lib/CliftEmitC/CEmitter.cpp b/lib/CliftEmitC/CEmitter.cpp index bc1336d4d..adaa69a20 100644 --- a/lib/CliftEmitC/CEmitter.cpp +++ b/lib/CliftEmitC/CEmitter.cpp @@ -82,10 +82,18 @@ private: return Name; } - std::optional commentableReturnValueGuard() { + std::optional + 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 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 Emitter = std::nullopt; // Function comment @@ -529,6 +540,10 @@ void CEmitter::emitFunctionDoxygenComment(FunctionOp Function) { auto CommentBody = mlir::cast(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"); } diff --git a/lib/CliftEmitC/CMakeLists.txt b/lib/CliftEmitC/CMakeLists.txt index df00f9421..52941eb5b 100644 --- a/lib/CliftEmitC/CMakeLists.txt +++ b/lib/CliftEmitC/CMakeLists.txt @@ -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) diff --git a/lib/CliftEmitC/Headers.cpp b/lib/CliftEmitC/Headers.cpp index d63e3574e..8d4a6971a 100644 --- a/lib/CliftEmitC/Headers.cpp +++ b/lib/CliftEmitC/Headers.cpp @@ -2,17 +2,47 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include + #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 { +private: + using Base = clift::ModuleVisitor; + +private: + std::set &Result; + +public: + OpaqueTypeSizeCollector(std::set &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 ByteSizeList = std::initializer_list> + 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 + collectOpaqueByteSizes(llvm::ArrayRef Modules) { + std::set Result; + + for (mlir::ModuleOp Module : Modules) { + auto R = OpaqueTypeSizeCollector::visit(Module, Result); + revng_assert(R.succeeded()); + } + + return Result; + } + public: void emitHelpers(llvm::ArrayRef 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 Modules) { + llvm::ArrayRef 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 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(); } diff --git a/lib/CliftEmitC/TypeDefinitionEmitter.cpp b/lib/CliftEmitC/TypeDefinitionEmitter.cpp index 3dcbf6475..873e917ab 100644 --- a/lib/CliftEmitC/TypeDefinitionEmitter.cpp +++ b/lib/CliftEmitC/TypeDefinitionEmitter.cpp @@ -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(Type)) { emitTypedefDefinition(Typedef); - } else if (auto Enum = mlir::dyn_cast(Type)) { - emitEnumDefinition(Enum); - } else if (auto Function = mlir::dyn_cast(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(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); } } diff --git a/lib/CliftEmitC/TypeDependencyGraph.cpp b/lib/CliftEmitC/TypeDependencyGraph.cpp index 5a984326d..310c94e9d 100644 --- a/lib/CliftEmitC/TypeDependencyGraph.cpp +++ b/lib/CliftEmitC/TypeDependencyGraph.cpp @@ -293,6 +293,10 @@ mlir::LogicalResult Builder::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; diff --git a/lib/CliftImportModel/CMakeLists.txt b/lib/CliftImportModel/CMakeLists.txt index a6c4af49e..3c2d3f502 100644 --- a/lib/CliftImportModel/CMakeLists.txt +++ b/lib/CliftImportModel/CMakeLists.txt @@ -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) diff --git a/lib/CliftImportModel/ImportDescriptiveInfo.cpp b/lib/CliftImportModel/ImportDescriptiveInfo.cpp index c11f9e423..bdc858319 100644 --- a/lib/CliftImportModel/ImportDescriptiveInfo.cpp +++ b/lib/CliftImportModel/ImportDescriptiveInfo.cpp @@ -5,6 +5,7 @@ #include #include +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "mlir/IR/RegionGraphTraits.h" @@ -110,7 +111,24 @@ public: class SymbolRenamer { llvm::DenseMap 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 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(Op)) return visitStatementOp(S); + if (auto U = mlir::dyn_cast(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(); + revng_assert(Module); + + mlir::Operation *Target = // + mlir::SymbolTable::lookupSymbolIn(Module, Use.getSymbolNameAttr()); + + if (auto F = mlir::dyn_cast_or_null(Target)) + return recordFunctionOpName(F); + + if (auto G = mlir::dyn_cast_or_null(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) { diff --git a/lib/CliftImportModel/ImportTypes.cpp b/lib/CliftImportModel/ImportTypes.cpp index 2eab727d4..03629227b 100644 --- a/lib/CliftImportModel/ImportTypes.cpp +++ b/lib/CliftImportModel/ImportTypes.cpp @@ -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 EmitError) : Context(Context), EmitError(EmitError) {} @@ -91,14 +93,12 @@ private: } template - clift::MutableStringAttr - makeNameAttr(llvm::StringRef Handle, llvm::StringRef Name = {}) { - return clift::makeNameAttr(Context, Handle, Name); + clift::MutableStringAttr makeNameAttr(llvm::StringRef Handle) { + return clift::makeNameAttr(Context, Handle); } template - clift::MutableStringAttr - makeCommentAttr(llvm::StringRef Handle, llvm::StringRef Comment = {}) { - return clift::makeCommentAttr(Context, Handle, Comment); + clift::MutableStringAttr makeCommentAttr(llvm::StringRef Handle) { + return clift::makeCommentAttr(Context, Handle); } static clift::IntegerKind @@ -655,18 +655,31 @@ private: mlir::Type clift::importType(llvm::function_ref 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 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; @@ -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 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 +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(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(&Context, Handle); + auto CommentAttr = makeCommentAttr(&Context, Handle); + auto Attrs = llvm::ArrayRef{}; + 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); } diff --git a/lib/CliftPipes/CMakeLists.txt b/lib/CliftPipes/CMakeLists.txt index e8061f043..a932cebae 100644 --- a/lib/CliftPipes/CMakeLists.txt +++ b/lib/CliftPipes/CMakeLists.txt @@ -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 diff --git a/lib/CliftPipes/CliftContainer.cpp b/lib/CliftPipes/CliftContainer.cpp index 83b24f620..266c84735 100644 --- a/lib/CliftPipes/CliftContainer.cpp +++ b/lib/CliftPipes/CliftContainer.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(); - return Registry; - }(); - return Registry; -} - -static ContextPtr makeContext() { - const auto Threading = MLIRContext::Threading::DISABLED; - return std::make_unique(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(); 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(); // 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(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(); 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(); // 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(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."); } diff --git a/lib/CliftPipes/Clifter.cpp b/lib/CliftPipes/Clifter.cpp index c6b58477b..7618da26d 100644 --- a/lib/CliftPipes/Clifter.cpp +++ b/lib/CliftPipes/Clifter.cpp @@ -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(); } 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 diff --git a/lib/CliftPipes/EmitC.cpp b/lib/CliftPipes/EmitC.cpp index a6589cc71..629e87310 100644 --- a/lib/CliftPipes/EmitC.cpp +++ b/lib/CliftPipes/EmitC.cpp @@ -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 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); } diff --git a/lib/CliftPipes/EmitCAsDirectory.cpp b/lib/CliftPipes/EmitCAsDirectory.cpp new file mode 100644 index 000000000..270717521 --- /dev/null +++ b/lib/CliftPipes/EmitCAsDirectory.cpp @@ -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 Out = Output.getOStream(ObjectID()); + revng_assert(Out); + + GzipTarWriter TarWriter{ *Out }; + + llvm::StringRef Buffer = InputC.getMemoryBuffer(ObjectID{})->getBuffer(); + TarWriter.append("decompiled/functions.c", + llvm::ArrayRef(Buffer.data(), Buffer.size())); + + Buffer = InputTypesAndGlobals.getMemoryBuffer(ObjectID{})->getBuffer(); + TarWriter.append("decompiled/types-and-globals.h", + llvm::ArrayRef(Buffer.data(), Buffer.size())); + + Buffer = InputHelpers.getMemoryBuffer(ObjectID{})->getBuffer(); + TarWriter.append("decompiled/helpers.h", + llvm::ArrayRef(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(); +} diff --git a/lib/CliftPipes/EmitCAsSingleFile.cpp b/lib/CliftPipes/EmitCAsSingleFile.cpp new file mode 100644 index 000000000..4dab48826 --- /dev/null +++ b/lib/CliftPipes/EmitCAsSingleFile.cpp @@ -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 + Reg; + +} // namespace revng::pipes + +class EmitCAsSingleFile { +public: + static constexpr auto Name = "emit-c-as-single-file"; + + std::array 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 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 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 diff --git a/lib/CliftPipes/HeaderContainers.h b/lib/CliftPipes/HeaderContainers.h new file mode 100644 index 000000000..bb79f68ba --- /dev/null +++ b/lib/CliftPipes/HeaderContainers.h @@ -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 +using SBF = revng::pipes::StringBufferContainer; + +// The real class is used here because aliasing an alias is not allowed. +namespace RPD = revng::pipes::detail; +template +using TSM = RPD::GenericStringMap<&revng::ranks::TypeDefinition, Values...>; + +template +using RegisterDCC = pipeline::RegisterDefaultConstructibleContainer; + +} // namespace detail + +using TypeAndGlobalHeaderContainer = detail::SBF< + &revng::kinds::TypeAndGlobalHeader, + detail::TypeAndGlobalHeaderName, + detail::HeaderMIMEType, + detail::HeaderSuffix>; +inline detail::RegisterDCC RegisteredMHC; + +using HelperHeaderContainer = detail::SBF<&revng::kinds::HelperHeader, + detail::HelperHeaderName, + detail::HeaderMIMEType, + detail::HeaderSuffix>; +inline detail::RegisterDCC RegisteredHHC; + +using TypeDefinitionContainer = detail::TSM<&revng::kinds::SingleTypeDefinition, + detail::TypeDefinitionName, + detail::TypeDefinitionMimeType, + detail::TypeDefinitionSuffix>; +inline detail::RegisterDCC RegisteredTDC; diff --git a/lib/CliftPipes/Headers.cpp b/lib/CliftPipes/Headers.cpp new file mode 100644 index 000000000..564b3740a --- /dev/null +++ b/lib/CliftPipes/Headers.cpp @@ -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 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 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 TypeAndGlobalHeader; + +class HelperHeaderPipe { +public: + static constexpr auto Name = "emit-helper-header"; + + std::array 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 HelperHeader; + +class SingleTypeDefinitionPipe { +public: + static constexpr auto Name = "emit-single-type-definition"; + + std::array 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 TypeDefinition; + +} // namespace + +// +// New style pipes +// + +namespace revng::pypeline::piperuns { + +void EmitTypeAndGlobalHeader::run() { + std::unique_ptr Out = Output.getOStream(ObjectID()); + emitTypeAndGlobalHeaderImpl(*Out, Input.getModule(), &Configuration); +} + +void EmitHelperHeader::run() { + std::unique_ptr Out = Output.getOStream(ObjectID()); + + std::vector 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 diff --git a/lib/CliftPipes/ImportDataModel.cpp b/lib/CliftPipes/ImportDataModel.cpp index 230cba8cb..08b135d63 100644 --- a/lib/CliftPipes/ImportDataModel.cpp +++ b/lib/CliftPipes/ImportDataModel.cpp @@ -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)); } // diff --git a/lib/CliftPipes/ImportTypes.cpp b/lib/CliftPipes/ImportTypes.cpp index 5ce501919..a9b227f08 100644 --- a/lib/CliftPipes/ImportTypes.cpp +++ b/lib/CliftPipes/ImportTypes.cpp @@ -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 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(); @@ -38,3 +41,67 @@ public: }; static pipeline::RegisterPipe Y; + +class ImportFunctionDeclarationsPipe { +public: + static constexpr auto Name = "import-function-declarations"; + + std::array 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::importAllModelFunctionDeclarations(*revng::getModelFromContext(EC), + CliftContainer.getModule()); + + EC.commitAllFor(CliftContainer); + } +}; + +static pipeline::RegisterPipe IFD; + +class ImportSegmentDeclarationsPipe { +public: + static constexpr auto Name = "import-segment-declarations"; + + std::array 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::importAllModelSegmentDeclarations(*revng::getModelFromContext(EC), + CliftContainer.getModule()); + + EC.commitAllFor(CliftContainer); + } +}; + +static pipeline::RegisterPipe 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 diff --git a/lib/Clifter/Clifter.cpp b/lib/Clifter/Clifter.cpp index 7c2ed5bfa..4ea7e0d90 100644 --- a/lib/Clifter/Clifter.cpp +++ b/lib/Clifter/Clifter.cpp @@ -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 TypeT importType(const ModelTypeT &Type) { - return mlir::cast(importType(Type)); + return mlir::cast(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(Context, Handle); - auto CommentAttr = makeCommentAttr(Context, Handle); - auto Attrs = llvm::ArrayRef{}; - 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(Type)) return importLLVMPointerType(T); - if (auto *T = llvm::dyn_cast(Type)) - return importOpaqueStruct(T); + if (auto *T = llvm::dyn_cast(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(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(C.importType(*StackType)); + Type = C.importType(*StackType); Handle = pipeline::locationString(revng::ranks::StackFrameVariable, ModelFunction.Entry()); } else { diff --git a/lib/FunctionIsolation/IsolateFunctions.cpp b/lib/FunctionIsolation/IsolateFunctions.cpp index e00ef352c..2b6f0e406 100644 --- a/lib/FunctionIsolation/IsolateFunctions.cpp +++ b/lib/FunctionIsolation/IsolateFunctions.cpp @@ -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 Users; Users.push(&Instruction); diff --git a/lib/FunctionIsolation/PromoteCSVs.cpp b/lib/FunctionIsolation/PromoteCSVs.cpp index 20979eead..0424337fe 100644 --- a/lib/FunctionIsolation/PromoteCSVs.cpp +++ b/lib/FunctionIsolation/PromoteCSVs.cpp @@ -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 { CSVsUsageMap PromoteCSVs::getUsedCSVs(ArrayRef 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 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 oneElement(T &Element) { } void PromoteCSVs::wrapCallsToHelpers(Function *F) { + revng_log(Log, "wrapCallsToHelpers: " << F->getName().str()); std::vector ToWrap; - for (BasicBlock &BB : *F) { - for (Instruction &I : BB) { - if (auto *Call = dyn_cast(&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(&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); + } } } } diff --git a/lib/HeadersGeneration/HelpersToHeaderPipe.cpp b/lib/HeadersGeneration/HelpersToHeaderPipe.cpp index 2689acd5c..55c8ceaa4 100644 --- a/lib/HeadersGeneration/HelpersToHeaderPipe.cpp +++ b/lib/HeadersGeneration/HelpersToHeaderPipe.cpp @@ -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 getContract() const { using namespace pipeline; @@ -29,7 +29,7 @@ public: return { ContractGroup{ Contract(StackAccessesSegregated, 0, - HelpersHeader, + LegacyHelpersHeader, 1, InputPreservation::Preserve) } }; } diff --git a/lib/HeadersGeneration/ModelToHeaderPipe.cpp b/lib/HeadersGeneration/ModelToHeaderPipe.cpp index 47715924b..da11179c8 100644 --- a/lib/HeadersGeneration/ModelToHeaderPipe.cpp +++ b/lib/HeadersGeneration/ModelToHeaderPipe.cpp @@ -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 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 }) }; } diff --git a/lib/HeadersGeneration/ModelTypeDefinitionPipe.cpp b/lib/HeadersGeneration/ModelTypeDefinitionPipe.cpp index dfabbba7d..0bd8c7a5b 100644 --- a/lib/HeadersGeneration/ModelTypeDefinitionPipe.cpp +++ b/lib/HeadersGeneration/ModelTypeDefinitionPipe.cpp @@ -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 getContract() const { using namespace pipeline; @@ -31,7 +32,7 @@ public: return { ContractGroup({ Contract(kinds::Binary, 0, - ModelTypeDefinition, + LegacyModelTypeDefinition, 1, InputPreservation::Preserve) }) }; } diff --git a/lib/HelperArgumentsAnalysis/HelpersModuleToDeclarations.cpp b/lib/HelperArgumentsAnalysis/HelpersModuleToDeclarations.cpp index eebd42788..09d3b9099 100644 --- a/lib/HelperArgumentsAnalysis/HelpersModuleToDeclarations.cpp +++ b/lib/HelperArgumentsAnalysis/HelpersModuleToDeclarations.cpp @@ -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); } diff --git a/lib/ImportFromC/CMakeLists.txt b/lib/ImportFromC/CMakeLists.txt index 1a626540a..ab0d8a041 100644 --- a/lib/ImportFromC/CMakeLists.txt +++ b/lib/ImportFromC/CMakeLists.txt @@ -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}) diff --git a/lib/ImportFromC/HeaderToModel.cpp b/lib/ImportFromC/HeaderToModel.cpp index 1bcd0744a..ca037b347 100644 --- a/lib/ImportFromC/HeaderToModel.cpp +++ b/lib/ImportFromC/HeaderToModel.cpp @@ -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() + || FD->hasAttr()) { + ModelFunction.Attributes().emplace(model::FunctionAttribute::NoReturn); + } + + if (FD->hasAttr()) + 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(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(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; diff --git a/lib/ImportFromC/ImportFromCAnalysis.cpp b/lib/ImportFromC/ImportFromCAnalysis.cpp index d8254375c..b8ab9a60b 100644 --- a/lib/ImportFromC/ImportFromCAnalysis.cpp +++ b/lib/ImportFromC/ImportFromCAnalysis.cpp @@ -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 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(&T) + or llvm::isa(&T) + or llvm::isa(&T); +} + +static std::pair, + mlir::OwningOpRef> +makeHeaderModule(const model::Binary &Model) { + std::unique_ptr Context = clift::makeContext(); + mlir::OwningOpRef 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); } diff --git a/lib/Model/Binary.cpp b/lib/Model/Binary.cpp index b012b19dc..9735a7894 100644 --- a/lib/Model/Binary.cpp +++ b/lib/Model/Binary.cpp @@ -5,6 +5,7 @@ #include #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 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 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 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(Edge) + && !llvm::isa(Edge)) { + if (const auto *Pointer = llvm::dyn_cast(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(Edge)) { + if (std::optional 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 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); diff --git a/lib/Model/Verification.cpp b/lib/Model/Verification.cpp index e588b8722..b7296b886 100644 --- a/lib/Model/Verification.cpp +++ b/lib/Model/Verification.cpp @@ -2,10 +2,13 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include + #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; + return compile_time::repeatAnd>([&VH] { + using Current = std::tuple_element_t; - // 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) { + // 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(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."); + } + }); } // diff --git a/lib/PTML/CTokenEmitter.cpp b/lib/PTML/CTokenEmitter.cpp index cba40a175..bdcf025bd 100644 --- a/lib/PTML/CTokenEmitter.cpp +++ b/lib/PTML/CTokenEmitter.cpp @@ -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()); } diff --git a/lib/Pipebox/Pipebox.cpp b/lib/Pipebox/Pipebox.cpp index 3ef969848..18da652d6 100644 --- a/lib/Pipebox/Pipebox.cpp +++ b/lib/Pipebox/Pipebox.cpp @@ -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 CONCAT3(TYPE, _, __COUNTER__) // // Containers // -static RegisterContainer C1; -static RegisterContainer C3; -static RegisterContainer C4; -static RegisterContainer C5; -static RegisterContainer C6; -static RegisterContainer C7; -static RegisterContainer C8; -static RegisterContainer C9; -static RegisterContainer C10; -static RegisterContainer C11; -static RegisterContainer C12; -static RegisterContainer C13; -static RegisterContainer C14; -static RegisterContainer C15; -static RegisterContainer C16; -static RegisterContainer C17; -static RegisterContainer C18; -static RegisterContainer C19; -static RegisterContainer 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 P1; -static RegisterPipe P2; -static RegisterPipe P3; -static RegisterSingleOutputPipeRun P4; -static RegisterTypeDefinitionPipeRun P5; -static RegisterFunctionPipeRun P6; -static RegisterFunctionPipeRun P7; -static RegisterFunctionPipeRun P8; -static RegisterFunctionPipeRun P9; -static RegisterFunctionPipeRun P10; -static RegisterSingleOutputPipeRun P11; -static RegisterFunctionPipeRun P12; -static RegisterFunctionPipeRun P13; -static RegisterSingleOutputPipeRun P14; -static RegisterSingleOutputPipeRun P15; -static RegisterSingleOutputPipeRun P16; -static RegisterSingleOutputPipeRun P17; -static RegisterFunctionPipeRun P18; -static RegisterFunctionPipeRun P19; -static RegisterFunctionPipeRun P20; -static RegisterFunctionPipeRun P21; -static RegisterFunctionPipeRun P22; -static RegisterFunctionPipeRun P23; -static RegisterFunctionPipeRun P24; -static RegisterFunctionPipeRun P25; -static RegisterFunctionPipeRun P26; -static RegisterFunctionPipeRun P27; -static RegisterPipe P28; -static RegisterSingleOutputPipeRun P29; -static RegisterSingleOutputPipeRun P30; -static RegisterFunctionPipeRun P31; -static RegisterSingleOutputPipeRun P32; -static RegisterSingleOutputPipeRun P33; -static RegisterSingleOutputPipeRun P34; -static RegisterSingleOutputPipeRun P35; -static RegisterSingleOutputPipeRun P36; -static RegisterFunctionPipeRun P37; -static RegisterFunctionPipeRun P38; -static RegisterFunctionPipeRun P39; -static RegisterFunctionPipeRun P40; -static RegisterFunctionPipeRun 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 P41; using namespace revng::pypeline::analyses; -static RegisterAnalysis A1; -static RegisterAnalysis A2; -static RegisterAnalysis A3; -static RegisterAnalysis A4; -static RegisterAnalysis A5; -static RegisterAnalysis A6; -static RegisterAnalysis A7; -static RegisterAnalysis A8; -static RegisterAnalysis A9; -static RegisterAnalysis A10; -static RegisterAnalysis A11; -static RegisterAnalysis A12; -static RegisterAnalysis 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); diff --git a/lib/TypeNames/TypePrinters.cpp b/lib/TypeNames/TypePrinters.cpp index 53d2ee31e..40bc93e63 100644 --- a/lib/TypeNames/TypePrinters.cpp +++ b/lib/TypeNames/TypePrinters.cpp @@ -347,62 +347,8 @@ void ptml::ModelCBuilder::printOpaqueTypeDefinition(uint64_t ByteSize) { << ";\n"; } -std::set ptml::ModelCBuilder::getModelOpaqueByteSizes() { - - std::set 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 Dependencies; - - const model::TypeDefinition *Definition = Type->tryGetAsDefinition(); - if (not Definition) - continue; - - if (const auto *TD = llvm::dyn_cast(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 ByteSizes = getModelOpaqueByteSizes(); + std::set 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; void MCB::printHelperOpaqueTypeDefinitions(const SizeSet &HelperByteSizes) { - std::set ModelByteSizes = getModelOpaqueByteSizes(); + std::set ModelByteSizes = Binary.collectAllTypeSizes(); for (uint64_t ByteSize : HelperByteSizes) { if (not ModelByteSizes.contains(ByteSize)) { *Out << "\n"; diff --git a/python/revng/internal/pipeline.yml b/python/revng/internal/pipeline.yml index 016abbde8..9d4bc2d28 100644 --- a/python/revng/internal/pipeline.yml +++ b/python/revng/internal/pipeline.yml @@ -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 diff --git a/python/revng/pypeline/pipeline_parser.py b/python/revng/pypeline/pipeline_parser.py index 2c359b6a1..6be2fddcb 100644 --- a/python/revng/pypeline/pipeline_parser.py +++ b/python/revng/pypeline/pipeline_parser.py @@ -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" ) diff --git a/python/revng/pypeline/storage/local_provider.py b/python/revng/pypeline/storage/local_provider.py index 38ff5dc9d..468b6a0ed 100644 --- a/python/revng/pypeline/storage/local_provider.py +++ b/python/revng/pypeline/storage/local_provider.py @@ -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 diff --git a/scripts/tuple_tree_generator/templates/struct_late.h.tpl b/scripts/tuple_tree_generator/templates/struct_late.h.tpl index 6228dc8eb..79c063cfb 100644 --- a/scripts/tuple_tree_generator/templates/struct_late.h.tpl +++ b/scripts/tuple_tree_generator/templates/struct_late.h.tpl @@ -42,8 +42,8 @@ using namespace std::string_view_literals; /*# --- TupleLikeTraits --- -#*/ template <> struct TupleLikeTraits { - 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 =*/ diff --git a/share/doc/revng/developer-manual/qemu-helpers.md b/share/doc/revng/developer-manual/qemu-helpers.md index 8bbaef15b..fd0f855de 100644 --- a/share/doc/revng/developer-manual/qemu-helpers.md +++ b/share/doc/revng/developer-manual/qemu-helpers.md @@ -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, !}` 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. diff --git a/share/doc/revng/references/cli/revng-analyze.md b/share/doc/revng/references/cli/revng-analyze.md index 50233c1bb..fef697eaf 100644 --- a/share/doc/revng/references/cli/revng-analyze.md +++ b/share/doc/revng/references/cli/revng-analyze.md @@ -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 diff --git a/share/doc/revng/references/cli/revng-artifact.md b/share/doc/revng/references/cli/revng-artifact.md index bc79798f4..403e9aa95 100644 --- a/share/doc/revng/references/cli/revng-artifact.md +++ b/share/doc/revng/references/cli/revng-artifact.md @@ -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 diff --git a/share/doc/revng/references/mime-types.md b/share/doc/revng/references/mime-types.md index 475f2fa66..de84bc0fa 100644 --- a/share/doc/revng/references/mime-types.md +++ b/share/doc/revng/references/mime-types.md @@ -6,29 +6,32 @@ USAGE: revng-artifact [options] 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 diff --git a/share/doc/revng/user-manual/python-scripting.md b/share/doc/revng/user-manual/python-scripting.md index 9d02dfc26..e0c923b51 100644 --- a/share/doc/revng/user-manual/python-scripting.md +++ b/share/doc/revng/user-manual/python-scripting.md @@ -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`: diff --git a/share/doc/revng/user-manual/tutorial/running-analyses.md b/share/doc/revng/user-manual/tutorial/running-analyses.md index a9ac0771a..afd00bb93 100644 --- a/share/doc/revng/user-manual/tutorial/running-analyses.md +++ b/share/doc/revng/user-manual/tutorial/running-analyses.md @@ -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; } ``` diff --git a/share/revng/pipelines/revng-pipelines.yml b/share/revng/pipelines/revng-pipelines.yml index be5655056..e5b2ae528 100644 --- a/share/revng/pipelines/revng-pipelines.yml +++ b/share/revng/pipelines/revng-pipelines.yml @@ -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: diff --git a/share/revng/test/configuration/revng/decompilation.yml b/share/revng/test/configuration/revng/decompilation.yml index 789520278..a1fd18eca 100644 --- a/share/revng/test/configuration/revng/decompilation.yml +++ b/share/revng/test/configuration/revng/decompilation.yml @@ -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 diff --git a/share/revng/test/configuration/revng/end-to-end.yml b/share/revng/test/configuration/revng/end-to-end.yml index 6c856d451..2b83a05d2 100644 --- a/share/revng/test/configuration/revng/end-to-end.yml +++ b/share/revng/test/configuration/revng/end-to-end.yml @@ -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"; diff --git a/share/revng/test/configuration/revng/import-from-c.yml b/share/revng/test/configuration/revng/import-from-c.yml index 1014a73e0..f1af93cf8 100644 --- a/share/revng/test/configuration/revng/import-from-c.yml +++ b/share/revng/test/configuration/revng/import-from-c.yml @@ -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 diff --git a/share/revng/test/configuration/revng/model-to-header.yml b/share/revng/test/configuration/revng/model-to-header.yml index 6dcec0019..0742d52ea 100644 --- a/share/revng/test/configuration/revng/model-to-header.yml +++ b/share/revng/test/configuration/revng/model-to-header.yml @@ -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"; diff --git a/share/revng/test/configuration/revng/precedence-tests.yml b/share/revng/test/configuration/revng/precedence-tests.yml deleted file mode 100644 index daf2c1c79..000000000 --- a/share/revng/test/configuration/revng/precedence-tests.yml +++ /dev/null @@ -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 diff --git a/share/revng/test/configuration/revng/pypeline-comparison.yml b/share/revng/test/configuration/revng/pypeline-comparison.yml index d5ff625ae..86a6ab87a 100644 --- a/share/revng/test/configuration/revng/pypeline-comparison.yml +++ b/share/revng/test/configuration/revng/pypeline-comparison.yml @@ -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 diff --git a/share/revng/test/tests/analysis/CollectFunctions/functions.S.yml.filecheck b/share/revng/test/tests/analysis/CollectFunctions/functions.S.yml.filecheck index 3f8118f03..89889b0f2 100644 --- a/share/revng/test/tests/analysis/CollectFunctions/functions.S.yml.filecheck +++ b/share/revng/test/tests/analysis/CollectFunctions/functions.S.yml.filecheck @@ -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: diff --git a/share/revng/test/tests/analysis/Decompilation/clang/linked-lists.c.filecheck b/share/revng/test/tests/analysis/Decompilation/clang/linked-lists.c.filecheck index cc528d2ec..1e593900e 100644 --- a/share/revng/test/tests/analysis/Decompilation/clang/linked-lists.c.filecheck +++ b/share/revng/test/tests/analysis/Decompilation/clang/linked-lists.c.filecheck @@ -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]]); diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.filecheck b/share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.filecheck deleted file mode 100644 index 4b6036977..000000000 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.filecheck +++ /dev/null @@ -1,7 +0,0 @@ -This file is distributed under the MIT License. See LICENSE.md for details. - -We should have correctly promoted the exit dispatcher switch to an if (switch -with two cases). The condition of the if should be the `loop_state_var` (we are -relying on the promotion of an exit dispatcher, which uses such variable as the -condition). -CHECK: if ({{.*}}loop_state_var{{.*}}) diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.filecheck b/share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.filecheck deleted file mode 100644 index 7c38735b9..000000000 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.filecheck +++ /dev/null @@ -1,38 +0,0 @@ -This file is distributed under the MIT License. See LICENSE.md for details. - -The following things should be printed in hexadecimal - -CHECK: {{.*}}64_t shift_left({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -CHECK: {{.*}}64_t logical_shift_right({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -CHECK: {{.*}}64_t arithmetic_shift_right({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -CHECK: {{.*}}64_t bitwise_and({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -CHECK: {{.*}}64_t bitwise_or({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -CHECK: {{.*}}64_t bitwise_xor({{.*}}64_t [[ARG:.*]]) { -CHECK: 0xABCDEF1234567890 -CHECK: } - -Character literal - -CHECK: write_and_get_char(void) { -CHECK: 'X' -CHECK: } - -NULL literal - -CHECK: do_stuff(void) { -CHECK: puts({{.*[ ]?}}NULL); diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.filecheck b/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.filecheck deleted file mode 100644 index fe6fde593..000000000 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.filecheck +++ /dev/null @@ -1,15 +0,0 @@ -This file is distributed under the MIT License. See LICENSE.md for details. - -The print_string function should use an inline string literal. - -CHECK: void print_string(void) { -CHECK: puts{{[_]?}}({{.*}}"hello world!"); -CHECK: } - -The sum_globals function should return the sum of two fields of the segment. -These fields should be accessed using member access operator to access sections -(first) and the specific data (TheData), recovered from public symbol names in -the binary. - -CHECK: sum_globals(void) -CHECK: return segment_[[SEGMENT_INDEX:[0-9]+]].data.TheData.offset_1004 + segment_[[SEGMENT_INDEX]].data.TheData.offset_0 diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.model.yml b/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.model.yml deleted file mode 100644 index 10873c252..000000000 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/segments-and-sections.c.model.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -# -# This file is distributed under the MIT License. See LICENSE.md for details. -# -Segments: - - IsReadable: true - Type: - Kind: DefinedType - Definition: "/TypeDefinitions/1812-StructDefinition" - - IsReadable: true - IsWriteable: true - Type: - Kind: DefinedType - Definition: "/TypeDefinitions/1815-StructDefinition" -TypeDefinitions: - - ID: 1812 - Kind: StructDefinition - Fields: - - Name: rodata - Type: - Kind: DefinedType - Definition: "/TypeDefinitions/1813-StructDefinition" - - ID: 1813 - Kind: StructDefinition - Size: 13 - - ID: 1815 - Kind: StructDefinition - Fields: - - Name: data - Type: - Kind: DefinedType - Definition: "/TypeDefinitions/1816-StructDefinition" - - ID: 1816 - Kind: StructDefinition - Fields: - - Name: TheData - Type: - Kind: DefinedType - Definition: "/TypeDefinitions/1817-StructDefinition" - - ID: 1817 - Kind: StructDefinition - Size: 1008 - Fields: - - Offset: 0 - Type: - Kind: PrimitiveType - PrimitiveKind: Generic - Size: 4 - - Offset: 1004 - Type: - Kind: PrimitiveType - PrimitiveKind: Generic - Size: 4 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/check-against.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/check-against.yml new file mode 100644 index 000000000..ea46e47ed --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/check-against.yml @@ -0,0 +1,32 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Functions: + - Entry: "0x400000:Code_x86_64" + Name: "does_this_function_return" + + Prototype: + Kind: DefinedType + Definition: "/TypeDefinitions/0-CABIFunctionDefinition" + + Attributes: ["NoReturn"] + +TypeDefinitions: + - Kind: CABIFunctionDefinition + ID: 0 + ABI: SystemV_x86_64 + + Arguments: [] + +Segments: + - StartAddress: "0x400000:Generic64" + VirtualSize: 4096 + StartOffset: 0 + FileSize: 4096 + IsReadable: true + IsWriteable: false + IsExecutable: true + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/edit.c new file mode 100644 index 000000000..825bf57f9 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/edit.c @@ -0,0 +1,6 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +typedef _ABI(SystemV_x86_64) _ALWAYS_INLINE + void this_function_IS_always_inline(void); diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/input-model.yml new file mode 100644 index 000000000..470071926 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/input-model.yml @@ -0,0 +1,30 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Functions: + - Entry: "0x400000:Code_x86_64" + Name: "is_this_function_always_inline" + + Prototype: + Kind: DefinedType + Definition: "/TypeDefinitions/0-CABIFunctionDefinition" + +TypeDefinitions: + - Kind: CABIFunctionDefinition + ID: 0 + ABI: SystemV_x86_64 + + Arguments: [] + +Segments: + - StartAddress: "0x400000:Generic64" + VirtualSize: 4096 + StartOffset: 0 + FileSize: 4096 + IsReadable: true + IsWriteable: false + IsExecutable: true + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/type-to-edit.location similarity index 71% rename from share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.model.yml rename to share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/type-to-edit.location index aade1d087..86435dc6e 100644 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/dual-case-switch.c.model.yml +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-inline/type-to-edit.location @@ -1,5 +1,5 @@ ---- # # This file is distributed under the MIT License. See LICENSE.md for details. # -Architecture: x86_64 + +/function/0x400000:Code_x86_64 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/check-against.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/check-against.yml new file mode 100644 index 000000000..ea46e47ed --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/check-against.yml @@ -0,0 +1,32 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Functions: + - Entry: "0x400000:Code_x86_64" + Name: "does_this_function_return" + + Prototype: + Kind: DefinedType + Definition: "/TypeDefinitions/0-CABIFunctionDefinition" + + Attributes: ["NoReturn"] + +TypeDefinitions: + - Kind: CABIFunctionDefinition + ID: 0 + ABI: SystemV_x86_64 + + Arguments: [] + +Segments: + - StartAddress: "0x400000:Generic64" + VirtualSize: 4096 + StartOffset: 0 + FileSize: 4096 + IsReadable: true + IsWriteable: false + IsExecutable: true + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/edit.c new file mode 100644 index 000000000..ad423ece4 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/edit.c @@ -0,0 +1,5 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +typedef _ABI(SystemV_x86_64) _NORETURN void this_function_does_NOT_return(void); diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/input-model.yml new file mode 100644 index 000000000..2bdbbc186 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/input-model.yml @@ -0,0 +1,30 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Functions: + - Entry: "0x400000:Code_x86_64" + Name: "does_this_function_return" + + Prototype: + Kind: DefinedType + Definition: "/TypeDefinitions/0-CABIFunctionDefinition" + +TypeDefinitions: + - Kind: CABIFunctionDefinition + ID: 0 + ABI: SystemV_x86_64 + + Arguments: [] + +Segments: + - StartAddress: "0x400000:Generic64" + VirtualSize: 4096 + StartOffset: 0 + FileSize: 4096 + IsReadable: true + IsWriteable: false + IsExecutable: true + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 diff --git a/share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/type-to-edit.location similarity index 71% rename from share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.model.yml rename to share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/type-to-edit.location index aade1d087..86435dc6e 100644 --- a/share/revng/test/tests/analysis/Decompilation/x86-64/pretty-ints.c.model.yml +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/cft-marked-noreturn/type-to-edit.location @@ -1,5 +1,5 @@ ---- # # This file is distributed under the MIT License. See LICENSE.md for details. # -Architecture: x86_64 + +/function/0x400000:Code_x86_64 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/check-against.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/check-against.yml new file mode 100644 index 000000000..b86acd644 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/check-against.yml @@ -0,0 +1,29 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +TypeDefinitions: + - Kind: TypedefDefinition + ID: 0 + Name: typedef_a + UnderlyingType: + Kind: PrimitiveType + PrimitiveKind: Signed + Size: 2 + + - Kind: EnumDefinition + ID: 1 + Name: "my_enum" + UnderlyingType: + Kind: PrimitiveType + PrimitiveKind: Unsigned + Size: 8 + Entries: + - Value: 0 + Name: "none" + - Value: 1 + Name: "positive" + - Value: 2 + Name: "negative" + - Value: 3 + Name: "neutral" diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/edit.c new file mode 100644 index 000000000..c71e14995 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/edit.c @@ -0,0 +1,10 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +enum _ENUM_UNDERLYING(uint64_t) _PACKED my_enum{ + none = 0x0U, + positive = 0x1U, + negative = 0x2U, + neutral = 0x3U, +}; diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/input-model.yml new file mode 100644 index 000000000..d9a847e03 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/input-model.yml @@ -0,0 +1,28 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +--- +Architecture: x86_64 +DefaultABI: SystemV_x86_64 +TypeDefinitions: + - Kind: TypedefDefinition + ID: 0 + Name: typedef_a + UnderlyingType: + Kind: PrimitiveType + PrimitiveKind: Signed + Size: 2 + + - Kind: EnumDefinition + ID: 1 + UnderlyingType: + Kind: DefinedType + Definition: "/TypeDefinitions/0-TypedefDefinition" + Entries: + - Value: 0 + Name: none + - Value: 1 + Name: positive + - Value: 2 + Name: negative diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/type-to-edit.location b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/type-to-edit.location new file mode 100644 index 000000000..ddfdc1d12 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/enum-type-change/type-to-edit.location @@ -0,0 +1,5 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +/type-definition/1-EnumDefinition diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/edit.c new file mode 100644 index 000000000..879e2a15d --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/edit.c @@ -0,0 +1,8 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +struct _PACKED _SIZE(24) my_struct { + _STARTS_AT(8) + typedef_a field; +}; diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/expected-error.txt b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/expected-error.txt new file mode 100644 index 000000000..ffe9bccc8 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/expected-error.txt @@ -0,0 +1,18 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +New model does not verify: Last field ends outside the struct +--- +ID: 2 +Kind: StructDefinition +Name: my_struct +Size: 24 +Fields: + - Offset: 8 + Name: field + Type: + Kind: DefinedType + Definition: "/TypeDefinitions/0-StructDefinition" +... + diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/input-model.yml new file mode 100644 index 000000000..20002594a --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/input-model.yml @@ -0,0 +1,21 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +TypeDefinitions: + - Kind: StructDefinition + ID: 0 + Fields: [] + Size: 24 + + - Kind: TypedefDefinition + ID: 1 + Name: typedef_a + UnderlyingType: + Kind: DefinedType + Definition: "/TypeDefinitions/0-StructDefinition" + + - Kind: StructDefinition + ID: 2 + Fields: [] + Size: 24 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/type-to-edit.location b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/type-to-edit.location new file mode 100644 index 000000000..fe891887a --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/oversized-field/type-to-edit.location @@ -0,0 +1,5 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +/type-definition/2-StructDefinition diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/check-against.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/check-against.yml new file mode 100644 index 000000000..03ed7b132 --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/check-against.yml @@ -0,0 +1,19 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +TypeDefinitions: + - Kind: StructDefinition + ID: 2 + Name: "my_struct" + Size: 24 + # The leading $ means that the size of the Fields list must match + $Fields: + - Offset: 8 + Name: "field" + Type: + Kind: PointerType + PointerSize: 8 + PointeeType: + Kind: DefinedType + Definition: "/TypeDefinitions/0-StructDefinition" diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/edit.c new file mode 100644 index 000000000..879e2a15d --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/edit.c @@ -0,0 +1,8 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +struct _PACKED _SIZE(24) my_struct { + _STARTS_AT(8) + typedef_a field; +}; diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/input-model.yml new file mode 100644 index 000000000..ad9de445e --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/input-model.yml @@ -0,0 +1,34 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 +TypeDefinitions: + - Kind: StructDefinition + ID: 0 + Size: 8 + Fields: + - Offset: 0 + Name: pointer + Type: + Kind: PointerType + PointerSize: 8 + PointeeType: + Kind: DefinedType + Definition: "/TypeDefinitions/2-StructDefinition" + + - Kind: TypedefDefinition + ID: 1 + Name: typedef_a + UnderlyingType: + Kind: PointerType + PointerSize: 8 + PointeeType: + Kind: DefinedType + Definition: "/TypeDefinitions/0-StructDefinition" + + - Kind: StructDefinition + ID: 2 + Fields: [] + Size: 24 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/type-to-edit.location b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/type-to-edit.location new file mode 100644 index 000000000..fe891887a --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-pointer/type-to-edit.location @@ -0,0 +1,5 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +/type-definition/2-StructDefinition diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/check-against.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/check-against.yml new file mode 100644 index 000000000..138c6855d --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/check-against.yml @@ -0,0 +1,16 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +TypeDefinitions: + - Kind: StructDefinition + ID: 2 + Name: "my_struct" + Size: 24 + # The leading $ means that the size of the Fields list must match + $Fields: + - Offset: 8 + Name: "field" + Type: + Kind: DefinedType + Definition: "/TypeDefinitions/1-TypedefDefinition" diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/edit.c b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/edit.c new file mode 100644 index 000000000..879e2a15d --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/edit.c @@ -0,0 +1,8 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +struct _PACKED _SIZE(24) my_struct { + _STARTS_AT(8) + typedef_a field; +}; diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/input-model.yml b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/input-model.yml new file mode 100644 index 000000000..5cf7c950d --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/input-model.yml @@ -0,0 +1,31 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +Architecture: x86_64 +DefaultABI: SystemV_x86_64 +TypeDefinitions: + - Kind: StructDefinition + ID: 0 + Size: 8 + Fields: + - Offset: 0 + Name: pointer + Type: + Kind: PointerType + PointerSize: 8 + PointeeType: + Kind: DefinedType + Definition: "/TypeDefinitions/2-StructDefinition" + + - Kind: TypedefDefinition + ID: 1 + Name: typedef_a + UnderlyingType: + Kind: DefinedType + Definition: "/TypeDefinitions/0-StructDefinition" + + - Kind: StructDefinition + ID: 2 + Fields: [] + Size: 24 diff --git a/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/type-to-edit.location b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/type-to-edit.location new file mode 100644 index 000000000..fe891887a --- /dev/null +++ b/share/revng/test/tests/analysis/ImportFromCAnalysis/typedef-to-value/type-to-edit.location @@ -0,0 +1,5 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +/type-definition/2-StructDefinition diff --git a/share/revng/test/tests/c-operator-precedence/precedence.c.filecheck b/share/revng/test/tests/c-operator-precedence/precedence.c.filecheck deleted file mode 100644 index b2bc16a94..000000000 --- a/share/revng/test/tests/c-operator-precedence/precedence.c.filecheck +++ /dev/null @@ -1,289 +0,0 @@ -// -// Copyright (c) rev.ng Labs Srl. See LICENSE.md for details. -// - -// Class 12 (&&) -CHECK-LABEL: from_class_12_to_class_11(generic64_t -// a || b && c -CHECK: return argument_0 != 0 || argument_1 != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_10(generic64_t -// a | b && c -CHECK: return (argument_0 | argument_1) != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_9(generic64_t -// a ^ b && c -CHECK: return argument_0 != argument_1 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_8(generic64_t -// a & b && c -CHECK: return (argument_0 & argument_1) != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_7(generic64_t -// a == b && c -CHECK: return argument_0 == argument_1 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_6(generic64_t -// a < b && c -CHECK: return argument_0 < argument_1 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_5(generic64_t -// a << b && c -CHECK: return argument_0 << (argument_1 & 0x3F) != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_4(generic64_t -// a + b && c -CHECK: return 0 - argument_1 != argument_0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_3(generic64_t -// a * b && c -CHECK: return argument_1 * argument_0 != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_2(generic64_t -// -a && c -CHECK: return argument_0 != 0 && argument_2 != 0; - -CHECK-LABEL: from_class_12_to_class_1(generic64_t -// a-- && c -CHECK: return argument_2 != 0 && argument_0 != 0; - - -// Class 11 (||) -CHECK-LABEL: from_class_11_to_class_10(generic64_t -// a | b || c -CHECK: return (argument_1 | argument_2 | argument_0) != 0; - -CHECK-LABEL: from_class_11_to_class_9(generic64_t -// a ^ b || c -CHECK: return argument_0 != argument_1 || argument_2 != 0; - -CHECK-LABEL: from_class_11_to_class_8(generic64_t -// a & b || c -CHECK: return ((argument_0 & argument_1) | argument_2) != 0; - -CHECK-LABEL: from_class_11_to_class_7(generic64_t -// a == b || c -CHECK: return argument_0 == argument_1 || argument_2 != 0; - -CHECK-LABEL: from_class_11_to_class_6(generic64_t -// a < b || c -CHECK: return argument_0 < argument_1 || argument_2 != 0; - -CHECK-LABEL: from_class_11_to_class_5(generic64_t -// a << b || c -CHECK: return ((argument_0 << (argument_1 & 0x3F)) | argument_2) != 0; - -CHECK-LABEL: from_class_11_to_class_4(generic64_t -// a + b || c -CHECK: return ((argument_0 + argument_1) | argument_2) != 0; - -CHECK-LABEL: from_class_11_to_class_3(generic64_t -// a * b || c -CHECK: return ((argument_1 * argument_0) | argument_2) != 0; - -CHECK-LABEL: from_class_11_to_class_2(generic64_t -// -a || c -CHECK: return (argument_0 | argument_2) != 0; - -CHECK-LABEL: from_class_11_to_class_1(generic64_t -// a-- || c -CHECK: return (argument_0 | argument_2) != 0; - - -// Class 10 (|) -CHECK-LABEL: from_class_10_to_class_9(generic64_t -// a ^ b | c -CHECK: return (argument_0 ^ argument_1) | argument_2; - -CHECK-LABEL: from_class_10_to_class_8(generic64_t -// a & b | c -CHECK: return (argument_0 & argument_1) | argument_2; - -CHECK-LABEL: from_class_10_to_class_7(generic64_t -// a == b | c -CHECK: return (argument_0 == argument_1) | argument_2; - -CHECK-LABEL: from_class_10_to_class_6(generic64_t -// a < b | c -CHECK: return (argument_0 < argument_1) | argument_2; - -CHECK-LABEL: from_class_10_to_class_5(generic64_t -// a << b | c -CHECK: return (argument_0 << (argument_1 & 0x3F)) | argument_2; - -CHECK-LABEL: from_class_10_to_class_4(generic64_t -// a + b | c -CHECK: return (argument_1 + argument_0) | argument_2; - -CHECK-LABEL: from_class_10_to_class_3(generic64_t -// a * b | c -CHECK: return (argument_1 * argument_0) | argument_2; - -CHECK-LABEL: from_class_10_to_class_2(generic64_t -// -a | c -CHECK: return (0 - argument_0) | argument_2; - -CHECK-LABEL: from_class_10_to_class_1(generic64_t -// a-- | c -CHECK: return argument_0 | argument_2; - - -// Class 9 (^) -CHECK-LABEL: from_class_9_to_class_8(generic64_t -// a & b ^ c -CHECK: return (argument_0 & argument_1) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_7(generic64_t -// a == b ^ c -CHECK: return (argument_0 == argument_1) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_6(generic64_t -// a < b ^ c -CHECK: return (argument_0 < argument_1) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_5(generic64_t -// a << b ^ c -CHECK: return (argument_0 << (argument_1 & 0x3F)) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_4(generic64_t -// a + b ^ c -CHECK: return (argument_1 + argument_0) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_3(generic64_t -// a * b ^ c -CHECK: return (argument_1 * argument_0) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_2(generic64_t -// -a ^ c -CHECK: return (0 - argument_0) ^ argument_2; - -CHECK-LABEL: from_class_9_to_class_1(generic64_t -// a-- ^ c -CHECK: return argument_0 ^ argument_2; - - -// Class 8 (&) -CHECK-LABEL: from_class_8_to_class_7(generic64_t -// a == b & c -CHECK: return (argument_0 == argument_1) & argument_2; - -CHECK-LABEL: from_class_8_to_class_6(generic64_t -// a < b & c -CHECK: return (argument_0 < argument_1) & argument_2; - -CHECK-LABEL: from_class_8_to_class_5(generic64_t -// a << b & c -CHECK: return (argument_0 << (argument_1 & 0x3F)) & argument_2; - -CHECK-LABEL: from_class_8_to_class_4(generic64_t -// a + b & c -CHECK: return (argument_1 + argument_0) & argument_2; - -CHECK-LABEL: from_class_8_to_class_3(generic64_t -// a * b & c -CHECK: return (argument_1 * argument_0) & argument_2; - -CHECK-LABEL: from_class_8_to_class_2(generic64_t -// -a & c -CHECK: return (0 - argument_0) & argument_2; - -CHECK-LABEL: from_class_8_to_class_1(generic64_t -// a-- & c -CHECK: return argument_0 & argument_2; - - - -// Class 7 (==) -CHECK-LABEL: from_class_7_to_class_6(generic64_t -// a < b == c -CHECK: return argument_0 < argument_1 == argument_2; - -CHECK-LABEL: from_class_7_to_class_5(generic64_t -// a << b == c -CHECK: return argument_0 << (argument_1 & 0x3F) == argument_2; - -CHECK-LABEL: from_class_7_to_class_4(generic64_t -// a + b == c -CHECK: return argument_0 + argument_1 == argument_2; - -CHECK-LABEL: from_class_7_to_class_3(generic64_t -// a * b == c -CHECK: return argument_1 * argument_0 == argument_2; - -CHECK-LABEL: from_class_7_to_class_2(generic64_t -// -a == c -CHECK: !(argument_2 + argument_0); - -CHECK-LABEL: from_class_7_to_class_1(generic64_t -// a-- == c -CHECK: return argument_0 == argument_2; - -// Class 6 (<) -CHECK-LABEL: from_class_6_to_class_5(generic64_t -// a << b < c -CHECK: return argument_0 << (argument_1 & 0x3F) < argument_2; - -CHECK-LABEL: from_class_6_to_class_4(generic64_t -// a + b < c -CHECK: return argument_0 + argument_1 < argument_2; - -CHECK-LABEL: from_class_6_to_class_3(generic64_t -// a * b < c -CHECK: return argument_1 * argument_0 < argument_2; - -CHECK-LABEL: from_class_6_to_class_2(generic64_t -// -a < c -CHECK: return 0 - argument_0 < argument_2; - -CHECK-LABEL: from_class_6_to_class_1(generic64_t -// a-- < c -CHECK: return argument_0 < argument_2; - - - -// Class 5 (<<) -CHECK-LABEL: from_class_5_to_class_4(generic64_t -// a + b << c -CHECK: return (argument_1 + argument_0) << (argument_2 & 0x3F); - -CHECK-LABEL: from_class_5_to_class_3(generic64_t -// a * b << c -CHECK: return (argument_1 * argument_0) << (argument_2 & 0x3F); - -CHECK-LABEL: from_class_5_to_class_2(generic64_t -// -a << c -CHECK: return (0 - argument_0) << (argument_2 & 0x3F); - -CHECK-LABEL: from_class_5_to_class_1(generic64_t -// a-- << c -CHECK: return argument_0 << (argument_2 & 0x3F); - - -// Class 4 (+) -CHECK-LABEL: from_class_4_to_class_3(generic64_t -// a * b + c -CHECK: return argument_1 * argument_0 + argument_2; - -CHECK-LABEL: from_class_4_to_class_2(generic64_t -// -a + c -CHECK: return argument_2 - argument_0; - -CHECK-LABEL: from_class_4_to_class_1(generic64_t -// a-- + c -CHECK: return argument_2 + argument_0; - - -// Class 3 (*) -CHECK-LABEL: from_class_3_to_class_2(generic64_t -// -a * c -CHECK: return 0 - argument_2 * argument_0; - -CHECK-LABEL: from_class_3_to_class_1(generic64_t -// a-- * c -CHECK: return argument_2 * argument_0; - - -// Class 2 (unary) -CHECK-LABEL: from_class_2_to_class_1(generic64_t -// -a-- -CHECK: return 0 - argument_0; diff --git a/share/revng/test/tests/model-to-header/annotate-attibutes.model.yml.filecheck b/share/revng/test/tests/model-to-header/annotate-attibutes.model.yml.filecheck index c0bb080ad..f26cc4ba4 100644 --- a/share/revng/test/tests/model-to-header/annotate-attibutes.model.yml.filecheck +++ b/share/revng/test/tests/model-to-header/annotate-attibutes.model.yml.filecheck @@ -4,18 +4,17 @@ # CHECK: typedef _ABI(SystemV_x86_64) void cabifunction_3001(int32_t, generic64_t, uint16_t); -# CHECK: typedef _ABI(raw_x86_64) void rawfunction_3008(generic64_t _REG(rcx_x86_64), generic64_t _REG(rdx_x86_64), generic64_t _REG(rsi_x86_64), generic64_t _REG(rdi_x86_64), generic64_t _REG(r8_x86_64), generic64_t _REG(r9_x86_64)); +# CHECK: typedef _ABI(raw_x86_64) void rawfunction_3008(generic64_t, generic64_t, generic64_t, generic64_t, generic64_t, generic64_t); # CHECK: typedef enum _PACKED prefix_enum_9 prefix_enum_9; + # CHECK: enum _ENUM_UNDERLYING(int64_t) _PACKED prefix_enum_9 { -# CHECK: prefix_enum_9_none = 0x0U, -# CHECK: prefix_enum_9_positive = 0x1U, -# CHECK: prefix_enum_9_max_held_value = 0xffffffffU, -# CHECK: enum_max_value_prefix_enum_9 = 0xffffffffffffffffU, +# CHECK: prefix_enum_9_none = 0x0U, +# CHECK: prefix_enum_9_positive = 0x1U, +# CHECK: prefix_enum_9_max_held_value = 0xFFFFFFFFU, # CHECK: }; +# CHECK: _ABI(SystemV_x86_64) void fn(int32_t b, generic64_t c, uint16_t d); +# CHECK: _ABI(raw_x86_64) void fn2(generic64_t register_rcx _REG(rcx_x86_64), generic64_t register_rdx _REG(rdx_x86_64), generic64_t register_rsi _REG(rsi_x86_64), generic64_t register_rdi _REG(rdi_x86_64), generic64_t register_r8 _REG(r8_x86_64), generic64_t register_r9 _REG(r9_x86_64)); + -# CHECK: _ABI(SystemV_x86_64) -# CHECK: void fn(int32_t b, generic64_t c, uint16_t d); -# CHECK: _ABI(raw_x86_64) -# CHECK: void fn2(generic64_t register_rcx _REG(rcx_x86_64), generic64_t register_rdx _REG(rdx_x86_64), generic64_t register_rsi _REG(rsi_x86_64), generic64_t register_rdi _REG(rdi_x86_64), generic64_t register_r8 _REG(r8_x86_64), generic64_t register_r9 _REG(r9_x86_64)); diff --git a/share/revng/test/tests/model-to-header/pointer-to-struct.h.model.yml.filecheck b/share/revng/test/tests/model-to-header/pointer-to-struct.h.model.yml.filecheck index b7112dc57..e6e309ace 100644 --- a/share/revng/test/tests/model-to-header/pointer-to-struct.h.model.yml.filecheck +++ b/share/revng/test/tests/model-to-header/pointer-to-struct.h.model.yml.filecheck @@ -9,5 +9,4 @@ CHECK: uint32_t a1 _STARTS_AT(0); CHECK: uint32_t a2 _STARTS_AT(4); CHECK: }; -CHECK: _ABI(SystemV_x86_64) -CHECK: void fn(B *b_array); +CHECK: _ABI(SystemV_x86_64) void fn(B *b_array); diff --git a/share/revng/test/tests/model-to-header/primitive-types.h.model.yml.filecheck b/share/revng/test/tests/model-to-header/primitive-types.h.model.yml.filecheck index 82f36af22..1ead0c806 100644 --- a/share/revng/test/tests/model-to-header/primitive-types.h.model.yml.filecheck +++ b/share/revng/test/tests/model-to-header/primitive-types.h.model.yml.filecheck @@ -2,10 +2,10 @@ # This file is distributed under the MIT License. See LICENSE.md for details. # -CHECK: void fn(int32_t *b, generic64_t *c, uint16_t *d); -CHECK: void fn2(generic8_t *arg1, generic16_t *arg2, generic32_t *arg3, generic64_t *arg4); -CHECK: void fn3(generic128_t *arg1, pointer_or_number8_t *arg2, pointer_or_number16_t *arg3, pointer_or_number32_t *arg4, pointer_or_number64_t *arg5, pointer_or_number128_t *arg6); -CHECK: void fn4(number8_t *arg1, number16_t *arg2, number32_t *arg3, number64_t *arg4, number128_t *arg5, uint8_t *arg6); -CHECK: void fn5(uint16_t *arg1, uint32_t *arg2, uint64_t *arg3, uint128_t *arg4, int8_t *arg5, int16_t *arg6); -CHECK: void fn6(int32_t *arg1, int64_t *arg2, int128_t *arg3, float16_t *arg4, float32_t *arg5, float64_t *arg6, float80_t *arg7, float96_t *arg8, float128_t *arg9); -CHECK: void fn7(generic80_t *arg5, generic96_t *arg6); +CHECK: _ABI(SystemV_x86_64) void fn(int32_t *b, generic64_t *c, uint16_t *d); +CHECK: _ABI(SystemV_x86_64) void fn2(generic8_t *arg1, generic16_t *arg2, generic32_t *arg3, generic64_t *arg4); +CHECK: _ABI(SystemV_x86_64) void fn3(generic128_t *arg1, pointer_or_number8_t *arg2, pointer_or_number16_t *arg3, pointer_or_number32_t *arg4, pointer_or_number64_t *arg5, pointer_or_number128_t *arg6); +CHECK: _ABI(SystemV_x86_64) void fn4(number8_t *arg1, number16_t *arg2, number32_t *arg3, number64_t *arg4, number128_t *arg5, uint8_t *arg6); +CHECK: _ABI(SystemV_x86_64) void fn5(uint16_t *arg1, uint32_t *arg2, uint64_t *arg3, uint128_t *arg4, int8_t *arg5, int16_t *arg6); +CHECK: _ABI(SystemV_x86_64) void fn6(int32_t *arg1, int64_t *arg2, int128_t *arg3, float16_t *arg4, float32_t *arg5, float64_t *arg6, float80_t *arg7, float96_t *arg8, float128_t *arg9); +CHECK: _ABI(SystemV_x86_64) void fn7(generic80_t *arg5, generic96_t *arg6); diff --git a/share/revng/test/tests/pypeline-comparison/compare.sh b/share/revng/test/tests/pypeline-comparison/compare.sh index 3a4de1b89..a2011514b 100755 --- a/share/revng/test/tests/pypeline-comparison/compare.sh +++ b/share/revng/test/tests/pypeline-comparison/compare.sh @@ -125,7 +125,7 @@ RC=0 # is temporary they are hardcorded here. if [[ "$ARTIFACT" = "disassemble" ]]; then compare "/function/" || RC=$? -elif [[ "$ARTIFACT" = "emit-type-definitions" ]]; then +elif [[ "$ARTIFACT" = "emit-single-type-definition" ]]; then compare "/type-definition/" || RC=$? elif [[ "$ARTIFACT" = "lift" ]]; then compare_lift || RC=$? diff --git a/share/revng/test/tests/scripting.py b/share/revng/test/tests/scripting.py index e4fb9df97..f8ba98937 100755 --- a/share/revng/test/tests/scripting.py +++ b/share/revng/test/tests/scripting.py @@ -70,7 +70,7 @@ def run_test(project_getter: Callable[[], Project], binary: str): assert function_original_name is not None # Run an artefact on `TypeDefinitions` - project.model.TypeDefinitions[1].get_artifact("emit-type-definitions") + project.model.TypeDefinitions[1].get_artifact("emit-single-type-definition") all_functions_entries = [function.Entry for function in project.model.Functions] # Assert that when we get the artifact for a single function we get diff --git a/tests/filecheck/CMakeLists.txt b/tests/filecheck/CMakeLists.txt index 44e442e90..3b5bdfaab 100644 --- a/tests/filecheck/CMakeLists.txt +++ b/tests/filecheck/CMakeLists.txt @@ -347,6 +347,7 @@ set(FILECHECK_TESTS clift/passes/implicit-cast-elision-call.mlir clift/passes/implicit-cast-elision-neg.mlir clift/passes/implicit-cast-elision-shl.mlir + clift/serialization/const.mlir clift/statement-rewrites/branch-equalization.mlir clift/statement-rewrites/do-while-conversion-iterative.mlir clift/statement-rewrites/do-while-conversion-with-empty-else.mlir @@ -406,7 +407,6 @@ set(FILECHECK_TESTS llvm-passes/inline-helpers.ll llvm-passes/mark-inline-helpers-up-to.ll llvm-passes/materialize-trivial-goto.ll - llvm-passes/operatorprecedence-resolution.ll llvm-passes/peephole-opt-for-decompilation.ll llvm-passes/remove-extractvalues.ll llvm-passes/scope-graph-dumper.ll diff --git a/tests/filecheck/clift/comments/0-import-types/CABIFunctionType.yml b/tests/filecheck/clift/comments/0-import-types/CABIFunctionType.yml index 7f0901707..6db08cb84 100644 --- a/tests/filecheck/clift/comments/0-import-types/CABIFunctionType.yml +++ b/tests/filecheck/clift/comments/0-import-types/CABIFunctionType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. @@ -30,9 +30,15 @@ TypeDefinitions: # CHECK: > # CHECK: ] # CHECK: > - # CHECK: module attributes {clift.module, clift.test = [!_type_definition_0_CABIFunctionDefinition]} { - # CHECK-NOT: clift.func - # CHECK: } + + # CHECK: module attributes { + # CHECK: clift.module + # CHECK: clift.types = [ + # CHECK-DAG: !_type_definition_0_CABIFunctionDefinition + # CHECK: ] + # CHECK: } { + # CHECK-NOT: clift.function + # CHECK: } - ID: 0 Kind: CABIFunctionDefinition Comment: |- diff --git a/tests/filecheck/clift/comments/0-import-types/EnumType.yml b/tests/filecheck/clift/comments/0-import-types/EnumType.yml index 7de7968d3..b0dd87587 100644 --- a/tests/filecheck/clift/comments/0-import-types/EnumType.yml +++ b/tests/filecheck/clift/comments/0-import-types/EnumType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. diff --git a/tests/filecheck/clift/comments/0-import-types/Extra.yml b/tests/filecheck/clift/comments/0-import-types/Extra.yml index c127089bb..e384c6173 100644 --- a/tests/filecheck/clift/comments/0-import-types/Extra.yml +++ b/tests/filecheck/clift/comments/0-import-types/Extra.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. diff --git a/tests/filecheck/clift/comments/0-import-types/RawFunctionType.yml b/tests/filecheck/clift/comments/0-import-types/RawFunctionType.yml index e1caeab24..43e65ec0f 100644 --- a/tests/filecheck/clift/comments/0-import-types/RawFunctionType.yml +++ b/tests/filecheck/clift/comments/0-import-types/RawFunctionType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. @@ -35,10 +35,14 @@ TypeDefinitions: # CHECK: > # CHECK: ] # CHECK: > - # CHECK: module attributes {clift.module, clift.test = [ - # CHECK-DAG: !_type_definition_1_StructDefinition - # CHECK-DAG: !_type_definition_0_RawFunctionDefinition - # CHECK: ]} { + + # CHECK: module attributes { + # CHECK: clift.module + # CHECK: clift.types = [ + # CHECK-DAG: !_type_definition_1_StructDefinition + # CHECK-DAG: !_type_definition_0_RawFunctionDefinition + # CHECK: ] + # CHECK: } { # CHECK-NOT: clift.function # CHECK: } - ID: 0 diff --git a/tests/filecheck/clift/comments/0-import-types/Segment.yml b/tests/filecheck/clift/comments/0-import-types/Segment.yml index 3bde29c37..30ae4b997 100644 --- a/tests/filecheck/clift/comments/0-import-types/Segment.yml +++ b/tests/filecheck/clift/comments/0-import-types/Segment.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-segment-declarations %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. @@ -9,8 +9,13 @@ # `import-comments` test reusing this model file (it has the same name). Segments: - # CHECK: module attributes {clift.module, clift.test = []} { - # CHECK-NOT: clift.global + # CHECK: module attributes { + # CHECK: clift.module + # CHECK: } { + # CHECK: clift.global @"0x4:Generic64-64" : !clift.array<64 x !uint8_t> + # CHECK: attributes { + # CHECK: handle = "/segment/0x4:Generic64-64" + # CHECK: } # CHECK: } - StartAddress: "0x4:Generic64" VirtualSize: 0x40 diff --git a/tests/filecheck/clift/comments/0-import-types/StructType.yml b/tests/filecheck/clift/comments/0-import-types/StructType.yml index 856f6e45d..8bb4d6e66 100644 --- a/tests/filecheck/clift/comments/0-import-types/StructType.yml +++ b/tests/filecheck/clift/comments/0-import-types/StructType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. diff --git a/tests/filecheck/clift/comments/0-import-types/TypedefType.yml b/tests/filecheck/clift/comments/0-import-types/TypedefType.yml index a7899997d..e394f317a 100644 --- a/tests/filecheck/clift/comments/0-import-types/TypedefType.yml +++ b/tests/filecheck/clift/comments/0-import-types/TypedefType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. diff --git a/tests/filecheck/clift/comments/0-import-types/UnionType.yml b/tests/filecheck/clift/comments/0-import-types/UnionType.yml index 66566f9c1..e44544f21 100644 --- a/tests/filecheck/clift/comments/0-import-types/UnionType.yml +++ b/tests/filecheck/clift/comments/0-import-types/UnionType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s # NOTE: comments are intentionally missing from clift at this point! # This test is verifying that they will not get introduced too early. diff --git a/tests/filecheck/clift/comments/2-emit-headers/EnumType.mlir b/tests/filecheck/clift/comments/2-emit-headers/EnumType.mlir index 80d2529da..401c8babc 100644 --- a/tests/filecheck/clift/comments/2-emit-headers/EnumType.mlir +++ b/tests/filecheck/clift/comments/2-emit-headers/EnumType.mlir @@ -7,6 +7,8 @@ !uint64_t = !clift.int +// CHECK: typedef enum _PACKED my_commented_enum my_commented_enum; + // CHECK: /// Take a look at struct and function comment tests for more "meat". // CHECK: /// // CHECK: /// This one is just to ensure enum-attached comments don't @@ -22,7 +24,6 @@ // CHECK: /// And this one is too big for its own good! // CHECK: enum_entry_my_commented_enum_18446744073709551615 = 0xFFFFFFFFFFFFFFFFU, // CHECK: }; -// CHECK: typedef enum _PACKED my_commented_enum my_commented_enum; !my_commented_enum = !clift.enum< "/type-definition/0-EnumDefinition" as "my_commented_enum" : !uint64_t { diff --git a/tests/filecheck/clift/emit-field-accesses/struct-enum-field-access.mlir b/tests/filecheck/clift/emit-field-accesses/struct-enum-field-access.mlir index feb9c1c8d..69118b38b 100644 --- a/tests/filecheck/clift/emit-field-accesses/struct-enum-field-access.mlir +++ b/tests/filecheck/clift/emit-field-accesses/struct-enum-field-access.mlir @@ -11,23 +11,23 @@ // Generic void function prototype with no argument !f = !clift.func< - "1000" as "f" : !void() + "/type-definition/1000-CABIFunctionDefinition" as "f" : !void() > // Enum type with underlying `int32_t` !my_enum = !clift.enum< - "1001" as "my_enum" : !int32_t { - "" as "A" : 0, - "" as "B" : 1 + "/type-definition/1001-EnumDefinition" as "my_enum" : !int32_t { + "/enum-entry/1001-EnumDefinition/0" as "A" : 0, + "/enum-entry/1001-EnumDefinition/1" as "B" : 1 } > // Struct with an `enum` field: the enum should be traversed correctly and // the underlying type should be reachable through the enum !s = !clift.struct< - "1" : size(8) { - "" : offset(0) !int32_t, - "" : offset(4) !my_enum + "/type-definition/1-StructDefinition" : size(8) { + "/struct-field/1-StructDefinition/0" : offset(0) !int32_t, + "/struct-field/1-StructDefinition/4" : offset(4) !my_enum } > @@ -47,8 +47,8 @@ module attributes {clift.module} { } // CHECK-LABEL: clift.func @test_enum_field - // CHECK: [[STRUCT:%[0-9]+]] = clift.local : !_1_ - // CHECK: [[ADDRESSOF1:%[0-9]+]] = clift.addressof [[STRUCT]] : !clift.ptr<8 to !_1_> + // CHECK: [[STRUCT:%[0-9]+]] = clift.local : !_type_definition_1_StructDefinition + // CHECK: [[ADDRESSOF1:%[0-9]+]] = clift.addressof [[STRUCT]] : !clift.ptr<8 to !_type_definition_1_StructDefinition> // CHECK: [[ACCESS:%[0-9]+]] = clift.access [[ADDRESSOF1]] // CHECK: [[ADDRESSOF2:%[0-9]+]] = clift.addressof [[ACCESS]] // CHECK: [[CAST:%[0-9]+]] = clift.bitcast [[ADDRESSOF2]] diff --git a/tests/filecheck/clift/model-import/ArrayType.yml b/tests/filecheck/clift/model-import/ArrayType.yml index 504648ce0..470c12cab 100644 --- a/tests/filecheck/clift/model-import/ArrayType.yml +++ b/tests/filecheck/clift/model-import/ArrayType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/CABIFunctionType1.yml b/tests/filecheck/clift/model-import/CABIFunctionType1.yml index f2f1a6fa2..d93c8ebc3 100644 --- a/tests/filecheck/clift/model-import/CABIFunctionType1.yml +++ b/tests/filecheck/clift/model-import/CABIFunctionType1.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 TypeDefinitions: diff --git a/tests/filecheck/clift/model-import/CABIFunctionType2.yml b/tests/filecheck/clift/model-import/CABIFunctionType2.yml index eea34bf0b..52cdcac19 100644 --- a/tests/filecheck/clift/model-import/CABIFunctionType2.yml +++ b/tests/filecheck/clift/model-import/CABIFunctionType2.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 TypeDefinitions: diff --git a/tests/filecheck/clift/model-import/EnumType.yml b/tests/filecheck/clift/model-import/EnumType.yml index 33f8b5b67..70672c59f 100644 --- a/tests/filecheck/clift/model-import/EnumType.yml +++ b/tests/filecheck/clift/model-import/EnumType.yml @@ -6,7 +6,7 @@ # input line. The angle brackets are matched separately in order to ignore # parameters other than those explicitly being checked for. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/RawFunctionType1.yml b/tests/filecheck/clift/model-import/RawFunctionType1.yml index 60538dab2..12e487a3d 100644 --- a/tests/filecheck/clift/model-import/RawFunctionType1.yml +++ b/tests/filecheck/clift/model-import/RawFunctionType1.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 TypeDefinitions: diff --git a/tests/filecheck/clift/model-import/RawFunctionType2.yml b/tests/filecheck/clift/model-import/RawFunctionType2.yml index 63d526ce0..6c437f2de 100644 --- a/tests/filecheck/clift/model-import/RawFunctionType2.yml +++ b/tests/filecheck/clift/model-import/RawFunctionType2.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 TypeDefinitions: diff --git a/tests/filecheck/clift/model-import/RawFunctionType3.yml b/tests/filecheck/clift/model-import/RawFunctionType3.yml index 69110bd89..c2d260e24 100644 --- a/tests/filecheck/clift/model-import/RawFunctionType3.yml +++ b/tests/filecheck/clift/model-import/RawFunctionType3.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 TypeDefinitions: diff --git a/tests/filecheck/clift/model-import/RecursiveStructType.yml b/tests/filecheck/clift/model-import/RecursiveStructType.yml index 5be4a5296..b2f9497ad 100644 --- a/tests/filecheck/clift/model-import/RecursiveStructType.yml +++ b/tests/filecheck/clift/model-import/RecursiveStructType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/StructType.yml b/tests/filecheck/clift/model-import/StructType.yml index 4e0d7ec7d..79b2bf2fd 100644 --- a/tests/filecheck/clift/model-import/StructType.yml +++ b/tests/filecheck/clift/model-import/StructType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/StructTypeWithCode.yml b/tests/filecheck/clift/model-import/StructTypeWithCode.yml index ddf36528a..9b613c13d 100644 --- a/tests/filecheck/clift/model-import/StructTypeWithCode.yml +++ b/tests/filecheck/clift/model-import/StructTypeWithCode.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/TypedefType.yml b/tests/filecheck/clift/model-import/TypedefType.yml index e5a2bdd56..37196df0a 100644 --- a/tests/filecheck/clift/model-import/TypedefType.yml +++ b/tests/filecheck/clift/model-import/TypedefType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/model-import/UnionType.yml b/tests/filecheck/clift/model-import/UnionType.yml index e046dc27a..4a2393608 100644 --- a/tests/filecheck/clift/model-import/UnionType.yml +++ b/tests/filecheck/clift/model-import/UnionType.yml @@ -1,7 +1,7 @@ # # This file is distributed under the MIT License. See LICENSE.md for details. # -# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s +# RUN: %root/bin/revng pipe import-types %s /dev/null /dev/null /dev/stdout | %root/bin/revng clift-opt | FileCheck %s Architecture: x86_64 diff --git a/tests/filecheck/clift/serialization/const.mlir b/tests/filecheck/clift/serialization/const.mlir new file mode 100644 index 000000000..b0437898b --- /dev/null +++ b/tests/filecheck/clift/serialization/const.mlir @@ -0,0 +1,24 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +// RUN: %root/bin/revng clift-opt %s 2>&1 --emit-bytecode | %root/bin/revng clift-opt %s 2>&1 | FileCheck %s + +!void = !clift.void + +// CHECK: !_type_definition_1_StructDefinition = !clift.struct +// CHECK: < +// CHECK: "/type-definition/1-StructDefinition" : size(8) +// CHECK: { +// CHECK: "/struct-field/1-StructDefinition/0" : offset(0) +// CHECK: !clift.ptr<8 to !clift.const> +// CHECK: } +// CHECK: > + +!s = !clift.struct< + "/type-definition/1-StructDefinition" : size(8) { + "/struct-field/1-StructDefinition/0" : offset(0) !clift.ptr<8 to !clift.const> + } +> + +module attributes {clift.module, clift.test = !s} {} diff --git a/tests/filecheck/llvm-passes/operatorprecedence-resolution.ll b/tests/filecheck/llvm-passes/operatorprecedence-resolution.ll deleted file mode 100644 index 2326920c6..000000000 --- a/tests/filecheck/llvm-passes/operatorprecedence-resolution.ll +++ /dev/null @@ -1,75 +0,0 @@ -; -; This file is distributed under the MIT License. See LICENSE.md for details. -; - -; RUN: %root/bin/revng opt %s -operatorprecedence-resolution -language=c -S -o - | FileCheck %s --check-prefix=C-LANG -; RUN: %root/bin/revng opt %s -operatorprecedence-resolution -language=nop -S -o - | FileCheck %s --check-prefix=NOP-LANG -; -; Ensures that OperatorPrecedenceResolutionPass has correctly parenthesized the expression -; according to the operator precedence priority. - -; int parenthesize_exp1(int a, int b, int c) { -; return a / b / c; -; } -define i32 @parenthesize_exp1(i32 %0, i32 %1, i32 %2) { - %4 = sdiv i32 %0, %1 - ; CHECK-NOT: %5 = call i32 @parentheses.1(i32 %4) - %5 = sdiv i32 %4, %2 - ret i32 %5 -} - -; int parenthesize_exp2(int a, int b, int c) { -; return a / (b / c); -; } -define i32 @parenthesize_exp2(i32 %0, i32 %1, i32 %2) { - %4 = sdiv i32 %1, %2 - ; CHECK: %5 = call i32 @parentheses.1(i32 %4) - ; CHECK-NEXT: %6 = sdiv i32 %0, %5 - %5 = sdiv i32 %0, %4 - ret i32 %5 -} - -; int parenthesize_exp3(int a, int b, int c, int d, int e) { -; return 5 + a * (b + c) + d / (2 + e); -; } -; Only associativity is taken into account in the NOP language, since all the operators -; have the same precedence. This implies that the reconstructed expression for NOP is the following: -; (5 + a * b + c) + (d / (2 + e)) -; Note that here the parentheses are needed to specify that the division comes before the addition. -define i32 @parenthesize_exp3(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4) { - %6 = add i32 %2, %1 - ; C-LANG: %7 = call i32 @parentheses(i32 %6) - ; C-LANG-NEXT: %8 = mul i32 %7, %0 - %7 = mul i32 %6, %0 - %8 = add i32 %7, 5 - %9 = add i32 %4, 2 - ; C-LANG: %11 = call i32 @parentheses(i32 %10) - ; C-LANG-NEXT: %12 = sdiv i32 %3, %11 - ; NOP-LANG: %10 = call i32 @parentheses(i32 %9) - ; NOP-LANG-NEXT: %11 = sdiv i32 %3, %10 - %10 = sdiv i32 %3, %9 - ; NOP-LANG: %12 = call i32 @parentheses(i32 %11) - ; NOP-LANG-NEXT: %13 = add i32 %8, %12 - %11 = add i32 %8, %10 - ret i32 %11 -} - -define i32 @parenthesize_unary_minus(i32 %0) { - %2 = call i32 @unary_minus(i32 1) - ; CHECK: %3 = call @parentheses.1(i32 %2) - ; CHECK-NEXT: %4 = add i32 %0, %3 - %3 = add i32 %0, %2 - ret i32 %3 -} - -define i32 @parenthesize_binary_not(i32 %0) { - %2 = call i32 @binary_not(i32 1) - ; CHECK: %3 = call @parentheses.1(i32 %2) - ; CHECK-NEXT: %4 = add i32 %0, %3 - %3 = add i32 %0, %2 - ret i32 %3 -} - -declare i32 @unary_minus(i32 %0) - -declare i32 @binary_not(i32 %0) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 2874e2f9a..f72958141 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -569,27 +569,6 @@ target_link_libraries( ${LLVM_LIBRARIES}) add_test(NAME test_clift_type COMMAND test_clift_type) -# -# test_pointer_array_emission -# - -revng_add_test_executable(test_pointer_array_emission - "${SRC}/PointerArrayEmission.cpp") -target_compile_definitions(test_pointer_array_emission - PRIVATE "BOOST_TEST_DYN_LINK=1") -target_include_directories( - test_pointer_array_emission PRIVATE "${CMAKE_SOURCE_DIR}" - "${Boost_INCLUDE_DIRS}") -target_link_libraries( - test_pointer_array_emission - revngSupport - revngTypeNames - revngModel - revngUnitTestHelpers - Boost::unit_test_framework - ${LLVM_LIBRARIES}) -add_test(NAME test_pointer_array_emission COMMAND test_pointer_array_emission) - # # test_statement_comment_placement # diff --git a/tests/unit/CliftType.cpp b/tests/unit/CliftType.cpp index 0c2bfc505..b88d4f190 100644 --- a/tests/unit/CliftType.cpp +++ b/tests/unit/CliftType.cpp @@ -24,20 +24,14 @@ static bool verify(const model::TypeDefinition &ModelType, const model::Binary &Binary, const bool Assert) { return withContext([&](const auto EmitError, mlir::MLIRContext *Context) { - return static_cast(clift::importType(EmitError, - Context, - ModelType, - Binary)); + return static_cast(clift::importType(EmitError, Context, ModelType)); }); } static bool verify(const model::Type &ModelType, const model::Binary &Binary, bool Assert) { return withContext([&](const auto EmitError, mlir::MLIRContext *Context) { - return static_cast(clift::importType(EmitError, - Context, - ModelType, - Binary)); + return static_cast(clift::importType(EmitError, Context, ModelType)); }); } diff --git a/tests/unit/PointerArrayEmission.cpp b/tests/unit/PointerArrayEmission.cpp deleted file mode 100644 index 58a163fd3..000000000 --- a/tests/unit/PointerArrayEmission.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/// Tests `getNamedCInstance` - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#define BOOST_TEST_MODULE PointerArrayEmission -bool init_unit_test(); -#include "boost/test/unit_test.hpp" - -#include "revng/Model/Binary.h" -#include "revng/Support/Assert.h" -#include "revng/TypeNames/ModelCBuilder.h" -#include "revng/UnitTestHelpers/UnitTestHelpers.h" - -// Tests whether the way we emit pointers and arrays is reasonable. -// Each layer of the types builds on the previous ones, so the test get more -// complex gradually. -// -// \note the easiest way to understand one of these is to see it fail: just edit -// one of the strings and watch what it prints: it'll show what -// we emitted, what we should have, and how the type in question looks -// like in the model. -// -// \note also, notice that there is no special logic surrounding function -// pointers, it's because we emit those as typedef, which leads to them -// having a concrete name to fall onto, making their emission trivial. -BOOST_AUTO_TEST_CASE(PointerArrayEmission) { - // Maps types to their expected output. - std::vector> Tests; - - TupleTree Binary = {}; - Binary->Architecture() = model::Architecture::x86_64; - - auto Void = model::PrimitiveType::makeVoid(); - Tests.emplace_back(Void, "void test"); - - auto Int = model::PrimitiveType::makeConstSigned(4); - Tests.emplace_back(Int, "const int32_t test"); - - auto &&[TypedefDef, Typedef] = Binary->makeTypedefDefinition(Void.copy()); - Tests.emplace_back(Typedef, "typedef_0 test"); - - auto VoidP = model::PointerType::make(Void.copy(), 8); - Tests.emplace_back(VoidP, "void *test"); - auto VoidCP = model::PointerType::makeConst(Void.copy(), 8); - Tests.emplace_back(VoidCP, "void *const test"); - auto IntP = model::PointerType::make(Int.copy(), 8); - Tests.emplace_back(IntP, "const int32_t *test"); - auto IntCP = model::PointerType::makeConst(Int.copy(), 8); - Tests.emplace_back(IntCP, "const int32_t *const test"); - auto TypedefP = model::PointerType::make(Typedef.copy(), 8); - Tests.emplace_back(TypedefP, "typedef_0 *test"); - auto TypedefCP = model::PointerType::makeConst(Typedef.copy(), 8); - Tests.emplace_back(TypedefCP, "typedef_0 *const test"); - - auto VoidPA = model::ArrayType::make(VoidP.copy(), 15); - Tests.emplace_back(VoidPA, "void *test[15]"); - auto VoidCPA = model::ArrayType::make(VoidCP.copy(), 16); - Tests.emplace_back(VoidCPA, "void *const test[16]"); - auto IntPA = model::ArrayType::make(IntP.copy(), 17); - Tests.emplace_back(IntPA, "const int32_t *test[17]"); - auto IntCPA = model::ArrayType::make(IntCP.copy(), 18); - Tests.emplace_back(IntCPA, "const int32_t *const test[18]"); - auto TypedefPA = model::ArrayType::make(TypedefP.copy(), 19); - Tests.emplace_back(TypedefPA, "typedef_0 *test[19]"); - auto TypedefCPA = model::ArrayType::make(TypedefCP.copy(), 20); - Tests.emplace_back(TypedefCPA, "typedef_0 *const test[20]"); - - auto VoidPAA = model::ArrayType::make(VoidPA.copy(), 42); - Tests.emplace_back(VoidPAA, "void *test[42][15]"); - auto VoidCPAA = model::ArrayType::make(VoidCPA.copy(), 41); - Tests.emplace_back(VoidCPAA, "void *const test[41][16]"); - auto IntPAA = model::ArrayType::make(IntPA.copy(), 40); - Tests.emplace_back(IntPAA, "const int32_t *test[40][17]"); - auto IntCPAA = model::ArrayType::make(IntCPA.copy(), 39); - Tests.emplace_back(IntCPAA, "const int32_t *const test[39][18]"); - auto TypedefPAA = model::ArrayType::make(TypedefPA.copy(), 38); - Tests.emplace_back(TypedefPAA, "typedef_0 *test[38][19]"); - auto TypedefCPAA = model::ArrayType::make(TypedefCPA.copy(), 37); - Tests.emplace_back(TypedefCPAA, "typedef_0 *const test[37][20]"); - - auto VoidPAACP = model::PointerType::makeConst(VoidPAA.copy(), 8); - Tests.emplace_back(VoidPAACP, "void *(*const test)[42][15]"); - auto VoidCPAACP = model::PointerType::makeConst(VoidCPAA.copy(), 8); - Tests.emplace_back(VoidCPAACP, "void *const (*const test)[41][16]"); - auto IntPAAP = model::PointerType::make(IntPAA.copy(), 8); - Tests.emplace_back(IntPAAP, "const int32_t *(*test)[40][17]"); - auto IntCPAAP = model::PointerType::make(IntCPAA.copy(), 8); - Tests.emplace_back(IntCPAAP, "const int32_t *const (*test)[39][18]"); - auto TypedefPAACP = model::PointerType::makeConst(TypedefPAA.copy(), 8); - Tests.emplace_back(TypedefPAACP, "typedef_0 *(*const test)[38][19]"); - auto TypedefCPAAP = model::PointerType::make(TypedefCPAA.copy(), 8); - Tests.emplace_back(TypedefCPAAP, "typedef_0 *const (*test)[37][20]"); - - auto FinalV = model::ArrayType::make(VoidPAACP.copy(), 1); - Tests.emplace_back(FinalV, "void *(*const test[1])[42][15]"); - auto FinalVC = model::ArrayType::make(VoidCPAACP.copy(), 2); - Tests.emplace_back(FinalVC, "void *const (*const test[2])[41][16]"); - auto FinalI = model::ArrayType::make(IntPAAP.copy(), 3); - Tests.emplace_back(FinalI, "const int32_t *(*test[3])[40][17]"); - auto FinalIC = model::ArrayType::make(IntCPAAP.copy(), 4); - Tests.emplace_back(FinalIC, "const int32_t *const (*test[4])[39][18]"); - auto FinalT = model::ArrayType::make(TypedefPAACP.copy(), 5); - Tests.emplace_back(FinalT, "typedef_0 *(*const test[5])[38][19]"); - auto FinalTC = model::ArrayType::make(TypedefCPAAP.copy(), 6); - Tests.emplace_back(FinalTC, "typedef_0 *const (*test[6])[37][20]"); - - auto Extra1 = model::PointerType::makeConst(FinalIC.copy(), 8); - Tests.emplace_back(Extra1, - "const int32_t *const (*(*const test)[4])[39][18]"); - auto Extra2 = model::PointerType::makeConst(Extra1.copy(), 8); - Tests.emplace_back(Extra2, - "const int32_t *const " - "(*(*const *const test)[4])[39][18]"); - auto Extra3 = model::PointerType::make(Extra2.copy(), 8); - Tests.emplace_back(Extra3, - "const int32_t *const " - "(*(*const *const *test)[4])[39][18]"); - auto Extra4 = model::PointerType::make(Extra3.copy(), 8); - Tests.emplace_back(Extra4, - "const int32_t *const " - "(*(*const *const **test)[4])[39][18]"); - auto Extra5 = model::PointerType::make(Extra4.copy(), 8); - Tests.emplace_back(Extra5, - "const int32_t *const " - "(*(*const *const ***test)[4])[39][18]"); - auto Extra6 = model::PointerType::makeConst(Extra5.copy(), 8); - Tests.emplace_back(Extra6, - "const int32_t *const " - "(*(*const *const ****const test)[4])[39][18]"); - - std::string FailureLog; - ptml::ModelCBuilder B(llvm::nulls(), *Binary, /* EnableTaglessMode = */ true); - for (auto &&[Type, ExpectedOutput] : Tests) { - std::string ActualOutput = B.getNamedCInstance(*Type, "test"); - if (ActualOutput != ExpectedOutput) { - FailureLog += "Output of `getNamedCInstance` (\"" + ActualOutput - + "\")\n"; - FailureLog += "didn't match the expectations (\"" + ExpectedOutput - + "\")\n"; - FailureLog += "for\n" + toString(Type) + "\n\n"; - } - } - - revng_check(FailureLog.empty(), FailureLog.c_str()); -}