mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
2077 lines
81 KiB
C++
2077 lines
81 KiB
C++
/// \brief DataFlow analysis to build the AST for a Function
|
|
|
|
//
|
|
// Copyright rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <compare>
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Analysis/ScalarEvolution.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 "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/Type.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/RecordLayout.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 ClangPointerType = clang::PointerType;
|
|
using LLVMType = llvm::Type;
|
|
using LLVMPointerType = llvm::PointerType;
|
|
using TypeDeclOrQualType = DeclCreator::TypeDeclOrQualType;
|
|
|
|
namespace IR2AST {
|
|
|
|
Expr *StmtBuilder::getParenthesizedExprForValue(const 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));
|
|
|
|
// 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 (Stmt *PointerArithmeticStmt = buildPointerArithmeticExpr(I))
|
|
return PointerArithmeticStmt;
|
|
|
|
// If we were not able to emit pointer arithmetic as a nice access to a
|
|
// field struct fallback to normal emission.
|
|
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();
|
|
|
|
Expr *ReturnedExpr = nullptr;
|
|
|
|
if (auto *ConstRet = dyn_cast_or_null<ConstantStruct>(RetVal)) {
|
|
revng_assert(not VarDecls.count(ConstRet));
|
|
|
|
// Create the VarDecl for the local variable
|
|
llvm::Function *TheFunction = Ret->getFunction();
|
|
clang::FunctionDecl &FDecl = Declarator.getFunctionDecl(TheFunction);
|
|
VarDecl *NewVarDecl = createVarDecl(ConstRet, TheFunction, FDecl);
|
|
VarDecls[ConstRet] = NewVarDecl;
|
|
|
|
// Create the inizializer
|
|
llvm::SmallVector<clang::Expr *, 8> Initializers;
|
|
for (llvm::Value *V : ConstRet->operands())
|
|
Initializers.push_back(getLiteralFromConstant(cast<llvm::Constant>(V)));
|
|
clang::Expr *InitExpr = new (ASTCtx)
|
|
clang::InitListExpr(ASTCtx, {}, Initializers, {});
|
|
NewVarDecl->setInit(InitExpr);
|
|
|
|
ReturnedExpr = getExprForValue(ConstRet);
|
|
|
|
} else if (auto *Zero = dyn_cast_or_null<ConstantAggregateZero>(RetVal)) {
|
|
revng_assert(not VarDecls.count(Zero));
|
|
|
|
// Create the VarDecl for the local variable
|
|
llvm::Function *TheFunction = Ret->getFunction();
|
|
clang::FunctionDecl &FDecl = Declarator.getFunctionDecl(TheFunction);
|
|
VarDecl *NewVarDecl = createVarDecl(Zero, TheFunction, FDecl);
|
|
VarDecls[Zero] = NewVarDecl;
|
|
|
|
// Create the inizializer
|
|
uint64_t ConstValue = 0;
|
|
QualType IntT = ASTCtx.IntTy;
|
|
APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue);
|
|
clang::Expr *ZeroLiteral = IntegerLiteral::Create(ASTCtx,
|
|
Const,
|
|
IntT,
|
|
{});
|
|
clang::Expr *ZeroInit = new (ASTCtx)
|
|
clang::InitListExpr(ASTCtx, {}, { ZeroLiteral }, {});
|
|
NewVarDecl->setInit(ZeroInit);
|
|
|
|
ReturnedExpr = getExprForValue(Zero);
|
|
|
|
} 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 clang::Type *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);
|
|
QualType TernaryTy = ASTType;
|
|
QualType TrueTy = TrueExpr->getType();
|
|
QualType FalseTy = FalseExpr->getType();
|
|
|
|
if (ASTType.getTypePtr()->isPointerType()
|
|
and not TrueExpr->getType()->isPointerType()
|
|
and not FalseExpr->getType()->isPointerType()) {
|
|
int Cmp = ASTCtx.getIntegerTypeOrder(TrueTy, FalseTy);
|
|
TernaryTy = (Cmp > 0) ? TrueTy : FalseTy;
|
|
}
|
|
|
|
clang::Expr *Ternary = new (ASTCtx) ConditionalOperator(Cond,
|
|
{},
|
|
TrueExpr,
|
|
{},
|
|
FalseExpr,
|
|
TernaryTy,
|
|
VK_RValue,
|
|
OK_Ordinary);
|
|
|
|
if (ASTType.getTypePtr()->isPointerType()
|
|
and not TrueExpr->getType()->isPointerType()
|
|
and not FalseExpr->getType()->isPointerType()) {
|
|
TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(ASTType);
|
|
Ternary = CStyleCastExpr::Create(ASTCtx,
|
|
ASTType,
|
|
VK_RValue,
|
|
CastKind::CK_IntegralToPointer,
|
|
Ternary,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
}
|
|
return Ternary;
|
|
}
|
|
case Instruction::Call: {
|
|
auto *TheCall = cast<CallInst>(&I);
|
|
|
|
// Skip llvm.assume() instrinsics
|
|
if (TheCall->getIntrinsicID() == llvm::Intrinsic::assume)
|
|
return nullptr;
|
|
|
|
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;
|
|
}
|
|
|
|
clang::Expr *StmtBuilder::getMemberAccessExpr(clang::Expr *BaseExpr,
|
|
const LayoutChildInfo &ChildInfo,
|
|
bool IsArrow) {
|
|
const auto &[Parent, ChildId] = ChildInfo;
|
|
|
|
clang::Expr *Result = nullptr;
|
|
|
|
switch (Parent->getKind()) {
|
|
|
|
case dla::Layout::LayoutKind::Base: {
|
|
revng_assert(not ChildId);
|
|
revng_abort("unexpected dla::Layout");
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Array: {
|
|
llvm::Optional<TypeDeclOrQualType> Opt = Declarator.lookupType(Parent);
|
|
revng_assert(Opt.hasValue());
|
|
clang::QualType ArrayQTy = DeclCreator::getQualType(Opt.getValue());
|
|
const clang::Type *ArrayTy = ArrayQTy.getTypePtr();
|
|
clang::QualType ElemTy = ArrayTy->getAsArrayTypeUnsafe()->getElementType();
|
|
|
|
// Compute expression for index in the array
|
|
llvm::APInt Idx(ASTCtx.getTypeSize(ASTCtx.IntTy), ChildId);
|
|
clang::Expr *ArrayIndex = new (ASTCtx)
|
|
clang::IntegerLiteral(ASTCtx, Idx, ASTCtx.IntTy, {});
|
|
|
|
Result = new (ASTCtx) clang::ArraySubscriptExpr(BaseExpr,
|
|
ArrayIndex,
|
|
ElemTy,
|
|
VK_LValue,
|
|
OK_Ordinary,
|
|
{});
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Struct:
|
|
case dla::Layout::LayoutKind::Union: {
|
|
clang::TypeDecl *Decl = Declarator.lookupTypeDeclOrNull(Parent);
|
|
auto *RecDecl = cast<clang::RecordDecl>(Decl);
|
|
const clang::ASTRecordLayout &RLayout = ASTCtx.getASTRecordLayout(RecDecl);
|
|
revng_assert(ChildId < RLayout.getFieldCount());
|
|
clang::FieldDecl *Field = *std::next(RecDecl->field_begin(), ChildId);
|
|
revng_assert(Field);
|
|
|
|
bool IsArrow = true;
|
|
Result = clang::MemberExpr::CreateImplicit(ASTCtx,
|
|
BaseExpr,
|
|
IsArrow,
|
|
Field,
|
|
Field->getType(),
|
|
VK_LValue,
|
|
OK_Ordinary);
|
|
} break;
|
|
|
|
case dla::Layout::LayoutKind::Padding:
|
|
default:
|
|
revng_abort("unexpected dla::Layout");
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
clang::Expr *StmtBuilder::buildPointerArithmeticExpr(llvm::Instruction &I) {
|
|
|
|
// If we have no ValueLayouts, the DLA did not run, so we do nothing.
|
|
if (not ValueLayouts)
|
|
return nullptr;
|
|
|
|
revng_assert(SE);
|
|
|
|
// If I is not SCEVable, we can't work with SCEVs, so we can't to anything.
|
|
if (not SE->isSCEVable(I.getType()))
|
|
return nullptr;
|
|
|
|
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.
|
|
if (Bases.size() > 1)
|
|
return nullptr;
|
|
|
|
// 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. Otherwise, the first base is considered the address.
|
|
const SCEV *Base = Bases.empty() ? ISCEV : *Bases.begin();
|
|
revng_assert(Base);
|
|
|
|
// We assume that if we find a Base SCEV, its is a SCEVUnknown, and we can get
|
|
// its Value, which is the associated base address in the LLMV IR.
|
|
// If it's not, we can't do anything for now.
|
|
// However, this is a potential spot to detect loop induction variables in the
|
|
// future.
|
|
if (not isa<llvm::SCEVUnknown>(Base))
|
|
return nullptr;
|
|
|
|
const auto *BaseValue = cast<llvm::SCEVUnknown>(Base)->getValue();
|
|
|
|
// Unwrap calls to revng_scev_barrier_*
|
|
// TODO: 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>(BaseValue)) {
|
|
if (Call->getType()->isIntOrPtrTy()) {
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to obtain the DLA type of BaseValue
|
|
auto *TUDecl = ASTCtx.getTranslationUnitDecl();
|
|
llvm::Optional<TypeDeclOrQualType>
|
|
DLAType = Declarator.getOrCreateDLAType(BaseValue, ASTCtx, *TUDecl);
|
|
|
|
// If we can't obtain the DLA type of BaseValue, we have nothing to work on to
|
|
// properly emit the address arithmetic expression, so we bail out.
|
|
if (not DLAType.hasValue())
|
|
return nullptr;
|
|
|
|
QualType DLAQualTy = DeclCreator::getQualType(DLAType.getValue());
|
|
// We only accept DLA Types that are pointers to structs.
|
|
if (not DLAQualTy.getTypePtr()->isPointerType())
|
|
return nullptr;
|
|
|
|
auto BasePointedLayouts = Declarator.getPointedLayouts(BaseValue);
|
|
// TODO: This assertion is eventually bound to fail whenever BaseValue has a
|
|
// struct type. For now we don't handle that case, but we will need to do it.
|
|
// This is just a hard reminder that we have to handle that case.
|
|
revng_assert(BasePointedLayouts.size() == 1);
|
|
const dla::Layout *BasePointedLayout = BasePointedLayouts.front();
|
|
revng_assert(BasePointedLayout);
|
|
|
|
// Compute SCEV for (- Base), and make sure (- Base) has the same size as
|
|
// ISCEV.
|
|
const SCEV *MinusBase = nullptr;
|
|
auto BaseSize = SE->getTypeSizeInBits(Base->getType());
|
|
auto ISCEVSize = SE->getTypeSizeInBits(ISCEV->getType());
|
|
std::strong_ordering Cmp = BaseSize <=> ISCEVSize;
|
|
if (Cmp < 0) {
|
|
|
|
// If Base is narrower, zero extend it and negate it.
|
|
// Leave ISCEV like it is.
|
|
const SCEV *ExtBase = SE->getZeroExtendExpr(Base, ISCEV->getType());
|
|
MinusBase = SE->getNegativeSCEV(ExtBase);
|
|
|
|
} else if (Cmp > 0) {
|
|
|
|
// If Base is wider, just negate it, and zero extend ISCEV.
|
|
MinusBase = SE->getNegativeSCEV(Base);
|
|
ISCEV = SE->getZeroExtendExpr(ISCEV, Base->getType());
|
|
|
|
} else { // Otherwise just negate Base.
|
|
MinusBase = SE->getNegativeSCEV(Base);
|
|
}
|
|
revng_assert(MinusBase);
|
|
|
|
// Off = I - Base
|
|
const SCEV *Off = SE->getAddExpr(ISCEV, MinusBase);
|
|
|
|
// TODO: this is a stub for now. Doing nothing it triggers the early exit path
|
|
// below, resulting in never being able to build pointer arithmetic expression
|
|
// in the form of a member access.
|
|
// This is equivalent to basically throwing away almost all DLA information.
|
|
// Eventually we need to replace this code with something that really builds
|
|
// member access expressions.
|
|
const auto getNestedFieldIds =
|
|
[](const SCEV *Off,
|
|
const dla::Layout *P,
|
|
llvm::ScalarEvolution *SE,
|
|
clang::ASTContext &) -> llvm::SmallVector<LayoutChildInfo, 8> {
|
|
return {};
|
|
};
|
|
|
|
llvm::SmallVector<LayoutChildInfo, 8>
|
|
NestedFields = getNestedFieldIds(Off, BasePointedLayout, SE, ASTCtx);
|
|
if (NestedFields.empty())
|
|
return nullptr;
|
|
|
|
clang::Expr *Result = getExprForValue(BaseValue);
|
|
|
|
// The first MemberExpr always has an arrow (BaseValue->field1) because
|
|
// BaseValue is a pointer.
|
|
Result = getMemberAccessExpr(Result,
|
|
NestedFields.front(),
|
|
/* IsArrow */ true);
|
|
if (Result == nullptr)
|
|
return nullptr;
|
|
|
|
// Create all the field past the first, if any.
|
|
// All the subsequent, if present, have a dot (field1.field2).
|
|
for (const LayoutChildInfo &ChildInfo : llvm::drop_begin(NestedFields, 1)) {
|
|
Result = getMemberAccessExpr(Result, ChildInfo, /* IsArrow */ false);
|
|
if (Result == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
// Wrap all the pointer arithmetic inside an AddrOf expression, to prevent the
|
|
// computed expression to have side effects.
|
|
// In this way we obtain &BaseValue->field1.field2.fieldn;
|
|
clang::QualType AddressType = ASTCtx.getPointerType(Result->getType());
|
|
Result = new (ASTCtx) clang::UnaryOperator(Result,
|
|
UnaryOperatorKind::UO_AddrOf,
|
|
AddressType,
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
false);
|
|
return Result;
|
|
}
|
|
|
|
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_*`
|
|
// TODO: 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 = 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 if (auto *ConstStruct = dyn_cast<llvm::ConstantStruct>(C)) {
|
|
ASTType = Declarator.getOrCreateType(ConstStruct->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 std::pair<Expr *, Expr *> getCastedBinaryOperands(ASTContext &ASTCtx,
|
|
const Instruction &I,
|
|
Expr *LHS,
|
|
Expr *RHS) {
|
|
|
|
const clang::Type *LHSTy = LHS->getType().getTypePtr();
|
|
const clang::Type *RHSTy = RHS->getType().getTypePtr();
|
|
revng_assert((LHSTy->isPointerType() or LHSTy->isIntegerType())
|
|
and (RHSTy->isPointerType() or RHSTy->isIntegerType()));
|
|
|
|
unsigned OpCode = I.getOpcode();
|
|
|
|
if (LHSTy->isPointerType() or RHSTy->isPointerType()) {
|
|
|
|
switch (OpCode) {
|
|
case Instruction::ICmp: {
|
|
// If it's an equality comparison and only one of the operands is a
|
|
// pointer, promote them both to pointer.
|
|
if (LHSTy->isPointerType() and not RHSTy->isPointerType()) {
|
|
RHS = ImplicitCastExpr::Create(ASTCtx,
|
|
LHS->getType(),
|
|
CastKind::CK_IntegralToPointer,
|
|
RHS,
|
|
nullptr,
|
|
VK_RValue);
|
|
RHSTy = LHSTy;
|
|
} else if (not LHSTy->isPointerType() and RHSTy->isPointerType()) {
|
|
LHS = ImplicitCastExpr::Create(ASTCtx,
|
|
RHS->getType(),
|
|
CastKind::CK_IntegralToPointer,
|
|
LHS,
|
|
nullptr,
|
|
VK_RValue);
|
|
LHSTy = RHSTy;
|
|
}
|
|
} break;
|
|
|
|
case Instruction::Add: {
|
|
// If it's an additive expression (see C99 standard 6.5.6), we can leave
|
|
// them like they are. The pointer remains a pointer, and the integer
|
|
// represents an offset.
|
|
// However, in case of sums, only one of the operands can be of pointer
|
|
// type.
|
|
if (LHSTy->isPointerType() and RHSTy->isPointerType()) {
|
|
|
|
QualType IntPtrTy = ASTCtx.getUIntPtrType();
|
|
TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(IntPtrTy);
|
|
LHS = CStyleCastExpr::Create(ASTCtx,
|
|
IntPtrTy,
|
|
VK_RValue,
|
|
CK_PointerToIntegral,
|
|
LHS,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
LHSTy = IntPtrTy.getTypePtr();
|
|
|
|
RHS = CStyleCastExpr::Create(ASTCtx,
|
|
IntPtrTy,
|
|
VK_RValue,
|
|
CK_PointerToIntegral,
|
|
RHS,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
RHSTy = IntPtrTy.getTypePtr();
|
|
}
|
|
} break;
|
|
|
|
case Instruction::Sub: {
|
|
// If it's an additive expression (see C99 standard 6.5.6), we can leave
|
|
// them like they are. The pointer remains a pointer, and the integer
|
|
// represents an offset.
|
|
break;
|
|
}
|
|
|
|
case Instruction::AShr:
|
|
case Instruction::LShr:
|
|
case Instruction::Shl:
|
|
case Instruction::SDiv:
|
|
case Instruction::UDiv:
|
|
case Instruction::SRem:
|
|
case Instruction::URem:
|
|
case Instruction::Mul:
|
|
case Instruction::And:
|
|
case Instruction::Or:
|
|
case Instruction::Xor: {
|
|
|
|
// This is supposed to never happen, but it does,
|
|
// due to DLA, so we have to handle it.
|
|
|
|
QualType IntPtrTy = ASTCtx.getUIntPtrType();
|
|
TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(IntPtrTy);
|
|
|
|
if (LHSTy->isPointerType()) {
|
|
LHS = CStyleCastExpr::Create(ASTCtx,
|
|
IntPtrTy,
|
|
VK_RValue,
|
|
CK_PointerToIntegral,
|
|
LHS,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
LHSTy = IntPtrTy.getTypePtr();
|
|
}
|
|
|
|
if (RHSTy->isPointerType()) {
|
|
RHS = CStyleCastExpr::Create(ASTCtx,
|
|
IntPtrTy,
|
|
VK_RValue,
|
|
CK_PointerToIntegral,
|
|
RHS,
|
|
nullptr,
|
|
TI,
|
|
{},
|
|
{});
|
|
RHSTy = IntPtrTy.getTypePtr();
|
|
}
|
|
|
|
} break;
|
|
|
|
default:
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
uint64_t LHSSize = ASTCtx.getTypeSize(LHSTy);
|
|
uint64_t RHSSize = ASTCtx.getTypeSize(RHSTy);
|
|
uint64_t MaxSize = std::max(LHSSize, RHSSize);
|
|
unsigned Size = static_cast<unsigned>(MaxSize);
|
|
|
|
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());
|
|
unsigned OpCode = I.getOpcode();
|
|
clang::QualType ResTy = (OpCode == Instruction::ICmp) ? ASTCtx.BoolTy :
|
|
LHS->getType();
|
|
Expr *Res = new (ASTCtx) clang::BinaryOperator(LHS,
|
|
RHS,
|
|
BinOpKind,
|
|
ResTy,
|
|
VK_RValue,
|
|
OK_Ordinary,
|
|
{},
|
|
FPOptions());
|
|
|
|
switch (OpCode) {
|
|
case Instruction::SDiv:
|
|
case Instruction::SRem:
|
|
case Instruction::AShr: {
|
|
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(const 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)
|
|
or isa<llvm::ConstantStruct>(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);
|
|
|
|
const 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 {
|
|
const Value *Stored = Store->getValueOperand();
|
|
PointeeType = Declarator.getOrCreateType(Stored, ASTCtx, TUDecl);
|
|
}
|
|
|
|
QualType PointeeQualType = DeclCreator::getQualType(PointeeType);
|
|
|
|
QualAddrType = AddrExpr->getType();
|
|
const clang::Type *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 clang::Type *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)) {
|
|
|
|
const 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
|