diff options
Diffstat (limited to 'lib/Analysis/BodyFarm.cpp')
-rw-r--r-- | lib/Analysis/BodyFarm.cpp | 366 |
1 files changed, 332 insertions, 34 deletions
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 59127246105d..e5d3c5ce5bc2 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -12,13 +12,20 @@ // //===----------------------------------------------------------------------===// -#include "BodyFarm.h" +#include "clang/Analysis/BodyFarm.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/NestedNameSpecifier.h" #include "clang/Analysis/CodeInjector.h" +#include "clang/Basic/OperatorKinds.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "body-farm" using namespace clang; @@ -55,7 +62,8 @@ public: CompoundStmt *makeCompound(ArrayRef<Stmt*>); /// Create a new DeclRefExpr for the referenced variable. - DeclRefExpr *makeDeclRefExpr(const VarDecl *D); + DeclRefExpr *makeDeclRefExpr(const VarDecl *D, + bool RefersToEnclosingVariableOrCapture = false); /// Create a new UnaryOperator representing a dereference. UnaryOperator *makeDereference(const Expr *Arg, QualType Ty); @@ -66,9 +74,19 @@ public: /// Create an implicit cast to a builtin boolean type. ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg); - // Create an implicit cast for lvalue-to-rvaluate conversions. + /// Create an implicit cast for lvalue-to-rvaluate conversions. ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty); + /// Make RValue out of variable declaration, creating a temporary + /// DeclRefExpr in the process. + ImplicitCastExpr * + makeLvalueToRvalue(const VarDecl *Decl, + bool RefersToEnclosingVariableOrCapture = false); + + /// Create an implicit cast of the given type. + ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty, + CastKind CK = CK_LValueToRValue); + /// Create an Objective-C bool literal. ObjCBoolLiteralExpr *makeObjCBool(bool Val); @@ -78,6 +96,18 @@ public: /// Create a Return statement. ReturnStmt *makeReturn(const Expr *RetVal); + /// Create an integer literal expression of the given type. + IntegerLiteral *makeIntegerLiteral(uint64_t Value, QualType Ty); + + /// Create a member expression. + MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl, + bool IsArrow = false, + ExprValueKind ValueKind = VK_LValue); + + /// Returns a *first* member field of a record declaration with a given name. + /// \return an nullptr if no member with such a name exists. + ValueDecl *findMemberField(const RecordDecl *RD, StringRef Name); + private: ASTContext &C; }; @@ -106,16 +136,14 @@ CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) { return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation()); } -DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) { - DeclRefExpr *DR = - DeclRefExpr::Create(/* Ctx = */ C, - /* QualifierLoc = */ NestedNameSpecifierLoc(), - /* TemplateKWLoc = */ SourceLocation(), - /* D = */ const_cast<VarDecl*>(D), - /* RefersToEnclosingVariableOrCapture = */ false, - /* NameLoc = */ SourceLocation(), - /* T = */ D->getType(), - /* VK = */ VK_LValue); +DeclRefExpr *ASTMaker::makeDeclRefExpr( + const VarDecl *D, + bool RefersToEnclosingVariableOrCapture) { + QualType Type = D->getType().getNonReferenceType(); + + DeclRefExpr *DR = DeclRefExpr::Create( + C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D), + RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue); return DR; } @@ -125,8 +153,25 @@ UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) { } ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { - return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, - const_cast<Expr*>(Arg), nullptr, VK_RValue); + return makeImplicitCast(Arg, Ty, CK_LValueToRValue); +} + +ImplicitCastExpr * +ASTMaker::makeLvalueToRvalue(const VarDecl *Arg, + bool RefersToEnclosingVariableOrCapture) { + QualType Type = Arg->getType().getNonReferenceType(); + return makeLvalueToRvalue(makeDeclRefExpr(Arg, + RefersToEnclosingVariableOrCapture), + Type); +} + +ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty, + CastKind CK) { + return ImplicitCastExpr::Create(C, Ty, + /* CastKind=*/ CK, + /* Expr=*/ const_cast<Expr *>(Arg), + /* CXXCastPath=*/ nullptr, + /* ExprValueKind=*/ VK_RValue); } Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) { @@ -161,12 +206,259 @@ ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) { nullptr); } +IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t Value, QualType Ty) { + llvm::APInt APValue = llvm::APInt(C.getTypeSize(Ty), Value); + return IntegerLiteral::Create(C, APValue, Ty, SourceLocation()); +} + +MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl, + bool IsArrow, + ExprValueKind ValueKind) { + + DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public); + return MemberExpr::Create( + C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(), + SourceLocation(), MemberDecl, FoundDecl, + DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()), + /* TemplateArgumentListInfo=*/ nullptr, MemberDecl->getType(), ValueKind, + OK_Ordinary); +} + +ValueDecl *ASTMaker::findMemberField(const RecordDecl *RD, StringRef Name) { + + CXXBasePaths Paths( + /* FindAmbiguities=*/false, + /* RecordPaths=*/false, + /* DetectVirtual=*/ false); + const IdentifierInfo &II = C.Idents.get(Name); + DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II); + + DeclContextLookupResult Decls = RD->lookup(DeclName); + for (NamedDecl *FoundDecl : Decls) + if (!FoundDecl->getDeclContext()->isFunctionOrMethod()) + return cast<ValueDecl>(FoundDecl); + + return nullptr; +} + //===----------------------------------------------------------------------===// // Creation functions for faux ASTs. //===----------------------------------------------------------------------===// typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D); +static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M, + const ParmVarDecl *Callback, + ArrayRef<Expr *> CallArgs) { + + QualType Ty = Callback->getType(); + DeclRefExpr *Call = M.makeDeclRefExpr(Callback); + CastKind CK; + if (Ty->isRValueReferenceType()) { + CK = CK_LValueToRValue; + } else { + assert(Ty->isLValueReferenceType()); + CK = CK_FunctionToPointerDecay; + Ty = C.getPointerType(Ty.getNonReferenceType()); + } + + return new (C) + CallExpr(C, M.makeImplicitCast(Call, Ty.getNonReferenceType(), CK), + /*args=*/CallArgs, + /*QualType=*/C.VoidTy, + /*ExprValueType=*/VK_RValue, + /*SourceLocation=*/SourceLocation()); +} + +static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, + const ParmVarDecl *Callback, + CXXRecordDecl *CallbackDecl, + ArrayRef<Expr *> CallArgs) { + assert(CallbackDecl != nullptr); + assert(CallbackDecl->isLambda()); + FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator(); + assert(callOperatorDecl != nullptr); + + DeclRefExpr *callOperatorDeclRef = + DeclRefExpr::Create(/* Ctx =*/ C, + /* QualifierLoc =*/ NestedNameSpecifierLoc(), + /* TemplateKWLoc =*/ SourceLocation(), + const_cast<FunctionDecl *>(callOperatorDecl), + /* RefersToEnclosingVariableOrCapture=*/ false, + /* NameLoc =*/ SourceLocation(), + /* T =*/ callOperatorDecl->getType(), + /* VK =*/ VK_LValue); + + return new (C) + CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef, + /*args=*/CallArgs, + /*QualType=*/C.VoidTy, + /*ExprValueType=*/VK_RValue, + /*SourceLocation=*/SourceLocation(), FPOptions()); +} + +/// Create a fake body for std::call_once. +/// Emulates the following function body: +/// +/// \code +/// typedef struct once_flag_s { +/// unsigned long __state = 0; +/// } once_flag; +/// template<class Callable> +/// void call_once(once_flag& o, Callable func) { +/// if (!o.__state) { +/// func(); +/// } +/// o.__state = 1; +/// } +/// \endcode +static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { + DEBUG(llvm::dbgs() << "Generating body for call_once\n"); + + // We need at least two parameters. + if (D->param_size() < 2) + return nullptr; + + ASTMaker M(C); + + const ParmVarDecl *Flag = D->getParamDecl(0); + const ParmVarDecl *Callback = D->getParamDecl(1); + + if (!Callback->getType()->isReferenceType()) { + llvm::dbgs() << "libcxx03 std::call_once implementation, skipping.\n"; + return nullptr; + } + if (!Flag->getType()->isReferenceType()) { + llvm::dbgs() << "unknown std::call_once implementation, skipping.\n"; + return nullptr; + } + + QualType CallbackType = Callback->getType().getNonReferenceType(); + + // Nullable pointer, non-null iff function is a CXXRecordDecl. + CXXRecordDecl *CallbackRecordDecl = CallbackType->getAsCXXRecordDecl(); + QualType FlagType = Flag->getType().getNonReferenceType(); + auto *FlagRecordDecl = dyn_cast_or_null<RecordDecl>(FlagType->getAsTagDecl()); + + if (!FlagRecordDecl) { + DEBUG(llvm::dbgs() << "Flag field is not a record: " + << "unknown std::call_once implementation, " + << "ignoring the call.\n"); + return nullptr; + } + + // We initially assume libc++ implementation of call_once, + // where the once_flag struct has a field `__state_`. + ValueDecl *FlagFieldDecl = M.findMemberField(FlagRecordDecl, "__state_"); + + // Otherwise, try libstdc++ implementation, with a field + // `_M_once` + if (!FlagFieldDecl) { + FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once"); + } + + if (!FlagFieldDecl) { + DEBUG(llvm::dbgs() << "No field _M_once or __state_ found on " + << "std::once_flag struct: unknown std::call_once " + << "implementation, ignoring the call."); + return nullptr; + } + + bool isLambdaCall = CallbackRecordDecl && CallbackRecordDecl->isLambda(); + if (CallbackRecordDecl && !isLambdaCall) { + DEBUG(llvm::dbgs() << "Not supported: synthesizing body for functors when " + << "body farming std::call_once, ignoring the call."); + return nullptr; + } + + SmallVector<Expr *, 5> CallArgs; + const FunctionProtoType *CallbackFunctionType; + if (isLambdaCall) { + + // Lambda requires callback itself inserted as a first parameter. + CallArgs.push_back( + M.makeDeclRefExpr(Callback, + /* RefersToEnclosingVariableOrCapture=*/ true)); + CallbackFunctionType = CallbackRecordDecl->getLambdaCallOperator() + ->getType() + ->getAs<FunctionProtoType>(); + } else if (!CallbackType->getPointeeType().isNull()) { + CallbackFunctionType = + CallbackType->getPointeeType()->getAs<FunctionProtoType>(); + } else { + CallbackFunctionType = CallbackType->getAs<FunctionProtoType>(); + } + + if (!CallbackFunctionType) + return nullptr; + + // First two arguments are used for the flag and for the callback. + if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) { + DEBUG(llvm::dbgs() << "Types of params of the callback do not match " + << "params passed to std::call_once, " + << "ignoring the call\n"); + return nullptr; + } + + // All arguments past first two ones are passed to the callback, + // and we turn lvalues into rvalues if the argument is not passed by + // reference. + for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) { + const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx); + Expr *ParamExpr = M.makeDeclRefExpr(PDecl); + if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) { + QualType PTy = PDecl->getType().getNonReferenceType(); + ParamExpr = M.makeLvalueToRvalue(ParamExpr, PTy); + } + CallArgs.push_back(ParamExpr); + } + + CallExpr *CallbackCall; + if (isLambdaCall) { + + CallbackCall = create_call_once_lambda_call(C, M, Callback, + CallbackRecordDecl, CallArgs); + } else { + + // Function pointer case. + CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs); + } + + DeclRefExpr *FlagDecl = + M.makeDeclRefExpr(Flag, + /* RefersToEnclosingVariableOrCapture=*/true); + + + MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FlagFieldDecl); + assert(Deref->isLValue()); + QualType DerefType = Deref->getType(); + + // Negation predicate. + UnaryOperator *FlagCheck = new (C) UnaryOperator( + /* input=*/ + M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType, + CK_IntegralToBoolean), + /* opc=*/ UO_LNot, + /* QualType=*/ C.IntTy, + /* ExprValueKind=*/ VK_RValue, + /* ExprObjectKind=*/ OK_Ordinary, SourceLocation()); + + // Create assignment. + BinaryOperator *FlagAssignment = M.makeAssignment( + Deref, M.makeIntegralCast(M.makeIntegerLiteral(1, C.IntTy), DerefType), + DerefType); + + IfStmt *Out = new (C) + IfStmt(C, SourceLocation(), + /* IsConstexpr=*/ false, + /* init=*/ nullptr, + /* var=*/ nullptr, + /* cond=*/ FlagCheck, + /* then=*/ M.makeCompound({CallbackCall, FlagAssignment})); + + return Out; +} + /// Create a fake body for dispatch_once. static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { // Check if we have at least two parameters. @@ -193,8 +485,8 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { // sets it, and calls the block. Basically, an AST dump of: // // void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) { - // if (!*predicate) { - // *predicate = 1; + // if (*predicate != ~0l) { + // *predicate = ~0l; // block(); // } // } @@ -202,22 +494,26 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { ASTMaker M(C); // (1) Create the call. - DeclRefExpr *DR = M.makeDeclRefExpr(Block); - ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); - CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, - SourceLocation()); + CallExpr *CE = new (C) CallExpr( + /*ASTContext=*/C, + /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block), + /*args=*/None, + /*QualType=*/C.VoidTy, + /*ExprValueType=*/VK_RValue, + /*SourceLocation=*/SourceLocation()); // (2) Create the assignment to the predicate. - IntegerLiteral *IL = - IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy), (uint64_t) 1), - C.IntTy, SourceLocation()); + Expr *DoneValue = + new (C) UnaryOperator(M.makeIntegerLiteral(0, C.LongTy), UO_Not, C.LongTy, + VK_RValue, OK_Ordinary, SourceLocation()); + BinaryOperator *B = M.makeAssignment( M.makeDereference( M.makeLvalueToRvalue( M.makeDeclRefExpr(Predicate), PredicateQPtrTy), PredicateTy), - M.makeIntegralCast(IL, PredicateTy), + M.makeIntegralCast(DoneValue, PredicateTy), PredicateTy); // (3) Create the compound statement. @@ -233,14 +529,15 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { PredicateQPtrTy), PredicateTy), PredicateTy); - - UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy, - VK_RValue, OK_Ordinary, - SourceLocation()); - + + Expr *GuardCondition = M.makeComparison(LValToRval, DoneValue, BO_NE); // (5) Create the 'if' statement. - IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr, - UO, CS); + IfStmt *If = new (C) IfStmt(C, SourceLocation(), + /* IsConstexpr=*/ false, + /* init=*/ nullptr, + /* var=*/ nullptr, + /* cond=*/ GuardCondition, + /* then=*/ CS); return If; } @@ -370,8 +667,9 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { if (Name.startswith("OSAtomicCompareAndSwap") || Name.startswith("objc_atomicCompareAndSwap")) { FF = create_OSAtomicCompareAndSwap; - } - else { + } else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) { + FF = create_call_once; + } else { FF = llvm::StringSwitch<FunctionFarmer>(Name) .Case("dispatch_sync", create_dispatch_sync) .Case("dispatch_once", create_dispatch_once) |