mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
4767fa63c3
This commit fixes a bug due to interacting behaviors between MarkForSerialization, AddSCEVBarrierPass, and the emission in C of calls to revng_init_local_sp. These interacting behaviors caused the following quirks: - At the beginning of Functions that contained a call to `revng_init_local_sp()`, that call was actually emitted twice. The first time was due to the actual call to `revng_init_local_sp()`, while the second was due to the first call being wrapped from a call to `revng_scev_barrier_*`. Now we properly emit only one call. - The original call to `revng_init_local_sp()` was supposed to generate a local variable, to be used in various places across the function. However, due to the fact that the call was not properly labeled by MarkForSerialization, there was no local variable, causing calls to `revng_init_local_sp()` to be scattered around the body of the functions, follwed by various arithmetic operations. This behavior has been fixed as well, and we now emit the local variable correctly.
1869 lines
74 KiB
C++
1869 lines
74 KiB
C++
/// \brief DataFlow analysis to build the AST for a Function
|
|
|
|
//
|
|
// Copyright rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclGroup.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/OperationKinds.h"
|
|
#include "clang/AST/Stmt.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "clang/Basic/IdentifierTable.h"
|
|
|
|
#include "revng/Support/IRHelpers.h"
|
|
|
|
#include "revng-c/Decompiler/DLALayouts.h"
|
|
|
|
#include "ASTBuildAnalysis.h"
|
|
|
|
#include "AddSCEVBarrierPass.h"
|
|
#include "DecompilationHelpers.h"
|
|
#include "IRASTTypeTranslation.h"
|
|
#include "Mangling.h"
|
|
#include "SCEVBaseAddressExplorer.h"
|
|
|
|
static Logger<> ASTBuildLog("ast-builder");
|
|
|
|
using namespace llvm;
|
|
using namespace clang;
|
|
using ClangType = clang::Type;
|
|
using ClangPointerType = clang::PointerType;
|
|
using LLVMType = llvm::Type;
|
|
using LLVMPointerType = llvm::PointerType;
|
|
using TypeDeclOrQualType = DeclCreator::TypeDeclOrQualType;
|
|
|
|
namespace IR2AST {
|
|
|
|
Expr *StmtBuilder::getParenthesizedExprForValue(Value *V) {
|
|
Expr *Res = getExprForValue(V);
|
|
if (isa<clang::BinaryOperator>(Res) or isa<ConditionalOperator>(Res))
|
|
Res = new (ASTCtx) ParenExpr({}, {}, Res);
|
|
return Res;
|
|
}
|
|
|
|
Stmt *StmtBuilder::buildStmt(Instruction &I) {
|
|
revng_log(ASTBuildLog, "Build AST for" << dumpToString(&I));
|
|
switch (I.getOpcode()) {
|
|
//
|
|
// ---- SUPPORTED INSTRUCTIONS ----
|
|
//
|
|
|
|
//
|
|
// ---- Terminators ----
|
|
//
|
|
case Instruction::Br: {
|
|
revng_abort("branch instructions are not supported yet");
|
|
auto *Branch = cast<BranchInst>(&I);
|
|
if (Branch->isUnconditional()) {
|
|
LabelDecl *Label = BBLabelDecls.at(Branch->getSuccessor(0));
|
|
GotoStmt *GoTo = new (ASTCtx) GotoStmt(Label, {}, {});
|
|
return GoTo;
|
|
} else {
|
|
LabelDecl *Then = BBLabelDecls.at(Branch->getSuccessor(0));
|
|
LabelDecl *Else = BBLabelDecls.at(Branch->getSuccessor(1));
|
|
GotoStmt *GoToThen = new (ASTCtx) GotoStmt(Then, {}, {});
|
|
GotoStmt *GoToElse = new (ASTCtx) GotoStmt(Else, {}, {});
|
|
Expr *Cond = getExprForValue(Branch->getCondition());
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and Cond)
|
|
Cond->dump();
|
|
if (Cond->isLValue())
|
|
Cond = ImplicitCastExpr::Create(ASTCtx,
|
|
Cond->getType(),
|
|
CastKind::CK_LValueToRValue,
|
|
Cond,
|
|
nullptr,
|
|
VK_RValue);
|
|
return IfStmt::Create(ASTCtx,
|
|
{},
|
|
false,
|
|
nullptr,
|
|
nullptr,
|
|
Cond,
|
|
GoToThen,
|
|
{},
|
|
GoToElse);
|
|
}
|
|
}
|
|
case Instruction::Ret: {
|
|
ReturnInst *Ret = cast<ReturnInst>(&I);
|
|
Value *RetVal = Ret->getReturnValue();
|
|
|
|
// TODO: for now we handle return instructions containing a `ConstantStruct`
|
|
// by emitting a `return void`. This should eventually be fixed, properly
|
|
// handling structs (both constants and not).
|
|
if (RetVal && isa<ConstantStruct>(RetVal)) {
|
|
return ReturnStmt::Create(ASTCtx, {}, nullptr, nullptr);
|
|
}
|
|
|
|
Expr *ReturnedExpr = nullptr;
|
|
if (auto *ZeroAggregate = dyn_cast_or_null<ConstantAggregateZero>(RetVal)) {
|
|
uint64_t ConstValue = 0;
|
|
QualType IntT = ASTCtx.IntTy;
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue);
|
|
|
|
llvm::Function *TheFunction = Ret->getFunction();
|
|
|
|
clang::FunctionDecl &FDecl = Declarator.getFunctionDecl(TheFunction);
|
|
|
|
revng_assert(VarDecls.count(ZeroAggregate) == 0);
|
|
VarDecl *NewVarDecl = createVarDecl(ZeroAggregate, TheFunction, FDecl);
|
|
VarDecls[ZeroAggregate] = NewVarDecl;
|
|
|
|
clang::Expr *Zero = IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
clang::Expr *ZeroInit = new (ASTCtx)
|
|
clang::InitListExpr(ASTCtx, {}, { Zero }, {});
|
|
NewVarDecl->setInit(ZeroInit);
|
|
ReturnedExpr = getExprForValue(ZeroAggregate);
|
|
} else {
|
|
ReturnedExpr = RetVal ? getExprForValue(RetVal) : nullptr;
|
|
}
|
|
return ReturnStmt::Create(ASTCtx, {}, ReturnedExpr, nullptr);
|
|
}
|
|
case Instruction::Switch: {
|
|
revng_abort("switch instructions are not supported yet");
|
|
auto *Switch = cast<SwitchInst>(&I);
|
|
|
|
Value *Cond = Switch->getCondition();
|
|
Expr *CondE = getExprForValue(Cond);
|
|
|
|
SwitchStmt *S = SwitchStmt::Create(ASTCtx, nullptr, nullptr, CondE);
|
|
|
|
unsigned NumCases = Switch->getNumCases() + 1; // +1 is for the default
|
|
CompoundStmt *Body = CompoundStmt::CreateEmpty(ASTCtx, NumCases);
|
|
|
|
BasicBlock *DefaultBlock = Switch->getDefaultDest();
|
|
LabelDecl *DefaultLabel = BBLabelDecls.at(DefaultBlock);
|
|
GotoStmt *GoToDefault = new (ASTCtx) GotoStmt(DefaultLabel, {}, {});
|
|
DefaultStmt *Default = new (ASTCtx) DefaultStmt({}, {}, GoToDefault);
|
|
S->addSwitchCase(Default);
|
|
|
|
int K = 0;
|
|
|
|
for (auto CIt : Switch->cases()) {
|
|
BasicBlock *CaseBlock = CIt.getCaseSuccessor();
|
|
if (CaseBlock == DefaultBlock)
|
|
continue;
|
|
ConstantInt *CaseVal = CIt.getCaseValue();
|
|
Expr *CaseCond = getExprForValue(CaseVal);
|
|
LabelDecl *CaseLabel = BBLabelDecls.at(CaseBlock);
|
|
GotoStmt *GoToCase = new (ASTCtx) GotoStmt(CaseLabel, {}, {});
|
|
CaseStmt *Case = CaseStmt::Create(ASTCtx, CaseCond, nullptr, {}, {}, {});
|
|
Case->setSubStmt(GoToCase);
|
|
S->addSwitchCase(Case);
|
|
Body->body_begin()[K++] = Case;
|
|
}
|
|
|
|
Body->body_begin()[K] = Default;
|
|
S->setBody(Body);
|
|
return S;
|
|
}
|
|
//
|
|
// ---- Standard binary operators ----
|
|
//
|
|
case Instruction::Add:
|
|
case Instruction::Sub:
|
|
case Instruction::Mul:
|
|
//
|
|
// ---- Standard division operators (with signedness) ----
|
|
//
|
|
case Instruction::UDiv:
|
|
case Instruction::SDiv:
|
|
case Instruction::URem:
|
|
case Instruction::SRem:
|
|
//
|
|
// ---- Logical operators ----
|
|
//
|
|
case Instruction::And:
|
|
case Instruction::Or:
|
|
case Instruction::Xor:
|
|
//
|
|
// ---- Other instructions ----
|
|
//
|
|
case Instruction::ICmp:
|
|
case Instruction::Shl:
|
|
case Instruction::LShr:
|
|
case Instruction::AShr: {
|
|
return createRValueExprForBinaryOperator(I);
|
|
}
|
|
//
|
|
// ---- Memory instructions ----
|
|
//
|
|
case Instruction::Alloca: {
|
|
revng_assert(I.getParent() == &I.getFunction()->getEntryBlock());
|
|
VarDecl *AllocatedVarDecl = AllocaDecls.at(cast<AllocaInst>(&I));
|
|
QualType AllocatedQualTy = AllocatedVarDecl->getType();
|
|
{
|
|
// TODO: Here we expect allocas to be arrays of bytes.
|
|
// Initially this was the only possibility, because we always created
|
|
// arrays of bytes for allocas. After the introduction of DLA this is no
|
|
// longer the case, so I expecte the following assertion to start failing.
|
|
// When that happens, we'll need to figure out what's the right thing to
|
|
// do here.
|
|
|
|
auto *AllocTy = AllocatedQualTy.getTypePtr();
|
|
using clang::ArrayType;
|
|
revng_assert(cast<ArrayType>(AllocTy)->getElementType() == ASTCtx.CharTy);
|
|
}
|
|
// Create an Expr for the address of the first element of the array.
|
|
Expr *AllocatedVarDeclRef = new (ASTCtx) DeclRefExpr(ASTCtx,
|
|
AllocatedVarDecl,
|
|
false,
|
|
AllocatedQualTy,
|
|
VK_LValue,
|
|
{});
|
|
QualType CharPtrTy = ASTCtx.getPointerType(ASTCtx.CharTy);
|
|
CastKind Kind = CastKind::CK_ArrayToPointerDecay;
|
|
Expr *ArrayPtrDecay = ImplicitCastExpr::Create(ASTCtx,
|
|
CharPtrTy,
|
|
Kind,
|
|
AllocatedVarDeclRef,
|
|
nullptr,
|
|
VK_RValue);
|
|
Expr *ArrayIdx = IntegerLiteral::Create(ASTCtx,
|
|
APInt::getNullValue(32),
|
|
ASTCtx.IntTy,
|
|
{});
|
|
Expr *ArraySubscript = new (ASTCtx) ArraySubscriptExpr(ArrayPtrDecay,
|
|
ArrayIdx,
|
|
ASTCtx.CharTy,
|
|
VK_LValue,
|
|
OK_Ordinary,
|
|
{});
|
|
using Unary = clang::UnaryOperator;
|
|
return new (ASTCtx) Unary(ArraySubscript,
|
|
UnaryOperatorKind::UO_AddrOf,
|
|
CharPtrTy,
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
false);
|
|
}
|
|
case Instruction::Load: {
|
|
auto *Load = cast<LoadInst>(&I);
|
|
Value *Addr = Load->getPointerOperand();
|
|
Expr *AddrExpr = getParenthesizedExprForValue(Addr);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and AddrExpr)
|
|
AddrExpr->dump();
|
|
if (not isa<GlobalVariable>(Addr)) {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType PTy = Declarator.getOrCreateType(Load, ASTCtx, TUDecl);
|
|
QualType PointeeType = DeclCreator::getQualType(PTy);
|
|
|
|
QualType QualAddrType = AddrExpr->getType();
|
|
const ClangType *AddrTy = QualAddrType.getTypePtr();
|
|
if (not AddrTy->isPointerType()) {
|
|
revng_assert(AddrTy->isBuiltinType());
|
|
revng_assert(AddrTy->isIntegerType());
|
|
|
|
QualType PtrTy = ASTCtx.getPointerType(PointeeType);
|
|
uint64_t PtrSize = ASTCtx.getTypeSize(PtrTy);
|
|
uint64_t IntegerSize = ASTCtx.getTypeSize(AddrTy);
|
|
revng_assert(PtrSize >= IntegerSize);
|
|
if (PtrSize > IntegerSize)
|
|
AddrExpr = createCast(ASTCtx.getUIntPtrType(), AddrExpr, ASTCtx);
|
|
AddrExpr = createCast(PtrTy, AddrExpr, ASTCtx);
|
|
}
|
|
|
|
if (isa<llvm::ConstantPointerNull>(Addr)) {
|
|
QualType QualPtrTy = AddrExpr->getType();
|
|
const auto *PtrType = cast<ClangPointerType>(QualPtrTy.getTypePtr());
|
|
QualType QualPointeeTy = PtrType->getPointeeType();
|
|
QualPointeeTy.addVolatile();
|
|
QualType PtrToVolatileTy = ASTCtx.getPointerType(QualPointeeTy);
|
|
AddrExpr = createCast(PtrToVolatileTy, AddrExpr, ASTCtx);
|
|
}
|
|
|
|
using Unary = clang::UnaryOperator;
|
|
return new (ASTCtx) Unary(AddrExpr,
|
|
UnaryOperatorKind::UO_Deref,
|
|
PointeeType,
|
|
VK_LValue,
|
|
OK_Ordinary,
|
|
{},
|
|
false);
|
|
}
|
|
return AddrExpr;
|
|
}
|
|
case Instruction::Store: {
|
|
auto *Store = cast<StoreInst>(&I);
|
|
Value *Stored = Store->getValueOperand();
|
|
if (isa<UndefValue>(Stored))
|
|
return nullptr;
|
|
Expr *LHS = getParenthesizedExprForValue(Store);
|
|
QualType LHSQualTy = LHS->getType();
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and LHS)
|
|
LHS->dump();
|
|
Expr *RHS = getParenthesizedExprForValue(Stored);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and RHS)
|
|
RHS->dump();
|
|
|
|
if (RHS->getType() != LHSQualTy) {
|
|
if (isa<clang::BinaryOperator>(RHS))
|
|
RHS = new (ASTCtx) ParenExpr({}, {}, RHS);
|
|
RHS = createCast(LHSQualTy, RHS, ASTCtx);
|
|
}
|
|
|
|
BinaryOperatorKind BinOpKind = BinaryOperatorKind::BO_Assign;
|
|
return new (ASTCtx) clang::BinaryOperator(LHS,
|
|
RHS,
|
|
BinOpKind,
|
|
LHSQualTy,
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
FPOptions());
|
|
}
|
|
//
|
|
// ---- Convert instructions ----
|
|
//
|
|
case Instruction::Trunc:
|
|
case Instruction::ZExt:
|
|
case Instruction::SExt:
|
|
case Instruction::IntToPtr:
|
|
case Instruction::PtrToInt:
|
|
case Instruction::BitCast: {
|
|
revng_assert(I.getNumOperands() == 1);
|
|
Expr *Res = getParenthesizedExprForValue(I.getOperand(0));
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType LeftQTy = Declarator.getOrCreateType(&I, ASTCtx, TUDecl);
|
|
QualType LHSQualType = DeclCreator::getQualType(LeftQTy);
|
|
if (LHSQualType != Res->getType())
|
|
Res = createCast(LHSQualType, Res, ASTCtx);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and Res)
|
|
Res->dump();
|
|
return Res;
|
|
}
|
|
// ---- Other instructions ----
|
|
case Instruction::Select: {
|
|
Expr *Cond = getParenthesizedExprForValue(I.getOperand(0));
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and Cond)
|
|
Cond->dump();
|
|
Expr *TrueExpr = getParenthesizedExprForValue(I.getOperand(1));
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and TrueExpr)
|
|
TrueExpr->dump();
|
|
Expr *FalseExpr = getParenthesizedExprForValue(I.getOperand(2));
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and FalseExpr)
|
|
FalseExpr->dump();
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType ASTTy = Declarator.getOrCreateType(&I, ASTCtx, TUDecl);
|
|
QualType ASTType = DeclCreator::getQualType(ASTTy);
|
|
return new (ASTCtx) ConditionalOperator(Cond,
|
|
{},
|
|
TrueExpr,
|
|
{},
|
|
FalseExpr,
|
|
ASTType,
|
|
VK_RValue,
|
|
OK_Ordinary);
|
|
}
|
|
case Instruction::Call: {
|
|
auto *TheCall = cast<CallInst>(&I);
|
|
Function *CalleeFun = getCallee(TheCall);
|
|
|
|
Expr *CalleeExpr = getExprForValue(CalleeFun);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and CalleeExpr)
|
|
CalleeExpr->dump();
|
|
|
|
size_t NumArgs = CalleeFun->arg_size();
|
|
FunctionDecl &FD = Declarator.getFunctionDecl(CalleeFun);
|
|
size_t NumParms = FD.param_size();
|
|
unsigned NumOps = TheCall->getNumArgOperands();
|
|
bool HasNoParms = NumParms == 0
|
|
or (NumParms == 1
|
|
and FD.getParamDecl(0)->getType() == ASTCtx.VoidTy);
|
|
revng_assert(HasNoParms or NumArgs == NumParms);
|
|
const bool IsVariadic = FD.isVariadic();
|
|
if (not FD.isVariadic())
|
|
revng_assert(NumArgs == NumOps);
|
|
|
|
auto Args = SmallVector<Expr *, 8>(NumOps, nullptr);
|
|
revng_assert(not(not HasNoParms and IsVariadic));
|
|
if (not HasNoParms) {
|
|
for (unsigned OpId = 0; OpId < NumOps; ++OpId) {
|
|
Value *Operand = TheCall->getOperand(OpId);
|
|
Expr *ArgExpr = getExprForValue(Operand);
|
|
QualType ArgQualTy = ArgExpr->getType();
|
|
|
|
ParmVarDecl *ParmDecl = FD.getParamDecl(OpId);
|
|
QualType ParmQualTy = ParmDecl->getType();
|
|
|
|
if (ParmQualTy != ArgQualTy) {
|
|
ArgExpr = new (ASTCtx) ParenExpr({}, {}, ArgExpr);
|
|
ArgExpr = createCast(ParmQualTy, ArgExpr, ASTCtx);
|
|
}
|
|
|
|
Args[OpId] = ArgExpr;
|
|
}
|
|
}
|
|
|
|
if (IsVariadic) {
|
|
for (unsigned OpId = 0; OpId < NumOps; ++OpId) {
|
|
Value *Operand = TheCall->getOperand(OpId);
|
|
Expr *ArgExpr = getExprForValue(Operand);
|
|
|
|
Args[OpId] = ArgExpr;
|
|
}
|
|
}
|
|
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType RTy = Declarator.getOrCreateType(TheCall->getType(),
|
|
CalleeFun,
|
|
ASTCtx,
|
|
TUDecl);
|
|
QualType ReturnType = DeclCreator::getQualType(RTy);
|
|
return CallExpr::Create(ASTCtx,
|
|
CalleeExpr,
|
|
Args,
|
|
ReturnType,
|
|
VK_RValue,
|
|
{});
|
|
}
|
|
case Instruction::Unreachable: {
|
|
Function *AbortFun = I.getModule()->getFunction("abort");
|
|
Expr *CalleeExpr = getExprForValue(AbortFun);
|
|
SmallVector<Expr *, 8> Args;
|
|
QualType ReturnType = ASTCtx.VoidTy;
|
|
return CallExpr::Create(ASTCtx,
|
|
CalleeExpr,
|
|
Args,
|
|
ReturnType,
|
|
VK_RValue,
|
|
{});
|
|
}
|
|
//
|
|
// ---- Instructions for struct manipulation ----
|
|
//
|
|
case Instruction::InsertValue: {
|
|
InsertValueInst *Insert = cast<InsertValueInst>(&I);
|
|
revng_assert(Insert->getNumIndices() == 1);
|
|
Value *AggregateOp = Insert->getAggregateOperand();
|
|
revng_assert(isa<UndefValue>(AggregateOp)
|
|
or isa<InsertValueInst>(AggregateOp)
|
|
or isa<ConstantStruct>(AggregateOp));
|
|
llvm::Type *AggregateTy = AggregateOp->getType();
|
|
revng_assert(AggregateTy->isAggregateType());
|
|
auto *TypeDecl = Declarator.lookupTypeDeclOrNull(AggregateTy);
|
|
auto *StructTypeDecl = cast<clang::RecordDecl>(TypeDecl);
|
|
Expr *StructExpr = getExprForValue(Insert);
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType InTy = Declarator.getOrCreateType(Insert->getType(),
|
|
Insert->getFunction(),
|
|
ASTCtx,
|
|
TUDecl);
|
|
revng_assert(not llvm::empty(Insert->indices()));
|
|
unsigned Idx = *Insert->indices().begin();
|
|
FieldDecl *FieldDecl = *std::next(StructTypeDecl->field_begin(), Idx);
|
|
clang::DeclarationName FieldDeclName = FieldDecl->getIdentifier();
|
|
clang::DeclarationNameInfo FieldDeclNameInfo(FieldDeclName, {});
|
|
auto DAP = DeclAccessPair::make(FieldDecl, FieldDecl->getAccess());
|
|
clang::Expr *LHS = MemberExpr::Create(ASTCtx,
|
|
StructExpr,
|
|
/*isarrow*/ false,
|
|
{},
|
|
{},
|
|
{},
|
|
FieldDecl,
|
|
DAP,
|
|
FieldDeclNameInfo,
|
|
/*TemplateArgs*/ nullptr,
|
|
DeclCreator::getQualType(InTy),
|
|
VK_LValue,
|
|
OK_Ordinary,
|
|
NOUR_None);
|
|
clang::Expr *RHS = getExprForValue(Insert->getInsertedValueOperand());
|
|
BinaryOperatorKind AssignOpKind = BinaryOperatorKind::BO_Assign;
|
|
AdditionalStmts[&I].push_back(new (ASTCtx)
|
|
clang::BinaryOperator(LHS,
|
|
RHS,
|
|
AssignOpKind,
|
|
LHS->getType(),
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
FPOptions()));
|
|
if (isa<UndefValue>(AggregateOp))
|
|
return nullptr;
|
|
if (isa<ConstantStruct>(AggregateOp))
|
|
return nullptr;
|
|
return getExprForValue(AggregateOp);
|
|
}
|
|
case Instruction::ExtractValue: {
|
|
ExtractValueInst *Extract = cast<ExtractValueInst>(&I);
|
|
revng_assert(Extract->getNumIndices() == 1);
|
|
Value *AggregateOp = Extract->getAggregateOperand();
|
|
if (isa<UndefValue>(AggregateOp))
|
|
return nullptr;
|
|
revng_assert(isa<CallInst>(AggregateOp));
|
|
llvm::Type *AggregateTy = AggregateOp->getType();
|
|
revng_assert(AggregateTy->isAggregateType());
|
|
auto *TypeDecl = Declarator.lookupTypeDeclOrNull(AggregateTy);
|
|
auto *StructTypeDecl = cast<clang::RecordDecl>(TypeDecl);
|
|
Expr *StructExpr = getExprForValue(AggregateOp);
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType ExtractedTy = Declarator.getOrCreateType(Extract,
|
|
ASTCtx,
|
|
TUDecl);
|
|
revng_assert(not llvm::empty(Extract->indices()));
|
|
unsigned Idx = *Extract->indices().begin();
|
|
auto *ExtractedFDecl = *std::next(StructTypeDecl->field_begin(), Idx);
|
|
clang::DeclarationName FieldDeclName = ExtractedFDecl->getIdentifier();
|
|
clang::DeclarationNameInfo FieldDeclNameInfo(FieldDeclName, {});
|
|
return MemberExpr::Create(ASTCtx,
|
|
StructExpr,
|
|
/*isarrow*/ false,
|
|
{},
|
|
{},
|
|
{},
|
|
ExtractedFDecl,
|
|
DeclAccessPair::make(ExtractedFDecl,
|
|
ExtractedFDecl->getAccess()),
|
|
FieldDeclNameInfo,
|
|
/*TemplateArgs*/ nullptr,
|
|
DeclCreator::getQualType(ExtractedTy),
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
NOUR_None);
|
|
}
|
|
|
|
// ---- UNSUPPORTED INSTRUCTIONS ----
|
|
// Terminators
|
|
case Instruction::IndirectBr:
|
|
case Instruction::Invoke:
|
|
case Instruction::Resume:
|
|
case Instruction::CleanupRet:
|
|
case Instruction::CatchRet:
|
|
case Instruction::CatchPad:
|
|
case Instruction::CatchSwitch:
|
|
// Memory instructions
|
|
case Instruction::GetElementPtr:
|
|
case Instruction::AtomicCmpXchg:
|
|
case Instruction::AtomicRMW:
|
|
case Instruction::Fence:
|
|
// Binary operators for floats
|
|
case Instruction::FAdd:
|
|
case Instruction::FSub:
|
|
case Instruction::FMul:
|
|
case Instruction::FDiv:
|
|
case Instruction::FRem:
|
|
// Convert instructions
|
|
case Instruction::FPTrunc:
|
|
case Instruction::FPExt:
|
|
case Instruction::FPToUI:
|
|
case Instruction::FPToSI:
|
|
case Instruction::UIToFP:
|
|
case Instruction::SIToFP:
|
|
case Instruction::AddrSpaceCast:
|
|
// Other instructions
|
|
case Instruction::PHI:
|
|
case Instruction::FCmp:
|
|
case Instruction::VAArg:
|
|
case Instruction::ExtractElement:
|
|
case Instruction::InsertElement:
|
|
case Instruction::ShuffleVector:
|
|
case Instruction::LandingPad:
|
|
case Instruction::CleanupPad:
|
|
default:
|
|
revng_abort("Unexpected operation");
|
|
}
|
|
revng_abort("Unexpected operation");
|
|
}
|
|
|
|
clang::VarDecl *
|
|
StmtBuilder::getOrCreateLoopStateVarDecl(clang::FunctionDecl &FDecl) {
|
|
if (not LoopStateVarDecl) {
|
|
IdentifierInfo &Id = ASTCtx.Idents.get("loop_state_var");
|
|
LoopStateVarDecl = VarDecl::Create(ASTCtx,
|
|
&FDecl,
|
|
{},
|
|
{},
|
|
&Id,
|
|
ASTCtx.UnsignedIntTy,
|
|
nullptr,
|
|
StorageClass::SC_None);
|
|
FDecl.addDecl(LoopStateVarDecl);
|
|
}
|
|
revng_assert(LoopStateVarDecl != nullptr);
|
|
return LoopStateVarDecl;
|
|
}
|
|
|
|
clang::VarDecl *
|
|
StmtBuilder::getOrCreateSwitchStateVarDecl(clang::FunctionDecl &FDecl) {
|
|
if (not SwitchStateVarDecl) {
|
|
IdentifierInfo &Id = ASTCtx.Idents.get("switch_state_var");
|
|
TypeDeclOrQualType BoolTy = Declarator.getOrCreateBoolType(ASTCtx);
|
|
SwitchStateVarDecl = VarDecl::Create(ASTCtx,
|
|
&FDecl,
|
|
{},
|
|
{},
|
|
&Id,
|
|
DeclCreator::getQualType(BoolTy),
|
|
nullptr,
|
|
StorageClass::SC_None);
|
|
FDecl.addDecl(SwitchStateVarDecl);
|
|
}
|
|
revng_assert(SwitchStateVarDecl != nullptr);
|
|
return SwitchStateVarDecl;
|
|
}
|
|
|
|
void StmtBuilder::createAST(llvm::Function &F, clang::FunctionDecl &FDecl) {
|
|
revng_log(ASTBuildLog,
|
|
"Building AST for Instructions in Function " << F.getName());
|
|
|
|
revng_assert(not ValueLayouts or SE);
|
|
|
|
uint64_t BBId = 0;
|
|
ReversePostOrderTraversal<Function *> RPOT(&F);
|
|
for (BasicBlock *BB : RPOT) {
|
|
revng_log(ASTBuildLog, "BB: " << BB->getName());
|
|
|
|
{
|
|
// Create labels for Basic Blocks. This could potentially be disabled if
|
|
// we choose not to have the option to emit goto statements ever.
|
|
IdentifierInfo &Id = ASTCtx.Idents.get("bb_" + std::to_string(BBId++));
|
|
LabelDecl *Label = LabelDecl::Create(ASTCtx, &FDecl, {}, &Id);
|
|
BBLabelDecls[BB] = Label;
|
|
}
|
|
|
|
for (Instruction &I : *BB) {
|
|
// Skip calls to `revng_scev_barrier_*`
|
|
// FIXME: calls to revng_scev_barrier_* should eventually be removed after
|
|
// using them and before actually generating C code for them.
|
|
if (auto *Call = dyn_cast<CallInst>(&I)) {
|
|
if (Call->getType()->isIntOrPtrTy()) {
|
|
|
|
const llvm::Type *BarrierTy = Call->getType();
|
|
const std::string BarrierName = makeSCEVBarrierName(BarrierTy);
|
|
const llvm::Function *SCEVBarrier = Call->getCalledFunction();
|
|
|
|
if (SCEVBarrier->getName().str() == BarrierName) {
|
|
revng_assert(SCEVBarrier->arg_size() == 1);
|
|
InstrStmts[&I] = getExprForValue(Call->getArgOperand(0));
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We don't build clang's AST expressions for PHINodes nor for
|
|
// BranchInsts and SwitchInsts.
|
|
|
|
// For BranchInsts, we don't create AST right now, because the emission of
|
|
// control flow statements in C is driven by the ASTTree
|
|
if (isa<BranchInst>(&I))
|
|
continue;
|
|
|
|
// For SwitchInsts, we don't create AST right now, because the emission of
|
|
// control flow statements in C is driven by the ASTTree
|
|
if (isa<SwitchInst>(&I))
|
|
continue;
|
|
|
|
// PHINodes are not expanded into expressions because they expand in a
|
|
// local variable, that is assigned multiple times for all the incoming
|
|
// Values of the PHINode.
|
|
// Each PHINode has an associated VarDecl
|
|
if (isa<PHINode>(&I)) {
|
|
revng_assert(VarDecls.count(&I) == 0);
|
|
VarDecl *NewVarDecl = createVarDecl(&I, FDecl);
|
|
VarDecls[&I] = NewVarDecl;
|
|
continue;
|
|
}
|
|
|
|
// Declare a special local variable for those instructions that need it to
|
|
// build the expression.
|
|
// Examples are AllocaInst and InsertValueInst.
|
|
auto ToSerializeIt = ToSerialize.find(&I);
|
|
auto ToSerializeEnd = ToSerialize.end();
|
|
if (ToSerializeIt != ToSerializeEnd
|
|
and ToSerializeIt->second.isSet(NeedsLocalVarToComputeExpr)) {
|
|
if (auto *Alloca = dyn_cast<AllocaInst>(&I)) {
|
|
// TODO: for now we ignore the alignment of the alloca. This might
|
|
// turn out not to be safe later, because it does not take into
|
|
// account the alignment of future accesses in the `Alloca`ted space.
|
|
// If the code is then recompiled for an architecture that does not
|
|
// support unaligned access this may cause crashes.
|
|
revng_assert(Alloca->isStaticAlloca());
|
|
revng_assert(AllocaDecls.count(Alloca) == 0);
|
|
VarDecl *NewAllocaDecl = createVarDecl(Alloca, FDecl);
|
|
AllocaDecls[Alloca] = NewAllocaDecl;
|
|
} else if (auto *Insert = dyn_cast<InsertValueInst>(&I)) {
|
|
revng_assert(VarDecls.count(Insert) == 0);
|
|
VarDecl *NewVarDecl = createVarDecl(Insert, FDecl);
|
|
VarDecls[Insert] = NewVarDecl;
|
|
|
|
// Setup the initial value for the NewVarDecl.
|
|
// This value will be emitted as an intialization.
|
|
Value *AggregateOp = Insert->getAggregateOperand();
|
|
if (auto *CS = dyn_cast<ConstantStruct>(AggregateOp)) {
|
|
std::vector<Expr *> StructOpExpr;
|
|
for (auto &OperandUse : CS->operands()) {
|
|
Value *Operand = OperandUse.get();
|
|
Constant *OperandConst = cast<Constant>(Operand);
|
|
clang::Expr *OperandExpr = nullptr;
|
|
if (isa<UndefValue>(OperandConst)) {
|
|
QualType IntT = ASTCtx.IntTy;
|
|
OperandExpr = new (ASTCtx) ImplicitValueInitExpr(IntT);
|
|
} else {
|
|
OperandExpr = getLiteralFromConstant(OperandConst);
|
|
}
|
|
revng_assert(OperandExpr != nullptr);
|
|
StructOpExpr.push_back(OperandExpr);
|
|
}
|
|
|
|
clang::Expr *ILE = new (ASTCtx)
|
|
InitListExpr(ASTCtx, {}, StructOpExpr, {});
|
|
NewVarDecl->setInit(ILE);
|
|
} else if (isa<InsertValueInst>(AggregateOp)
|
|
or isa<UndefValue>(AggregateOp)) {
|
|
// If the InsertValueInst is inserting something inside an undef
|
|
// aggregate, or inside a struct coming from another InsertValue, we
|
|
// simply don't initialize it.
|
|
// Given that the initialization can be dynamic, we just leave its
|
|
// handling to the actual emission of the assignments that happens
|
|
// later, in the call to buildStmt.
|
|
} else {
|
|
revng_unreachable();
|
|
}
|
|
} else {
|
|
revng_unreachable();
|
|
}
|
|
}
|
|
|
|
Stmt *NewStmt = nullptr;
|
|
|
|
// If we have type info we try to understand if the current instruction
|
|
// can represents some form of pointer arithmetic that can be translated
|
|
// into a nice access to a field of a struct.
|
|
if (ValueLayouts) {
|
|
|
|
revng_assert(SE);
|
|
|
|
if (false and SE->isSCEVable(I.getType())) {
|
|
const SCEV *ISCEV = SE->getSCEV(&I);
|
|
|
|
// Compute the base address of the Instruction SCEV
|
|
auto Bases = SCEVBaseAddressExplorer().findBases(SE, ISCEV, {});
|
|
|
|
// We expect no bases or at most one base address. If we get more than
|
|
// one possible candidate base address for ISCEV we haven't decided
|
|
// which type to emit yet, so this is not handled.
|
|
revng_assert(Bases.empty() or Bases.size() == 1);
|
|
|
|
const SCEV *Base = nullptr;
|
|
if (Bases.empty()) {
|
|
// It was impossible to find a valid base address for ISCEV.
|
|
// This means that we can still try to interpret ISCEV as base
|
|
// address of itself.
|
|
Base = ISCEV;
|
|
} else {
|
|
Base = *Bases.begin();
|
|
}
|
|
revng_assert(Base);
|
|
const auto *BaseValue = cast<llvm::SCEVUnknown>(Base)->getValue();
|
|
|
|
// Unwrap calls to revng_scev_barrier_*
|
|
if (auto *Call = dyn_cast<CallInst>(BaseValue)) {
|
|
const llvm::Type *BarrierTy = Call->getType();
|
|
const llvm::Function *SCEVBarrier = Call->getCalledFunction();
|
|
const std::string BarrierName = makeSCEVBarrierName(BarrierTy);
|
|
if (SCEVBarrier->getName().str() == BarrierName) {
|
|
revng_assert(SCEVBarrier->arg_size() == 1);
|
|
BaseValue = Call->getArgOperand(0);
|
|
}
|
|
}
|
|
revng_assert(not isa<CallInst>(BaseValue));
|
|
|
|
// Try to obtain the DLA type of BaseValue
|
|
auto *TUDecl = ASTCtx.getTranslationUnitDecl();
|
|
auto DLAType = Declarator.getOrCreateDLAType(BaseValue,
|
|
ASTCtx,
|
|
*TUDecl);
|
|
clang::Stmt *PointerArithmeticStmt = nullptr;
|
|
if (DLAType.hasValue()) {
|
|
|
|
auto BasePointedLayouts = Declarator.getPointedLayouts(BaseValue);
|
|
revng_assert(BasePointedLayouts.size() == 1);
|
|
const dla::Layout *BasePointedLayout = BasePointedLayouts.front();
|
|
|
|
using llvm::SCEVAddRecExpr;
|
|
using llvm::SCEVConstant;
|
|
|
|
// Off = I - Base
|
|
const SCEV *Off = SE->getAddExpr(ISCEV, SE->getNegativeSCEV(Base));
|
|
|
|
if (auto *ConstOff = dyn_cast<SCEVConstant>(Off)) {
|
|
llvm::ConstantInt *O = ConstOff->getValue();
|
|
const dla::Layout *OuterLay = BasePointedLayout;
|
|
revng_assert(not O->isNegative());
|
|
uint64_t RemainingOffset = O->getZExtValue();
|
|
|
|
// FIXME: qui devo fare una funzione, che si prende il remainig
|
|
// offset, e poi itera dentro i tipi, segnandosi i QualType in
|
|
// giro, e anche i numeri dei field che attraversa, e sottraendo
|
|
// man mano le size delle cose che attraversa dal remaining
|
|
// offset.
|
|
//
|
|
// - Le struct cerca un membro allineato, se c'è ho finito, se no
|
|
// entro all'ultimo che contiene il coso
|
|
// - Le union cerca il figlio più grosso che contiene l'offset
|
|
// - gli array cerca il figlio che contiene l'offset
|
|
// - il padding è come gli array
|
|
// - i tipi base sono le foglie e ho finito
|
|
clang::Expr *MemberExpr = nullptr;
|
|
|
|
struct NestedTypeInfo {
|
|
uint64_t ElemId;
|
|
clang::IdentifierInfo *Identifier;
|
|
TypeDeclOrQualType QTy;
|
|
};
|
|
|
|
// Vector to hold the nested QualTypes, from outer to inner.
|
|
llvm::SmallVector<NestedTypeInfo, 8> NestedTypes;
|
|
|
|
while (OuterLay) {
|
|
const dla::Layout *InnerLayout = nullptr;
|
|
|
|
revng_assert(RemainingOffset < OuterLay->size());
|
|
|
|
auto &ValCtx = BaseValue->getContext();
|
|
auto OutQTy = Declarator.getOrCreateTypeFromLayout(OuterLay,
|
|
ASTCtx,
|
|
ValCtx);
|
|
uint64_t RemainingOffset = 0ULL;
|
|
switch (OuterLay->getKind()) {
|
|
|
|
// Ignore padding for now
|
|
case dla::Layout::LayoutKind::Padding: {
|
|
revng_unreachable();
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Base: {
|
|
// If the layout points to a base type it should point to its
|
|
// beginning, not in the middle.
|
|
revng_assert(RemainingOffset == 0ULL);
|
|
NestedTypes.push_back({ 0, nullptr, OutQTy });
|
|
RemainingOffset = 0ULL;
|
|
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Array: {
|
|
const auto *Array = cast<dla::ArrayLayout>(OuterLay);
|
|
const dla::Layout *ArrayElem = Array->getElem();
|
|
auto ElemSize = ArrayElem->size();
|
|
revng_assert(ElemSize < RemainingOffset);
|
|
auto ElemId = RemainingOffset / ElemSize;
|
|
NestedTypes.push_back({ ElemId, nullptr, OutQTy });
|
|
RemainingOffset = RemainingOffset % ElemSize;
|
|
InnerLayout = ArrayElem;
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Struct: {
|
|
revng_unreachable();
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Union: {
|
|
const auto *Union = cast<dla::UnionLayout>(OuterLay);
|
|
|
|
const dla::Layout *LargerElem = nullptr;
|
|
for (const dla::Layout *Elem : Union->elements()) {
|
|
if (LargerElem) {
|
|
if (LargerElem->size() < Elem->size())
|
|
LargerElem = Elem;
|
|
} else {
|
|
LargerElem = Elem;
|
|
}
|
|
}
|
|
revng_assert(LargerElem);
|
|
revng_assert(RemainingOffset < LargerElem->size());
|
|
// Remaining offset stays the same here.
|
|
InnerLayout = LargerElem;
|
|
} break;
|
|
|
|
default:
|
|
revng_unreachable();
|
|
}
|
|
|
|
RemainingOffset -= InnerLayout->size();
|
|
OuterLay = InnerLayout;
|
|
}
|
|
|
|
// FIXME qui finisce la funzione di cui sopra
|
|
// poi ne devo fare un'altra che prende i tipi nestati e
|
|
// costruisce le member expr nestate
|
|
|
|
if (not RemainingOffset) {
|
|
// We were able to find a combination of types that is exactly
|
|
// at the pointed layout.
|
|
// So we can emit a field access expression for that.
|
|
// Otherwise we back off.
|
|
revng_assert(not NestedTypes.empty());
|
|
|
|
TypeDeclOrQualType InnerTy = NestedTypes.back().QTy;
|
|
clang::QualType InnerQualTy = DeclCreator::getQualType(InnerTy);
|
|
using Unary = clang::UnaryOperator;
|
|
NewStmt = new (ASTCtx) Unary(MemberExpr,
|
|
UnaryOperatorKind::UO_AddrOf,
|
|
ASTCtx.getPointerType(InnerQualTy),
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
false);
|
|
}
|
|
|
|
} else if (auto *AddRecOff = dyn_cast<SCEVAddRecExpr>(Off)) {
|
|
} else {
|
|
// We don't handle other cases. Actually we don't even expect
|
|
// them. If they show up, it should be fine to just ignore
|
|
// them and fallback to the normal `buildStmt`. But for now just
|
|
// leave a check here to avoid missing surprising stuff.
|
|
revng_unreachable();
|
|
}
|
|
}
|
|
|
|
NewStmt = PointerArithmeticStmt;
|
|
}
|
|
}
|
|
|
|
// If we were not able to emit pointer arithmetic as a nice access to a
|
|
// field struct fallback to normal emission.
|
|
if (not NewStmt)
|
|
NewStmt = buildStmt(I);
|
|
|
|
// If we didn't emit anything, just skip the rest.
|
|
if (not NewStmt)
|
|
continue;
|
|
|
|
InstrStmts[&I] = NewStmt;
|
|
|
|
// Build the local variable for all the other instructions that need it.
|
|
if (ToSerializeIt != ToSerializeEnd) {
|
|
const SerializationFlags Flags = ToSerializeIt->second;
|
|
revng_assert(not Flags.isSet(NeedsManyStatements)
|
|
or Flags.isSet(NeedsLocalVarToComputeExpr));
|
|
if (SerializationFlags::needsVarDecl(Flags)
|
|
and not Flags.isSet(NeedsLocalVarToComputeExpr)) {
|
|
|
|
revng_assert(VarDecls.count(&I) == 0);
|
|
VarDecl *NewVarDecl = createVarDecl(&I, FDecl);
|
|
VarDecls[&I] = NewVarDecl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VarDecl *
|
|
StmtBuilder::createVarDecl(const Instruction *I, clang::FunctionDecl &FDecl) {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType ASTType;
|
|
if (const auto *Alloca = dyn_cast<AllocaInst>(I)) {
|
|
// First, create a VarDecl, for an array of char to place in the
|
|
// BasicBlock where the AllocaInst is
|
|
const DataLayout &DL = I->getModule()->getDataLayout();
|
|
uint64_t AllocaSize = *Alloca->getAllocationSizeInBits(DL);
|
|
revng_assert(AllocaSize <= std::numeric_limits<unsigned>::max());
|
|
APInt ArraySize = APInt(32, static_cast<unsigned>(AllocaSize));
|
|
using ArraySizeMod = clang::ArrayType::ArraySizeModifier;
|
|
ArraySizeMod SizeMod = ArraySizeMod::Normal;
|
|
QualType CharTy = ASTCtx.CharTy;
|
|
QualType ArrayTy = ASTCtx.getConstantArrayType(CharTy,
|
|
ArraySize,
|
|
nullptr,
|
|
SizeMod,
|
|
0);
|
|
ASTType = ArrayTy;
|
|
} else if (const auto *Call = dyn_cast<llvm::CallInst>(I)) {
|
|
ASTType = Declarator.getOrCreateType(Call->getType(),
|
|
Call->getCalledFunction(),
|
|
ASTCtx,
|
|
TUDecl);
|
|
} else if (const auto *Insert = dyn_cast<llvm::InsertValueInst>(I)) {
|
|
ASTType = Declarator.getOrCreateType(Insert->getType(),
|
|
Insert->getFunction(),
|
|
ASTCtx,
|
|
TUDecl);
|
|
} else {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
ASTType = Declarator.getOrCreateType(I, ASTCtx, TUDecl);
|
|
}
|
|
|
|
clang::QualType ASTQualType = DeclCreator::getQualType(ASTType);
|
|
revng_assert(not ASTQualType.isNull());
|
|
const std::string VarName = I->hasName() ?
|
|
I->getName().str() :
|
|
(std::string("var_") + std::to_string(NVar++));
|
|
IdentifierInfo &Id = ASTCtx.Idents.get(makeCIdentifier(VarName));
|
|
VarDecl *NewVarDecl = VarDecl::Create(ASTCtx,
|
|
&FDecl,
|
|
{},
|
|
{},
|
|
&Id,
|
|
ASTQualType,
|
|
nullptr,
|
|
StorageClass::SC_None);
|
|
// Add the NewVarDecl to the function declaration context, so that clang's
|
|
// AST printer will print the variable declaration.
|
|
FDecl.addDecl(NewVarDecl);
|
|
return NewVarDecl;
|
|
}
|
|
|
|
VarDecl *StmtBuilder::createVarDecl(Constant *C,
|
|
Value *NamingVal,
|
|
clang::FunctionDecl &FDecl) {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType ASTType = clang::QualType();
|
|
if (auto *ZeroAggregate = dyn_cast<llvm::ConstantAggregateZero>(C)) {
|
|
ASTType = Declarator.getOrCreateType(ZeroAggregate->getType(),
|
|
NamingVal,
|
|
ASTCtx,
|
|
TUDecl);
|
|
} else {
|
|
revng_abort("trying to create VarDecl for unexpected constant");
|
|
}
|
|
|
|
QualType ASTQualType = DeclCreator::getQualType(ASTType);
|
|
revng_assert(not ASTQualType.isNull());
|
|
const std::string VarName = C->hasName() ?
|
|
C->getName().str() :
|
|
(std::string("var_") + std::to_string(NVar++));
|
|
IdentifierInfo &Id = ASTCtx.Idents.get(makeCIdentifier(VarName));
|
|
VarDecl *NewVarDecl = VarDecl::Create(ASTCtx,
|
|
&FDecl,
|
|
{},
|
|
{},
|
|
&Id,
|
|
ASTQualType,
|
|
nullptr,
|
|
StorageClass::SC_None);
|
|
FDecl.addDecl(NewVarDecl);
|
|
return NewVarDecl;
|
|
}
|
|
|
|
static clang::BinaryOperatorKind
|
|
getClangBinaryOpKind(const Instruction &I,
|
|
const clang::Type *LHSTy,
|
|
const clang::Type *RHSTy) {
|
|
clang::BinaryOperatorKind Res;
|
|
switch (I.getOpcode()) {
|
|
case Instruction::Add: {
|
|
Res = clang::BinaryOperatorKind::BO_Add;
|
|
} break;
|
|
case Instruction::Sub: {
|
|
Res = clang::BinaryOperatorKind::BO_Sub;
|
|
} break;
|
|
case Instruction::Mul: {
|
|
Res = clang::BinaryOperatorKind::BO_Mul;
|
|
} break;
|
|
case Instruction::And: {
|
|
if (LHSTy->isBooleanType() and RHSTy->isBooleanType())
|
|
Res = clang::BinaryOperatorKind::BO_LAnd;
|
|
else
|
|
Res = clang::BinaryOperatorKind::BO_And;
|
|
} break;
|
|
case Instruction::Or: {
|
|
if (LHSTy->isBooleanType() and RHSTy->isBooleanType())
|
|
Res = clang::BinaryOperatorKind::BO_LOr;
|
|
else
|
|
Res = clang::BinaryOperatorKind::BO_Or;
|
|
} break;
|
|
case Instruction::Xor: {
|
|
Res = clang::BinaryOperatorKind::BO_Xor;
|
|
} break;
|
|
case Instruction::ICmp: {
|
|
auto *CompareI = cast<CmpInst>(&I);
|
|
switch (CompareI->getPredicate()) {
|
|
case CmpInst::ICMP_EQ: {
|
|
Res = clang::BinaryOperatorKind::BO_EQ;
|
|
} break;
|
|
case CmpInst::ICMP_NE: {
|
|
Res = clang::BinaryOperatorKind::BO_NE;
|
|
} break;
|
|
case CmpInst::ICMP_UGT:
|
|
case CmpInst::ICMP_SGT: {
|
|
Res = clang::BinaryOperatorKind::BO_GT;
|
|
} break;
|
|
case CmpInst::ICMP_UGE:
|
|
case CmpInst::ICMP_SGE: {
|
|
Res = clang::BinaryOperatorKind::BO_GE;
|
|
} break;
|
|
case CmpInst::ICMP_ULT:
|
|
case CmpInst::ICMP_SLT: {
|
|
Res = clang::BinaryOperatorKind::BO_LT;
|
|
} break;
|
|
case CmpInst::ICMP_ULE:
|
|
case CmpInst::ICMP_SLE: {
|
|
Res = clang::BinaryOperatorKind::BO_LE;
|
|
} break;
|
|
case CmpInst::BAD_ICMP_PREDICATE:
|
|
case CmpInst::BAD_FCMP_PREDICATE:
|
|
case CmpInst::FCMP_TRUE:
|
|
case CmpInst::FCMP_FALSE:
|
|
case CmpInst::FCMP_OEQ:
|
|
case CmpInst::FCMP_ONE:
|
|
case CmpInst::FCMP_OGE:
|
|
case CmpInst::FCMP_OGT:
|
|
case CmpInst::FCMP_OLE:
|
|
case CmpInst::FCMP_OLT:
|
|
case CmpInst::FCMP_ORD:
|
|
case CmpInst::FCMP_UNO:
|
|
case CmpInst::FCMP_UEQ:
|
|
case CmpInst::FCMP_UNE:
|
|
case CmpInst::FCMP_UGT:
|
|
case CmpInst::FCMP_UGE:
|
|
case CmpInst::FCMP_ULT:
|
|
case CmpInst::FCMP_ULE:
|
|
revng_abort("Unsupported comparison operator");
|
|
}
|
|
} break;
|
|
case Instruction::Shl: {
|
|
Res = clang::BinaryOperatorKind::BO_Shl;
|
|
} break;
|
|
case Instruction::LShr:
|
|
case Instruction::AShr: {
|
|
Res = clang::BinaryOperatorKind::BO_Shr;
|
|
} break;
|
|
case Instruction::UDiv:
|
|
case Instruction::SDiv: {
|
|
Res = clang::BinaryOperatorKind::BO_Div;
|
|
} break;
|
|
case Instruction::URem:
|
|
case Instruction::SRem: {
|
|
Res = clang::BinaryOperatorKind::BO_Rem;
|
|
} break;
|
|
default: {
|
|
revng_log(ASTBuildLog, "Unsupported operation" << dumpToString(&I) << '\n');
|
|
revng_abort("Unsupported binary operator");
|
|
}
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
static bool is128Int(ASTContext &ASTCtx, clang::Expr *E) {
|
|
const clang::Type *T = E->getType().getTypePtr();
|
|
const clang::Type *Int128T = ASTCtx.Int128Ty.getTypePtr();
|
|
const clang::Type *UInt128T = ASTCtx.UnsignedInt128Ty.getTypePtr();
|
|
return T == Int128T or T == UInt128T;
|
|
}
|
|
|
|
static std::pair<Expr *, Expr *> getCastedBinaryOperands(ASTContext &ASTCtx,
|
|
const Instruction &I,
|
|
Expr *LHS,
|
|
Expr *RHS) {
|
|
|
|
QualType LHSQualTy = LHS->getType();
|
|
QualType RHSQualTy = RHS->getType();
|
|
const ClangType *LHSTy = LHSQualTy.getTypePtr();
|
|
const ClangType *RHSTy = RHSQualTy.getTypePtr();
|
|
revng_assert(LHSTy->isIntegerType() and RHSTy->isIntegerType());
|
|
uint64_t LHSSize = ASTCtx.getTypeSize(LHSTy);
|
|
uint64_t RHSSize = ASTCtx.getTypeSize(RHSTy);
|
|
unsigned OpCode = I.getOpcode();
|
|
revng_assert(LHSSize == RHSSize or OpCode == Instruction::Shl
|
|
or OpCode == Instruction::LShr or OpCode == Instruction::AShr
|
|
or is128Int(ASTCtx, RHS) or is128Int(ASTCtx, LHS));
|
|
unsigned Size = static_cast<unsigned>(std::max(LHSSize, RHSSize));
|
|
QualType SignedTy = ASTCtx.getIntTypeForBitwidth(Size, /* Signed */ true);
|
|
|
|
std::pair<Expr *, Expr *> Res = std::make_pair(LHS, RHS);
|
|
switch (OpCode) {
|
|
// These instructions have unsigned semantics in llvm IR.
|
|
// We emit unsigned integers by default, so these operations do not need
|
|
// any cast to preserve the semantics in C.
|
|
|
|
case Instruction::Add:
|
|
case Instruction::Sub:
|
|
case Instruction::And:
|
|
case Instruction::Or:
|
|
case Instruction::Xor:
|
|
// This set of instructions (described in paragraphs 6.5.6 'Additive
|
|
// operators', paragraph 6.5.10 'Bitwise AND operator', paragraph 6.5.11
|
|
// 'Bitwise exclusive OR operator', and paragraph 6.5.12 'Bitwise
|
|
// inclusive OR operator' of the C11 standard) may have a large unsigned
|
|
// integer literal as one or both operands. In those cases, it is
|
|
// beneficial for the readability of the generate C code to substitute
|
|
// such large unsigned integer literal with negative signed integer
|
|
// literal. This enables printing idiomatic expressions such as 'X - 1'
|
|
// instead of 'X + 0xFFFFFFFFFFFFFFFF'.
|
|
|
|
if (auto *RHSLiteral = dyn_cast<clang::IntegerLiteral>(RHS)) {
|
|
llvm::APInt RHSVal = RHSLiteral->getValue();
|
|
revng_assert(RHSVal.getBitWidth() == RHSSize);
|
|
if (RHSVal.isNegative()) {
|
|
QualType SIntT = ASTCtx.getIntTypeForBitwidth(RHSVal.getBitWidth(),
|
|
/*signed*/ true);
|
|
auto NegRHS = IntegerLiteral::Create(ASTCtx, RHSVal, SIntT, {});
|
|
Res.second = new (ASTCtx) ParenExpr({}, {}, NegRHS);
|
|
}
|
|
}
|
|
|
|
[[fallthrough]];
|
|
|
|
case Instruction::Shl:
|
|
case Instruction::LShr:
|
|
// Shifts are undefined behavior if the RHS is negative (see
|
|
// paragraph 6.5.7 of the C11 standard: 'Bitwise shift operators'), so we
|
|
// don't try to promote big unsigned integer literals at constants RHS to
|
|
// negative signed integer literals.
|
|
//
|
|
if (auto *LHSLiteral = dyn_cast<clang::IntegerLiteral>(LHS)) {
|
|
llvm::APInt LHSVal = LHSLiteral->getValue();
|
|
revng_assert(LHSVal.getBitWidth() == LHSSize);
|
|
if (LHSVal.isNegative()) {
|
|
QualType SIntT = ASTCtx.getIntTypeForBitwidth(LHSVal.getBitWidth(),
|
|
/*signed*/ true);
|
|
auto NegLHS = IntegerLiteral::Create(ASTCtx, LHSVal, SIntT, {});
|
|
Res.second = new (ASTCtx) ParenExpr({}, {}, NegLHS);
|
|
}
|
|
}
|
|
|
|
[[fallthrough]];
|
|
|
|
case Instruction::Mul:
|
|
case Instruction::UDiv:
|
|
case Instruction::URem:
|
|
// For multiplication, division, and reminder (paragraph 6.5.5 of the C11
|
|
// standard: 'Multiplicative operators'), we could in principle promote
|
|
// big positive unsigned integer literals to negative signed literals, but
|
|
// the consequence on the sign of the result are not clear to me now, so I
|
|
// just leave them like they are for now.
|
|
{}
|
|
break;
|
|
|
|
case Instruction::SDiv:
|
|
case Instruction::SRem:
|
|
case Instruction::AShr:
|
|
case Instruction::ICmp: {
|
|
if (OpCode != Instruction::ICmp or cast<CmpInst>(&I)->isSigned()) {
|
|
if (RHSTy->isUnsignedIntegerType())
|
|
Res.second = createCast(SignedTy, Res.second, ASTCtx);
|
|
if (LHSTy->isUnsignedIntegerType())
|
|
Res.first = createCast(SignedTy, Res.first, ASTCtx);
|
|
}
|
|
} break;
|
|
|
|
default: {
|
|
revng_log(ASTBuildLog, "Unsupported operation" << dumpToString(&I) << '\n');
|
|
revng_abort("Unsupported binary operator");
|
|
}
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
Expr *StmtBuilder::createRValueExprForBinaryOperator(Instruction &I) {
|
|
revng_assert(I.getNumOperands() == 2);
|
|
|
|
Value *LHSVal = I.getOperand(0);
|
|
Expr *LHS = getParenthesizedExprForValue(LHSVal);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and LHS)
|
|
LHS->dump();
|
|
if (LHS->isLValue())
|
|
LHS = ImplicitCastExpr::Create(ASTCtx,
|
|
LHS->getType(),
|
|
CastKind::CK_LValueToRValue,
|
|
LHS,
|
|
nullptr,
|
|
VK_RValue);
|
|
|
|
Value *RHSVal = I.getOperand(1);
|
|
Expr *RHS = getParenthesizedExprForValue(RHSVal);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and RHS)
|
|
RHS->dump();
|
|
if (RHS->isLValue())
|
|
RHS = ImplicitCastExpr::Create(ASTCtx,
|
|
RHS->getType(),
|
|
CastKind::CK_LValueToRValue,
|
|
RHS,
|
|
nullptr,
|
|
VK_RValue);
|
|
|
|
std::tie(LHS, RHS) = getCastedBinaryOperands(ASTCtx, I, LHS, RHS);
|
|
|
|
auto BinOpKind = getClangBinaryOpKind(I,
|
|
LHS->getType().getTypePtr(),
|
|
RHS->getType().getTypePtr());
|
|
Expr *Res = new (ASTCtx) clang::BinaryOperator(LHS,
|
|
RHS,
|
|
BinOpKind,
|
|
LHS->getType(),
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
FPOptions());
|
|
|
|
unsigned OpCode = I.getOpcode();
|
|
switch (OpCode) {
|
|
case Instruction::SDiv:
|
|
case Instruction::SRem:
|
|
case Instruction::AShr:
|
|
case Instruction::ICmp: {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType ResType = Declarator.getOrCreateType(&I, ASTCtx, TUDecl);
|
|
Res = new (ASTCtx) ParenExpr({}, {}, Res);
|
|
Res = createCast(DeclCreator::getQualType(ResType), Res, ASTCtx);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
Expr *StmtBuilder::getBoolLiteral(bool V) {
|
|
QualType IntT = ASTCtx.IntTy;
|
|
TypeDeclOrQualType BoolTy = Declarator.getOrCreateBoolType(ASTCtx);
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), V ? 1 : 0, true);
|
|
Expr *IntLiteral = IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
return createCast(DeclCreator::getQualType(BoolTy), IntLiteral, ASTCtx);
|
|
}
|
|
|
|
Expr *StmtBuilder::getUIntLiteral(uint64_t U) {
|
|
QualType UIntT = ASTCtx.UnsignedIntTy;
|
|
APInt Const = APInt(ASTCtx.getIntWidth(UIntT), U);
|
|
return IntegerLiteral::Create(ASTCtx, Const, UIntT, {});
|
|
}
|
|
|
|
Expr *StmtBuilder::getExprForValue(Value *V) {
|
|
revng_log(ASTBuildLog, "getExprForValue: " << dumpToString(V));
|
|
|
|
if (auto *FunctionOrGlobal = dyn_cast<GlobalObject>(V)) {
|
|
|
|
DeclaratorDecl *Decl = Declarator.globalDecls().at(FunctionOrGlobal);
|
|
QualType Type = Decl->getType();
|
|
DeclRefExpr *Res = new (ASTCtx)
|
|
DeclRefExpr(ASTCtx, Decl, false, Type, VK_LValue, {});
|
|
return Res;
|
|
|
|
} else if (isa<llvm::ConstantAggregateZero>(V)) {
|
|
|
|
VarDecl *VDecl = VarDecls.at(V);
|
|
QualType Type = VDecl->getType();
|
|
DeclRefExpr *Res = new (ASTCtx)
|
|
DeclRefExpr(ASTCtx, VDecl, false, Type, VK_LValue, {});
|
|
return Res;
|
|
|
|
} else if (isa<llvm::ConstantData>(V) or isa<llvm::ConstantExpr>(V)) {
|
|
|
|
return getLiteralFromConstant(cast<llvm::Constant>(V));
|
|
|
|
} else if (auto *I = dyn_cast<Instruction>(V)) {
|
|
|
|
// For all the other instructions that have already been marked for
|
|
// serialization we should have an associated entry in VarDecl.
|
|
// We simply return a DeclRefExpr wrapping the VarDecl associated with I.
|
|
auto VarDeclIt = VarDecls.find(I);
|
|
if (VarDeclIt != VarDecls.end()) {
|
|
revng_assert(VarDeclIt->second != nullptr);
|
|
VarDecl *VDecl = VarDeclIt->second;
|
|
QualType Type = VDecl->getType();
|
|
DeclRefExpr *Res = new (ASTCtx)
|
|
DeclRefExpr(ASTCtx, VDecl, false, Type, VK_LValue, {});
|
|
return Res;
|
|
}
|
|
|
|
auto InstrStmtIt = InstrStmts.find(I);
|
|
if (InstrStmtIt != InstrStmts.end()) {
|
|
// If the Instruction has an entry in InstrStmts, it means that we have
|
|
// already computed an expression for it, so we can directly use that.
|
|
return cast<Expr>(InstrStmtIt->second);
|
|
}
|
|
|
|
// If we reach this point we are creating an expression for a new
|
|
// Instruction. This should only happen for Load, Store and casts.
|
|
|
|
// If we don't have a VarDecl associated with I
|
|
if (isa<LoadInst>(I) or isa<StoreInst>(I)) {
|
|
// Load and Store Instruction are serialized as ExprLHS = ExprRHS.
|
|
// getExprForValue returns the ExprLHS.
|
|
auto *Store = dyn_cast<StoreInst>(I);
|
|
auto *Load = dyn_cast<LoadInst>(I);
|
|
|
|
Value *Addr = nullptr;
|
|
if (Load)
|
|
Addr = Load->getPointerOperand();
|
|
else
|
|
Addr = Store->getPointerOperand();
|
|
|
|
Expr *AddrExpr = getParenthesizedExprForValue(Addr);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled() and AddrExpr)
|
|
AddrExpr->dump();
|
|
// If we're moving from or into a GlobalVariable ExprLHS is just
|
|
// DeclRefExpr for that GlobalVariable
|
|
if (isa<GlobalVariable>(Addr))
|
|
return AddrExpr;
|
|
|
|
// Otherwise ExprLHS dereferences AddrExpr
|
|
QualType QualAddrType = AddrExpr->getType();
|
|
AddrExpr = ImplicitCastExpr::Create(ASTCtx,
|
|
QualAddrType,
|
|
CastKind::CK_LValueToRValue,
|
|
AddrExpr,
|
|
nullptr,
|
|
VK_RValue);
|
|
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
|
|
TypeDeclOrQualType PointeeType;
|
|
|
|
if (Load) {
|
|
PointeeType = Declarator.getOrCreateType(Load, ASTCtx, TUDecl);
|
|
} else {
|
|
Value *Stored = Store->getValueOperand();
|
|
PointeeType = Declarator.getOrCreateType(Stored, ASTCtx, TUDecl);
|
|
}
|
|
|
|
QualType PointeeQualType = DeclCreator::getQualType(PointeeType);
|
|
|
|
QualAddrType = AddrExpr->getType();
|
|
const ClangType *AddrTy = QualAddrType.getTypePtr();
|
|
if (not AddrTy->isPointerType()) {
|
|
revng_assert(AddrTy->isBuiltinType());
|
|
revng_assert(AddrTy->isIntegerType());
|
|
|
|
QualType PtrTy = ASTCtx.getPointerType(PointeeQualType);
|
|
uint64_t PtrSize = ASTCtx.getTypeSize(PtrTy);
|
|
uint64_t IntegerSize = ASTCtx.getTypeSize(AddrTy);
|
|
revng_assert(PtrSize >= IntegerSize);
|
|
if (PtrSize > IntegerSize)
|
|
AddrExpr = createCast(ASTCtx.getUIntPtrType(), AddrExpr, ASTCtx);
|
|
AddrExpr = createCast(PtrTy, AddrExpr, ASTCtx);
|
|
}
|
|
|
|
if (isa<llvm::ConstantPointerNull>(Addr)) {
|
|
QualType QualPtrTy = AddrExpr->getType();
|
|
const auto *PtrType = cast<ClangPointerType>(QualPtrTy.getTypePtr());
|
|
QualType QualPointeeTy = PtrType->getPointeeType();
|
|
QualPointeeTy.addVolatile();
|
|
QualType PtrToVolatileTy = ASTCtx.getPointerType(QualPointeeTy);
|
|
AddrExpr = createCast(PtrToVolatileTy, AddrExpr, ASTCtx);
|
|
}
|
|
|
|
using Unary = clang::UnaryOperator;
|
|
return new (ASTCtx) Unary(AddrExpr,
|
|
UnaryOperatorKind::UO_Deref,
|
|
PointeeQualType,
|
|
VK_LValue,
|
|
OK_Ordinary,
|
|
{},
|
|
false);
|
|
}
|
|
if (auto *Cast = dyn_cast<CastInst>(I)) {
|
|
Value *RHS = Cast->getOperand(0);
|
|
Expr *Result = getParenthesizedExprForValue(RHS);
|
|
LLVMType *RHSTy = Cast->getSrcTy();
|
|
LLVMType *LHSTy = Cast->getDestTy();
|
|
if (RHSTy != LHSTy) {
|
|
revng_assert(RHSTy->isIntOrPtrTy() and LHSTy->isIntOrPtrTy());
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType DestTy = Declarator.getOrCreateType(LHSTy,
|
|
nullptr,
|
|
ASTCtx,
|
|
TUDecl);
|
|
QualType DestQualTy = DeclCreator::getQualType(DestTy);
|
|
|
|
CastKind CK;
|
|
switch (Cast->getOpcode()) {
|
|
case Instruction::Trunc:
|
|
case Instruction::ZExt:
|
|
case Instruction::SExt: {
|
|
revng_assert(not RHSTy->isPointerTy() and not LHSTy->isPointerTy());
|
|
/// CK_IntegralCast - A cast between integral types (other than to
|
|
/// boolean). Variously a bitcast, a truncation, a sign-extension,
|
|
/// or a zero-extension.
|
|
/// long l = 5;
|
|
/// (unsigned) i
|
|
/// CAST_OPERATION(IntegralCast)
|
|
CK = CastKind::CK_IntegralCast;
|
|
} break;
|
|
case Instruction::IntToPtr: {
|
|
revng_assert(not RHSTy->isPointerTy() and LHSTy->isPointerTy());
|
|
/// CK_IntegralToPointer - Integral to pointer. A special kind of
|
|
/// reinterpreting conversion. Applies to normal, ObjC, and block
|
|
/// pointers.
|
|
/// (char*) 0x1001aab0
|
|
/// reinterpret_cast<int*>(0)
|
|
/// CAST_OPERATION(IntegralToPointer)
|
|
QualType IntQualType = Result->getType();
|
|
const ClangType *PtrType = DestQualTy.getTypePtr();
|
|
revng_assert(PtrType->isPointerType());
|
|
uint64_t PtrSize = ASTCtx.getTypeSize(DestQualTy);
|
|
uint64_t IntegerSize = ASTCtx.getTypeSize(IntQualType);
|
|
revng_assert(PtrSize >= IntegerSize);
|
|
if (PtrSize > IntegerSize)
|
|
Result = createCast(ASTCtx.getUIntPtrType(), Result, ASTCtx);
|
|
CK = CastKind::CK_IntegralToPointer;
|
|
} break;
|
|
case Instruction::PtrToInt: {
|
|
revng_assert(RHSTy->isPointerTy() and not LHSTy->isPointerTy());
|
|
/// CK_PointerToIntegral - Pointer to integral. A special kind of
|
|
/// reinterpreting conversion. Applies to normal, ObjC, and block
|
|
/// pointers.
|
|
/// (intptr_t) "help!"
|
|
/// CAST_OPERATION(PointerToIntegral)
|
|
CK = CastKind::CK_PointerToIntegral;
|
|
} break;
|
|
case Instruction::BitCast: {
|
|
revng_assert(RHSTy->isPointerTy() and LHSTy->isPointerTy());
|
|
/// CK_BitCast - A conversion which causes a bit pattern of one type
|
|
/// to be reinterpreted as a bit pattern of another type. Generally
|
|
/// the operands must have equivalent size and unrelated types.
|
|
///
|
|
/// The pointer conversion char* -> int* is a bitcast. A conversion
|
|
/// from any pointer type to a C pointer type is a bitcast unless
|
|
/// it's actually BaseToDerived or DerivedToBase. A conversion to a
|
|
/// block pointer or ObjC pointer type is a bitcast only if the
|
|
/// operand has the same type kind; otherwise, it's one of the
|
|
/// specialized casts below.
|
|
///
|
|
/// Vector coercions are bitcasts.
|
|
/// CAST_OPERATION(BitCast)
|
|
CK = CastKind::CK_BitCast;
|
|
} break;
|
|
case Instruction::FPTrunc:
|
|
case Instruction::FPExt:
|
|
case Instruction::FPToUI:
|
|
case Instruction::FPToSI:
|
|
case Instruction::UIToFP:
|
|
case Instruction::SIToFP:
|
|
case Instruction::AddrSpaceCast:
|
|
case Instruction::CastOpsEnd:
|
|
default:
|
|
revng_abort();
|
|
}
|
|
|
|
TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(DestQualTy);
|
|
Result = CStyleCastExpr::Create(ASTCtx,
|
|
DestQualTy,
|
|
VK_RValue,
|
|
CK,
|
|
Result,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
}
|
|
revng_assert(Result);
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
if (ASTBuildLog.isEnabled())
|
|
Result->dump();
|
|
return Result;
|
|
}
|
|
revng_abort();
|
|
|
|
} else if (auto *Arg = dyn_cast<Argument>(V)) {
|
|
|
|
llvm::Function *ArgFun = Arg->getParent();
|
|
llvm::FunctionType *FType = ArgFun->getFunctionType();
|
|
revng_assert(not FType->isVarArg());
|
|
unsigned NumLLVMParams = FType->getNumParams();
|
|
unsigned ArgNo = Arg->getArgNo();
|
|
clang::FunctionDecl &FunDecl = Declarator.getFunctionDecl(ArgFun);
|
|
unsigned DeclNumParams = FunDecl.getNumParams();
|
|
revng_assert(NumLLVMParams == DeclNumParams);
|
|
clang::ParmVarDecl *ParamVDecl = FunDecl.getParamDecl(ArgNo);
|
|
QualType Type = ParamVDecl->getType();
|
|
DeclRefExpr *Res = new (ASTCtx)
|
|
DeclRefExpr(ASTCtx, ParamVDecl, false, Type, VK_LValue, {});
|
|
return Res;
|
|
|
|
} else {
|
|
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
Expr *StmtBuilder::getLiteralFromConstant(const llvm::Constant *C) {
|
|
if (auto *CD = dyn_cast<ConstantData>(C)) {
|
|
if (auto *CInt = dyn_cast<ConstantInt>(CD)) {
|
|
clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl();
|
|
TypeDeclOrQualType LiteralTy = Declarator.getOrCreateType(CInt->getType(),
|
|
nullptr,
|
|
ASTCtx,
|
|
TUDecl);
|
|
QualType LiteralQualTy = DeclCreator::getQualType(LiteralTy);
|
|
const clang::Type *UnderlyingTy = LiteralQualTy.getTypePtrOrNull();
|
|
revng_assert(UnderlyingTy != nullptr);
|
|
// Desugar stdint.h typedefs
|
|
UnderlyingTy = UnderlyingTy->getUnqualifiedDesugaredType();
|
|
const BuiltinType *BuiltinTy = cast<BuiltinType>(UnderlyingTy);
|
|
switch (BuiltinTy->getKind()) {
|
|
case BuiltinType::Bool: {
|
|
QualType IntT = ASTCtx.IntTy;
|
|
TypeDeclOrQualType
|
|
BoolTy = Declarator.getOrCreateBoolType(ASTCtx, C->getType());
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue, true);
|
|
Expr *IntLiteral = IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
return createCast(DeclCreator::getQualType(BoolTy), IntLiteral, ASTCtx);
|
|
}
|
|
case BuiltinType::Char_U:
|
|
case BuiltinType::Char_S:
|
|
case BuiltinType::UChar:
|
|
case BuiltinType::SChar: {
|
|
using CharKind = CharacterLiteral::CharacterKind;
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
return new (ASTCtx) CharacterLiteral(static_cast<unsigned>(ConstValue),
|
|
CharKind::Ascii,
|
|
ASTCtx.CharTy,
|
|
{});
|
|
}
|
|
case BuiltinType::UShort: {
|
|
QualType IntT = ASTCtx.UnsignedIntTy;
|
|
QualType ShortT = ASTCtx.UnsignedShortTy;
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue);
|
|
Expr *Literal = IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
return createCast(ShortT, Literal, ASTCtx);
|
|
}
|
|
case BuiltinType::Short: {
|
|
QualType IntT = ASTCtx.IntTy;
|
|
QualType ShortT = ASTCtx.ShortTy;
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue, true);
|
|
Expr *Literal = IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
return createCast(ShortT, Literal, ASTCtx);
|
|
}
|
|
case BuiltinType::UInt:
|
|
case BuiltinType::ULong:
|
|
case BuiltinType::ULongLong: {
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
APInt Const = APInt(ASTCtx.getIntWidth(LiteralQualTy), ConstValue);
|
|
return IntegerLiteral::Create(ASTCtx, Const, LiteralQualTy, {});
|
|
}
|
|
case BuiltinType::Int:
|
|
case BuiltinType::Long:
|
|
case BuiltinType::LongLong: {
|
|
uint64_t ConstValue = CInt->getValue().getZExtValue();
|
|
APInt Const = APInt(ASTCtx.getIntWidth(LiteralQualTy),
|
|
ConstValue,
|
|
true);
|
|
return IntegerLiteral::Create(ASTCtx, Const, LiteralQualTy, {});
|
|
}
|
|
case BuiltinType::UInt128: {
|
|
// With LLVM compiled in debug this asserts whenever ConstValue is
|
|
// larger than 64 bits.
|
|
// We don't use 128 instead of 64 because C hasn't 128 bits integer
|
|
// literals.
|
|
const APInt &OldConst = CInt->getValue();
|
|
unsigned Width = OldConst.getBitWidth();
|
|
|
|
// Check that we are not at the boundaries of the representable
|
|
// integers with 64 bit, and in case enforce a full check.
|
|
if (Width <= 64) {
|
|
uint64_t ConstValue = OldConst.getZExtValue();
|
|
APInt Const = APInt(64, ConstValue);
|
|
QualType T = ASTCtx.UnsignedLongLongTy;
|
|
return IntegerLiteral::Create(ASTCtx, Const, T, {});
|
|
} else {
|
|
uint64_t ConstValue = OldConst.getLimitedValue();
|
|
APInt Const = APInt(64, ConstValue);
|
|
|
|
// HACK: We actually have values which need 128 bits to be
|
|
// represented, so we disable temporarly the check and simply
|
|
// truncate the value to 64 bit.
|
|
// revng_assert(not Const.isMaxValue());
|
|
QualType T = ASTCtx.UnsignedLongLongTy;
|
|
return IntegerLiteral::Create(ASTCtx, Const, T, {});
|
|
}
|
|
}
|
|
case BuiltinType::Int128: {
|
|
// With LLVM compiled in debug this asserts whenever ConstValue is
|
|
// larger than 64 bits.
|
|
// We don't use 128 instead of 64 because C hasn't 128 bits integer
|
|
// literals.
|
|
const APInt &OldConst = CInt->getValue();
|
|
unsigned Width = OldConst.getBitWidth();
|
|
|
|
// Check that we are not at the boundaries of the representable
|
|
// integers with 64 bit, and in case enforce a full check.
|
|
if (Width <= 64) {
|
|
uint64_t ConstValue = OldConst.getZExtValue();
|
|
APInt Const = APInt(64, ConstValue);
|
|
QualType T = ASTCtx.UnsignedLongLongTy;
|
|
return IntegerLiteral::Create(ASTCtx, Const, T, {});
|
|
} else {
|
|
uint64_t ConstValue = OldConst.getLimitedValue();
|
|
APInt Const = APInt(64, ConstValue, true);
|
|
revng_assert(not Const.isMaxSignedValue()
|
|
and not Const.isMinSignedValue());
|
|
QualType T = ASTCtx.LongLongTy;
|
|
return IntegerLiteral::Create(ASTCtx, Const, T, {});
|
|
}
|
|
}
|
|
case BuiltinType::Dependent:
|
|
case BuiltinType::Overload:
|
|
case BuiltinType::BoundMember:
|
|
case BuiltinType::PseudoObject:
|
|
case BuiltinType::UnknownAny:
|
|
case BuiltinType::BuiltinFn:
|
|
case BuiltinType::ARCUnbridgedCast:
|
|
case BuiltinType::OMPArraySection:
|
|
case BuiltinType::Void:
|
|
case BuiltinType::WChar_U:
|
|
case BuiltinType::WChar_S:
|
|
case BuiltinType::Char8:
|
|
case BuiltinType::Char16:
|
|
case BuiltinType::Char32:
|
|
case BuiltinType::Accum:
|
|
case BuiltinType::ShortAccum:
|
|
case BuiltinType::LongAccum:
|
|
case BuiltinType::UAccum:
|
|
case BuiltinType::UShortAccum:
|
|
case BuiltinType::ULongAccum:
|
|
case BuiltinType::SatAccum:
|
|
case BuiltinType::SatShortAccum:
|
|
case BuiltinType::SatLongAccum:
|
|
case BuiltinType::SatUAccum:
|
|
case BuiltinType::SatUShortAccum:
|
|
case BuiltinType::SatULongAccum:
|
|
case BuiltinType::Fract:
|
|
case BuiltinType::ShortFract:
|
|
case BuiltinType::LongFract:
|
|
case BuiltinType::UFract:
|
|
case BuiltinType::UShortFract:
|
|
case BuiltinType::ULongFract:
|
|
case BuiltinType::SatFract:
|
|
case BuiltinType::SatShortFract:
|
|
case BuiltinType::SatLongFract:
|
|
case BuiltinType::SatUFract:
|
|
case BuiltinType::SatUShortFract:
|
|
case BuiltinType::SatULongFract:
|
|
case BuiltinType::Half:
|
|
case BuiltinType::Float:
|
|
case BuiltinType::Double:
|
|
case BuiltinType::LongDouble:
|
|
case BuiltinType::Float16:
|
|
case BuiltinType::Float128:
|
|
case BuiltinType::NullPtr:
|
|
case BuiltinType::ObjCId:
|
|
case BuiltinType::ObjCClass:
|
|
case BuiltinType::ObjCSel:
|
|
case BuiltinType::OCLSampler:
|
|
case BuiltinType::OCLEvent:
|
|
case BuiltinType::OCLClkEvent:
|
|
case BuiltinType::OCLQueue:
|
|
case BuiltinType::OCLReserveID:
|
|
case BuiltinType::OCLImage1dRO:
|
|
case BuiltinType::OCLImage1dWO:
|
|
case BuiltinType::OCLImage1dRW:
|
|
case BuiltinType::OCLImage1dArrayRO:
|
|
case BuiltinType::OCLImage1dArrayWO:
|
|
case BuiltinType::OCLImage1dArrayRW:
|
|
case BuiltinType::OCLImage1dBufferRO:
|
|
case BuiltinType::OCLImage1dBufferWO:
|
|
case BuiltinType::OCLImage1dBufferRW:
|
|
case BuiltinType::OCLImage2dRO:
|
|
case BuiltinType::OCLImage2dWO:
|
|
case BuiltinType::OCLImage2dRW:
|
|
case BuiltinType::OCLImage2dArrayRO:
|
|
case BuiltinType::OCLImage2dArrayWO:
|
|
case BuiltinType::OCLImage2dArrayRW:
|
|
case BuiltinType::OCLImage2dDepthRO:
|
|
case BuiltinType::OCLImage2dDepthWO:
|
|
case BuiltinType::OCLImage2dDepthRW:
|
|
case BuiltinType::OCLImage2dArrayDepthRO:
|
|
case BuiltinType::OCLImage2dArrayDepthWO:
|
|
case BuiltinType::OCLImage2dArrayDepthRW:
|
|
case BuiltinType::OCLImage2dMSAARO:
|
|
case BuiltinType::OCLImage2dMSAAWO:
|
|
case BuiltinType::OCLImage2dMSAARW:
|
|
case BuiltinType::OCLImage2dArrayMSAARO:
|
|
case BuiltinType::OCLImage2dArrayMSAAWO:
|
|
case BuiltinType::OCLImage2dArrayMSAARW:
|
|
case BuiltinType::OCLImage2dMSAADepthRO:
|
|
case BuiltinType::OCLImage2dMSAADepthWO:
|
|
case BuiltinType::OCLImage2dMSAADepthRW:
|
|
case BuiltinType::OCLImage2dArrayMSAADepthRO:
|
|
case BuiltinType::OCLImage2dArrayMSAADepthWO:
|
|
case BuiltinType::OCLImage2dArrayMSAADepthRW:
|
|
case BuiltinType::OCLImage3dRO:
|
|
case BuiltinType::OCLImage3dWO:
|
|
case BuiltinType::OCLImage3dRW:
|
|
case BuiltinType::OCLIntelSubgroupAVCImePayload:
|
|
case BuiltinType::OCLIntelSubgroupAVCMcePayload:
|
|
case BuiltinType::OCLIntelSubgroupAVCRefPayload:
|
|
case BuiltinType::OCLIntelSubgroupAVCSicPayload:
|
|
case BuiltinType::OCLIntelSubgroupAVCImeResult:
|
|
case BuiltinType::OCLIntelSubgroupAVCMceResult:
|
|
case BuiltinType::OCLIntelSubgroupAVCRefResult:
|
|
case BuiltinType::OCLIntelSubgroupAVCSicResult:
|
|
case BuiltinType::OCLIntelSubgroupAVCImeSingleRefStreamin:
|
|
case BuiltinType::OCLIntelSubgroupAVCImeDualRefStreamin:
|
|
case BuiltinType::OCLIntelSubgroupAVCImeResultSingleRefStreamout:
|
|
case BuiltinType::OCLIntelSubgroupAVCImeResultDualRefStreamout:
|
|
case BuiltinType::SveBool:
|
|
case BuiltinType::SveFloat16:
|
|
case BuiltinType::SveFloat32:
|
|
case BuiltinType::SveFloat64:
|
|
case BuiltinType::SveInt8:
|
|
case BuiltinType::SveInt16:
|
|
case BuiltinType::SveInt32:
|
|
case BuiltinType::SveInt64:
|
|
case BuiltinType::SveUint8:
|
|
case BuiltinType::SveUint16:
|
|
case BuiltinType::SveUint32:
|
|
case BuiltinType::SveUint64:
|
|
revng_abort();
|
|
}
|
|
} else if (isa<ConstantPointerNull>(CD)) {
|
|
QualType UIntPtr = ASTCtx.getUIntPtrType();
|
|
unsigned UIntPtrSize = static_cast<unsigned>(ASTCtx.getTypeSize(UIntPtr));
|
|
return IntegerLiteral::Create(ASTCtx,
|
|
APInt::getNullValue(UIntPtrSize),
|
|
UIntPtr,
|
|
{});
|
|
} else if (isa<UndefValue>(CD)) {
|
|
uint64_t ConstValue = 0;
|
|
APInt Const = APInt(64, ConstValue);
|
|
QualType IntT = ASTCtx.LongTy;
|
|
return IntegerLiteral::Create(ASTCtx, Const, IntT, {});
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
if (auto *CE = dyn_cast<llvm::ConstantExpr>(C)) {
|
|
Expr *Result = nullptr;
|
|
switch (CE->getOpcode()) {
|
|
case Instruction::Trunc:
|
|
case Instruction::ZExt:
|
|
case Instruction::SExt:
|
|
case Instruction::IntToPtr:
|
|
case Instruction::PtrToInt:
|
|
case Instruction::BitCast: {
|
|
Result = getExprForValue(CE->getOperand(0));
|
|
revng_log(ASTBuildLog, "GOT!");
|
|
revng_assert(Result);
|
|
if (ASTBuildLog.isEnabled())
|
|
Result->dump();
|
|
} break;
|
|
default:
|
|
revng_abort();
|
|
}
|
|
return Result;
|
|
}
|
|
revng_abort();
|
|
}
|
|
|
|
} // namespace IR2AST
|