Path: blob/main/contrib/llvm-project/clang/lib/Analysis/BodyFarm.cpp
35233 views
//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// BodyFarm is a factory for creating faux implementations for functions/methods9// for analysis purposes.10//11//===----------------------------------------------------------------------===//1213#include "clang/Analysis/BodyFarm.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/CXXInheritance.h"16#include "clang/AST/Decl.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprCXX.h"19#include "clang/AST/ExprObjC.h"20#include "clang/AST/NestedNameSpecifier.h"21#include "clang/Analysis/CodeInjector.h"22#include "clang/Basic/Builtins.h"23#include "clang/Basic/OperatorKinds.h"24#include "llvm/ADT/StringSwitch.h"25#include "llvm/Support/Debug.h"26#include <optional>2728#define DEBUG_TYPE "body-farm"2930using namespace clang;3132//===----------------------------------------------------------------------===//33// Helper creation functions for constructing faux ASTs.34//===----------------------------------------------------------------------===//3536static bool isDispatchBlock(QualType Ty) {37// Is it a block pointer?38const BlockPointerType *BPT = Ty->getAs<BlockPointerType>();39if (!BPT)40return false;4142// Check if the block pointer type takes no arguments and43// returns void.44const FunctionProtoType *FT =45BPT->getPointeeType()->getAs<FunctionProtoType>();46return FT && FT->getReturnType()->isVoidType() && FT->getNumParams() == 0;47}4849namespace {50class ASTMaker {51public:52ASTMaker(ASTContext &C) : C(C) {}5354/// Create a new BinaryOperator representing a simple assignment.55BinaryOperator *makeAssignment(const Expr *LHS, const Expr *RHS, QualType Ty);5657/// Create a new BinaryOperator representing a comparison.58BinaryOperator *makeComparison(const Expr *LHS, const Expr *RHS,59BinaryOperator::Opcode Op);6061/// Create a new compound stmt using the provided statements.62CompoundStmt *makeCompound(ArrayRef<Stmt*>);6364/// Create a new DeclRefExpr for the referenced variable.65DeclRefExpr *makeDeclRefExpr(const VarDecl *D,66bool RefersToEnclosingVariableOrCapture = false);6768/// Create a new UnaryOperator representing a dereference.69UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);7071/// Create an implicit cast for an integer conversion.72Expr *makeIntegralCast(const Expr *Arg, QualType Ty);7374/// Create an implicit cast to a builtin boolean type.75ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);7677/// Create an implicit cast for lvalue-to-rvaluate conversions.78ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);7980/// Make RValue out of variable declaration, creating a temporary81/// DeclRefExpr in the process.82ImplicitCastExpr *83makeLvalueToRvalue(const VarDecl *Decl,84bool RefersToEnclosingVariableOrCapture = false);8586/// Create an implicit cast of the given type.87ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,88CastKind CK = CK_LValueToRValue);8990/// Create a cast to reference type.91CastExpr *makeReferenceCast(const Expr *Arg, QualType Ty);9293/// Create an Objective-C bool literal.94ObjCBoolLiteralExpr *makeObjCBool(bool Val);9596/// Create an Objective-C ivar reference.97ObjCIvarRefExpr *makeObjCIvarRef(const Expr *Base, const ObjCIvarDecl *IVar);9899/// Create a Return statement.100ReturnStmt *makeReturn(const Expr *RetVal);101102/// Create an integer literal expression of the given type.103IntegerLiteral *makeIntegerLiteral(uint64_t Value, QualType Ty);104105/// Create a member expression.106MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,107bool IsArrow = false,108ExprValueKind ValueKind = VK_LValue);109110/// Returns a *first* member field of a record declaration with a given name.111/// \return an nullptr if no member with such a name exists.112ValueDecl *findMemberField(const RecordDecl *RD, StringRef Name);113114private:115ASTContext &C;116};117}118119BinaryOperator *ASTMaker::makeAssignment(const Expr *LHS, const Expr *RHS,120QualType Ty) {121return BinaryOperator::Create(122C, const_cast<Expr *>(LHS), const_cast<Expr *>(RHS), BO_Assign, Ty,123VK_PRValue, OK_Ordinary, SourceLocation(), FPOptionsOverride());124}125126BinaryOperator *ASTMaker::makeComparison(const Expr *LHS, const Expr *RHS,127BinaryOperator::Opcode Op) {128assert(BinaryOperator::isLogicalOp(Op) ||129BinaryOperator::isComparisonOp(Op));130return BinaryOperator::Create(131C, const_cast<Expr *>(LHS), const_cast<Expr *>(RHS), Op,132C.getLogicalOperationType(), VK_PRValue, OK_Ordinary, SourceLocation(),133FPOptionsOverride());134}135136CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) {137return CompoundStmt::Create(C, Stmts, FPOptionsOverride(), SourceLocation(),138SourceLocation());139}140141DeclRefExpr *ASTMaker::makeDeclRefExpr(142const VarDecl *D,143bool RefersToEnclosingVariableOrCapture) {144QualType Type = D->getType().getNonReferenceType();145146DeclRefExpr *DR = DeclRefExpr::Create(147C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl *>(D),148RefersToEnclosingVariableOrCapture, SourceLocation(), Type, VK_LValue);149return DR;150}151152UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) {153return UnaryOperator::Create(C, const_cast<Expr *>(Arg), UO_Deref, Ty,154VK_LValue, OK_Ordinary, SourceLocation(),155/*CanOverflow*/ false, FPOptionsOverride());156}157158ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) {159return makeImplicitCast(Arg, Ty, CK_LValueToRValue);160}161162ImplicitCastExpr *163ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,164bool RefersToEnclosingVariableOrCapture) {165QualType Type = Arg->getType().getNonReferenceType();166return makeLvalueToRvalue(makeDeclRefExpr(Arg,167RefersToEnclosingVariableOrCapture),168Type);169}170171ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty,172CastKind CK) {173return ImplicitCastExpr::Create(C, Ty,174/* CastKind=*/CK,175/* Expr=*/const_cast<Expr *>(Arg),176/* CXXCastPath=*/nullptr,177/* ExprValueKind=*/VK_PRValue,178/* FPFeatures */ FPOptionsOverride());179}180181CastExpr *ASTMaker::makeReferenceCast(const Expr *Arg, QualType Ty) {182assert(Ty->isReferenceType());183return CXXStaticCastExpr::Create(184C, Ty.getNonReferenceType(),185Ty->isLValueReferenceType() ? VK_LValue : VK_XValue, CK_NoOp,186const_cast<Expr *>(Arg), /*CXXCastPath=*/nullptr,187/*Written=*/C.getTrivialTypeSourceInfo(Ty), FPOptionsOverride(),188SourceLocation(), SourceLocation(), SourceRange());189}190191Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {192if (Arg->getType() == Ty)193return const_cast<Expr*>(Arg);194return makeImplicitCast(Arg, Ty, CK_IntegralCast);195}196197ImplicitCastExpr *ASTMaker::makeIntegralCastToBoolean(const Expr *Arg) {198return makeImplicitCast(Arg, C.BoolTy, CK_IntegralToBoolean);199}200201ObjCBoolLiteralExpr *ASTMaker::makeObjCBool(bool Val) {202QualType Ty = C.getBOOLDecl() ? C.getBOOLType() : C.ObjCBuiltinBoolTy;203return new (C) ObjCBoolLiteralExpr(Val, Ty, SourceLocation());204}205206ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base,207const ObjCIvarDecl *IVar) {208return new (C) ObjCIvarRefExpr(const_cast<ObjCIvarDecl*>(IVar),209IVar->getType(), SourceLocation(),210SourceLocation(), const_cast<Expr*>(Base),211/*arrow=*/true, /*free=*/false);212}213214ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) {215return ReturnStmt::Create(C, SourceLocation(), const_cast<Expr *>(RetVal),216/* NRVOCandidate=*/nullptr);217}218219IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t Value, QualType Ty) {220llvm::APInt APValue = llvm::APInt(C.getTypeSize(Ty), Value);221return IntegerLiteral::Create(C, APValue, Ty, SourceLocation());222}223224MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl,225bool IsArrow,226ExprValueKind ValueKind) {227228DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public);229return MemberExpr::Create(230C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),231SourceLocation(), MemberDecl, FoundDecl,232DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),233/* TemplateArgumentListInfo=*/ nullptr, MemberDecl->getType(), ValueKind,234OK_Ordinary, NOUR_None);235}236237ValueDecl *ASTMaker::findMemberField(const RecordDecl *RD, StringRef Name) {238239CXXBasePaths Paths(240/* FindAmbiguities=*/false,241/* RecordPaths=*/false,242/* DetectVirtual=*/ false);243const IdentifierInfo &II = C.Idents.get(Name);244DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);245246DeclContextLookupResult Decls = RD->lookup(DeclName);247for (NamedDecl *FoundDecl : Decls)248if (!FoundDecl->getDeclContext()->isFunctionOrMethod())249return cast<ValueDecl>(FoundDecl);250251return nullptr;252}253254//===----------------------------------------------------------------------===//255// Creation functions for faux ASTs.256//===----------------------------------------------------------------------===//257258typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);259260static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M,261const ParmVarDecl *Callback,262ArrayRef<Expr *> CallArgs) {263264QualType Ty = Callback->getType();265DeclRefExpr *Call = M.makeDeclRefExpr(Callback);266Expr *SubExpr;267if (Ty->isRValueReferenceType()) {268SubExpr = M.makeImplicitCast(269Call, Ty.getNonReferenceType(), CK_LValueToRValue);270} else if (Ty->isLValueReferenceType() &&271Call->getType()->isFunctionType()) {272Ty = C.getPointerType(Ty.getNonReferenceType());273SubExpr = M.makeImplicitCast(Call, Ty, CK_FunctionToPointerDecay);274} else if (Ty->isLValueReferenceType()275&& Call->getType()->isPointerType()276&& Call->getType()->getPointeeType()->isFunctionType()){277SubExpr = Call;278} else {279llvm_unreachable("Unexpected state");280}281282return CallExpr::Create(C, SubExpr, CallArgs, C.VoidTy, VK_PRValue,283SourceLocation(), FPOptionsOverride());284}285286static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,287const ParmVarDecl *Callback,288CXXRecordDecl *CallbackDecl,289ArrayRef<Expr *> CallArgs) {290assert(CallbackDecl != nullptr);291assert(CallbackDecl->isLambda());292FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();293assert(callOperatorDecl != nullptr);294295DeclRefExpr *callOperatorDeclRef =296DeclRefExpr::Create(/* Ctx =*/ C,297/* QualifierLoc =*/ NestedNameSpecifierLoc(),298/* TemplateKWLoc =*/ SourceLocation(),299const_cast<FunctionDecl *>(callOperatorDecl),300/* RefersToEnclosingVariableOrCapture=*/ false,301/* NameLoc =*/ SourceLocation(),302/* T =*/ callOperatorDecl->getType(),303/* VK =*/ VK_LValue);304305return CXXOperatorCallExpr::Create(306/*AstContext=*/C, OO_Call, callOperatorDeclRef,307/*Args=*/CallArgs,308/*QualType=*/C.VoidTy,309/*ExprValueType=*/VK_PRValue,310/*SourceLocation=*/SourceLocation(),311/*FPFeatures=*/FPOptionsOverride());312}313314/// Create a fake body for 'std::move' or 'std::forward'. This is just:315///316/// \code317/// return static_cast<return_type>(param);318/// \endcode319static Stmt *create_std_move_forward(ASTContext &C, const FunctionDecl *D) {320LLVM_DEBUG(llvm::dbgs() << "Generating body for std::move / std::forward\n");321322ASTMaker M(C);323324QualType ReturnType = D->getType()->castAs<FunctionType>()->getReturnType();325Expr *Param = M.makeDeclRefExpr(D->getParamDecl(0));326Expr *Cast = M.makeReferenceCast(Param, ReturnType);327return M.makeReturn(Cast);328}329330/// Create a fake body for std::call_once.331/// Emulates the following function body:332///333/// \code334/// typedef struct once_flag_s {335/// unsigned long __state = 0;336/// } once_flag;337/// template<class Callable>338/// void call_once(once_flag& o, Callable func) {339/// if (!o.__state) {340/// func();341/// }342/// o.__state = 1;343/// }344/// \endcode345static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {346LLVM_DEBUG(llvm::dbgs() << "Generating body for call_once\n");347348// We need at least two parameters.349if (D->param_size() < 2)350return nullptr;351352ASTMaker M(C);353354const ParmVarDecl *Flag = D->getParamDecl(0);355const ParmVarDecl *Callback = D->getParamDecl(1);356357if (!Callback->getType()->isReferenceType()) {358llvm::dbgs() << "libcxx03 std::call_once implementation, skipping.\n";359return nullptr;360}361if (!Flag->getType()->isReferenceType()) {362llvm::dbgs() << "unknown std::call_once implementation, skipping.\n";363return nullptr;364}365366QualType CallbackType = Callback->getType().getNonReferenceType();367368// Nullable pointer, non-null iff function is a CXXRecordDecl.369CXXRecordDecl *CallbackRecordDecl = CallbackType->getAsCXXRecordDecl();370QualType FlagType = Flag->getType().getNonReferenceType();371auto *FlagRecordDecl = FlagType->getAsRecordDecl();372373if (!FlagRecordDecl) {374LLVM_DEBUG(llvm::dbgs() << "Flag field is not a record: "375<< "unknown std::call_once implementation, "376<< "ignoring the call.\n");377return nullptr;378}379380// We initially assume libc++ implementation of call_once,381// where the once_flag struct has a field `__state_`.382ValueDecl *FlagFieldDecl = M.findMemberField(FlagRecordDecl, "__state_");383384// Otherwise, try libstdc++ implementation, with a field385// `_M_once`386if (!FlagFieldDecl) {387FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once");388}389390if (!FlagFieldDecl) {391LLVM_DEBUG(llvm::dbgs() << "No field _M_once or __state_ found on "392<< "std::once_flag struct: unknown std::call_once "393<< "implementation, ignoring the call.");394return nullptr;395}396397bool isLambdaCall = CallbackRecordDecl && CallbackRecordDecl->isLambda();398if (CallbackRecordDecl && !isLambdaCall) {399LLVM_DEBUG(llvm::dbgs()400<< "Not supported: synthesizing body for functors when "401<< "body farming std::call_once, ignoring the call.");402return nullptr;403}404405SmallVector<Expr *, 5> CallArgs;406const FunctionProtoType *CallbackFunctionType;407if (isLambdaCall) {408409// Lambda requires callback itself inserted as a first parameter.410CallArgs.push_back(411M.makeDeclRefExpr(Callback,412/* RefersToEnclosingVariableOrCapture=*/ true));413CallbackFunctionType = CallbackRecordDecl->getLambdaCallOperator()414->getType()415->getAs<FunctionProtoType>();416} else if (!CallbackType->getPointeeType().isNull()) {417CallbackFunctionType =418CallbackType->getPointeeType()->getAs<FunctionProtoType>();419} else {420CallbackFunctionType = CallbackType->getAs<FunctionProtoType>();421}422423if (!CallbackFunctionType)424return nullptr;425426// First two arguments are used for the flag and for the callback.427if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) {428LLVM_DEBUG(llvm::dbgs() << "Types of params of the callback do not match "429<< "params passed to std::call_once, "430<< "ignoring the call\n");431return nullptr;432}433434// All arguments past first two ones are passed to the callback,435// and we turn lvalues into rvalues if the argument is not passed by436// reference.437for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) {438const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx);439assert(PDecl);440if (CallbackFunctionType->getParamType(ParamIdx - 2)441.getNonReferenceType()442.getCanonicalType() !=443PDecl->getType().getNonReferenceType().getCanonicalType()) {444LLVM_DEBUG(llvm::dbgs() << "Types of params of the callback do not match "445<< "params passed to std::call_once, "446<< "ignoring the call\n");447return nullptr;448}449Expr *ParamExpr = M.makeDeclRefExpr(PDecl);450if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) {451QualType PTy = PDecl->getType().getNonReferenceType();452ParamExpr = M.makeLvalueToRvalue(ParamExpr, PTy);453}454CallArgs.push_back(ParamExpr);455}456457CallExpr *CallbackCall;458if (isLambdaCall) {459460CallbackCall = create_call_once_lambda_call(C, M, Callback,461CallbackRecordDecl, CallArgs);462} else {463464// Function pointer case.465CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs);466}467468DeclRefExpr *FlagDecl =469M.makeDeclRefExpr(Flag,470/* RefersToEnclosingVariableOrCapture=*/true);471472473MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FlagFieldDecl);474assert(Deref->isLValue());475QualType DerefType = Deref->getType();476477// Negation predicate.478UnaryOperator *FlagCheck = UnaryOperator::Create(479C,480/* input=*/481M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType,482CK_IntegralToBoolean),483/* opc=*/UO_LNot,484/* QualType=*/C.IntTy,485/* ExprValueKind=*/VK_PRValue,486/* ExprObjectKind=*/OK_Ordinary, SourceLocation(),487/* CanOverflow*/ false, FPOptionsOverride());488489// Create assignment.490BinaryOperator *FlagAssignment = M.makeAssignment(491Deref, M.makeIntegralCast(M.makeIntegerLiteral(1, C.IntTy), DerefType),492DerefType);493494auto *Out =495IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,496/* Init=*/nullptr,497/* Var=*/nullptr,498/* Cond=*/FlagCheck,499/* LPL=*/SourceLocation(),500/* RPL=*/SourceLocation(),501/* Then=*/M.makeCompound({CallbackCall, FlagAssignment}));502503return Out;504}505506/// Create a fake body for dispatch_once.507static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {508// Check if we have at least two parameters.509if (D->param_size() != 2)510return nullptr;511512// Check if the first parameter is a pointer to integer type.513const ParmVarDecl *Predicate = D->getParamDecl(0);514QualType PredicateQPtrTy = Predicate->getType();515const PointerType *PredicatePtrTy = PredicateQPtrTy->getAs<PointerType>();516if (!PredicatePtrTy)517return nullptr;518QualType PredicateTy = PredicatePtrTy->getPointeeType();519if (!PredicateTy->isIntegerType())520return nullptr;521522// Check if the second parameter is the proper block type.523const ParmVarDecl *Block = D->getParamDecl(1);524QualType Ty = Block->getType();525if (!isDispatchBlock(Ty))526return nullptr;527528// Everything checks out. Create a fakse body that checks the predicate,529// sets it, and calls the block. Basically, an AST dump of:530//531// void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) {532// if (*predicate != ~0l) {533// *predicate = ~0l;534// block();535// }536// }537538ASTMaker M(C);539540// (1) Create the call.541CallExpr *CE = CallExpr::Create(542/*ASTContext=*/C,543/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),544/*Args=*/std::nullopt,545/*QualType=*/C.VoidTy,546/*ExprValueType=*/VK_PRValue,547/*SourceLocation=*/SourceLocation(), FPOptionsOverride());548549// (2) Create the assignment to the predicate.550Expr *DoneValue =551UnaryOperator::Create(C, M.makeIntegerLiteral(0, C.LongTy), UO_Not,552C.LongTy, VK_PRValue, OK_Ordinary, SourceLocation(),553/*CanOverflow*/ false, FPOptionsOverride());554555BinaryOperator *B =556M.makeAssignment(557M.makeDereference(558M.makeLvalueToRvalue(559M.makeDeclRefExpr(Predicate), PredicateQPtrTy),560PredicateTy),561M.makeIntegralCast(DoneValue, PredicateTy),562PredicateTy);563564// (3) Create the compound statement.565Stmt *Stmts[] = { B, CE };566CompoundStmt *CS = M.makeCompound(Stmts);567568// (4) Create the 'if' condition.569ImplicitCastExpr *LValToRval =570M.makeLvalueToRvalue(571M.makeDereference(572M.makeLvalueToRvalue(573M.makeDeclRefExpr(Predicate),574PredicateQPtrTy),575PredicateTy),576PredicateTy);577578Expr *GuardCondition = M.makeComparison(LValToRval, DoneValue, BO_NE);579// (5) Create the 'if' statement.580auto *If = IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,581/* Init=*/nullptr,582/* Var=*/nullptr,583/* Cond=*/GuardCondition,584/* LPL=*/SourceLocation(),585/* RPL=*/SourceLocation(),586/* Then=*/CS);587return If;588}589590/// Create a fake body for dispatch_sync.591static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) {592// Check if we have at least two parameters.593if (D->param_size() != 2)594return nullptr;595596// Check if the second parameter is a block.597const ParmVarDecl *PV = D->getParamDecl(1);598QualType Ty = PV->getType();599if (!isDispatchBlock(Ty))600return nullptr;601602// Everything checks out. Create a fake body that just calls the block.603// This is basically just an AST dump of:604//605// void dispatch_sync(dispatch_queue_t queue, void (^block)(void)) {606// block();607// }608//609ASTMaker M(C);610DeclRefExpr *DR = M.makeDeclRefExpr(PV);611ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);612CallExpr *CE = CallExpr::Create(C, ICE, std::nullopt, C.VoidTy, VK_PRValue,613SourceLocation(), FPOptionsOverride());614return CE;615}616617static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)618{619// There are exactly 3 arguments.620if (D->param_size() != 3)621return nullptr;622623// Signature:624// _Bool OSAtomicCompareAndSwapPtr(void *__oldValue,625// void *__newValue,626// void * volatile *__theValue)627// Generate body:628// if (oldValue == *theValue) {629// *theValue = newValue;630// return YES;631// }632// else return NO;633634QualType ResultTy = D->getReturnType();635bool isBoolean = ResultTy->isBooleanType();636if (!isBoolean && !ResultTy->isIntegralType(C))637return nullptr;638639const ParmVarDecl *OldValue = D->getParamDecl(0);640QualType OldValueTy = OldValue->getType();641642const ParmVarDecl *NewValue = D->getParamDecl(1);643QualType NewValueTy = NewValue->getType();644645assert(OldValueTy == NewValueTy);646647const ParmVarDecl *TheValue = D->getParamDecl(2);648QualType TheValueTy = TheValue->getType();649const PointerType *PT = TheValueTy->getAs<PointerType>();650if (!PT)651return nullptr;652QualType PointeeTy = PT->getPointeeType();653654ASTMaker M(C);655// Construct the comparison.656Expr *Comparison =657M.makeComparison(658M.makeLvalueToRvalue(M.makeDeclRefExpr(OldValue), OldValueTy),659M.makeLvalueToRvalue(660M.makeDereference(661M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy),662PointeeTy),663PointeeTy),664BO_EQ);665666// Construct the body of the IfStmt.667Stmt *Stmts[2];668Stmts[0] =669M.makeAssignment(670M.makeDereference(671M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy),672PointeeTy),673M.makeLvalueToRvalue(M.makeDeclRefExpr(NewValue), NewValueTy),674NewValueTy);675676Expr *BoolVal = M.makeObjCBool(true);677Expr *RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal)678: M.makeIntegralCast(BoolVal, ResultTy);679Stmts[1] = M.makeReturn(RetVal);680CompoundStmt *Body = M.makeCompound(Stmts);681682// Construct the else clause.683BoolVal = M.makeObjCBool(false);684RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal)685: M.makeIntegralCast(BoolVal, ResultTy);686Stmt *Else = M.makeReturn(RetVal);687688/// Construct the If.689auto *If =690IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,691/* Init=*/nullptr,692/* Var=*/nullptr, Comparison,693/* LPL=*/SourceLocation(),694/* RPL=*/SourceLocation(), Body, SourceLocation(), Else);695696return If;697}698699Stmt *BodyFarm::getBody(const FunctionDecl *D) {700std::optional<Stmt *> &Val = Bodies[D];701if (Val)702return *Val;703704Val = nullptr;705706if (D->getIdentifier() == nullptr)707return nullptr;708709StringRef Name = D->getName();710if (Name.empty())711return nullptr;712713FunctionFarmer FF;714715if (unsigned BuiltinID = D->getBuiltinID()) {716switch (BuiltinID) {717case Builtin::BIas_const:718case Builtin::BIforward:719case Builtin::BIforward_like:720case Builtin::BImove:721case Builtin::BImove_if_noexcept:722FF = create_std_move_forward;723break;724default:725FF = nullptr;726break;727}728} else if (Name.starts_with("OSAtomicCompareAndSwap") ||729Name.starts_with("objc_atomicCompareAndSwap")) {730FF = create_OSAtomicCompareAndSwap;731} else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) {732FF = create_call_once;733} else {734FF = llvm::StringSwitch<FunctionFarmer>(Name)735.Case("dispatch_sync", create_dispatch_sync)736.Case("dispatch_once", create_dispatch_once)737.Default(nullptr);738}739740if (FF) { Val = FF(C, D); }741else if (Injector) { Val = Injector->getBody(D); }742return *Val;743}744745static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) {746const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl();747748if (IVar)749return IVar;750751// When a readonly property is shadowed in a class extensions with a752// a readwrite property, the instance variable belongs to the shadowing753// property rather than the shadowed property. If there is no instance754// variable on a readonly property, check to see whether the property is755// shadowed and if so try to get the instance variable from shadowing756// property.757if (!Prop->isReadOnly())758return nullptr;759760auto *Container = cast<ObjCContainerDecl>(Prop->getDeclContext());761const ObjCInterfaceDecl *PrimaryInterface = nullptr;762if (auto *InterfaceDecl = dyn_cast<ObjCInterfaceDecl>(Container)) {763PrimaryInterface = InterfaceDecl;764} else if (auto *CategoryDecl = dyn_cast<ObjCCategoryDecl>(Container)) {765PrimaryInterface = CategoryDecl->getClassInterface();766} else if (auto *ImplDecl = dyn_cast<ObjCImplDecl>(Container)) {767PrimaryInterface = ImplDecl->getClassInterface();768} else {769return nullptr;770}771772// FindPropertyVisibleInPrimaryClass() looks first in class extensions, so it773// is guaranteed to find the shadowing property, if it exists, rather than774// the shadowed property.775auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass(776Prop->getIdentifier(), Prop->getQueryKind());777if (ShadowingProp && ShadowingProp != Prop) {778IVar = ShadowingProp->getPropertyIvarDecl();779}780781return IVar;782}783784static Stmt *createObjCPropertyGetter(ASTContext &Ctx,785const ObjCMethodDecl *MD) {786// First, find the backing ivar.787const ObjCIvarDecl *IVar = nullptr;788const ObjCPropertyDecl *Prop = nullptr;789790// Property accessor stubs sometimes do not correspond to any property decl791// in the current interface (but in a superclass). They still have a792// corresponding property impl decl in this case.793if (MD->isSynthesizedAccessorStub()) {794const ObjCInterfaceDecl *IntD = MD->getClassInterface();795const ObjCImplementationDecl *ImpD = IntD->getImplementation();796for (const auto *PI : ImpD->property_impls()) {797if (const ObjCPropertyDecl *Candidate = PI->getPropertyDecl()) {798if (Candidate->getGetterName() == MD->getSelector()) {799Prop = Candidate;800IVar = Prop->getPropertyIvarDecl();801}802}803}804}805806if (!IVar) {807Prop = MD->findPropertyDecl();808IVar = Prop ? findBackingIvar(Prop) : nullptr;809}810811if (!IVar || !Prop)812return nullptr;813814// Ignore weak variables, which have special behavior.815if (Prop->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak)816return nullptr;817818// Look to see if Sema has synthesized a body for us. This happens in819// Objective-C++ because the return value may be a C++ class type with a820// non-trivial copy constructor. We can only do this if we can find the821// @synthesize for this property, though (or if we know it's been auto-822// synthesized).823const ObjCImplementationDecl *ImplDecl =824IVar->getContainingInterface()->getImplementation();825if (ImplDecl) {826for (const auto *I : ImplDecl->property_impls()) {827if (I->getPropertyDecl() != Prop)828continue;829830if (I->getGetterCXXConstructor()) {831ASTMaker M(Ctx);832return M.makeReturn(I->getGetterCXXConstructor());833}834}835}836837// We expect that the property is the same type as the ivar, or a reference to838// it, and that it is either an object pointer or trivially copyable.839if (!Ctx.hasSameUnqualifiedType(IVar->getType(),840Prop->getType().getNonReferenceType()))841return nullptr;842if (!IVar->getType()->isObjCLifetimeType() &&843!IVar->getType().isTriviallyCopyableType(Ctx))844return nullptr;845846// Generate our body:847// return self->_ivar;848ASTMaker M(Ctx);849850const VarDecl *selfVar = MD->getSelfDecl();851if (!selfVar)852return nullptr;853854Expr *loadedIVar = M.makeObjCIvarRef(855M.makeLvalueToRvalue(M.makeDeclRefExpr(selfVar), selfVar->getType()),856IVar);857858if (!MD->getReturnType()->isReferenceType())859loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType());860861return M.makeReturn(loadedIVar);862}863864Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {865// We currently only know how to synthesize property accessors.866if (!D->isPropertyAccessor())867return nullptr;868869D = D->getCanonicalDecl();870871// We should not try to synthesize explicitly redefined accessors.872// We do not know for sure how they behave.873if (!D->isImplicit())874return nullptr;875876std::optional<Stmt *> &Val = Bodies[D];877if (Val)878return *Val;879Val = nullptr;880881// For now, we only synthesize getters.882// Synthesizing setters would cause false negatives in the883// RetainCountChecker because the method body would bind the parameter884// to an instance variable, causing it to escape. This would prevent885// warning in the following common scenario:886//887// id foo = [[NSObject alloc] init];888// self.foo = foo; // We should warn that foo leaks here.889//890if (D->param_size() != 0)891return nullptr;892893// If the property was defined in an extension, search the extensions for894// overrides.895const ObjCInterfaceDecl *OID = D->getClassInterface();896if (dyn_cast<ObjCInterfaceDecl>(D->getParent()) != OID)897for (auto *Ext : OID->known_extensions()) {898auto *OMD = Ext->getInstanceMethod(D->getSelector());899if (OMD && !OMD->isImplicit())900return nullptr;901}902903Val = createObjCPropertyGetter(C, D);904905return *Val;906}907908909