// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/Type.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/raw_ostream.h" #include "revng/ABI/FunctionType/Layout.h" #include "revng/Model/Binary.h" #include "revng/Model/CABIFunctionDefinition.h" #include "revng/Model/FunctionAttribute.h" #include "revng/Model/Helpers.h" #include "revng/Model/Identifier.h" #include "revng/Model/RawFunctionDefinition.h" #include "revng/PTML/Constants.h" #include "revng/PTML/Tag.h" #include "revng/Pipeline/Location.h" #include "revng/Support/Assert.h" #include "revng/Support/FunctionTags.h" #include "revng-c/Pipes/Ranks.h" #include "revng-c/Support/FunctionTags.h" #include "revng-c/Support/ModelHelpers.h" #include "revng-c/Support/PTMLC.h" #include "revng-c/TypeNames/LLVMTypeNames.h" #include "revng-c/TypeNames/ModelTypeNames.h" using llvm::dyn_cast; using llvm::StringRef; using llvm::Twine; using tokenDefinition::types::TypeString; using pipeline::serializedLocation; using ptml::Tag; namespace attributes = ptml::attributes; namespace tokens = ptml::c::tokens; namespace ranks = revng::ranks; using namespace ArtificialTypes; template concept ModelFunction = std::same_as or std::same_as; static std::string serializeVariableLocation(llvm::StringRef VariableName, const model::DynamicFunction &F) { return pipeline::serializedLocation(ranks::DynamicFunctionArgument, F.key(), VariableName.str()); } static std::string serializeVariableLocation(llvm::StringRef VariableName, const model::Function &F) { return pipeline::serializedLocation(ranks::LocalVariable, F.key(), VariableName.str()); } template static std::string getArgumentLocation(llvm::StringRef ArgumentName, const FunctionType &F, ptml::PTMLCBuilder &B) { return B.getTag(ptml::tags::Span, ArgumentName) .addAttribute(attributes::Token, tokens::FunctionParameter) .addAttribute(B.getLocationAttribute(IsDefinition), serializeVariableLocation(ArgumentName, F)) .serialize(); } static std::string getArgumentLocationDefinition(llvm::StringRef ArgumentName, const model::DynamicFunction &F, ptml::PTMLCBuilder &B) { return getArgumentLocation(ArgumentName, F, B); } static std::string getArgumentLocationDefinition(llvm::StringRef ArgumentName, const model::Function &F, ptml::PTMLCBuilder &B) { return getArgumentLocation(ArgumentName, F, B); } std::string getArgumentLocationReference(llvm::StringRef ArgumentName, const model::Function &F, ptml::PTMLCBuilder &B) { return getArgumentLocation(ArgumentName, F, B); } template static std::string getVariableLocation(llvm::StringRef VariableName, const model::Function &F, ptml::PTMLCBuilder &B) { return B.getTag(ptml::tags::Span, VariableName) .addAttribute(attributes::Token, tokens::Variable) .addAttribute(B.getLocationAttribute(IsDefinition), serializeVariableLocation(VariableName, F)) .serialize(); } std::string getVariableLocationDefinition(llvm::StringRef VariableName, const model::Function &F, ptml::PTMLCBuilder &B) { return getVariableLocation(VariableName, F, B); } std::string getVariableLocationReference(llvm::StringRef VariableName, const model::Function &F, ptml::PTMLCBuilder &B) { return getVariableLocation(VariableName, F, B); } struct NamedCInstanceImpl { const ptml::PTMLCBuilder &B; llvm::ArrayRef AllowedActions; bool OmitInnerTypeName; public: RecursiveCoroutine getString(const model::Type &Type, std::string &&Emitted, bool PreviousWasAPointer = false) { bool NeedsSpace = true; // Emit a space except in cases where we are if (Emitted.empty()) NeedsSpace = false; // emitting a nameless instance, if (llvm::isa(Type) and not Type.IsConst()) NeedsSpace = false; // a non-const pointer, if (llvm::isa(Type)) NeedsSpace = false; // or an array. if (NeedsSpace) Emitted = " " + std::move(Emitted); if (auto *Array = llvm::dyn_cast(&Type)) { rc_return rc_recur impl(*Array, std::move(Emitted), PreviousWasAPointer); } else if (auto *Pointer = llvm::dyn_cast(&Type)) { rc_return rc_recur impl(*Pointer, std::move(Emitted), PreviousWasAPointer); } else if (auto *Def = llvm::dyn_cast(&Type)) { rc_return rc_recur impl(*Def, std::move(Emitted)); } else if (auto *Primitive = llvm::dyn_cast(&Type)) { rc_return rc_recur impl(*Primitive, std::move(Emitted)); } else { revng_abort("Unsupported type."); } } private: RecursiveCoroutine impl(const model::ArrayType &Array, std::string &&Emitted, bool PreviousWasAPointer) { revng_assert(Array.IsConst() == false); if (PreviousWasAPointer) Emitted = "(" + std::move(Emitted) + ")"; Emitted += "[" + std::to_string(Array.ElementCount()) + "]"; rc_return rc_recur getString(*Array.ElementType(), std::move(Emitted), false); } RecursiveCoroutine impl(const model::PointerType &Pointer, std::string &&Emitted, bool PreviousWasAPointer) { std::string Current = B.getTag(ptml::tags::Span, "*") .addAttribute(attributes::Token, tokens::Operator) .serialize(); if (Pointer.IsConst()) Current += constKeyword(); Current += std::move(Emitted); rc_return rc_recur getString(*Pointer.PointeeType(), std::move(Current), true); } RecursiveCoroutine impl(const model::DefinedType &Def, std::string &&Emitted) { std::string Result = ""; if (not OmitInnerTypeName) { if (Def.IsConst()) Result += constKeyword() + " "; Result += B.getLocationReference(Def.unwrap(), AllowedActions); } Result += std::move(Emitted); rc_return Result; } RecursiveCoroutine impl(const model::PrimitiveType &Primitive, std::string &&Emitted) { std::string Result = ""; if (not OmitInnerTypeName) { if (Primitive.IsConst()) Result += constKeyword() + " "; Result += B.getLocationReference(Primitive); } Result += std::move(Emitted); rc_return Result; } std::string constKeyword() { using PTMLKW = ptml::PTMLCBuilder::Keyword; return B.getKeyword(PTMLKW::Const).serialize(); } }; TypeString getNamedCInstance(const model::Type &Type, StringRef InstanceName, const ptml::PTMLCBuilder &B, llvm::ArrayRef AllowedActions, bool OmitInnerTypeName) { NamedCInstanceImpl Helper(B, AllowedActions, OmitInnerTypeName); std::string Result = InstanceName.str(); Result = Helper.getString(Type, std::move(Result)); return TypeString(std::move(Result)); } static RecursiveCoroutine getArrayWrapperImpl(const model::Type &Type, const ptml::PTMLCBuilder &B) { if (auto *Array = llvm::dyn_cast(&Type)) { std::string Result = "array_" + std::to_string(Array->ElementCount()) + "_of_"; Result += rc_recur getArrayWrapperImpl(*Array->ElementType(), B); rc_return Result; } else if (auto *D = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_" : ""); rc_return std::move(Result += D->unwrap().name().str().str()); } else if (auto *Pointer = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_ptr_to_" : "ptr_to_"); rc_return std::move(Result += rc_recur getArrayWrapperImpl(*Pointer->PointeeType(), B)); } else if (auto *Primitive = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_" : ""); rc_return std::move(Result += Primitive->getCName()); } else { revng_abort("Unsupported model::Type."); } } TypeString getArrayWrapper(const model::ArrayType &ArrayType, const ptml::PTMLCBuilder &B) { std::string Result = ArrayWrapperPrefix; Result += getArrayWrapperImpl(ArrayType, B); return TypeString(B.getTag(ptml::tags::Span, std::move(Result)).serialize()); } TypeString getNamedInstanceOfReturnType(const model::TypeDefinition &Function, llvm::StringRef InstanceName, const ptml::PTMLCBuilder &B, bool IsDefinition) { TypeString Result; std::vector AllowedActions = { ptml::actions::Rename }; using namespace abi::FunctionType; const auto Layout = Layout::make(Function); auto ReturnMethod = Layout.returnMethod(); switch (ReturnMethod) { case abi::FunctionType::ReturnMethod::Void: Result = B.getTag(ptml::tags::Span, "void") .addAttribute(attributes::Token, tokens::Type) .serialize(); if (not InstanceName.empty()) Result.append((Twine(" ") + Twine(InstanceName)).str()); break; case ReturnMethod::ModelAggregate: case ReturnMethod::Scalar: { const model::Type *ReturnType = nullptr; if (ReturnMethod == ReturnMethod::ModelAggregate) { ReturnType = &Layout.returnValueAggregateType(); } else { revng_assert(Layout.ReturnValues.size() == 1); ReturnType = Layout.ReturnValues[0].Type.get(); } // When returning arrays, they need to be wrapped into an artificial // struct if (const model::ArrayType *Array = ReturnType->getArray()) { Result = getArrayWrapper(*Array, B); if (not InstanceName.empty()) Result.append((Twine(" ") + Twine(InstanceName)).str()); } else { Result = getNamedCInstance(*ReturnType, InstanceName, B, AllowedActions); } } break; case ReturnMethod::RegisterSet: { // RawFunctionTypes can return multiple values, which need to be wrapped // in a struct revng_assert(llvm::isa(Function)); std::string Name = (Twine(RetStructPrefix) + Function.name()).str(); std::string Location = pipeline::serializedLocation(ranks::ArtificialStruct, Function.key()); Result = B.tokenTag(Name, ptml::c::tokens::Type) .addAttribute(B.getLocationAttribute(IsDefinition), Location) .serialize(); if (not InstanceName.empty()) Result.append((Twine(" ") + Twine(InstanceName)).str()); } break; default: revng_abort(); } revng_assert(not llvm::StringRef(Result).trim().empty()); return TypeString(B.getTag(ptml::tags::Span, Result) .addAttribute(attributes::ActionContextLocation, serializedLocation(ranks::ReturnValue, Function.key())) .serialize()); } static std::string getFunctionAttributeString(const model::FunctionAttribute::Values &A) { using namespace model::FunctionAttribute; switch (A) { case NoReturn: return "_Noreturn"; case Inline: return "inline"; default: revng_abort("cannot print unexpected model::FunctionAttribute"); } return ""; } using AttributesSet = TrackingMutableSet; static std::string getFunctionAttributesString(const AttributesSet &Attributes) { std::string Result; for (const auto &A : Attributes) Result += " " + getFunctionAttributeString(A); return Result; } template static void printFunctionPrototypeImpl(const FunctionType *Function, const model::RawFunctionDefinition &RF, const llvm::StringRef &FunctionName, llvm::raw_ostream &Header, ptml::PTMLCBuilder &B, const model::Binary &Model, bool SingleLine) { using namespace abi::FunctionType; auto Layout = Layout::make(RF); revng_assert(not Layout.hasSPTAR()); revng_assert(Layout.returnMethod() != ReturnMethod::ModelAggregate); Header << B.getAnnotateABI(("raw_" + model::Architecture::getName(RF.Architecture())) .str()); if (Function and not Function->Attributes().empty()) Header << getFunctionAttributesString(Function->Attributes()); Header << (SingleLine ? " " : "\n"); Header << getNamedInstanceOfReturnType(RF, FunctionName, B, false); if (RF.Arguments().empty() and RF.StackArgumentsType().isEmpty()) { Header << "(" << B.tokenTag("void", ptml::c::tokens::Type) << ")"; } else { const StringRef Open = "("; const StringRef Comma = ", "; StringRef Separator = Open; for (const model::NamedTypedRegister &Arg : RF.Arguments()) { std::string ArgName = Arg.name().str().str(); std::string ArgString; if (Function != nullptr) ArgString = getArgumentLocationDefinition(ArgName, *Function, B); std::string MarkedType = getNamedCInstance(*Arg.Type(), ArgString, B).str().str(); std::string MarkedReg = B.getAnnotateReg(model::Register::getName(Arg.Location())); Tag ArgTag = B.getTag(ptml::tags::Span, MarkedType + " " + MarkedReg); ArgTag.addAttribute(attributes::ActionContextLocation, serializedLocation(ranks::RawArgument, RF.key(), Arg.key())); Header << Separator << ArgTag.serialize(); Separator = Comma; } if (not RF.StackArgumentsType().isEmpty()) { // Add last argument representing a pointer to the stack arguments std::string StackArgName; if (Function != nullptr) StackArgName = getArgumentLocationDefinition("_stack_arguments", *Function, B); Header << Separator << getNamedCInstance(*RF.StackArgumentsType(), StackArgName, B); Header << " " << B.getAnnotateStack(); } Header << ")"; } } template static void printFunctionPrototypeImpl(const FunctionType *Function, const model::CABIFunctionDefinition &CF, const llvm::StringRef &FunctionName, llvm::raw_ostream &Header, ptml::PTMLCBuilder &B, const model::Binary &Model, bool SingleLine) { Header << B.getAnnotateABI(model::ABI::getName(CF.ABI())); if (Function and not Function->Attributes().empty()) Header << getFunctionAttributesString(Function->Attributes()); Header << (SingleLine ? " " : "\n"); Header << getNamedInstanceOfReturnType(CF, FunctionName, B, false); if (CF.Arguments().empty()) { Header << "(" << B.tokenTag("void", ptml::c::tokens::Type) << ")"; } else { const StringRef Open = "("; const StringRef Comma = ", "; StringRef Separator = Open; for (const auto &Arg : CF.Arguments()) { std::string ArgName = Arg.name().str().str(); std::string ArgString; if (Function != nullptr) ArgString = getArgumentLocationDefinition(ArgName, *Function, B); TypeString ArgDeclaration; if (const model::ArrayType *Array = Arg.Type()->getArray()) { ArgDeclaration = getArrayWrapper(*Array, B); if (not ArgString.empty()) { ArgDeclaration.append(" "); ArgDeclaration.append(ArgString); } } else { ArgDeclaration = getNamedCInstance(*Arg.Type(), ArgString, B); } Tag ArgTag = B.getTag(ptml::tags::Span, ArgDeclaration); ArgTag.addAttribute(attributes::ActionContextLocation, serializedLocation(ranks::CABIArgument, CF.key(), Arg.key())); Header << Separator << ArgTag.serialize(); Separator = Comma; } Header << ")"; } } void printFunctionPrototype(const model::TypeDefinition &FT, const model::Function &Function, llvm::raw_ostream &Header, ptml::PTMLCBuilder &B, const model::Binary &Model, bool SingleLine) { std::string Location = serializedLocation(ranks::Function, Function.key()); Tag FunctionTag = B.tokenTag(Function.name(), ptml::c::tokens::Function) .addAttribute(attributes::ActionContextLocation, Location) .addAttribute(attributes::LocationDefinition, Location); if (auto *RF = dyn_cast(&FT)) { printFunctionPrototypeImpl(&Function, *RF, FunctionTag.serialize(), Header, B, Model, SingleLine); } else if (auto *CF = dyn_cast(&FT)) { printFunctionPrototypeImpl(&Function, *CF, FunctionTag.serialize(), Header, B, Model, SingleLine); } else { revng_abort(); } } void printFunctionPrototype(const model::TypeDefinition &FT, const model::DynamicFunction &Function, llvm::raw_ostream &Header, ptml::PTMLCBuilder &B, const model::Binary &Model, bool SingleLine) { std::string Location = serializedLocation(ranks::DynamicFunction, Function.key()); Tag FunctionTag = B.tokenTag(Function.name(), ptml::c::tokens::Function) .addAttribute(attributes::ActionContextLocation, Location) .addAttribute(attributes::LocationDefinition, Location); if (auto *RF = dyn_cast(&FT)) { printFunctionPrototypeImpl(&Function, *RF, FunctionTag.serialize(), Header, B, Model, SingleLine); } else if (auto *CF = dyn_cast(&FT)) { printFunctionPrototypeImpl(&Function, *CF, FunctionTag.serialize(), Header, B, Model, SingleLine); } else { revng_abort(); } } void printFunctionTypeDeclaration(const model::TypeDefinition &FT, llvm::raw_ostream &Header, ptml::PTMLCBuilder &B, const model::Binary &Model) { auto TypeName = B.getLocationDefinition(FT); if (auto *RF = dyn_cast(&FT)) { printFunctionPrototypeImpl(nullptr, *RF, TypeName, Header, B, Model, true); } else if (auto *CF = dyn_cast(&FT)) { printFunctionPrototypeImpl(nullptr, *CF, TypeName, Header, B, Model, true); } else { revng_abort(); } }