================
@@ -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
----------------
melver wrote:

A comment would help, and if we have tests exercising this, one of the ASan CI 
bots would tell us if we're wrong.  So I think it's borderline, and being more 
efficient and simpler would be my preference.

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