Files
revng-revng/lib/TypeNames/ModelTypeNames.cpp
T
Pietro Fezzardi 0426c773b2 CABIFunctionDefinition: drop array argument/return
This commit bans from the model array types in as argument or return
types in CABIFunctionDefinition.
CABIFunctionDefinition implies we're decompiling towards C, and C does
not support array arguments (passed by copy) nor array return types.

Banning these from the model reduces the number of situations where we
have to emit artificial array wrappers (not present in the model) around
types when printing C code, simplifying the handling of such situations
when translating to clift and in general in the decompilation pipeline.
2025-05-28 17:11:11 +02:00

407 lines
14 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/CBuilder.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/TypeNames/LLVMTypeNames.h"
#include "revng/TypeNames/PTMLCTypeBuilder.h"
using llvm::dyn_cast;
using llvm::StringRef;
using llvm::Twine;
using ptml::Tag;
namespace attributes = ptml::attributes;
namespace tokens = ptml::c::tokens;
namespace ranks = revng::ranks;
struct NamedCInstanceImpl {
const ptml::CTypeBuilder &B;
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) {
auto Current = B.getOperator(ptml::CBuilder::Operator::PointerDereference)
.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.getReferenceTag(Def.unwrap());
}
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.getReferenceTag(Primitive);
}
Result += std::move(Emitted);
rc_return Result;
}
std::string constKeyword() {
return B.getKeyword(ptml::CBuilder::Keyword::Const).toString();
}
};
using PCTB = ptml::CTypeBuilder;
std::string PCTB::getNamedCInstance(const model::Type &Type,
StringRef InstanceName,
bool OmitInnerTypeName) const {
NamedCInstanceImpl Helper(*this, OmitInnerTypeName);
return Helper.getString(Type, InstanceName.str());
}
std::string
PCTB::getNamedInstanceOfReturnType(const model::TypeDefinition &Function,
llvm::StringRef InstanceName) const {
std::string Suffix = "";
if (not InstanceName.empty())
Suffix.append(" " + InstanceName.str());
const auto Layout = abi::FunctionType::Layout::make(Function);
auto ReturnMethod = Layout.returnMethod();
switch (ReturnMethod) {
case abi::FunctionType::ReturnMethod::Void:
return getReturnValueTag(getVoidTag(), Function) + Suffix;
case abi::FunctionType::ReturnMethod::ModelAggregate:
case abi::FunctionType::ReturnMethod::Scalar: {
const model::Type *ReturnType = nullptr;
if (ReturnMethod == abi::FunctionType::ReturnMethod::ModelAggregate) {
ReturnType = &Layout.returnValueAggregateType();
} else {
revng_assert(Layout.ReturnValues.size() == 1);
ReturnType = Layout.ReturnValues[0].Type.get();
}
return getReturnValueTag(getNamedCInstance(*ReturnType, InstanceName),
Function);
}
case abi::FunctionType::ReturnMethod::RegisterSet: {
// RawFunctionTypes can return multiple values, which need to be wrapped
// in a struct
const auto &RFT = llvm::cast<model::RawFunctionDefinition>(Function);
return getReturnValueTag(getArtificialStructTag<false>(RFT), RFT) + Suffix;
}
default:
revng_abort();
}
revng_abort("Unsupported function return method.");
}
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<typename FT>
concept ModelFunction = std::same_as<FT, model::Function>
or std::same_as<FT, model::DynamicFunction>;
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);
if (RF.Arguments().empty() and RF.StackArgumentsType().isEmpty()) {
Result += "(" + B.getVoidTag() + ")";
} else {
const StringRef Open = "(";
const StringRef Comma = ", ";
StringRef Separator = Open;
for (const model::NamedTypedRegister &Argument : RF.Arguments()) {
std::string ArgumentName;
if (Function != nullptr)
ArgumentName = B.getDefinitionTag(RF, Argument);
std::string MarkedType = B.getNamedCInstance(*Argument.Type(),
ArgumentName);
auto RegName = model::Register::getName(Argument.Location());
std::string Reg = ptml::AttributeRegistry::getAnnotation<"_REG">(RegName);
Result += Separator.str()
+ B.getCommentableTag(MarkedType + " " + Reg, RF, Argument);
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.getStackArgumentDefinitionTag(RF);
auto N = B.getNamedCInstance(*RF.StackArgumentsType(), StackArgName);
static auto Attribute = ptml::AttributeRegistry::getAttribute<"_STACK">();
Result += Separator.str()
+ B.getCommentableTag(N + " " + Attribute,
*RF.stackArgumentsType());
}
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);
if (CF.Arguments().empty()) {
Result += "(" + B.getVoidTag() + ")";
} else {
const StringRef Open = "(";
const StringRef Comma = ", ";
StringRef Separator = Open;
for (const auto &Argument : CF.Arguments()) {
std::string ArgumentName;
if (Function != nullptr)
ArgumentName = B.getDefinitionTag(CF, Argument);
Result += Separator.str();
Result += B.getCommentableTag(B.getNamedCInstance(*Argument.Type(),
ArgumentName),
CF,
Argument);
Separator = Comma;
}
Result += ")";
}
return Result;
}
template<ModelFunction FunctionType>
std::string printFunctionPrototypeImpl(const model::TypeDefinition &FT,
const FunctionType *Function,
const llvm::StringRef &FunctionName,
const ptml::CTypeBuilder &B,
bool SingleLine) {
std::string Result;
if (auto *RF = dyn_cast<model::RawFunctionDefinition>(&FT)) {
Result = printFunctionPrototypeImpl(Function,
*RF,
FunctionName,
B,
SingleLine);
} else if (auto *CF = dyn_cast<model::CABIFunctionDefinition>(&FT)) {
Result = printFunctionPrototypeImpl(Function,
*CF,
FunctionName,
B,
SingleLine);
} else {
revng_abort();
}
if (Function)
return B.getCommentableTag(std::move(Result), *Function);
else
return B.getCommentableTag(std::move(Result), FT);
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition &FT,
const model::Function &Function,
bool SingleLine) {
*Out << printFunctionPrototypeImpl(FT,
&Function,
getDefinitionTag(Function),
*this,
SingleLine);
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition &FT,
const model::DynamicFunction
&Function,
bool SingleLine) {
*Out << printFunctionPrototypeImpl(FT,
&Function,
getDefinitionTag(Function),
*this,
SingleLine);
}
void ptml::CTypeBuilder::printFunctionPrototype(const model::TypeDefinition
&FT) {
*Out << printFunctionPrototypeImpl(FT,
(const model::Function *) nullptr,
getDefinitionTag(FT),
*this,
true);
}
void ptml::CTypeBuilder::printSegmentType(const model::Segment &Segment) {
std::string Result = "\n" + getModelCommentWithoutLeadingNewline(Segment);
if (not Segment.Type().isEmpty()) {
Result += getNamedCInstance(*Segment.Type(), getDefinitionTag(Segment));
} 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());
Result += getNamedCInstance(*Array, getDefinitionTag(Segment));
}
*Out << getCommentableTag(std::move(Result) + ";\n", Binary, Segment);
}