================
@@ -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