/// \brief DataFlow analysis to build the AST for a Function // // Copyright rev.ng Srls. See LICENSE.md for details. // #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 "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(Res) or isa(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(&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(&I); Value *RetVal = Ret->getReturnValue(); // TODO: for now we handle return instructions containing a `ConstantStruct` // by emitting a `return void`. This should eventually be fixed, properly // handling structs (both constants and not). if (RetVal && isa(RetVal)) { return ReturnStmt::Create(ASTCtx, {}, nullptr, nullptr); } Expr *ReturnedExpr = nullptr; if (auto *ZeroAggregate = dyn_cast_or_null(RetVal)) { uint64_t ConstValue = 0; QualType IntT = ASTCtx.IntTy; APInt Const = APInt(ASTCtx.getIntWidth(IntT), ConstValue); llvm::Function *TheFunction = Ret->getFunction(); clang::FunctionDecl &FDecl = Declarator.getFunctionDecl(TheFunction); revng_assert(VarDecls.count(ZeroAggregate) == 0); VarDecl *NewVarDecl = createVarDecl(ZeroAggregate, TheFunction, FDecl); VarDecls[ZeroAggregate] = NewVarDecl; clang::Expr *Zero = IntegerLiteral::Create(ASTCtx, Const, IntT, {}); clang::Expr *ZeroInit = new (ASTCtx) clang::InitListExpr(ASTCtx, {}, { Zero }, {}); NewVarDecl->setInit(ZeroInit); ReturnedExpr = getExprForValue(ZeroAggregate); } else { ReturnedExpr = RetVal ? getExprForValue(RetVal) : nullptr; } return ReturnStmt::Create(ASTCtx, {}, ReturnedExpr, nullptr); } case Instruction::Switch: { revng_abort("switch instructions are not supported yet"); auto *Switch = cast(&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(&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(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(&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(); 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(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(); TypeDeclOrQualType LeftQTy = Declarator.getOrCreateType(&I, ASTCtx, TUDecl); QualType LHSQualType = DeclCreator::getQualType(LeftQTy); if (LHSQualType != Res->getType()) Res = createCast(LHSQualType, Res, ASTCtx); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and Res) Res->dump(); return Res; } // ---- Other instructions ---- case Instruction::Select: { Expr *Cond = getParenthesizedExprForValue(I.getOperand(0)); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and Cond) Cond->dump(); Expr *TrueExpr = getParenthesizedExprForValue(I.getOperand(1)); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and TrueExpr) TrueExpr->dump(); Expr *FalseExpr = getParenthesizedExprForValue(I.getOperand(2)); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and FalseExpr) FalseExpr->dump(); clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); TypeDeclOrQualType ASTTy = Declarator.getOrCreateType(&I, ASTCtx, TUDecl); QualType ASTType = DeclCreator::getQualType(ASTTy); return new (ASTCtx) ConditionalOperator(Cond, {}, TrueExpr, {}, FalseExpr, ASTType, VK_RValue, OK_Ordinary); } case Instruction::Call: { auto *TheCall = cast(&I); Function *CalleeFun = getCallee(TheCall); Expr *CalleeExpr = getExprForValue(CalleeFun); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and CalleeExpr) CalleeExpr->dump(); size_t NumArgs = CalleeFun->arg_size(); FunctionDecl &FD = Declarator.getFunctionDecl(CalleeFun); size_t NumParms = FD.param_size(); unsigned NumOps = TheCall->getNumArgOperands(); bool HasNoParms = NumParms == 0 or (NumParms == 1 and FD.getParamDecl(0)->getType() == ASTCtx.VoidTy); revng_assert(HasNoParms or NumArgs == NumParms); const bool IsVariadic = FD.isVariadic(); if (not FD.isVariadic()) revng_assert(NumArgs == NumOps); auto Args = SmallVector(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 Args; QualType ReturnType = ASTCtx.VoidTy; return CallExpr::Create(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) or isa(AggregateOp)); llvm::Type *AggregateTy = AggregateOp->getType(); revng_assert(AggregateTy->isAggregateType()); auto *TypeDecl = Declarator.lookupTypeDeclOrNull(AggregateTy); auto *StructTypeDecl = cast(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(AggregateOp)) return nullptr; 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(); revng_assert(AggregateTy->isAggregateType()); auto *TypeDecl = Declarator.lookupTypeDeclOrNull(AggregateTy); auto *StructTypeDecl = cast(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 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(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. revng_assert(Bases.empty() or Bases.size() == 1); // 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. const auto *BaseValue = cast(Base)->getValue(); // Unwrap calls to revng_scev_barrier_* // FIXME: calls to revng_scev_barrier_* should eventually be removed after // using them and before actually generating C code for them. if (auto *Call = dyn_cast(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 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); // Off = I - Base const SCEV *Off = SE->getAddExpr(ISCEV, SE->getNegativeSCEV(Base)); const auto getNestedFieldIds = [](const SCEV *Off, const dla::Layout *P, llvm::ScalarEvolution *SE, clang::ASTContext &) -> llvm::SmallVector { return {}; }; llvm::SmallVector 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 RPOT(&F); for (BasicBlock *BB : RPOT) { revng_log(ASTBuildLog, "BB: " << BB->getName()); { // Create labels for Basic Blocks. This could potentially be disabled if // we choose not to have the option to emit goto statements ever. IdentifierInfo &Id = ASTCtx.Idents.get("bb_" + std::to_string(BBId++)); LabelDecl *Label = LabelDecl::Create(ASTCtx, &FDecl, {}, &Id); BBLabelDecls[BB] = Label; } for (Instruction &I : *BB) { // Skip calls to `revng_scev_barrier_*` // FIXME: calls to revng_scev_barrier_* should eventually be removed after // using them and before actually generating C code for them. if (auto *Call = dyn_cast(&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(&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(&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(&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(&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(&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(AggregateOp)) { std::vector StructOpExpr; for (auto &OperandUse : CS->operands()) { Value *Operand = OperandUse.get(); Constant *OperandConst = cast(Operand); clang::Expr *OperandExpr = nullptr; if (isa(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(AggregateOp) or isa(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(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::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, nullptr, SizeMod, 0); ASTType = ArrayTy; } else if (const auto *Call = dyn_cast(I)) { ASTType = Declarator.getOrCreateType(Call->getType(), Call->getCalledFunction(), ASTCtx, TUDecl); } else if (const auto *Insert = dyn_cast(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(C)) { ASTType = Declarator.getOrCreateType(ZeroAggregate->getType(), NamingVal, ASTCtx, TUDecl); } else { revng_abort("trying to create VarDecl for unexpected constant"); } QualType ASTQualType = DeclCreator::getQualType(ASTType); revng_assert(not ASTQualType.isNull()); const std::string VarName = C->hasName() ? C->getName().str() : (std::string("var_") + std::to_string(NVar++)); IdentifierInfo &Id = ASTCtx.Idents.get(makeCIdentifier(VarName)); VarDecl *NewVarDecl = VarDecl::Create(ASTCtx, &FDecl, {}, {}, &Id, ASTQualType, nullptr, StorageClass::SC_None); FDecl.addDecl(NewVarDecl); return NewVarDecl; } static clang::BinaryOperatorKind getClangBinaryOpKind(const Instruction &I, const clang::Type *LHSTy, const clang::Type *RHSTy) { clang::BinaryOperatorKind Res; switch (I.getOpcode()) { case Instruction::Add: { Res = clang::BinaryOperatorKind::BO_Add; } break; case Instruction::Sub: { Res = clang::BinaryOperatorKind::BO_Sub; } break; case Instruction::Mul: { Res = clang::BinaryOperatorKind::BO_Mul; } break; case Instruction::And: { if (LHSTy->isBooleanType() and RHSTy->isBooleanType()) Res = clang::BinaryOperatorKind::BO_LAnd; else Res = clang::BinaryOperatorKind::BO_And; } break; case Instruction::Or: { if (LHSTy->isBooleanType() and RHSTy->isBooleanType()) Res = clang::BinaryOperatorKind::BO_LOr; else Res = clang::BinaryOperatorKind::BO_Or; } break; case Instruction::Xor: { Res = clang::BinaryOperatorKind::BO_Xor; } break; case Instruction::ICmp: { auto *CompareI = cast(&I); switch (CompareI->getPredicate()) { case CmpInst::ICMP_EQ: { Res = clang::BinaryOperatorKind::BO_EQ; } break; case CmpInst::ICMP_NE: { Res = clang::BinaryOperatorKind::BO_NE; } break; case CmpInst::ICMP_UGT: case CmpInst::ICMP_SGT: { Res = clang::BinaryOperatorKind::BO_GT; } break; case CmpInst::ICMP_UGE: case CmpInst::ICMP_SGE: { Res = clang::BinaryOperatorKind::BO_GE; } break; case CmpInst::ICMP_ULT: case CmpInst::ICMP_SLT: { Res = clang::BinaryOperatorKind::BO_LT; } break; case CmpInst::ICMP_ULE: case CmpInst::ICMP_SLE: { Res = clang::BinaryOperatorKind::BO_LE; } break; case CmpInst::BAD_ICMP_PREDICATE: case CmpInst::BAD_FCMP_PREDICATE: case CmpInst::FCMP_TRUE: case CmpInst::FCMP_FALSE: case CmpInst::FCMP_OEQ: case CmpInst::FCMP_ONE: case CmpInst::FCMP_OGE: case CmpInst::FCMP_OGT: case CmpInst::FCMP_OLE: case CmpInst::FCMP_OLT: case CmpInst::FCMP_ORD: case CmpInst::FCMP_UNO: case CmpInst::FCMP_UEQ: case CmpInst::FCMP_UNE: case CmpInst::FCMP_UGT: case CmpInst::FCMP_UGE: case CmpInst::FCMP_ULT: case CmpInst::FCMP_ULE: revng_abort("Unsupported comparison operator"); } } break; case Instruction::Shl: { Res = clang::BinaryOperatorKind::BO_Shl; } break; case Instruction::LShr: case Instruction::AShr: { Res = clang::BinaryOperatorKind::BO_Shr; } break; case Instruction::UDiv: case Instruction::SDiv: { Res = clang::BinaryOperatorKind::BO_Div; } break; case Instruction::URem: case Instruction::SRem: { Res = clang::BinaryOperatorKind::BO_Rem; } break; default: { revng_log(ASTBuildLog, "Unsupported operation" << dumpToString(&I) << '\n'); revng_abort("Unsupported binary operator"); } } return Res; } static bool is128Int(ASTContext &ASTCtx, clang::Expr *E) { const clang::Type *T = E->getType().getTypePtr(); const clang::Type *Int128T = ASTCtx.Int128Ty.getTypePtr(); const clang::Type *UInt128T = ASTCtx.UnsignedInt128Ty.getTypePtr(); return T == Int128T or T == UInt128T; } static std::pair getCastedBinaryOperands(ASTContext &ASTCtx, const Instruction &I, Expr *LHS, Expr *RHS) { QualType LHSQualTy = LHS->getType(); QualType RHSQualTy = RHS->getType(); const clang::Type *LHSTy = LHSQualTy.getTypePtr(); const clang::Type *RHSTy = RHSQualTy.getTypePtr(); revng_assert((LHSTy->isPointerType() or LHSTy->isIntegerType()) and (RHSTy->isPointerType() or RHSTy->isIntegerType())); uint64_t LHSSize = ASTCtx.getTypeSize(LHSTy); uint64_t RHSSize = ASTCtx.getTypeSize(RHSTy); unsigned OpCode = I.getOpcode(); revng_assert(LHSSize == RHSSize or OpCode == Instruction::Shl or OpCode == Instruction::LShr or OpCode == Instruction::AShr or is128Int(ASTCtx, RHS) or is128Int(ASTCtx, LHS)); unsigned Size = static_cast(std::max(LHSSize, RHSSize)); QualType SignedTy = ASTCtx.getIntTypeForBitwidth(Size, /* Signed */ true); std::pair 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(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(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(&I)->isSigned()) { if (RHSTy->isUnsignedIntegerType()) Res.second = createCast(SignedTy, Res.second, ASTCtx); if (LHSTy->isUnsignedIntegerType()) Res.first = createCast(SignedTy, Res.first, ASTCtx); } } break; default: { revng_log(ASTBuildLog, "Unsupported operation" << dumpToString(&I) << '\n'); revng_abort("Unsupported binary operator"); } } return Res; } Expr *StmtBuilder::createRValueExprForBinaryOperator(Instruction &I) { revng_assert(I.getNumOperands() == 2); Value *LHSVal = I.getOperand(0); Expr *LHS = getParenthesizedExprForValue(LHSVal); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and LHS) LHS->dump(); if (LHS->isLValue()) LHS = ImplicitCastExpr::Create(ASTCtx, LHS->getType(), CastKind::CK_LValueToRValue, LHS, nullptr, VK_RValue); Value *RHSVal = I.getOperand(1); Expr *RHS = getParenthesizedExprForValue(RHSVal); revng_log(ASTBuildLog, "GOT!"); if (ASTBuildLog.isEnabled() and RHS) RHS->dump(); if (RHS->isLValue()) RHS = ImplicitCastExpr::Create(ASTCtx, RHS->getType(), CastKind::CK_LValueToRValue, RHS, nullptr, VK_RValue); std::tie(LHS, RHS) = getCastedBinaryOperands(ASTCtx, I, LHS, RHS); auto BinOpKind = getClangBinaryOpKind(I, LHS->getType().getTypePtr(), RHS->getType().getTypePtr()); Expr *Res = new (ASTCtx) clang::BinaryOperator(LHS, RHS, BinOpKind, LHS->getType(), VK_RValue, OK_Ordinary, {}, FPOptions()); unsigned OpCode = I.getOpcode(); switch (OpCode) { case Instruction::SDiv: case Instruction::SRem: case Instruction::AShr: case Instruction::ICmp: { clang::DeclContext &TUDecl = *ASTCtx.getTranslationUnitDecl(); TypeDeclOrQualType ResType = Declarator.getOrCreateType(&I, ASTCtx, TUDecl); Res = new (ASTCtx) ParenExpr({}, {}, Res); Res = createCast(DeclCreator::getQualType(ResType), Res, ASTCtx); } break; default: break; } return Res; } Expr *StmtBuilder::getBoolLiteral(bool V) { QualType IntT = ASTCtx.IntTy; TypeDeclOrQualType BoolTy = Declarator.getOrCreateBoolType(ASTCtx); APInt Const = APInt(ASTCtx.getIntWidth(IntT), V ? 1 : 0, true); Expr *IntLiteral = IntegerLiteral::Create(ASTCtx, Const, IntT, {}); return createCast(DeclCreator::getQualType(BoolTy), IntLiteral, ASTCtx); } Expr *StmtBuilder::getUIntLiteral(uint64_t U) { QualType UIntT = ASTCtx.UnsignedIntTy; APInt Const = APInt(ASTCtx.getIntWidth(UIntT), U); return IntegerLiteral::Create(ASTCtx, Const, UIntT, {}); } Expr *StmtBuilder::getExprForValue(const Value *V) { revng_log(ASTBuildLog, "getExprForValue: " << dumpToString(V)); if (auto *FunctionOrGlobal = dyn_cast(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(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(V) or isa(V)) { return getLiteralFromConstant(cast(V)); } 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(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(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); 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(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(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, PointeeQualType, 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(); 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(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(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(C)) { if (auto *CInt = dyn_cast(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(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(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(CD)) { QualType UIntPtr = ASTCtx.getUIntPtrType(); unsigned UIntPtrSize = static_cast(ASTCtx.getTypeSize(UIntPtr)); return IntegerLiteral::Create(ASTCtx, APInt::getNullValue(UIntPtrSize), UIntPtr, {}); } else if (isa(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(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