================
@@ -1617,123 +1618,191 @@ static bool getStaticBooleanValue(Expr *E, bool 
&TCond) {
   return false;
 }
 
-// If Cond can be traced back to a function call, return the call expression.
-// The negate variable should be called with false, and will be set to true
-// if the function call is negated, e.g. if (!mu.tryLock(...))
-const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
-                                                         LocalVarContext C,
-                                                         bool &Negate) {
+// If Cond can be traced back to a try-acquire function call, the `D` variable
+// will be populated with the call and with how the branched-on value relates
+// to its result.
+void ThreadSafetyAnalyzer::decodeTrylockCond(const Stmt *Cond,
+                                             LocalVarContext C,
+                                             TrylockDecode &D) {
   if (!Cond)
-    return nullptr;
+    return;
 
   if (const auto *CallExp = dyn_cast<CallExpr>(Cond)) {
     if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
-      return getTrylockCallExpr(CallExp->getArg(0), C, Negate);
-    return CallExp;
+      return decodeTrylockCond(CallExp->getArg(0), C, D);
+    const auto *FD = dyn_cast_or_null<NamedDecl>(CallExp->getCalleeDecl());
+    if (FD && FD->hasAttr<TryAcquireCapabilityAttr>())
+      D.TrylockCall = CallExp;
+    return;
   }
   else if (const auto *PE = dyn_cast<ParenExpr>(Cond))
-    return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
+    return decodeTrylockCond(PE->getSubExpr(), C, D);
   else if (const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
-    return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
+    return decodeTrylockCond(CE->getSubExpr(), C, D);
   else if (const auto *FE = dyn_cast<FullExpr>(Cond))
-    return getTrylockCallExpr(FE->getSubExpr(), C, Negate);
+    return decodeTrylockCond(FE->getSubExpr(), C, D);
   else if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
     const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
-    return getTrylockCallExpr(E, C, Negate);
+    return decodeTrylockCond(E, C, D);
   }
   else if (const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
     if (UOP->getOpcode() == UO_LNot) {
-      Negate = !Negate;
-      return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
+      D.Negate = !D.Negate;
+      return decodeTrylockCond(UOP->getSubExpr(), C, D);
     }
-    return nullptr;
+    return;
   }
   else if (const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
     if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
       if (BOP->getOpcode() == BO_NE)
-        Negate = !Negate;
+        D.Negate = !D.Negate;
 
       bool TCond = false;
       if (getStaticBooleanValue(BOP->getRHS(), TCond)) {
-        if (!TCond) Negate = !Negate;
-        return getTrylockCallExpr(BOP->getLHS(), C, Negate);
+        if (!TCond)
+          D.Negate = !D.Negate;
+        return decodeTrylockCond(BOP->getLHS(), C, D);
       }
       TCond = false;
       if (getStaticBooleanValue(BOP->getLHS(), TCond)) {
-        if (!TCond) Negate = !Negate;
-        return getTrylockCallExpr(BOP->getRHS(), C, Negate);
+        if (!TCond)
+          D.Negate = !D.Negate;
+        return decodeTrylockCond(BOP->getRHS(), C, D);
       }
-      return nullptr;
+      return;
     }
     if (BOP->getOpcode() == BO_LAnd) {
       // LHS must have been evaluated in a different block.
-      return getTrylockCallExpr(BOP->getRHS(), C, Negate);
+      return decodeTrylockCond(BOP->getRHS(), C, D);
     }
     if (BOP->getOpcode() == BO_LOr)
-      return getTrylockCallExpr(BOP->getRHS(), C, Negate);
-    return nullptr;
+      return decodeTrylockCond(BOP->getRHS(), C, D);
+    return;
   } else if (const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {
     bool TCond, FCond;
     if (getStaticBooleanValue(COP->getTrueExpr(), TCond) &&
         getStaticBooleanValue(COP->getFalseExpr(), FCond)) {
       if (TCond && !FCond)
-        return getTrylockCallExpr(COP->getCond(), C, Negate);
+        return decodeTrylockCond(COP->getCond(), C, D);
       if (!TCond && FCond) {
-        Negate = !Negate;
-        return getTrylockCallExpr(COP->getCond(), C, Negate);
+        D.Negate = !D.Negate;
+        return decodeTrylockCond(COP->getCond(), C, D);
       }
     }
   } else if (const auto *SE = dyn_cast<StmtExpr>(Cond)) {
     if (const auto *CS = SE->getSubStmt(); CS && !CS->body_empty()) {
       if (const auto *E = dyn_cast<Expr>(CS->body_back()))
-        return getTrylockCallExpr(E, C, Negate);
+        return decodeTrylockCond(E, C, D);
     }
   }
-  return nullptr;
+}
+
+/// Decode a try-acquire attribute's success value.
+static bool getTrySuccessValue(const Expr *BrE) {
+  if (const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
+    return BLE->getValue();
+  if (const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
+    return ILE->getValue().getBoolValue();
+  return false;
 }
 
 /// If the terminator of \p Block branches on the result of a call to a
 /// function annotated with try_acquire_capability (possibly negated or stored
-/// in a local variable), return that call and its callee. \p Negate is set if
-/// the branch tests the negated result of the call. In beta mode, this leaves
-/// the local variable lookup closure of SExprBuilder installed so that callers
-/// can translate the callee's attribute expressions
-ThreadSafetyAnalyzer::TerminatorTrylockCall
-ThreadSafetyAnalyzer::getTerminatorTrylockCall(const CFGBlock *Block,
-                                               bool &Negate) {
-  assert(!Negate && "Must be called with Negate initialized to false");
+/// in a local variable), return the capabilities the call's attributes name,
+/// each with the resolution every branch direction proves for it. Attributes
+/// may carry different success values; each is decoded on its own.
+const ThreadSafetyAnalyzer::TrylockBranch &
+ThreadSafetyAnalyzer::decodeTrylockBranch(const CFGBlock *Block) {
+  const unsigned BlockID = Block->getBlockID();
+
+  if (auto It = TerminatorTrylockCache.find(BlockID);
+      It != TerminatorTrylockCache.end())
+    return It->second;
+  auto CacheMiss = [&]() -> const TrylockBranch & {
+    return TerminatorTrylockCache[BlockID] = TrylockBranch{};
+  };
 
   const Stmt *Cond = Block->getTerminatorCondition();
   if (!Cond)
-    return {};
+    return CacheMiss();
 
   // We don't acquire try-locks on ?: branches, except when its result is used.
   if (const auto *COp =
           dyn_cast_if_present<ConditionalOperator>(Block->getTerminatorStmt()))
     if (!COp->getType()->isVoidType())
-      return {};
+      return CacheMiss();
+
+  const LocalVarContext &LVarCtx = BlockInfo[BlockID].ExitContext;
 
-  const LocalVarContext &LVarCtx = BlockInfo[Block->getBlockID()].ExitContext;
+  TrylockDecode D;
+  decodeTrylockCond(Cond, LVarCtx, D);
+  if (!D.TrylockCall)
+    return CacheMiss();
+  const auto *FunDecl = cast<NamedDecl>(D.TrylockCall->getCalleeDecl());
 
-  std::optional<llvm::scope_exit<std::function<void()>>> Cleanup;
   if (Handler.issueBetaWarnings()) {
     // Temporarily set the lookup context for SExprBuilder.
     SxBuilder.setLookupLocalVarExpr(
-        [this, Ctx = LVarCtx](const NamedDecl *D) mutable -> const Expr * {
-          return LocalVarMap.lookupExpr(D, Ctx);
+        [this, Ctx = LVarCtx](const NamedDecl *VD) mutable -> const Expr * {
+          return LocalVarMap.lookupExpr(VD, Ctx);
         });
-    Cleanup.emplace([this] { SxBuilder.setLookupLocalVarExpr(nullptr); });
   }
+  CapExprSet TruthyExclusive, TruthyShared, FalsyExclusive, FalsyShared;
+  for (const auto *Attr : FunDecl->specific_attrs<TryAcquireCapabilityAttr>()) 
{
+    const bool Success = getTrySuccessValue(Attr->getSuccessValue());
+    getMutexIDs(Success ? (Attr->isShared() ? TruthyShared : TruthyExclusive)
+                        : (Attr->isShared() ? FalsyShared : FalsyExclusive),
+                Attr, D.TrylockCall, FunDecl);
+  }
+  if (Handler.issueBetaWarnings())
+    SxBuilder.setLookupLocalVarExpr(nullptr);
+
+  // Translate call truthiness to branch truthiness.
+  TrylockBranch Result;
+  Result.TrylockCall = D.TrylockCall;
+  auto AddCaps = [&](const CapExprSet &CapSet, LockKind LK, bool Success) {
+    for (const CapabilityExpr &CE : CapSet) {
+      (Success != D.Negate ? Result.OnTrue : Result.OnFalse)
+          .push_back({CE, LK, CapResolution::Success});
+      (Success != D.Negate ? Result.OnFalse : Result.OnTrue)
+          .push_back({CE, LK, CapResolution::Failure});
+    }
+  };
+  AddCaps(TruthyExclusive, LK_Exclusive, /*Success=*/true);
+  AddCaps(TruthyShared, LK_Shared, /*Success=*/true);
+  AddCaps(FalsyExclusive, LK_Exclusive, /*Success=*/false);
+  AddCaps(FalsyShared, LK_Shared, /*Success=*/false);
+  return TerminatorTrylockCache[BlockID] = std::move(Result);
+}
 
-  const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
-  if (!Exp)
-    return {};
-
-  auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
-  if (!FunDecl || !FunDecl->hasAttr<TryAcquireCapabilityAttr>())
-    return {};
-
-  return {Exp, FunDecl, std::move(Cleanup)};
+/// Decode what the edge from \p PredBlock to \p CurrBlock proves about
+/// conditional capabilities.
+ThreadSafetyAnalyzer::TrylockEdge
----------------
vtjnash wrote:

I was a bit nervous about reviewers not knowing the lifetime of the pointer (it 
is almost always an empty set, so the copy is nearly always free), but if 
you're okay with it, I'm okay with it.

https://github.com/llvm/llvm-project/pull/220634
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to