23#include "llvm/ADT/ArrayRef.h"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/Support/Casting.h"
26#include "llvm/Support/Signals.h"
27#include "llvm/Support/TimeProfiler.h"
30using llvm::isa_and_present;
32OriginList *FactsGenerator::getOriginsList(
const ValueDecl &D) {
33 return FactMgr.getOriginMgr().getOrCreateList(&D);
35OriginList *FactsGenerator::getOriginsList(
const Expr &E) {
36 return FactMgr.getOriginMgr().getOrCreateList(&E);
56 "Dst is non-null but Src is null. List must have the same length");
57 assert(Dst->getLength() == Src->getLength() &&
58 "Lists must have the same length");
61 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
62 Dst->getOuterOriginID(), Src->getOuterOriginID(), Kill));
63 Dst = Dst->peelOuterOrigin();
64 Src = Src->peelOuterOrigin();
74 if (
const auto *VD = dyn_cast<ValueDecl>(DRE->
getDecl())) {
92 llvm::TimeTraceScope TimeProfile(
"FactGenerator");
93 const CFG &Cfg = *AC.getCFG();
98 CurrentBlockFacts.clear();
99 EscapesInCurrentBlock.clear();
101 CurrentBlockFacts.append(PlaceholderLoanFacts.begin(),
102 PlaceholderLoanFacts.end());
103 for (
unsigned I = 0; I <
Block->size(); ++I) {
105 if (std::optional<CFGStmt> CS = Element.
getAs<
CFGStmt>())
106 Visit(CS->getStmt());
107 else if (std::optional<CFGInitializer>
Initializer =
109 handleCXXCtorInitializer(
Initializer->getInitializer());
110 else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
112 handleLifetimeEnds(*LifetimeEnds);
113 else if (std::optional<CFGFullExprCleanup> FullExprCleanup =
115 handleFullExprCleanup(*FullExprCleanup);
121 CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
122 EscapesInCurrentBlock.end());
123 FactMgr.addBlockFacts(
Block, CurrentBlockFacts);
142 if (
const auto *VD = dyn_cast<VarDecl>(D))
143 if (
const Expr *InitExpr = VD->getInit()) {
147 OriginList *InitList = getOriginsList(*InitExpr);
148 assert(InitList &&
"VarDecl had origins but InitExpr did not");
149 flow(VDList, InitList,
true);
172 "gl-value DRE of non-pointer type should have an origin list");
175 CurrentBlockFacts.push_back(
182 handleGSLPointerConstruction(CCE);
195 flow(getOriginsList(*CCE), ArgList,
true);
200 {CCE->getArgs(), CCE->getNumArgs()},
208 killAndFlowOrigin(*FD, *CII->
getInit());
219 {MCE->getImplicitObjectArgument()},
230 handleFunctionCall(MCE,
Method, Args,
false);
237 assert(ME->
isGLValue() &&
"Field member should be GL value");
239 assert(Dst &&
"Field member should have an origin list as it is GL value");
241 assert(Src &&
"Base expression should be a pointer/reference type");
252 {CE->getArgs(), CE->getNumArgs()});
270 case CK_LValueToRValue:
275 assert(Src &&
"LValue being cast to RValue has no origin list");
283 case CK_NullToPointer:
284 getOriginsList(*ICE);
288 case CK_ConstructorConversion:
289 case CK_UserDefinedConversion:
290 flow(Dest, Src,
true);
292 case CK_UncheckedDerivedToBase:
293 case CK_DerivedToBase:
297 flow(Dest, Src,
true);
299 case CK_FunctionToPointerDecay:
300 case CK_BuiltinFnToFnPtr:
301 case CK_ArrayToPointerDecay:
317 killAndFlowOrigin(*UO, *SubExpr);
322 killAndFlowOrigin(*UO, *SubExpr);
332 if (
OriginList *List = getOriginsList(*RetExpr))
335 L->getOuterOriginID(), RetExpr));
339void FactsGenerator::handleAssignment(
const Expr *LHSExpr,
340 const Expr *RHSExpr) {
344 if (
const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) {
345 LHSList = getOriginsList(*DRE_LHS);
346 assert(LHSList &&
"LHS is a DRE and should have an origin list");
351 if (
const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) {
352 LHSList = getOriginsList(*ME_LHS);
353 assert(LHSList &&
"LHS is a MemberExpr and should have an origin list");
357 OriginList *RHSList = getOriginsList(*RHSExpr);
365 if (
const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
366 markUseAsWrite(DRE_LHS);
407 if (handleTestPoint(FCE))
419 killAndFlowOrigin(*ILE, *ILE->
getInit(0));
434 assert((!SubExprList ||
436 "MTE top level origin should contain a loan to the MTE itself");
439 flow(RValMTEList, SubExprList,
true);
444 CurrentBlockFacts.push_back(
457 for (
const Expr *
Init : LE->capture_inits()) {
473bool FactsGenerator::escapesViaReturn(
OriginID OID)
const {
474 return llvm::any_of(EscapesInCurrentBlock, [OID](
const Fact *F) {
476 return EF->getEscapedOriginID() == OID;
481void FactsGenerator::handleLifetimeEnds(
const CFGLifetimeEnds &LifetimeEnds) {
487 std::optional<OriginID> ExpiredOID;
488 if (OriginList *List = getOriginsList(*LifetimeEndsVD)) {
489 OriginID OID = List->getOuterOriginID();
492 if (!escapesViaReturn(OID))
497 if (
const auto *BL = dyn_cast<PathLoan>(Loan)) {
500 const AccessPath AP = BL->getAccessPath();
501 const ValueDecl *Path = AP.getAsValueDecl();
502 if (Path == LifetimeEndsVD)
503 CurrentBlockFacts.push_back(FactMgr.
createFact<ExpireFact>(
510void FactsGenerator::handleFullExprCleanup(
511 const CFGFullExprCleanup &FullExprCleanup) {
513 for (
const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
514 if (
const auto *PL = dyn_cast<PathLoan>(Loan)) {
517 const AccessPath &AP = PL->getAccessPath();
518 const MaterializeTemporaryExpr *Path = AP.getAsMaterializeTemporaryExpr();
521 if (llvm::is_contained(FullExprCleanup.getExpiringMTEs(), Path)) {
522 CurrentBlockFacts.push_back(
523 FactMgr.createFact<ExpireFact>(PL->getID(), Path->getEndLoc()));
529void FactsGenerator::handleExitBlock() {
530 for (
const Origin &O : FactMgr.getOriginMgr().getOrigins())
531 if (
auto *FD = dyn_cast_if_present<FieldDecl>(O.getDecl()))
533 EscapesInCurrentBlock.push_back(
534 FactMgr.createFact<FieldEscapeFact>(O.ID, FD));
535 else if (
auto *VD = dyn_cast_if_present<VarDecl>(O.getDecl())) {
538 if (VD->hasGlobalStorage()) {
539 EscapesInCurrentBlock.push_back(
540 FactMgr.createFact<GlobalEscapeFact>(O.ID, VD));
545void FactsGenerator::handleGSLPointerConstruction(
const CXXConstructExpr *CCE) {
547 if (CCE->getNumArgs() != 1)
550 const Expr *Arg = CCE->getArg(0);
552 OriginList *ArgList = getOriginsList(*Arg);
553 assert(ArgList &&
"GSL pointer argument should have an origin list");
559 flow(getOriginsList(*CCE), ArgList,
true);
560 }
else if (Arg->getType()->isPointerType()) {
565 OriginList *ArgList = getOriginsList(*Arg);
566 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
567 getOriginsList(*CCE)->getOuterOriginID(), ArgList->getOuterOriginID(),
572 handleFunctionCall(CCE, CCE->getConstructor(),
573 {CCE->getArgs(), CCE->getNumArgs()},
578void FactsGenerator::handleMovedArgsInCall(
const FunctionDecl *FD,
579 ArrayRef<const Expr *> Args) {
580 unsigned IsInstance = 0;
581 if (
const auto *MD = dyn_cast<CXXMethodDecl>(FD);
588 const Expr *UniquePtrExpr = Args[0];
589 OriginList *MovedOrigins = getOriginsList(*UniquePtrExpr);
591 CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>(
592 UniquePtrExpr, MovedOrigins->getOuterOriginID()));
597 for (
unsigned I = IsInstance;
598 I < Args.size() && I < FD->getNumParams() + IsInstance; ++I) {
599 const ParmVarDecl *PVD = FD->getParamDecl(I - IsInstance);
600 if (!PVD->getType()->isRValueReferenceType())
602 const Expr *Arg = Args[I];
603 OriginList *MovedOrigins = getOriginsList(*Arg);
604 assert(MovedOrigins->getLength() >= 1 &&
605 "unexpected length for r-value reference param");
607 CurrentBlockFacts.push_back(FactMgr.createFact<MovedOriginFact>(
608 Arg, MovedOrigins->getOuterOriginID()));
612void FactsGenerator::handleInvalidatingCall(
const Expr *
Call,
613 const FunctionDecl *FD,
614 ArrayRef<const Expr *> Args) {
615 const auto *MD = dyn_cast<CXXMethodDecl>(FD);
616 if (!MD || !MD->isInstance())
622 auto *DRE = dyn_cast<DeclRefExpr>(Args[0]);
623 if (!DRE || DRE->getDecl()->getType()->isReferenceType())
626 OriginList *ThisList = getOriginsList(*Args[0]);
628 CurrentBlockFacts.push_back(FactMgr.createFact<InvalidateOriginFact>(
629 ThisList->getOuterOriginID(),
Call));
632void FactsGenerator::handleFunctionCall(
const Expr *
Call,
633 const FunctionDecl *FD,
634 ArrayRef<const Expr *> Args,
635 bool IsGslConstruction) {
636 OriginList *CallList = getOriginsList(*
Call);
642 for (
const Expr *Arg : Args)
644 handleInvalidatingCall(
Call, FD, Args);
645 handleMovedArgsInCall(FD, Args);
648 auto IsArgLifetimeBound = [FD](
unsigned I) ->
bool {
649 const ParmVarDecl *PVD =
nullptr;
650 if (
const auto *
Method = dyn_cast<CXXMethodDecl>(FD);
657 if ((I - 1) <
Method->getNumParams())
660 PVD =
Method->getParamDecl(I - 1);
663 }
else if (I < FD->getNumParams()) {
665 PVD = FD->getParamDecl(I);
667 return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() :
false;
669 auto shouldTrackPointerImplicitObjectArg = [FD](
unsigned I) ->
bool {
670 const auto *
Method = dyn_cast<CXXMethodDecl>(FD);
681 for (
unsigned I = 0; I < Args.size(); ++I) {
682 OriginList *ArgList = getOriginsList(*Args[I]);
685 if (IsGslConstruction) {
689 assert(!Args[I]->isGLValue() || ArgList->getLength() >= 2);
695 flow(CallList, ArgList, KillSrc);
698 }
else if (shouldTrackPointerImplicitObjectArg(I)) {
699 assert(ArgList->getLength() >= 2 &&
700 "Object arg of pointer type should have atleast two origins");
702 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
703 CallList->getOuterOriginID(),
704 ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
706 }
else if (IsArgLifetimeBound(I)) {
710 CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
711 CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
719bool FactsGenerator::handleTestPoint(
const CXXFunctionalCastExpr *FCE) {
720 if (!FCE->getType()->isVoidType())
723 const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
724 if (
const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
725 llvm::StringRef LiteralValue = SL->getString();
726 const std::string Prefix =
"__lifetime_test_point_";
728 if (LiteralValue.starts_with(Prefix)) {
729 StringRef Annotation = LiteralValue.drop_front(Prefix.length());
730 CurrentBlockFacts.push_back(
731 FactMgr.createFact<TestPointFact>(Annotation));
738void FactsGenerator::handleUse(
const Expr *E) {
739 OriginList *List = getOriginsList(*E);
745 if (
auto *DRE = dyn_cast<DeclRefExpr>(E);
746 DRE && !DRE->getDecl()->
getType()->isReferenceType())
751 if (!UseFacts.contains(E)) {
752 UseFact *UF = FactMgr.createFact<UseFact>(E, List);
753 CurrentBlockFacts.push_back(UF);
758void FactsGenerator::markUseAsWrite(
const DeclRefExpr *DRE) {
759 if (UseFacts.contains(DRE))
760 UseFacts[DRE]->markAsWritten();
765llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
766 const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl());
770 llvm::SmallVector<Fact *> PlaceholderLoanFacts;
771 if (
auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) {
772 OriginList *List = *ThisOrigins;
773 const PlaceholderLoan *L = FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(
775 PlaceholderLoanFacts.push_back(
776 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
778 for (
const ParmVarDecl *PVD : FD->parameters()) {
779 OriginList *List = getOriginsList(*PVD);
782 const PlaceholderLoan *L =
783 FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(PVD);
784 PlaceholderLoanFacts.push_back(
785 FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
787 return PlaceholderLoanFacts;
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
static bool isCompoundAssignmentOp(Opcode Opc)
Represents a single basic block in a source-level CFG.
Represents a top-level expression in a basic block.
std::optional< T > getAs() const
Convert to the specified CFGElement type, returning std::nullopt if this CFGElement is not of the des...
Represents C++ base or member initializer from constructor's initialization list.
Represents the point where the lifetime of an automatic object ends.
const Stmt * getTriggerStmt() const
const VarDecl * getVarDecl() const
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Represents binding an expression to a temporary.
const Expr * getSubExpr() const
Represents a call to a C++ constructor.
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
bool isCopyOrMoveConstructor(unsigned &TypeQuals) const
Determine whether this is a copy or move constructor.
Represents a C++ base or member initializer.
Expr * getInit() const
Get the initializer.
FieldDecl * getAnyMember() const
Represents an explicit C++ type conversion that uses "functional" notation (C++ [expr....
Represents a call to a member function that may be written either with member call syntax (e....
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Represents a static or instance method of a struct/union/class.
The null pointer literal (C++11 [lex.nullptr])
A call to an overloaded operator written using operator syntax.
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
bool isLambda() const
Determine whether this class describes a lambda function object.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Expr ** getArgs()
Retrieve the call arguments.
CastKind getCastKind() const
ConditionalOperator - The ?
Expr * getFalseExpr() const
getFalseExpr - Return the subexpression representing the value of the expression if the condition eva...
Expr * getTrueExpr() const
getTrueExpr - Return the subexpression representing the value of the expression if the condition eval...
A reference to a declared variable, function, enum, etc.
NamedDecl * getFoundDecl()
Get the NamedDecl through which this reference occurred.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Decl - This represents one declaration (or definition), e.g.
bool isFunctionOrFunctionTemplate() const
Whether this declaration is a function or function template.
This represents one expression.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Represents a member of a struct/union/class.
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Describes an C or C++ initializer list.
unsigned getNumInits() const
const Expr * getInit(unsigned Init) const
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
StorageDuration getStorageDuration() const
Retrieve the storage duration for the materialized temporary.
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
ValueDecl * getMemberDecl() const
Retrieve the member declaration to which this expression refers.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
RetTy Visit(PTR(Stmt) S, ParamTys... P)
SourceLocation getEndLoc() const LLVM_READONLY
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
Represents a variable declaration or definition.
Represents the storage location being borrowed, e.g., a specific stack variable.
FactType * createFact(Args &&...args)
LoanManager & getLoanMgr()
An abstract base class for a single, atomic lifetime-relevant event.
void VisitDeclRefExpr(const DeclRefExpr *DRE)
void VisitBinaryOperator(const BinaryOperator *BO)
void VisitCallExpr(const CallExpr *CE)
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE)
void VisitReturnStmt(const ReturnStmt *RS)
void VisitCXXConstructExpr(const CXXConstructExpr *CCE)
void VisitImplicitCastExpr(const ImplicitCastExpr *ICE)
void VisitMemberExpr(const MemberExpr *ME)
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE)
void VisitInitListExpr(const InitListExpr *ILE)
void VisitLambdaExpr(const LambdaExpr *LE)
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N)
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE)
void VisitUnaryOperator(const UnaryOperator *UO)
void VisitDeclStmt(const DeclStmt *DS)
void VisitConditionalOperator(const ConditionalOperator *CO)
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE)
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE)
llvm::ArrayRef< const Loan * > getLoans() const
LoanType * createLoan(Args &&...args)
An abstract base class for a single "Loan" which represents lending a storage in memory.
A list of origins representing levels of indirection for pointer-like types.
OriginID getOuterOriginID() const
OriginList * peelOuterOrigin() const
PathLoan represents lending a storage location that is visible within the function's scope (e....
Represents that an origin escapes via a return statement.
static const PathLoan * createLoan(FactManager &FactMgr, const DeclRefExpr *DRE)
Creates a loan for the storage path of a given declaration reference.
utils::ID< struct OriginTag > OriginID
static OriginList * getRValueOrigins(const Expr *E, OriginList *List)
Simulates LValueToRValue conversion by peeling the outer lvalue origin if the expression is a GLValue...
bool doesDeclHaveStorage(const ValueDecl *D)
Returns true if the declaration has its own storage that can be borrowed.
bool hasOrigins(QualType QT)
bool isGslPointerType(QualType QT)
bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, bool RunningUnderLifetimeSafety)
bool shouldTrackFirstArgument(const FunctionDecl *FD)
bool isContainerInvalidationMethod(const CXXMethodDecl &MD)
bool isUniquePtrRelease(const CXXMethodDecl &MD)
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD)
const FunctionDecl * getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD)
bool isGslOwnerType(QualType QT)
bool isa(CodeGen::Address addr)
@ SD_FullExpression
Full-expression storage duration (for temporaries).
U cast(CodeGen::Address addr)