Files
revng-revng/lib/TypeNames/ModelTypeNames.cpp
T
2022-11-15 15:54:20 +01:00

477 lines
18 KiB
C++

//
// Copyright (c) rev.ng Labs Srl. 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 "revng/Model/Binary.h"
#include "revng/Model/CABIFunctionType.h"
#include "revng/Model/Identifier.h"
#include "revng/Model/QualifiedType.h"
#include "revng/Model/RawFunctionType.h"
#include "revng/PTML/Constants.h"
#include "revng/PTML/ModelHelpers.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/ModelTypeNames.h"
using llvm::dyn_cast;
using llvm::isa;
using llvm::StringRef;
using llvm::Twine;
using tokenDefinition::types::TypeString;
using modelEditPath::getCustomNamePath;
using pipeline::serializedLocation;
using ptml::str;
using ptml::Tag;
namespace tags = ptml::tags;
namespace attributes = ptml::attributes;
namespace tokens = ptml::c::tokens;
namespace ranks = revng::ranks;
using namespace ArtificialTypes;
TypeString getReturnField(const model::RawFunctionType &F, size_t Index) {
revng_assert(F.ReturnValues.size() > 1);
return TypeString((Twine(RetFieldPrefix) + Twine(Index)).str());
}
TypeString
getNamedCInstance(const model::QualifiedType &QT, StringRef InstanceName) {
const auto &isConst = model::Qualifier::isConst;
const auto &isPointer = model::Qualifier::isPointer;
TypeString Result;
// Here we have a bunch of pointers, const, and array qualifiers.
// Because of arrays, we have to emit the types with C infamous clockwise
// spiral rule. Luckily all our function types have names, so at least this
// cannot become too nasty.
auto QIt = QT.Qualifiers.begin();
auto QEnd = QT.Qualifiers.end();
do {
// Find the first qualifer that is an array.
auto QArrayIt = std::find_if(QIt, QEnd, model::Qualifier::isArray);
{
// If we find it, go back to the first previous const-qualifier that
// const-qualifies the array itself. This is necessary because C does not
// have const arrays, only arrays of const, so we have to handle
// const-arrays specially, and emit the const-qualifier on the element in
// C, even if in the model it was on the array.
if (QArrayIt != QEnd and QArrayIt != QIt
and isConst(*std::make_reverse_iterator(QArrayIt)))
QArrayIt = std::prev(QArrayIt);
}
// Emit non-array qualifiers.
{
bool PrevPointer = false;
for (const model::Qualifier &Q :
llvm::reverse(llvm::make_range(QIt, QArrayIt))) {
if (not PrevPointer)
Result.append(" ");
switch (Q.Kind) {
case model::QualifierKind::Const:
Result.append(keywords::Const.serialize());
PrevPointer = false;
break;
case model::QualifierKind::Pointer:
Result.append(Tag(tags::Span, "*")
.addAttribute(attributes::Token, tokens::Operator)
.serialize());
PrevPointer = true;
break;
default:
revng_abort();
}
}
}
bool IsPointer = QArrayIt != QIt
and isPointer(*std::make_reverse_iterator(QArrayIt));
// Print the actual instance name.
if (QIt == QT.Qualifiers.begin() and not InstanceName.empty()) {
if (not IsPointer)
Result.append(" ");
Result.append(InstanceName.str());
}
// Find the next non-array qualifier. Skip over const-qualifiers, because in
// C there are no const-arrays, so we'll have to deal with const-arrays
// separately.
auto QPointerIt = std::find_if(QArrayIt, QEnd, model::Qualifier::isPointer);
{
// If we find the next pointer qualifier, go back to the first previous
// const-qualifier that const-qualifies the pointer itself. This is
// necessary, so that we can reason about the element of the array being
// const, and we can deal properly with const arrays.
if (QPointerIt != QEnd and QPointerIt != QArrayIt
and isConst(*std::make_reverse_iterator(QPointerIt)))
QPointerIt = std::prev(QPointerIt);
}
if (QArrayIt != QPointerIt) {
// If QT is s a pointer to an array we have to add parentheses for the
// clockwise spiral rule
if (IsPointer)
Result = (Twine("(") + Twine(Result) + Twine(")")).str();
const auto &ArrayOrConstRange = llvm::make_range(QArrayIt, QPointerIt);
bool ConstQualifiedArray = llvm::any_of(ArrayOrConstRange, isConst);
// If the array is const-qualfied and its element is not const-qualified,
// just print it as an array of const-qualified elements, because that's
// the equivalent semantics in C anyway.
if (ConstQualifiedArray) {
bool ElementIsConstQualified = QPointerIt != QEnd
and isConst(*QPointerIt);
// If the array is const qualified but the element is not, we have to
// force const-ness onto the element, because in C there's no way to
// const-qualify arrays. If the element is already const-qualified, then
// there's no need to do that, because we're still gonna print the
// const-qualifier for the element.
if (not ElementIsConstQualified) {
const auto &Const = keywords::Const.serialize();
Result = (Twine(" ") + Twine(Const) + Twine(" ") + Twine(Result))
.str();
}
}
for (const model::Qualifier &ArrayQ :
llvm::reverse(llvm::make_filter_range(ArrayOrConstRange,
model::Qualifier::isArray)))
Result.append((Twine("[") + Twine(ArrayQ.Size) + Twine("]")).str());
}
QIt = QPointerIt;
} while (QIt != QEnd);
const model::Type &Unqualified = *QT.UnqualifiedType.getConst();
std::string TypeName = ptml::getLocationReference(Unqualified);
Result = (Twine(TypeName) + Twine(" ") + Twine(Result)).str();
return Result;
}
TypeString getArrayWrapper(const model::QualifiedType &QT) {
revng_assert(QT.isArray());
TypeString Result;
Result.append(ArrayWrapperPrefix);
for (const auto &Qualifier : QT.Qualifiers) {
switch (Qualifier.Kind) {
case model::QualifierKind::Const: {
Result.append("const_");
} break;
case model::QualifierKind::Pointer: {
Result.append("ptr_to_");
} break;
case model::QualifierKind::Array: {
auto NElem = Qualifier.Size;
Result.append(("array_" + Twine(NElem) + "_of_").str());
} break;
default:
revng_abort();
}
}
Result.append(QT.UnqualifiedType.get()->name());
Tag ResultTag = Tag(tags::Span, Result.str());
return TypeString(ResultTag.serialize());
}
TypeString getReturnTypeName(const model::RawFunctionType &F) {
TypeString Result;
if (F.ReturnValues.size() == 0) {
Result = Tag(tags::Span, "void")
.addAttribute(attributes::Token, tokens::Type)
.serialize();
} else if (F.ReturnValues.size() == 1) {
auto RetTy = F.ReturnValues.begin()->Type;
// RawFunctionTypes should never be returning an array
revng_assert(not RetTy.isArray());
Result = getNamedCInstance(RetTy, "");
} else {
// RawFunctionTypes can return multiple values, which need to be wrapped
// in a struct
Result = ptml::tokenTag((Twine(RetStructPrefix) + "returned_by_"
+ model::Identifier::fromString(F.name()))
.str(),
tokens::Type)
.serialize();
}
revng_assert(not Result.empty());
return Result;
}
TypeString getReturnTypeName(const model::CABIFunctionType &F) {
TypeString Result;
const auto &RetTy = F.ReturnType;
if (RetTy.isArray()) {
// Returned arrays get wrapped in an artificial struct
Result = getArrayWrapper(RetTy);
} else {
Result = getNamedCInstance(RetTy, "");
}
revng_assert(not Result.empty());
return Result;
}
using model::NamedTypedRegister;
using std::function;
using RawArgumentPrinter = function<std::string(const NamedTypedRegister &)>;
static void printFunctionPrototypeImpl(const model::RawFunctionType &RF,
const llvm::StringRef &FunctionName,
RawArgumentPrinter ArgumentPrinter,
const llvm::StringRef StackVarsName,
llvm::raw_ostream &Header,
const model::Binary &Model,
bool Declaration) {
Header << getReturnTypeName(RF);
if (RF.ReturnValues.size() == 1) {
const model::QualifiedType &ReturnType = RF.ReturnValues.begin()->Type;
if (not ReturnType.isPointer() or ReturnType.isConst())
Header << " ";
} else {
Header << " ";
}
Header << FunctionName;
revng_assert(RF.StackArgumentsType.Qualifiers.empty());
if (RF.Arguments.empty()
and not RF.StackArgumentsType.UnqualifiedType.isValid()) {
Header << "(" << ptml::tokenTag("void", tokens::Type) << ")";
} else {
const StringRef Open = "(";
const StringRef Comma = ", ";
StringRef Separator = Open;
for (const auto &Arg : RF.Arguments) {
std::string ArgumentName = ArgumentPrinter(Arg);
Header << Separator << getNamedCInstance(Arg.Type, ArgumentName);
Separator = Comma;
}
revng_assert(RF.StackArgumentsType.Qualifiers.empty());
if (RF.StackArgumentsType.UnqualifiedType.isValid()) {
// Add last argument representing a pointer to the stack arguments
Header << Separator
<< getNamedCInstance(Model.getPointerTo(RF.StackArgumentsType),
StackVarsName);
}
Header << ")";
}
}
using model::Argument;
using CABIArgumentPrinter = std::function<std::string(const Argument &)>;
static void printFunctionPrototypeImpl(const model::CABIFunctionType &CF,
const llvm::StringRef &FunctionName,
CABIArgumentPrinter ArgumentPrinter,
llvm::raw_ostream &Header,
const model::Binary &Model,
bool Declaration) {
Header << getReturnTypeName(CF);
if (not CF.ReturnType.isPointer() or CF.ReturnType.isConst())
Header << " ";
Header << FunctionName;
if (CF.Arguments.empty()) {
Header << "(" << ptml::tokenTag("void", tokens::Type) << ")";
} else {
const StringRef Open = "(";
const StringRef Comma = ", ";
StringRef Separator = Open;
for (const auto &Arg : CF.Arguments) {
TypeString ArgTypeName;
std::string ArgumentName = ArgumentPrinter(Arg);
if (Arg.Type.isArray()) {
ArgTypeName = getArrayWrapper(Arg.Type);
if (not ArgumentName.empty()) {
ArgTypeName.append(" ");
ArgTypeName.append(ArgumentName);
}
} else {
ArgTypeName = getNamedCInstance(Arg.Type, ArgumentName);
}
Header << Separator << ArgTypeName;
Separator = Comma;
}
Header << ")";
}
}
void printFunctionPrototype(const model::Type &FT,
const model::Function &Function,
llvm::raw_ostream &Header,
const model::Binary &Model,
bool Declaration) {
Tag FunctionTag = ptml::tokenTag(Function.name(), tokens::Function)
.addAttribute(attributes::ModelEditPath,
getCustomNamePath(Function))
.addAttribute(Declaration ?
attributes::LocationDefinition :
attributes::LocationReferences,
serializedLocation(ranks::Function,
Function.key()));
if (auto *RF = dyn_cast<model::RawFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const NamedTypedRegister &Reg) {
std::string
ArgIdentifier = model::Identifier::fromString(Reg.name()).str().str();
return ptml::tokenTag(ArgIdentifier, tokens::FunctionParameter)
.addAttribute(attributes::LocationDefinition,
serializedLocation(ranks::LocalVariable,
Function.key(),
ArgIdentifier))
.serialize();
};
std::string
StackName = ptml::tokenTag("stack_args", tokens::FunctionParameter)
.addAttribute(attributes::LocationDefinition,
serializedLocation(ranks::LocalVariable,
Function.key(),
"stack_args"))
.serialize();
printFunctionPrototypeImpl(*RF,
FunctionTag.serialize(),
ArgumentPrinter,
StackName,
Header,
Model,
Declaration);
} else if (auto *CF = dyn_cast<model::CABIFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const Argument &Arg) -> std::string {
std::string
ArgIdentifier = model::Identifier::fromString(Arg.name()).str().str();
return " "
+ ptml::tokenTag(ArgIdentifier, tokens::FunctionParameter)
.addAttribute(attributes::LocationDefinition,
serializedLocation(ranks::LocalVariable,
Function.key(),
ArgIdentifier))
.serialize();
};
printFunctionPrototypeImpl(*CF,
FunctionTag.serialize(),
ArgumentPrinter,
Header,
Model,
Declaration);
} else {
revng_abort();
}
}
void printFunctionPrototype(const model::Type &FT,
const model::DynamicFunction &Function,
llvm::raw_ostream &Header,
const model::Binary &Model,
bool Declaration) {
Tag FunctionTag = ptml::tokenTag(Function.name(), tokens::Function)
.addAttribute(attributes::ModelEditPath,
getCustomNamePath(Function))
.addAttribute(Declaration ?
attributes::LocationDefinition :
attributes::LocationReferences,
serializedLocation(ranks::DynamicFunction,
Function.key()));
if (auto *RF = dyn_cast<model::RawFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const NamedTypedRegister &Reg) {
std::string
ArgIdentifier = model::Identifier::fromString(Reg.name()).str().str();
return ptml::tokenTag(ArgIdentifier, tokens::FunctionParameter)
.addAttribute(attributes::LocationDefinition,
serializedLocation(ranks::DynamicFunctionArgument,
Function.key(),
ArgIdentifier))
.serialize();
};
printFunctionPrototypeImpl(*RF,
FunctionTag.serialize(),
ArgumentPrinter,
"stack_args",
Header,
Model,
Declaration);
} else if (auto *CF = dyn_cast<model::CABIFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const Argument &Arg) {
std::string
ArgIdentifier = model::Identifier::fromString(Arg.name()).str().str();
return ptml::tokenTag(ArgIdentifier, tokens::FunctionParameter)
.addAttribute(attributes::LocationDefinition,
serializedLocation(ranks::DynamicFunctionArgument,
Function.key(),
ArgIdentifier))
.serialize();
};
printFunctionPrototypeImpl(*CF,
FunctionTag.serialize(),
ArgumentPrinter,
Header,
Model,
Declaration);
} else {
revng_abort();
}
}
void printFunctionTypeDeclaration(const model::Type &FT,
llvm::StringRef TypeName,
llvm::raw_ostream &Header,
const model::Binary &Model) {
if (auto *RF = dyn_cast<model::RawFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const NamedTypedRegister &Reg) { return ""; };
printFunctionPrototypeImpl(*RF,
TypeName,
ArgumentPrinter,
"stack_args",
Header,
Model,
true);
} else if (auto *CF = dyn_cast<model::CABIFunctionType>(&FT)) {
auto ArgumentPrinter = [&](const Argument &Arg) { return ""; };
printFunctionPrototypeImpl(*CF,
TypeName,
ArgumentPrinter,
Header,
Model,
true);
} else {
revng_abort();
}
}