/// \brief DataFlow analysis to build the AST for a Function // // This file is distributed under the MIT License. See LICENSE.md for details. // // LLVM includes #include // clang includes #include #include #include #include #include #include // revng includes #include // local includes #include "ASTBuildAnalysis.h" #include "DecompilationHelpers.h" #include "IRASTTypeTranslation.h" #include "Mangling.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 namespace IRASTTypeTranslation; namespace IR2AST { static bool needsLabel(const BasicBlock &) { return false; } Expr *StmtBuilder::getParenthesizedExprForValue(Value *V) { Expr *Res = getExprForValue(V); if (isa(Res) or isa(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(&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 new (ASTCtx) IfStmt(ASTCtx, {}, false, nullptr, nullptr, Cond, GoToThen, {}, GoToElse); } } case Instruction::Ret: { // FIXME: handle returned values properly ReturnInst *Ret = cast(&I); Value *RetVal = Ret->getReturnValue(); Expr *ReturnedExpr = RetVal ? getExprForValue(RetVal) : nullptr; return new (ASTCtx) ReturnStmt({}, ReturnedExpr, nullptr); } case Instruction::Switch: { revng_abort("switch instructions are not supported yet"); auto *Switch = cast(&I); Value *Cond = Switch->getCondition(); Expr *CondE = getExprForValue(Cond); SwitchStmt *S = new (ASTCtx) SwitchStmt(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 = new (ASTCtx) CaseStmt(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: { VarDecl *ArrayDecl = AllocaDecls.at(cast(&I)); QualType ArrayTy = ArrayDecl->getType(); // Create an Expr for the address of the first element of the array. QualType CharPtrTy = ASTCtx.getPointerType(ASTCtx.CharTy); Expr *ArrayDeclRef = new (ASTCtx) DeclRefExpr(ArrayDecl, false, ArrayTy, VK_LValue, {}); CastKind Kind = CastKind::CK_ArrayToPointerDecay; Expr *ArrayPtrDecay = ImplicitCastExpr::Create(ASTCtx, CharPtrTy, Kind, ArrayDeclRef, 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(&I); Value *Addr = Load->getPointerOperand(); Expr *AddrExpr = getParenthesizedExprForValue(Addr); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and AddrExpr) AddrExpr->dump(); if (not isa(Addr)) { clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType PointeeType = getOrCreateQualType(Load, ASTCtx, TUDecl, TypeDecls, FieldDecls); 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(Addr)) { QualType QualPtrTy = AddrExpr->getType(); const auto *PtrType = cast(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(&I); Value *Stored = Store->getValueOperand(); if (isa(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(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(); QualType LHSQualType = getOrCreateQualType(&I, ASTCtx, TUDecl, TypeDecls, FieldDecls); 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(); QualType ASTType = getOrCreateQualType(&I, ASTCtx, TUDecl, TypeDecls, FieldDecls); return new (ASTCtx) ConditionalOperator(Cond, {}, TrueExpr, {}, FalseExpr, ASTType, VK_RValue, OK_Ordinary); } case Instruction::Call: { auto *TheCall = cast(&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 = FunctionDecls.at(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); revng_assert(NumArgs == NumOps); auto Args = SmallVector(NumOps, nullptr); 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; } } clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType ReturnType = getOrCreateQualType(TheCall, ASTCtx, TUDecl, TypeDecls, FieldDecls); return new (ASTCtx) CallExpr(ASTCtx, CalleeExpr, Args, ReturnType, VK_RValue, {}); } case Instruction::Unreachable: { Function *AbortFun = F.getParent()->getFunction("abort"); Expr *CalleeExpr = getExprForValue(AbortFun); SmallVector Args; QualType ReturnType = ASTCtx.VoidTy; return new (ASTCtx) CallExpr(ASTCtx, CalleeExpr, Args, ReturnType, VK_RValue, {}); } // // ---- Instructions for struct manipulation ---- // case Instruction::InsertValue: { InsertValueInst *Insert = cast(&I); revng_assert(Insert->getNumIndices() == 1); Value *AggregateOp = Insert->getAggregateOperand(); revng_assert(isa(AggregateOp) or isa(AggregateOp)); llvm::Type *AggregateTy = AggregateOp->getType(); clang::TypeDecl *StructTypeDecl = TypeDecls.at(AggregateTy); Expr *StructExpr = getExprForValue(Insert); clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType InsertedTy = getOrCreateQualType(Insert, ASTCtx, TUDecl, TypeDecls, FieldDecls); unsigned Idx = *Insert->idx_begin(); FieldDecl *InsertedFieldDecl = FieldDecls.at(StructTypeDecl)[Idx]; clang::DeclarationName FieldDeclName = InsertedFieldDecl->getIdentifier(); clang::DeclarationNameInfo FieldDeclNameInfo(FieldDeclName, {}); clang::Expr *LHS = new (ASTCtx) MemberExpr(StructExpr, /*isarrow*/ false, {}, InsertedFieldDecl, FieldDeclNameInfo, InsertedTy, VK_LValue, OK_Ordinary); clang::Expr *RHS = getExprForValue(Insert->getInsertedValueOperand()); BinaryOperatorKind BinOpKind = BinaryOperatorKind::BO_Assign; AdditionalStmts[&I].push_back(new (ASTCtx) clang::BinaryOperator(LHS, RHS, BinOpKind, LHS->getType(), VK_RValue, OK_Ordinary, {}, FPOptions())); if (isa(AggregateOp)) return nullptr; return getExprForValue(AggregateOp); } case Instruction::ExtractValue: { ExtractValueInst *Extract = cast(&I); revng_assert(Extract->getNumIndices() == 1); Value *AggregateOp = Extract->getAggregateOperand(); if (isa(AggregateOp)) return nullptr; revng_assert(isa(AggregateOp)); llvm::Type *AggregateTy = AggregateOp->getType(); clang::TypeDecl *StructTypeDecl = TypeDecls.at(AggregateTy); Expr *StructExpr = getExprForValue(AggregateOp); clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType ExtractedTy = getOrCreateQualType(Extract, ASTCtx, TUDecl, TypeDecls, FieldDecls); unsigned Idx = *Extract->idx_begin(); FieldDecl *ExtractedFieldDecl = FieldDecls.at(StructTypeDecl)[Idx]; clang::DeclarationName FieldDeclName = ExtractedFieldDecl->getIdentifier(); clang::DeclarationNameInfo FieldDeclNameInfo(FieldDeclName, {}); return new (ASTCtx) MemberExpr(StructExpr, /*isarrow*/ false, {}, ExtractedFieldDecl, FieldDeclNameInfo, ExtractedTy, VK_RValue, OK_Ordinary); } // ---- 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"); } static bool isPure(const Instruction & /*Call*/) { return false; } void StmtBuilder::createAST() { revng_log(ASTBuildLog, "Building AST for Instructions in Function " << F.getName()); uint64_t BBId = 0; { IdentifierInfo &Id = ASTCtx.Idents.get("loop_state_var"); LoopStateVarDecl = VarDecl::Create(ASTCtx, &FDecl, {}, {}, &Id, ASTCtx.UnsignedIntTy, nullptr, StorageClass::SC_None); FDecl.addDecl(LoopStateVarDecl); } ReversePostOrderTraversal 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) { // We don't build clang's AST expressions for PHINodes nor for // BranchInsts. // 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. // 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(&I)) continue; // Each PHINode has an associated VarDecl if (isa(&I)) { revng_assert(VarDecls.count(&I) == 0); VarDecl *NewVarDecl = createVarDecl(&I); VarDecls[&I] = NewVarDecl; continue; } if (isa(&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. AllocaInst *Alloca = cast(&I); revng_assert(Alloca->isStaticAlloca()); // First, create a VarDecl, for an array of char to place in the // BasicBlock where the AllocaInst is const DataLayout &DL = F.getParent()->getDataLayout(); uint64_t AllocaSize = *Alloca->getAllocationSizeInBits(DL); revng_assert(AllocaSize <= std::numeric_limits::max()); APInt ArraySize = APInt(32, static_cast(AllocaSize)); using ArraySizeMod = clang::ArrayType::ArraySizeModifier; ArraySizeMod SizeMod = ArraySizeMod::Normal; QualType CharTy = ASTCtx.CharTy; QualType ArrayTy = ASTCtx.getConstantArrayType(CharTy, ArraySize, SizeMod, 0); const std::string VarName = "var_" + std::to_string(NVar++); IdentifierInfo &Id = ASTCtx.Idents.get(VarName); VarDecl *ArrayDecl = VarDecl::Create(ASTCtx, &FDecl, {}, {}, &Id, ArrayTy, nullptr, StorageClass::SC_None); FDecl.addDecl(ArrayDecl); AllocaDecls[Alloca] = ArrayDecl; } if (isa(&I) or isa(&I)) { revng_assert(VarDecls.count(&I) == 0); VarDecl *NewVarDecl = createVarDecl(&I); VarDecls[&I] = NewVarDecl; } Stmt *NewStmt = buildStmt(I); if (NewStmt == nullptr) continue; InstrStmts[&I] = NewStmt; if (not isa(&I) and not isa(&I) and I.getNumUses() > 0 and ToSerialize.count(&I)) { revng_assert(VarDecls.count(&I) == 0); VarDecl *NewVarDecl = createVarDecl(&I); VarDecls[&I] = NewVarDecl; } } } } VarDecl *StmtBuilder::createVarDecl(Instruction *I) { clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType ASTType = getOrCreateQualType(I, ASTCtx, TUDecl, TypeDecls, FieldDecls); revng_assert(not ASTType.isNull()); const std::string VarName = "var_" + std::to_string(NVar++); IdentifierInfo &Id = ASTCtx.Idents.get(VarName); VarDecl *NewVarDecl = VarDecl::Create(ASTCtx, &FDecl, {}, {}, &Id, ASTType, nullptr, StorageClass::SC_None); FDecl.addDecl(NewVarDecl); return NewVarDecl; } static clang::BinaryOperatorKind getClangBinaryOpKind(const Instruction &I) { 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; } case Instruction::And: { Res = clang::BinaryOperatorKind::BO_And; } break; case Instruction::Or: { Res = clang::BinaryOperatorKind::BO_Or; } break; case Instruction::Xor: { Res = clang::BinaryOperatorKind::BO_Xor; } break; case Instruction::ICmp: { auto *CompareI = cast(&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; default: 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 getCastedBinaryOperands(ASTContext &ASTCtx, const Instruction &I, Expr *LHS, Expr *RHS) { std::pair Res = std::make_pair(LHS, 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)); uint64_t Size = std::max(LHSSize, RHSSize); QualType SignedTy = ASTCtx.getIntTypeForBitwidth(Size, /* Signed */ true); switch (OpCode) { case Instruction::Add: case Instruction::Sub: case Instruction::Mul: case Instruction::And: case Instruction::Or: case Instruction::Xor: case Instruction::UDiv: case Instruction::URem: case Instruction::Shl: case Instruction::LShr: { // 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. } break; case Instruction::SDiv: case Instruction::SRem: case Instruction::AShr: case Instruction::ICmp: { if (OpCode != Instruction::ICmp or cast(&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); BinaryOperatorKind BinOpKind = getClangBinaryOpKind(I); 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); 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(); QualType ResType = getOrCreateQualType(&I, ASTCtx, TUDecl, TypeDecls, FieldDecls); Res = new (ASTCtx) ParenExpr({}, {}, Res); Res = createCast(ResType, Res, ASTCtx); } break; default: break; } return Res; } 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 (isa(V) or isa(V)) { return getLiteralFromConstant(cast(V)); } else if (auto *F = dyn_cast(V)) { FunctionDecl *FDecl = FunctionDecls.at(F); QualType Type = FDecl->getType(); DeclRefExpr *Res = new (ASTCtx) DeclRefExpr(FDecl, false, Type, VK_LValue, {}); return Res; } else if (auto *G = dyn_cast(V)) { VarDecl *GlobalVarDecl = GlobalDecls.at(G); QualType Type = GlobalVarDecl->getType(); DeclRefExpr *Res = new (ASTCtx) DeclRefExpr(GlobalVarDecl, false, Type, VK_LValue, {}); return Res; } else if (auto *I = dyn_cast(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(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(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(I) or isa(I)) { // Load and Store Instruction are serialized as ExprLHS = ExprRHS. // getExprForValue returns the ExprLHS. auto *Store = dyn_cast(I); auto *Load = dyn_cast(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(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(); QualType PointeeType; if (Load) { PointeeType = getOrCreateQualType(Load, ASTCtx, TUDecl, TypeDecls, FieldDecls); } else { Value *Stored = Store->getValueOperand(); PointeeType = getOrCreateQualType(Stored, ASTCtx, TUDecl, TypeDecls, FieldDecls); } 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(Addr)) { QualType QualPtrTy = AddrExpr->getType(); const auto *PtrType = cast(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); } if (auto *Cast = dyn_cast(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(); QualType DestTy = getOrCreateQualType(LHSTy, ASTCtx, TUDecl, TypeDecls, FieldDecls); 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(0) /// CAST_OPERATION(IntegralToPointer) QualType IntQualType = Result->getType(); const ClangType *PtrType = DestTy.getTypePtr(); revng_assert(PtrType->isPointerType()); uint64_t PtrSize = ASTCtx.getTypeSize(DestTy); 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: default: revng_abort(); } TypeSourceInfo *TI = ASTCtx.CreateTypeSourceInfo(DestTy); Result = CStyleCastExpr::Create(ASTCtx, DestTy, 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(V)) { llvm::Function *F = Arg->getParent(); llvm::FunctionType *FType = F->getFunctionType(); revng_assert(not FType->isVarArg()); unsigned NumLLVMParams = FType->getNumParams(); unsigned ArgNo = Arg->getArgNo(); clang::FunctionDecl *FDecl = FunctionDecls.at(F); unsigned DeclNumParams = FDecl->getNumParams(); revng_assert(NumLLVMParams == DeclNumParams); clang::ParmVarDecl *ParamVDecl = FDecl->getParamDecl(ArgNo); QualType Type = ParamVDecl->getType(); DeclRefExpr *Res = new (ASTCtx) DeclRefExpr(ParamVDecl, false, Type, VK_LValue, {}); return Res; } else { revng_abort(); } } Expr *StmtBuilder::getLiteralFromConstant(Constant *C) { if (auto *CD = dyn_cast(C)) { if (auto *CInt = dyn_cast(CD)) { clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); QualType LiteralTy = getOrCreateQualType(CInt, ASTCtx, TUDecl, TypeDecls, FieldDecls); const clang::Type *UnderlyingTy = LiteralTy.getTypePtrOrNull(); revng_assert(UnderlyingTy != nullptr); // Desugar stdint.h typedefs UnderlyingTy = UnderlyingTy->getUnqualifiedDesugaredType(); const BuiltinType *BuiltinTy = cast(UnderlyingTy); uint64_t ConstValue = CInt->getValue().getZExtValue(); switch (BuiltinTy->getKind()) { case BuiltinType::Bool: { QualType IntT = ASTCtx.IntTy; QualType BoolTy = getOrCreateBoolQualType(C->getType(), ASTCtx, TypeDecls); APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue, true); Expr *IntLiteral = IntegerLiteral::Create(ASTCtx, Const, IntT, {}); return createCast(BoolTy, IntLiteral, ASTCtx); } case BuiltinType::Char_U: case BuiltinType::Char_S: case BuiltinType::UChar: case BuiltinType::SChar: { using CharKind = CharacterLiteral::CharacterKind; return new (ASTCtx) CharacterLiteral(ConstValue, CharKind::Ascii, ASTCtx.CharTy, {}); } case BuiltinType::UShort: { QualType IntT = ASTCtx.UnsignedIntTy; QualType ShortT = ASTCtx.UnsignedShortTy; 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; 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: { APInt Const = APInt(ASTCtx.getIntWidth(LiteralTy), ConstValue); return IntegerLiteral::Create(ASTCtx, Const, LiteralTy, {}); } case BuiltinType::Int: case BuiltinType::Long: case BuiltinType::LongLong: { APInt Const = APInt(ASTCtx.getIntWidth(LiteralTy), ConstValue, true); return IntegerLiteral::Create(ASTCtx, Const, LiteralTy, {}); } 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. APInt Const = APInt(64, ConstValue); 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. APInt Const = APInt(64, ConstValue, true); QualType T = ASTCtx.LongLongTy; return IntegerLiteral::Create(ASTCtx, Const, T, {}); } default: revng_abort(); } } else if (isa(CD)) { QualType UIntPtr = ASTCtx.getUIntPtrType(); uint64_t UIntPtrSize = ASTCtx.getTypeSize(UIntPtr); return IntegerLiteral::Create(ASTCtx, APInt::getNullValue(UIntPtrSize), UIntPtr, {}); } revng_abort(); } if (auto *CE = dyn_cast(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(cast(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