Files
revng-revng/lib/TypeNames/ModelTypeNames.cpp
T
Ivan Krysak c6e38cd485 Return to using constant name builders
Because of how name builders used to lazy gather namespaces on the first
requested name, the objects were self mutating. As such only non-const
references could be used to pass them around.

Since that is no longer the case, this restores most of lost const
qualifiers.
2025-04-17 11:19:17 +03:00

597 lines
22 KiB
C++

//
// 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/ABI/ModelHelpers.h"
#include "revng/Model/Binary.h"
#include "revng/Model/CABIFunctionDefinition.h"
#include "revng/Model/FunctionAttribute.h"
#include "revng/Model/Helpers.h"
#include "revng/Model/RawFunctionDefinition.h"
#include "revng/PTML/Constants.h"
#include "revng/PTML/Tag.h"
#include "revng/Pipeline/Location.h"
#include "revng/Pipes/Ranks.h"
#include "revng/Support/Annotations.h"
#include "revng/Support/Assert.h"
#include "revng/Support/FunctionTags.h"
#include "revng/Support/PTMLC.h"
#include "revng/TypeNames/LLVMTypeNames.h"
#include "revng/TypeNames/PTMLCTypeBuilder.h"
using llvm::dyn_cast;
using llvm::StringRef;
using llvm::Twine;
using tokenDefinition::types::TypeString;
using ptml::Tag;
namespace attributes = ptml::attributes;
namespace tokens = ptml::c::tokens;
namespace ranks = revng::ranks;
template<typename FT>
concept ModelFunction = std::same_as<FT, model::Function>
or std::same_as<FT, model::DynamicFunction>;
static std::string toStringVariableLocation(llvm::StringRef VariableName,
const model::DynamicFunction &F) {
return pipeline::locationString(ranks::DynamicFunctionArgument,
F.key(),
VariableName.str());
}
static std::string toStringVariableLocation(llvm::StringRef VariableName,
const model::Function &F) {
return pipeline::locationString(ranks::LocalVariable,
F.key(),
VariableName.str());
}
static std::string toStringGotoLabelLocation(llvm::StringRef GotoLabelName,
const model::Function &F) {
return pipeline::locationString(ranks::GotoLabel,
F.key(),
GotoLabelName.str());
}
template<bool IsDefinition, ModelFunction FunctionType>
std::string getArgumentLocation(llvm::StringRef ArgumentName,
const FunctionType &F,
const ptml::CTypeBuilder &B) {
return B.getTag(ptml::tags::Span, ArgumentName)
.addAttribute(attributes::Token, tokens::FunctionParameter)
.addAttribute(B.getLocationAttribute(IsDefinition),
toStringVariableLocation(ArgumentName, F))
.toString();
}
static std::string
getArgumentLocationDefinition(llvm::StringRef ArgumentName,
const model::DynamicFunction &F,
const ptml::CTypeBuilder &B) {
return getArgumentLocation<true>(ArgumentName, F, B);
}
static std::string getArgumentLocationDefinition(llvm::StringRef ArgumentName,
const model::Function &F,
const ptml::CTypeBuilder &B) {
return getArgumentLocation<true>(ArgumentName, F, B);
}
using PCTB = ptml::CTypeBuilder;
std::string PCTB::getArgumentLocationReference(llvm::StringRef ArgumentName,
const model::Function &F) const {
return getArgumentLocation<false>(ArgumentName, F, *this);
}
template<bool IsDefinition>
static std::string getVariableLocation(llvm::StringRef VariableName,
const model::Function &F,
const ptml::CTypeBuilder &B) {
return B.getTag(ptml::tags::Span, VariableName)
.addAttribute(attributes::Token, tokens::Variable)
.addAttribute(B.getLocationAttribute(IsDefinition),
toStringVariableLocation(VariableName, F))
.toString();
}
std::string
PCTB::getVariableLocationDefinition(llvm::StringRef Name,
const model::Function &F) const {
return getVariableLocation<true>(Name, F, *this);
}
std::string PCTB::getVariableLocationReference(llvm::StringRef Name,
const model::Function &F) const {
return getVariableLocation<false>(Name, F, *this);
}
template<bool IsDefinition>
static std::string getGotoLabelLocation(llvm::StringRef GotoLabelName,
const model::Function &F,
const ptml::CTypeBuilder &B) {
return B.getTag(ptml::tags::Span, GotoLabelName)
.addAttribute(attributes::Token, tokens::GotoLabel)
.addAttribute(B.getLocationAttribute(IsDefinition),
toStringGotoLabelLocation(GotoLabelName, F))
.toString();
}
std::string
PCTB::getGotoLabelLocationDefinition(llvm::StringRef Name,
const model::Function &F) const {
return getGotoLabelLocation<true>(Name, F, *this);
}
std::string
PCTB::getGotoLabelLocationReference(llvm::StringRef Name,
const model::Function &F) const {
return getGotoLabelLocation<false>(Name, F, *this);
}
std::string PCTB::getLocationReference(const model::Function &F) const {
std::string Location = pipeline::locationString(ranks::Function, F.key());
return getTag(ptml::tags::Span, NameBuilder.name(F))
.addAttribute(attributes::Token, ptml::c::tokens::Function)
.addAttribute(attributes::ActionContextLocation, Location)
.addAttribute(attributes::LocationReferences, Location)
.toString();
}
std::string PCTB::getLocationReference(const model::DynamicFunction &F) const {
std::string Location = pipeline::locationString(ranks::DynamicFunction,
F.key());
return getTag(ptml::tags::Span, NameBuilder.name(F))
.addAttribute(attributes::Token, ptml::c::tokens::Function)
.addAttribute(attributes::ActionContextLocation, Location)
.addAttribute(attributes::LocationReferences, Location)
.toString();
}
struct NamedCInstanceImpl {
const ptml::CTypeBuilder &B;
llvm::ArrayRef<std::string> AllowedActions;
bool OmitInnerTypeName;
public:
RecursiveCoroutine<std::string> 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<model::PointerType>(Type) and not Type.IsConst())
NeedsSpace = false; // a non-const pointer,
if (llvm::isa<model::ArrayType>(Type))
NeedsSpace = false; // or an array.
if (NeedsSpace)
Emitted = " " + std::move(Emitted);
if (auto *Array = llvm::dyn_cast<model::ArrayType>(&Type)) {
rc_return rc_recur impl(*Array, std::move(Emitted), PreviousWasAPointer);
} else if (auto *Pointer = llvm::dyn_cast<model::PointerType>(&Type)) {
rc_return rc_recur impl(*Pointer,
std::move(Emitted),
PreviousWasAPointer);
} else if (auto *Def = llvm::dyn_cast<model::DefinedType>(&Type)) {
rc_return rc_recur impl(*Def, std::move(Emitted));
} else if (auto *Primitive = llvm::dyn_cast<model::PrimitiveType>(&Type)) {
rc_return rc_recur impl(*Primitive, std::move(Emitted));
} else {
revng_abort("Unsupported type.");
}
}
private:
RecursiveCoroutine<std::string> 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<std::string> impl(const model::PointerType &Pointer,
std::string &&Emitted,
bool PreviousWasAPointer) {
std::string Current = B.getTag(ptml::tags::Span, "*")
.addAttribute(attributes::Token, tokens::Operator)
.toString();
if (Pointer.IsConst())
Current += constKeyword();
Current += std::move(Emitted);
rc_return rc_recur getString(*Pointer.PointeeType(),
std::move(Current),
true);
}
RecursiveCoroutine<std::string> 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<std::string> 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() {
return B.getKeyword(ptml::CBuilder::Keyword::Const).toString();
}
};
TypeString PCTB::getNamedCInstance(const model::Type &Type,
StringRef InstanceName,
llvm::ArrayRef<std::string> AllowedActions,
bool OmitInnerTypeName) const {
NamedCInstanceImpl Helper(*this, AllowedActions, OmitInnerTypeName);
std::string Result = InstanceName.str();
Result = Helper.getString(Type, std::move(Result));
return TypeString(std::move(Result));
}
TypeString PCTB::getArrayWrapper(const model::ArrayType &ArrayType) const {
auto Name = NameBuilder.artificialArrayWrapperName(ArrayType);
return TypeString(getTag(ptml::tags::Span, std::move(Name)).toString());
}
TypeString
PCTB::getNamedInstanceOfReturnType(const model::TypeDefinition &Function,
llvm::StringRef InstanceName,
bool IsDefinition) const {
TypeString Result;
std::vector<std::string> 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 = getTag(ptml::tags::Span, "void")
.addAttribute(attributes::Token, c::tokens::Type)
.toString();
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);
if (not InstanceName.empty())
Result.append((Twine(" ") + Twine(InstanceName)).str());
} else {
Result = getNamedCInstance(*ReturnType, InstanceName, AllowedActions);
}
} break;
case ReturnMethod::RegisterSet: {
// RawFunctionTypes can return multiple values, which need to be wrapped
// in a struct
auto *RFT = llvm::dyn_cast<model::RawFunctionDefinition>(&Function);
revng_assert(RFT);
std::string Location = pipeline::locationString(ranks::ArtificialStruct,
RFT->key());
Result = tokenTag(NameBuilder.artificialReturnValueWrapperName(*RFT),
ptml::c::tokens::Type)
.addAttribute(getLocationAttribute(IsDefinition), Location)
.toString();
if (not InstanceName.empty())
Result.append((Twine(" ") + Twine(InstanceName)).str());
} break;
default:
revng_abort();
}
revng_assert(not llvm::StringRef(Result).trim().empty());
return TypeString(getTag(ptml::tags::Span, Result)
.addAttribute(attributes::ActionContextLocation,
pipeline::locationString(ranks::ReturnValue,
Function.key()))
.toString());
}
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<model::FunctionAttribute::Values>;
static std::string
getFunctionAttributesString(const AttributesSet &Attributes) {
std::string Result;
for (const auto &A : Attributes)
Result += " " + getFunctionAttributeString(A);
return Result;
}
template<ModelFunction FunctionType>
std::string printFunctionPrototypeImpl(const FunctionType *Function,
const model::RawFunctionDefinition &RF,
const llvm::StringRef &FunctionName,
const ptml::CTypeBuilder &B,
bool SingleLine) {
using namespace abi::FunctionType;
auto Layout = Layout::make(RF);
revng_assert(not Layout.hasSPTAR());
revng_assert(Layout.returnMethod() != ReturnMethod::ModelAggregate);
std::string Result;
auto ABI = model::Architecture::getName(RF.Architecture());
Result += ptml::AttributeRegistry::getAnnotation<"_ABI">("raw_" + ABI.str());
if (Function and not Function->Attributes().empty())
Result += getFunctionAttributesString(Function->Attributes());
Result += (SingleLine ? " " : "\n");
Result += B.getNamedInstanceOfReturnType(RF, FunctionName, false);
if (RF.Arguments().empty() and RF.StackArgumentsType().isEmpty()) {
Result += "(" + 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 = B.NameBuilder.name(RF, Arg);
std::string ArgString;
if (Function != nullptr)
ArgString = getArgumentLocationDefinition(ArgName, *Function, B);
std::string
MarkedType = B.getNamedCInstance(*Arg.Type(), ArgString).str().str();
auto Name = model::Register::getName(Arg.Location());
std::string Reg = ptml::AttributeRegistry::getAnnotation<"_REG">(Name);
Tag ArgTag = B.getTag(ptml::tags::Span, MarkedType + " " + Reg);
ArgTag.addAttribute(attributes::ActionContextLocation,
locationString(ranks::RawArgument,
RF.key(),
Arg.key()));
Result += Separator.str() + ArgTag.toString();
Separator = Comma;
}
if (not RF.StackArgumentsType().isEmpty()) {
// Add last argument representing a pointer to the stack arguments
std::string StackArgName;
if (Function != nullptr) {
StackArgName = B.NameBuilder.Configuration.rawStackArgumentName();
StackArgName = getArgumentLocationDefinition(StackArgName,
*Function,
B);
}
auto N = B.getNamedCInstance(*RF.StackArgumentsType(), StackArgName);
Result += Separator.str() + N.str().str() + " "
+ ptml::AttributeRegistry::getAttribute<"_STACK">();
}
Result += ")";
}
return Result;
}
template<ModelFunction FunctionType>
std::string printFunctionPrototypeImpl(const FunctionType *Function,
const model::CABIFunctionDefinition &CF,
const llvm::StringRef &FunctionName,
const ptml::CTypeBuilder &B,
bool SingleLine) {
using namespace abi::FunctionType;
auto Layout = Layout::make(CF);
revng_assert(Layout.returnMethod() != ReturnMethod::RegisterSet);
std::string Result;
llvm::StringRef ABIName = model::ABI::getName(CF.ABI());
Result += ptml::AttributeRegistry::getAnnotation<"_ABI">(ABIName);
if (Function and not Function->Attributes().empty())
Result += getFunctionAttributesString(Function->Attributes());
Result += (SingleLine ? " " : "\n");
Result += B.getNamedInstanceOfReturnType(CF, FunctionName, false);
if (CF.Arguments().empty()) {
Result += "(" + 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 = B.NameBuilder.name(CF, Arg);
std::string ArgString;
if (Function != nullptr)
ArgString = getArgumentLocationDefinition(ArgName, *Function, B);
TypeString ArgDeclaration;
if (const model::ArrayType *Array = Arg.Type()->getArray()) {
ArgDeclaration = B.getArrayWrapper(*Array);
if (not ArgString.empty()) {
ArgDeclaration.append(" ");
ArgDeclaration.append(ArgString);
}
} else {
ArgDeclaration = B.getNamedCInstance(*Arg.Type(), ArgString);
}
Tag ArgTag = B.getTag(ptml::tags::Span, ArgDeclaration);
ArgTag.addAttribute(attributes::ActionContextLocation,
locationString(ranks::CABIArgument,
CF.key(),
Arg.key()));
Result += Separator.str() + ArgTag.toString();
Separator = Comma;
}
Result += ")";
}
return Result;
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition &FT,
const model::Function &Function,
bool SingleLine) {
std::string Location = pipeline::locationString(ranks::Function,
Function.key());
auto FunctionTag = tokenTag(NameBuilder.name(Function),
ptml::c::tokens::Function)
.addAttribute(attributes::ActionContextLocation,
Location)
.addAttribute(attributes::LocationDefinition, Location);
if (auto *RF = dyn_cast<model::RawFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl(&Function,
*RF,
FunctionTag.toString(),
*this,
SingleLine);
} else if (auto *CF = dyn_cast<model::CABIFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl(&Function,
*CF,
FunctionTag.toString(),
*this,
SingleLine);
} else {
revng_abort();
}
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition &FT,
const model::DynamicFunction
&Function,
bool SingleLine) {
std::string Location = pipeline::locationString(ranks::DynamicFunction,
Function.key());
auto FunctionTag = tokenTag(NameBuilder.name(Function),
ptml::c::tokens::Function)
.addAttribute(attributes::ActionContextLocation,
Location)
.addAttribute(attributes::LocationDefinition, Location);
if (auto *RF = dyn_cast<model::RawFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl(&Function,
*RF,
FunctionTag.toString(),
*this,
SingleLine);
} else if (auto *CF = dyn_cast<model::CABIFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl(&Function,
*CF,
FunctionTag.toString(),
*this,
SingleLine);
} else {
revng_abort();
}
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition
&FT) {
auto TypeName = getLocationDefinition(FT);
if (auto *RF = dyn_cast<model::RawFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl<model::Function>(nullptr,
*RF,
TypeName,
*this,
true);
} else if (auto *CF = dyn_cast<model::CABIFunctionDefinition>(&FT)) {
*Out << printFunctionPrototypeImpl<model::Function>(nullptr,
*CF,
TypeName,
*this,
true);
} else {
revng_abort();
}
}
void ptml::CTypeBuilder::printSegmentType(const model::Segment &Segment) {
if (not Segment.Type().isEmpty()) {
*Out << getNamedCInstance(*Segment.Type(), getLocationDefinition(Segment))
<< ";\n";
} else {
// If the segment has no type, emit it as an array of bytes.
auto Array = model::ArrayType::make(model::PrimitiveType::makeGeneric(1),
Segment.VirtualSize());
*Out << getNamedCInstance(*Array, getLocationDefinition(Segment)) << ";\n";
}
}