-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][dataflow] Add reverse null checker #1
Conversation
You can test this locally with the following command:git-clang-format --diff 35d771fd4ffd300e527da87b8329bd120b220a83 086c092dedf063d327cc118aac56f6d2345208eb -- clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h clang-tools-extra/test/clang-tidy/checkers/bugprone/reverse-null.cpp clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp View the diff from clang-format here.diff --git a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
index 253bfb130..2fdf8b497 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
@@ -32,7 +32,6 @@ using ast_matchers::MatchFinder;
using dataflow::ReverseNullDiagnoser;
using dataflow::ReverseNullModel;
-
static constexpr llvm::StringLiteral FuncID("fun");
static std::optional<ReverseNullDiagnoser::ResultType>
@@ -61,19 +60,21 @@ analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) {
std::optional<DataflowAnalysisState<ReverseNullModel::Lattice>>>>
BlockToOutputState = dataflow::runDataflowAnalysis(
*Context, Analysis, Env,
- [&ASTCtx, &Diagnoser, &Diagnostics](
- const CFGElement &Elt,
- const DataflowAnalysisState<ReverseNullModel::Lattice>
- &State) mutable {
+ [&ASTCtx, &Diagnoser,
+ &Diagnostics](const CFGElement &Elt,
+ const DataflowAnalysisState<ReverseNullModel::Lattice>
+ &State) mutable {
auto EltDiagnostics = Diagnoser.diagnose(ASTCtx, &Elt, State.Env);
- llvm::move(EltDiagnostics.first, std::back_inserter(Diagnostics.first));
- llvm::move(EltDiagnostics.second, std::back_inserter(Diagnostics.second));
+ llvm::move(EltDiagnostics.first,
+ std::back_inserter(Diagnostics.first));
+ llvm::move(EltDiagnostics.second,
+ std::back_inserter(Diagnostics.second));
});
// FIXME: Port this over into UncheckedOptionalAccessCheck
if (llvm::Error E = BlockToOutputState.takeError()) {
- llvm::dbgs() << "Dataflow analysis failed: "
- << llvm::toString(std::move(E)) << ".\n";
+ llvm::dbgs() << "Dataflow analysis failed: " << llvm::toString(std::move(E))
+ << ".\n";
return std::nullopt;
}
@@ -96,8 +97,7 @@ void ReverseNullCheck::registerMatchers(MatchFinder *Finder) {
this);
}
-void ReverseNullCheck::check(
- const MatchFinder::MatchResult &Result) {
+void ReverseNullCheck::check(const MatchFinder::MatchResult &Result) {
// llvm::errs() << "Starting result parse\n";
if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred())
return;
@@ -114,7 +114,8 @@ void ReverseNullCheck::check(
diag(Loc, "pointer value is checked despite dereferencing it earlier");
for (const SourceLocation &Loc : Errors->first)
- diag(Loc, "pointer value is checked but it can only be null at this point");
+ diag(Loc,
+ "pointer value is checked but it can only be null at this point");
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
index 2ab182c9a..4295bae75 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
@@ -20,7 +20,7 @@ class ReverseNullCheck : public ClangTidyCheck {
public:
ReverseNullCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
-
+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
@@ -29,7 +29,7 @@ public:
};
} // namespace bugprone
-}
-}
+} // namespace tidy
+} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REVERSENULLCHECK_H
diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
index 9ee8a0d28..683cdfcfb 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
@@ -29,7 +29,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-
namespace clang {
namespace dataflow {
@@ -39,13 +38,13 @@ namespace dataflow {
// invalidate the analysis. It also could be optimized to drop out-of-scope
// variables from the map.
class ReverseNullModel
- : public DataflowAnalysis<ReverseNullModel,
- NoopLattice> {
+ : public DataflowAnalysis<ReverseNullModel, NoopLattice> {
public:
struct TransferArgs {
bool Branch;
Environment &Env;
};
+
private:
CFGMatchSwitch<Environment> TransferMatchSwitch;
ASTMatchSwitch<Stmt, TransferArgs> BranchTransferMatchSwitch;
@@ -53,30 +52,27 @@ private:
public:
explicit ReverseNullModel(ASTContext &Context);
- static NoopLattice initialElement() {
- return {};
- }
+ static NoopLattice initialElement() { return {}; }
static ast_matchers::StatementMatcher ptrValueMatcher();
// Used to initialize the storage locations of function arguments.
// Required to merge these values within the environment.
- void initializeFunctionParameters(const ControlFlowContext &CFCtx, Environment &Env);
+ void initializeFunctionParameters(const ControlFlowContext &CFCtx,
+ Environment &Env);
- void transfer(const CFGElement &E, NoopLattice &State,
- Environment &Env);
+ void transfer(const CFGElement &E, NoopLattice &State, Environment &Env);
void transferBranch(bool Branch, const Stmt *E, NoopLattice &State,
- Environment &Env);
+ Environment &Env);
- bool merge(QualType Type,
- const Value &Val1, const Environment &Env1,
- const Value &Val2, const Environment &Env2,
- Value &MergedVal, Environment &MergedEnv) override;
+ bool merge(QualType Type, const Value &Val1, const Environment &Env1,
+ const Value &Val2, const Environment &Env2, Value &MergedVal,
+ Environment &MergedEnv) override;
- ComparisonResult compare(QualType Type,
- const Value &Val1, const Environment &Env1,
- const Value &Val2, const Environment &Env2) override;
+ ComparisonResult compare(QualType Type, const Value &Val1,
+ const Environment &Env1, const Value &Val2,
+ const Environment &Env2) override;
Value *widen(QualType Type, Value &Prev, const Environment &PrevEnv,
Value &Current, Environment &CurrentEnv) override;
@@ -85,17 +81,17 @@ public:
class ReverseNullDiagnoser {
public:
ReverseNullDiagnoser();
- using ResultType = std::pair<std::vector<SourceLocation>, std::vector<SourceLocation>>;
+ using ResultType =
+ std::pair<std::vector<SourceLocation>, std::vector<SourceLocation>>;
ResultType diagnose(ASTContext &Ctx, const CFGElement *Elt,
- const Environment &Env);
+ const Environment &Env);
private:
- CFGMatchSwitch<const Environment, ResultType>
- DiagnoseMatchSwitch;
+ CFGMatchSwitch<const Environment, ResultType> DiagnoseMatchSwitch;
};
-}
-}
+} // namespace dataflow
+} // namespace clang
#endif // CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_REVERSENULLMODEL_H
diff --git a/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
index 3f0a5f31e..e2e9dbe54 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
@@ -37,24 +37,19 @@ constexpr char kVar[] = "var"; // FIXME: Make into prvalue, rvalue
constexpr char kIsNonnull[] = "is-nonnull";
constexpr char kIsNull[] = "is-null";
-enum class SatisfiabilityResult {
- Nullptr,
- Top,
- True,
- False,
- Unknown
-};
+enum class SatisfiabilityResult { Nullptr, Top, True, False, Unknown };
using SR = SatisfiabilityResult;
-auto ptrToVar(llvm::StringRef VarName = kVar) {
- return traverse(TK_IgnoreUnlessSpelledInSource,
+auto ptrToVar(llvm::StringRef VarName = kVar) {
+ return traverse(TK_IgnoreUnlessSpelledInSource,
declRefExpr(hasType(isAnyPointer())).bind(VarName));
}
-auto namedPtrToVar(llvm::StringRef name) {
- return functionDecl(hasDescendant(declRefExpr(allOf(hasType(isAnyPointer()),
- hasDeclaration(namedDecl(hasName(name))))).bind(kVar)
- ));
+auto namedPtrToVar(llvm::StringRef name) {
+ return functionDecl(
+ hasDescendant(declRefExpr(allOf(hasType(isAnyPointer()),
+ hasDeclaration(namedDecl(hasName(name)))))
+ .bind(kVar)));
}
auto derefMatcher() {
@@ -72,56 +67,56 @@ auto arrowMatcher() {
// FIXME: Deliberately simple - a proper check would only match direct ptr casts
auto castExprMatcher() {
return castExpr(hasCastKind(CK_PointerToBoolean),
- hasSourceExpression(
- ptrToVar()))
+ hasSourceExpression(ptrToVar()))
.bind(kCond);
}
-auto nullptrMatcher() {
- return expr(nullPointerConstant()).bind(kVar);
-}
+auto nullptrMatcher() { return expr(nullPointerConstant()).bind(kVar); }
auto addressofMatcher() {
return unaryOperator(hasOperatorName("&")).bind(kVar);
}
auto functionCallMatcher() {
- return callExpr(hasDeclaration(functionDecl(returns(isAnyPointer())))).bind(kVar);
+ return callExpr(hasDeclaration(functionDecl(returns(isAnyPointer()))))
+ .bind(kVar);
}
-auto anyPointerMatcher() {
- return expr(hasType(isAnyPointer())).bind(kVar);
-}
+auto anyPointerMatcher() { return expr(hasType(isAnyPointer())).bind(kVar); }
// Only computes simple comparisons against boolean True and False values.
// Faster, but produces many Unknown values.
-SatisfiabilityResult shallowComputeSatisfiability(BoolValue *Val, const Environment &Env) {
+SatisfiabilityResult shallowComputeSatisfiability(BoolValue *Val,
+ const Environment &Env) {
if (!Val)
return SR::Nullptr;
-
+
if (isa<TopBoolValue>(Val))
return SR::Top;
if (Val == &Env.getBoolLiteralValue(true))
return SR::True;
-
+
if (Val == &Env.getBoolLiteralValue(false))
return SR::False;
return SR::Unknown;
}
-// Computes satisfiability by using the flow condition. Slower, but more precise.
-SatisfiabilityResult computeSatisfiability(BoolValue *Val, const Environment &Env) {
+// Computes satisfiability by using the flow condition. Slower, but more
+// precise.
+SatisfiabilityResult computeSatisfiability(BoolValue *Val,
+ const Environment &Env) {
// Early return with the fast compute values.
- if (SatisfiabilityResult ShallowResult = shallowComputeSatisfiability(Val, Env);
+ if (SatisfiabilityResult ShallowResult =
+ shallowComputeSatisfiability(Val, Env);
ShallowResult != SR::Unknown) {
return ShallowResult;
}
if (Env.flowConditionImplies(Val->formula()))
return SR::True;
-
+
if (Env.flowConditionImplies(Env.arena().makeNot(Val->formula())))
return SR::False;
@@ -134,14 +129,14 @@ inline BoolValue &getVal(llvm::StringRef Name, Value &RootValue) {
void initializeRootValue(Value &RootValue, Environment &Env) {
- Arena &A = Env.arena();
+ Arena &A = Env.arena();
- BoolValue &IsNull = A.makeAtomValue();
- BoolValue &IsNonnull = A.makeAtomValue();
- RootValue.setProperty(kIsNull, IsNull);
- RootValue.setProperty(kIsNonnull, IsNonnull);
+ BoolValue &IsNull = A.makeAtomValue();
+ BoolValue &IsNonnull = A.makeAtomValue();
+ RootValue.setProperty(kIsNull, IsNull);
+ RootValue.setProperty(kIsNonnull, IsNonnull);
- Env.addToFlowCondition(A.makeOr(IsNull.formula(), IsNonnull.formula()));
+ Env.addToFlowCondition(A.makeOr(IsNull.formula(), IsNonnull.formula()));
}
void setLValue(const Expr &E, Value &Val, Environment &Env) {
@@ -165,40 +160,41 @@ void setUnknownValue(const Expr &E, Value &Val, Environment &Env) {
}
Value *getValue(const Expr &Var, Environment &Env) {
- if (auto *EnvVal = Env.getValue(Var)) {
- // FIXME: Hack
- if (!EnvVal->getProperty(kIsNull) && !EnvVal->getProperty(kIsNonnull)) {
- initializeRootValue(*EnvVal, Env);
- }
-
- return EnvVal;
+ if (auto *EnvVal = Env.getValue(Var)) {
+ // FIXME: Hack
+ if (!EnvVal->getProperty(kIsNull) && !EnvVal->getProperty(kIsNonnull)) {
+ initializeRootValue(*EnvVal, Env);
}
- auto *RootValue = Env.createValue(Var.getType());
+ return EnvVal;
+ }
- if (!RootValue)
- return nullptr;
+ auto *RootValue = Env.createValue(Var.getType());
- initializeRootValue(*RootValue, Env);
+ if (!RootValue)
+ return nullptr;
+
+ initializeRootValue(*RootValue, Env);
- setLValue(Var, *RootValue, Env);
+ setLValue(Var, *RootValue, Env);
- return RootValue;
+ return RootValue;
}
void matchDereferenceExpr(const Stmt *stmt,
const MatchFinder::MatchResult &Result,
Environment &Env) {
- const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
- assert(Var != nullptr);
+ const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+ assert(Var != nullptr);
- // FIXME: Optimize
- auto *RootValue = getValue(*Var, Env);
- if (!RootValue) {
- return;
- }
+ // FIXME: Optimize
+ auto *RootValue = getValue(*Var, Env);
+ if (!RootValue) {
+ return;
+ }
- Env.addToFlowCondition(Env.arena().makeNot(getVal(kIsNull, *RootValue).formula()));
+ Env.addToFlowCondition(
+ Env.arena().makeNot(getVal(kIsNull, *RootValue).formula()));
}
void matchCastExpr(const CastExpr *cond, const MatchFinder::MatchResult &Result,
@@ -211,109 +207,117 @@ void matchCastExpr(const CastExpr *cond, const MatchFinder::MatchResult &Result,
auto *RootValue = getValue(*Var, Env);
if (!RootValue) {
return;
- }
+ }
- auto *NewRootValue = Env.createValue(Var->getType());
- if (!NewRootValue)
- return;
+ auto *NewRootValue = Env.createValue(Var->getType());
+ if (!NewRootValue)
+ return;
- setLValue(*Var, *NewRootValue, Env);
+ setLValue(*Var, *NewRootValue, Env);
- Arena &A = Env.arena();
+ Arena &A = Env.arena();
- if (IsNonnullBranch) {
- BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
- BoolValue &IsNull = getVal(kIsNull, *RootValue);
+ if (IsNonnullBranch) {
+ BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
+ BoolValue &IsNull = getVal(kIsNull, *RootValue);
- Env.addToFlowCondition(A.makeNot(IsNull.formula()));
- NewRootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
+ Env.addToFlowCondition(A.makeNot(IsNull.formula()));
+ NewRootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
- NewRootValue->setProperty(kIsNonnull, IsNonnull);
- } else {
- BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
- BoolValue &IsNull = getVal(kIsNull, *RootValue);
+ NewRootValue->setProperty(kIsNonnull, IsNonnull);
+ } else {
+ BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
+ BoolValue &IsNull = getVal(kIsNull, *RootValue);
- NewRootValue->setProperty(kIsNull, IsNull);
+ NewRootValue->setProperty(kIsNull, IsNull);
- Env.addToFlowCondition(A.makeNot(IsNonnull.formula()));
- NewRootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
- }
+ Env.addToFlowCondition(A.makeNot(IsNonnull.formula()));
+ NewRootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
+ }
}
-void matchNullptrExpr(const Expr *expr, const MatchFinder::MatchResult &Result, Environment &Env) {
- const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
- assert(PrVar != nullptr);
+void matchNullptrExpr(const Expr *expr, const MatchFinder::MatchResult &Result,
+ Environment &Env) {
+ const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
+ assert(PrVar != nullptr);
- if (Env.getValue(*PrVar))
- return;
+ if (Env.getValue(*PrVar))
+ return;
- // FIXME: Nullptr does not create a value
- auto *RootValue = Env.createValue(PrVar->getType());
- if (!RootValue) {
- return;
- }
+ // FIXME: Nullptr does not create a value
+ auto *RootValue = Env.createValue(PrVar->getType());
+ if (!RootValue) {
+ return;
+ }
- RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(true));
- RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
- Env.setValue(*PrVar, *RootValue);
+ RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(true));
+ RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
+ Env.setValue(*PrVar, *RootValue);
}
-void matchAddressofExpr(const Expr *expr, const MatchFinder::MatchResult &Result, Environment &Env) {
- const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
- assert(PrVar != nullptr);
+void matchAddressofExpr(const Expr *expr,
+ const MatchFinder::MatchResult &Result,
+ Environment &Env) {
+ const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
+ assert(PrVar != nullptr);
- auto *RootValue = Env.createValue(PrVar->getType());
- if (!RootValue) {
- return;
- }
+ auto *RootValue = Env.createValue(PrVar->getType());
+ if (!RootValue) {
+ return;
+ }
- RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
- RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(true));
- Env.setValue(*PrVar, *RootValue);
+ RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
+ RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(true));
+ Env.setValue(*PrVar, *RootValue);
}
-void matchFunctionCallExpr(const Expr *fncall, const MatchFinder::MatchResult &Result, Environment &Env) {
- // This is not necessarily a prvalue, since operators such as prefix ++ are lvalues.
- const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
- assert(Var != nullptr);
+void matchFunctionCallExpr(const Expr *fncall,
+ const MatchFinder::MatchResult &Result,
+ Environment &Env) {
+ // This is not necessarily a prvalue, since operators such as prefix ++ are
+ // lvalues.
+ const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+ assert(Var != nullptr);
- // Initialize to (Unknown, Unknown)
- if (Env.getValue(*Var))
- return;
+ // Initialize to (Unknown, Unknown)
+ if (Env.getValue(*Var))
+ return;
- auto *RootValue = Env.createValue(Var->getType());
- if (!RootValue)
- return;
-
- initializeRootValue(*RootValue, Env);
- setUnknownValue(*Var, *RootValue, Env);
+ auto *RootValue = Env.createValue(Var->getType());
+ if (!RootValue)
+ return;
+
+ initializeRootValue(*RootValue, Env);
+ setUnknownValue(*Var, *RootValue, Env);
}
ReverseNullDiagnoser::ResultType
diagnoseCastExpr(const CastExpr *Stmt, const MatchFinder::MatchResult &Result,
- const Environment &Env) {
- const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
- assert(Var != nullptr);
-
- Arena &A = Env.arena();
-
- if (auto *RootValue = Env.getValue(*Var)) {
- // FIXME: Hack
- if (RootValue->getProperty(kIsNull) && RootValue->getProperty(kIsNonnull)) {
- bool IsNull = !Env.flowConditionImplies(A.makeNot(getVal(kIsNull, *RootValue).formula()));
- bool IsNonnull = !Env.flowConditionImplies(A.makeNot(getVal(kIsNonnull, *RootValue).formula()));
-
- if (!IsNull && IsNonnull) {
- return {{}, {Var->getBeginLoc()}};
- }
-
- if (IsNull && !IsNonnull) {
- return {{Var->getBeginLoc()}, {}};
- }
- }
+ const Environment &Env) {
+ const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+ assert(Var != nullptr);
+
+ Arena &A = Env.arena();
+
+ if (auto *RootValue = Env.getValue(*Var)) {
+ // FIXME: Hack
+ if (RootValue->getProperty(kIsNull) && RootValue->getProperty(kIsNonnull)) {
+ bool IsNull = !Env.flowConditionImplies(
+ A.makeNot(getVal(kIsNull, *RootValue).formula()));
+ bool IsNonnull = !Env.flowConditionImplies(
+ A.makeNot(getVal(kIsNonnull, *RootValue).formula()));
+
+ if (!IsNull && IsNonnull) {
+ return {{}, {Var->getBeginLoc()}};
+ }
+
+ if (IsNull && !IsNonnull) {
+ return {{Var->getBeginLoc()}, {}};
+ }
}
+ }
- return {};
+ return {};
}
auto buildTransferMatchSwitch() {
@@ -334,7 +338,8 @@ auto buildBranchTransferMatchSwitch() {
}
auto buildDiagnoseMatchSwitch() {
- return CFGMatchSwitchBuilder<const Environment, ReverseNullDiagnoser::ResultType>()
+ return CFGMatchSwitchBuilder<const Environment,
+ ReverseNullDiagnoser::ResultType>()
.CaseOfCFGStmt<CastExpr>(castExprMatcher(), diagnoseCastExpr)
.Build();
}
@@ -344,14 +349,14 @@ auto buildDiagnoseMatchSwitch() {
ReverseNullModel::ReverseNullModel(ASTContext &Context)
: DataflowAnalysis<ReverseNullModel, NoopLattice>(Context),
TransferMatchSwitch(buildTransferMatchSwitch()),
- BranchTransferMatchSwitch(buildBranchTransferMatchSwitch()) {
-}
+ BranchTransferMatchSwitch(buildBranchTransferMatchSwitch()) {}
ast_matchers::StatementMatcher ReverseNullModel::ptrValueMatcher() {
return ptrToVar();
}
-void ReverseNullModel::initializeFunctionParameters(const ControlFlowContext &CFCtx, Environment &Env) {
+void ReverseNullModel::initializeFunctionParameters(
+ const ControlFlowContext &CFCtx, Environment &Env) {
const FunctionDecl &FD = cast<FunctionDecl>(CFCtx.getDecl());
for (const auto *Param : FD.parameters()) {
@@ -381,12 +386,12 @@ void ReverseNullModel::transfer(const CFGElement &E, NoopLattice &State,
TransferMatchSwitch(E, getASTContext(), Env);
}
-void ReverseNullModel::transferBranch(bool Branch, const Stmt *E, NoopLattice &State,
- Environment &Env) {
+void ReverseNullModel::transferBranch(bool Branch, const Stmt *E,
+ NoopLattice &State, Environment &Env) {
if (!E)
return;
- TransferArgs Args = { Branch, Env };
+ TransferArgs Args = {Branch, Env};
BranchTransferMatchSwitch(*E, getASTContext(), Args);
}
@@ -395,310 +400,319 @@ bool ReverseNullModel::merge(QualType Type, const Value &Val1,
const Environment &Env1, const Value &Val2,
const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) {
- if (!Type->isAnyPointerType())
- return false; // FIXME: Maybe requires true?
-
- // Alternative attempt: is any branch is true ie. can be a pointer, merging it is also a pointer
- const auto FastMergeToTrue = [&](llvm::StringRef Name) -> BoolValue & {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ if (!Type->isAnyPointerType())
+ return false; // FIXME: Maybe requires true?
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ // Alternative attempt: is any branch is true ie. can be a pointer, merging it
+ // is also a pointer
+ const auto FastMergeToTrue = [&](llvm::StringRef Name) -> BoolValue & {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
- if (lhsResult == SR::Top || rhsResult == SR::Top)
- return MergedEnv.makeTopBoolValue();
- else if (lhsResult == SR::True || rhsResult == SR::True)
- return MergedEnv.getBoolLiteralValue(true);
-
- if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
- return MergedEnv.makeAtomicBoolValue();
- else if (lhsResult == SR::False && rhsResult == SR::False)
- return MergedEnv.getBoolLiteralValue(false);
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ if (lhsResult == SR::Top || rhsResult == SR::Top)
return MergedEnv.makeTopBoolValue();
- };
-
- // Attempt 1: Merge different values to Top.
- const auto FastMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ else if (lhsResult == SR::True || rhsResult == SR::True)
+ return MergedEnv.getBoolLiteralValue(true);
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
+ return MergedEnv.makeAtomicBoolValue();
+ else if (lhsResult == SR::False && rhsResult == SR::False)
+ return MergedEnv.getBoolLiteralValue(false);
- if (lhsResult == SR::Top || rhsResult == SR::Top)
- return MergedEnv.makeTopBoolValue();
-
- if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
- return MergedEnv.makeAtomicBoolValue();
+ return MergedEnv.makeTopBoolValue();
+ };
- if (lhsResult == SR::True || rhsResult == SR::True)
- return MergedEnv.getBoolLiteralValue(true);
- else if (lhsResult == SR::False && rhsResult == SR::False)
- return MergedEnv.getBoolLiteralValue(false);
+ // Attempt 1: Merge different values to Top.
+ const auto FastMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ if (lhsResult == SR::Top || rhsResult == SR::Top)
return MergedEnv.makeTopBoolValue();
- };
- // Attempt 2: Slow evaluation, but only produces simple variables.
- const auto MergeValues = [&](llvm::StringRef Name) -> BoolValue & {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
+ return MergedEnv.makeAtomicBoolValue();
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ if (lhsResult == SR::True || rhsResult == SR::True)
+ return MergedEnv.getBoolLiteralValue(true);
+ else if (lhsResult == SR::False && rhsResult == SR::False)
+ return MergedEnv.getBoolLiteralValue(false);
- // Handle special cases.
- if (lhsResult == SR::Top || rhsResult == SR::Top) {
- return MergedEnv.makeTopBoolValue();
- } else if (lhsResult == rhsResult) {
- switch (lhsResult) {
- case SR::Nullptr:
- return MergedEnv.makeAtomicBoolValue();
- case SR::Top:
- return *lhsVar;
- case SR::True:
- return MergedEnv.getBoolLiteralValue(true);
- case SR::False:
- return MergedEnv.getBoolLiteralValue(false);
- case SR::Unknown:
- if (MergedEnv.flowConditionImplies(MergedEnv.arena().makeEquals(lhsVar->formula(), rhsVar->formula())))
- return *lhsVar;
-
- return MergedEnv.makeTopBoolValue();
- }
- }
+ return MergedEnv.makeTopBoolValue();
+ };
- return MergedEnv.makeTopBoolValue();
- };
+ // Attempt 2: Slow evaluation, but only produces simple variables.
+ const auto MergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
- // Attempt 3: Merge to (L or R), a more complex variable
- const auto SlowMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ // Handle special cases.
+ if (lhsResult == SR::Top || rhsResult == SR::Top) {
+ return MergedEnv.makeTopBoolValue();
+ } else if (lhsResult == rhsResult) {
+ switch (lhsResult) {
+ case SR::Nullptr:
+ return MergedEnv.makeAtomicBoolValue();
+ case SR::Top:
+ return *lhsVar;
+ case SR::True:
+ return MergedEnv.getBoolLiteralValue(true);
+ case SR::False:
+ return MergedEnv.getBoolLiteralValue(false);
+ case SR::Unknown:
+ if (MergedEnv.flowConditionImplies(MergedEnv.arena().makeEquals(
+ lhsVar->formula(), rhsVar->formula())))
+ return *lhsVar;
- // Handle special cases.
- if (lhsResult == SR::Top || rhsResult == SR::Top) {
return MergedEnv.makeTopBoolValue();
- } else if (lhsResult == rhsResult) {
- switch (lhsResult) {
- case SR::Nullptr:
- return MergedEnv.makeAtomicBoolValue();
- case SR::Top:
- return *lhsVar;
- case SR::True:
- return MergedEnv.getBoolLiteralValue(true);
- case SR::False:
- return MergedEnv.getBoolLiteralValue(false);
- case SR::Unknown:
- break;
- }
}
-
- const Formula *mergedLhsVar;
- const Formula *mergedRhsVar;
+ }
+
+ return MergedEnv.makeTopBoolValue();
+ };
- Arena &A = MergedEnv.arena();
- const Formula *lhsToken = &A.makeAtomRef(Env1.getFlowConditionToken());
- const Formula *rhsToken = &A.makeAtomRef(Env2.getFlowConditionToken());
+ // Attempt 3: Merge to (L or R), a more complex variable
+ const auto SlowMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ // Handle special cases.
+ if (lhsResult == SR::Top || rhsResult == SR::Top) {
+ return MergedEnv.makeTopBoolValue();
+ } else if (lhsResult == rhsResult) {
switch (lhsResult) {
- case SR::Nullptr:
- case SR::Top: // would have returned earlier
- mergedLhsVar = nullptr;
- break;
- case SR::True:
- mergedLhsVar = lhsToken;
- break;
- case SR::False:
- mergedLhsVar = &A.makeNot(*lhsToken);
- break;
- case SR::Unknown:
- mergedLhsVar = &A.makeImplies(*lhsToken, lhsVar->formula());
- break;
+ case SR::Nullptr:
+ return MergedEnv.makeAtomicBoolValue();
+ case SR::Top:
+ return *lhsVar;
+ case SR::True:
+ return MergedEnv.getBoolLiteralValue(true);
+ case SR::False:
+ return MergedEnv.getBoolLiteralValue(false);
+ case SR::Unknown:
+ break;
}
+ }
- switch (rhsResult) {
- case SR::Nullptr:
- case SR::Top:
- mergedRhsVar = nullptr;
- break;
- case SR::True:
- mergedRhsVar = rhsToken;
- break;
- case SR::False:
- mergedRhsVar = &A.makeNot(*rhsToken);
- break;
- case SR::Unknown:
- mergedRhsVar = &A.makeImplies(*rhsToken, rhsVar->formula());
- break;
- }
+ const Formula *mergedLhsVar;
+ const Formula *mergedRhsVar;
+
+ Arena &A = MergedEnv.arena();
+ const Formula *lhsToken = &A.makeAtomRef(Env1.getFlowConditionToken());
+ const Formula *rhsToken = &A.makeAtomRef(Env2.getFlowConditionToken());
+
+ switch (lhsResult) {
+ case SR::Nullptr:
+ case SR::Top: // would have returned earlier
+ mergedLhsVar = nullptr;
+ break;
+ case SR::True:
+ mergedLhsVar = lhsToken;
+ break;
+ case SR::False:
+ mergedLhsVar = &A.makeNot(*lhsToken);
+ break;
+ case SR::Unknown:
+ mergedLhsVar = &A.makeImplies(*lhsToken, lhsVar->formula());
+ break;
+ }
- if (!lhsVar && !rhsVar)
- return MergedEnv.makeAtomicBoolValue();
- else if (!lhsVar)
- return A.makeBoolValue(*mergedRhsVar);
- else if (!rhsVar)
- return A.makeBoolValue(*mergedLhsVar);
- else
- return A.makeBoolValue(A.makeOr(*mergedLhsVar, *mergedRhsVar));
- };
+ switch (rhsResult) {
+ case SR::Nullptr:
+ case SR::Top:
+ mergedRhsVar = nullptr;
+ break;
+ case SR::True:
+ mergedRhsVar = rhsToken;
+ break;
+ case SR::False:
+ mergedRhsVar = &A.makeNot(*rhsToken);
+ break;
+ case SR::Unknown:
+ mergedRhsVar = &A.makeImplies(*rhsToken, rhsVar->formula());
+ break;
+ }
- BoolValue &NonnullValue = FastMergeValues(kIsNonnull);
- BoolValue &NullValue = FastMergeValues(kIsNull);
+ if (!lhsVar && !rhsVar)
+ return MergedEnv.makeAtomicBoolValue();
+ else if (!lhsVar)
+ return A.makeBoolValue(*mergedRhsVar);
+ else if (!rhsVar)
+ return A.makeBoolValue(*mergedLhsVar);
+ else
+ return A.makeBoolValue(A.makeOr(*mergedLhsVar, *mergedRhsVar));
+ };
- MergedVal.setProperty(kIsNonnull, NonnullValue);
- MergedVal.setProperty(kIsNull, NullValue);
+ BoolValue &NonnullValue = FastMergeValues(kIsNonnull);
+ BoolValue &NullValue = FastMergeValues(kIsNull);
- MergedEnv.addToFlowCondition(MergedEnv.makeOr(NonnullValue, NullValue).formula());
+ MergedVal.setProperty(kIsNonnull, NonnullValue);
+ MergedVal.setProperty(kIsNull, NullValue);
- return true;
+ MergedEnv.addToFlowCondition(
+ MergedEnv.makeOr(NonnullValue, NullValue).formula());
+
+ return true;
}
-ComparisonResult ReverseNullModel::compare(QualType Type,
- const Value &Val1, const Environment &Env1,
- const Value &Val2, const Environment &Env2) {
+ComparisonResult ReverseNullModel::compare(QualType Type, const Value &Val1,
+ const Environment &Env1,
+ const Value &Val2,
+ const Environment &Env2) {
- if (!Type->isAnyPointerType())
- return ComparisonResult::Unknown;
+ if (!Type->isAnyPointerType())
+ return ComparisonResult::Unknown;
- // Attempt 1: Different values by pointer compare Unknown, Top to Different, same to Same.
- auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ // Attempt 1: Different values by pointer compare Unknown, Top to Different,
+ // same to Same.
+ auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
- SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
+ SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
- if (lhsResult == SR::Top || rhsResult == SR::Top)
- return ComparisonResult::Same; // FIXME: Should this be different?
+ if (lhsResult == SR::Top || rhsResult == SR::Top)
+ return ComparisonResult::Same; // FIXME: Should this be different?
- if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
- return ComparisonResult::Unknown;
-
- if (lhsResult == rhsResult)
- return ComparisonResult::Same;
+ if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+ return ComparisonResult::Unknown;
- return ComparisonResult::Different;
- };
+ if (lhsResult == rhsResult)
+ return ComparisonResult::Same;
- // Attempt 2: Evaluate values, but different values compare to Unknown.
- auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ return ComparisonResult::Different;
+ };
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ // Attempt 2: Evaluate values, but different values compare to Unknown.
+ auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
- if (lhsResult == SR::Top || rhsResult == SR::Top)
- return ComparisonResult::Same; // FIXME: Should this be different?
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
- if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
- return ComparisonResult::Unknown;
-
- if (lhsResult == rhsResult)
- return ComparisonResult::Same;
+ if (lhsResult == SR::Top || rhsResult == SR::Top)
+ return ComparisonResult::Same; // FIXME: Should this be different?
- return ComparisonResult::Different;
- };
+ if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+ return ComparisonResult::Unknown;
- ComparisonResult NullComparison = FastCompareValues(kIsNull);
- ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+ if (lhsResult == rhsResult)
+ return ComparisonResult::Same;
- if (NullComparison == ComparisonResult::Different || NonnullComparison == ComparisonResult::Different)
- return ComparisonResult::Different;
+ return ComparisonResult::Different;
+ };
- if (NullComparison == ComparisonResult::Unknown || NonnullComparison == ComparisonResult::Unknown)
- return ComparisonResult::Unknown;
+ ComparisonResult NullComparison = FastCompareValues(kIsNull);
+ ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
- return ComparisonResult::Same;
-}
+ if (NullComparison == ComparisonResult::Different ||
+ NonnullComparison == ComparisonResult::Different)
+ return ComparisonResult::Different;
-// Different in that it replaces differing boolean values with Top.
-ComparisonResult compareAndReplace(QualType Type,
- Value &Val1, const Environment &Env1,
- Value &Val2, Environment &Env2) {
+ if (NullComparison == ComparisonResult::Unknown ||
+ NonnullComparison == ComparisonResult::Unknown)
+ return ComparisonResult::Unknown;
- if (!Type->isAnyPointerType())
- return ComparisonResult::Unknown;
+ return ComparisonResult::Same;
+}
- // Attempt 1: Different values by pointer compare Unknown, Top to Different, same to Same.
- auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+// Different in that it replaces differing boolean values with Top.
+ComparisonResult compareAndReplace(QualType Type, Value &Val1,
+ const Environment &Env1, Value &Val2,
+ Environment &Env2) {
- SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
+ if (!Type->isAnyPointerType())
+ return ComparisonResult::Unknown;
- if (lhsResult == SR::Top || rhsResult == SR::Top) {
- Val2.setProperty(Name, Env2.makeTopBoolValue());
- return ComparisonResult::Same; // FIXME: Should this be different?
- }
+ // Attempt 1: Different values by pointer compare Unknown, Top to Different,
+ // same to Same.
+ auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
- if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
- return ComparisonResult::Unknown;
-
- if (lhsResult == rhsResult)
- return ComparisonResult::Same;
+ SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
+ if (lhsResult == SR::Top || rhsResult == SR::Top) {
Val2.setProperty(Name, Env2.makeTopBoolValue());
- return ComparisonResult::Different;
- };
+ return ComparisonResult::Same; // FIXME: Should this be different?
+ }
- // Attempt 2: Evaluate values, but different values compare to Unknown.
- auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
- BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
- BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+ return ComparisonResult::Unknown;
- SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
- SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+ if (lhsResult == rhsResult)
+ return ComparisonResult::Same;
- if (lhsResult == SR::Top || rhsResult == SR::Top) {
- Val2.setProperty(Name, Env2.makeTopBoolValue());
- return ComparisonResult::Same; // FIXME: Should this be different?
- }
+ Val2.setProperty(Name, Env2.makeTopBoolValue());
+ return ComparisonResult::Different;
+ };
- if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
- return ComparisonResult::Unknown;
-
- if (lhsResult == rhsResult)
- return ComparisonResult::Same;
+ // Attempt 2: Evaluate values, but different values compare to Unknown.
+ auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+ BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+ BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+ SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+ SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+
+ if (lhsResult == SR::Top || rhsResult == SR::Top) {
Val2.setProperty(Name, Env2.makeTopBoolValue());
- return ComparisonResult::Different;
- };
+ return ComparisonResult::Same; // FIXME: Should this be different?
+ }
+
+ if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+ return ComparisonResult::Unknown;
- ComparisonResult NullComparison = FastCompareValues(kIsNull);
- ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+ if (lhsResult == rhsResult)
+ return ComparisonResult::Same;
- if (NullComparison == ComparisonResult::Different || NonnullComparison == ComparisonResult::Different)
- return ComparisonResult::Different;
+ Val2.setProperty(Name, Env2.makeTopBoolValue());
+ return ComparisonResult::Different;
+ };
- if (NullComparison == ComparisonResult::Unknown || NonnullComparison == ComparisonResult::Unknown)
- return ComparisonResult::Unknown;
+ ComparisonResult NullComparison = FastCompareValues(kIsNull);
+ ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+
+ if (NullComparison == ComparisonResult::Different ||
+ NonnullComparison == ComparisonResult::Different)
+ return ComparisonResult::Different;
- return ComparisonResult::Same;
+ if (NullComparison == ComparisonResult::Unknown ||
+ NonnullComparison == ComparisonResult::Unknown)
+ return ComparisonResult::Unknown;
+
+ return ComparisonResult::Same;
}
-Value *ReverseNullModel::widen(QualType Type,
- Value &Prev, const Environment &PrevEnv,
- Value &Current, Environment &CurrentEnv) {
- if (!Type->isAnyPointerType())
- return nullptr;
+Value *ReverseNullModel::widen(QualType Type, Value &Prev,
+ const Environment &PrevEnv, Value &Current,
+ Environment &CurrentEnv) {
+ if (!Type->isAnyPointerType())
+ return nullptr;
- switch (compareAndReplace(Type, Prev, PrevEnv, Current, CurrentEnv)) {
- case ComparisonResult::Same:
- return &Prev;
- case ComparisonResult::Unknown:
- return nullptr;
- case ComparisonResult::Different:
- return &Current; // FIXME: Replace with Tops in compareAndReplace
- }
+ switch (compareAndReplace(Type, Prev, PrevEnv, Current, CurrentEnv)) {
+ case ComparisonResult::Same:
+ return &Prev;
+ case ComparisonResult::Unknown:
+ return nullptr;
+ case ComparisonResult::Different:
+ return &Current; // FIXME: Replace with Tops in compareAndReplace
+ }
}
ReverseNullDiagnoser::ReverseNullDiagnoser()
diff --git a/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
index 6e4148bf4..4d0b933bc 100644
--- a/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a test for pointer nullability, specifically focused on
+// This file defines a test for pointer nullability, specifically focused on
// finding invalid dereferences, and unnecessary null-checks. Only a limited
// set of operations are currently recognized.
//
@@ -14,6 +14,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/FlowSensitive/Models/ReverseNullModel.h"
#include "TestingSupport.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -28,7 +29,6 @@
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/MapLattice.h"
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
-#include "clang/Analysis/FlowSensitive/Models/ReverseNullModel.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
@@ -61,7 +61,7 @@ constexpr char kBoolInvalid[] = "truefalse";
constexpr char kBoolUnknown[] = "unknown";
constexpr char kBoolNullptr[] = "is-nullptr";
-std::string debugBoolValue(BoolValue *value, const Environment &Env) {
+std::string debugBoolValue(BoolValue *value, const Environment &Env) {
if (value == nullptr) {
return std::string(kBoolNullptr);
} else {
@@ -77,46 +77,42 @@ std::string debugBoolValue(BoolValue *value, const Environment &Env) {
}
auto ptrToVar() { return declRefExpr(hasType(isAnyPointer())).bind(kVar); }
-auto namedPtrToVar(llvm::StringRef name) {
+auto namedPtrToVar(llvm::StringRef name) {
return declRefExpr(allOf(hasType(isAnyPointer()),
- hasDeclaration(namedDecl(hasName(name)))
- )).bind(kVar);
+ hasDeclaration(namedDecl(hasName(name)))))
+ .bind(kVar);
}
auto nameToVar(llvm::StringRef name) {
- return declRefExpr(hasType(isAnyPointer()), hasDeclaration(
- namedDecl(hasName(name))
- )).bind(kVar);
+ return declRefExpr(hasType(isAnyPointer()),
+ hasDeclaration(namedDecl(hasName(name))))
+ .bind(kVar);
}
auto voidCastMatcher() {
- return stmt(hasDescendant(castExpr(hasCastKind(CK_ToVoid),
- hasSourceExpression(ptrToVar())
- )));
+ return stmt(hasDescendant(
+ castExpr(hasCastKind(CK_ToVoid), hasSourceExpression(ptrToVar()))));
}
auto derefMatcher() {
- return traverse(TK_IgnoreUnlessSpelledInSource,
- unaryOperator(
- hasOperatorName("*"),
- hasUnaryOperand(ptrToVar())
- )
- );
+ return traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ unaryOperator(hasOperatorName("*"), hasUnaryOperand(ptrToVar())));
}
// FIXME: Deliberately simple - a proper check would only match direct ptr casts
auto castExprMatcher() {
- return castExpr(hasCastKind(CK_PointerToBoolean),
- hasSourceExpression(ptrToVar())).bind(kCond)
- ;
+ return castExpr(hasCastKind(CK_PointerToBoolean),
+ hasSourceExpression(ptrToVar()))
+ .bind(kCond);
}
using ::clang::dataflow::test::AnalysisInputs;
using ::clang::dataflow::test::AnalysisOutputs;
using ::clang::dataflow::test::checkDataflow;
using ::llvm::IsStringMapEntry;
-using ::testing::Pair;
using ::testing::Args;
+using ::testing::Pair;
using ::testing::UnorderedElementsAre;
MATCHER_P2(HasNullabilityState, null, nonnull, "") {
@@ -128,14 +124,14 @@ MATCHER_P2(HasNullabilityState, null, nonnull, "") {
*arg.second) == null;
}
-MATCHER_P3(HoldsVariable, name, output, checks,
- ((negation ? "doesn't hold" : "holds") +
+MATCHER_P3(HoldsVariable, name, output, checks,
+ ((negation ? "doesn't hold" : "holds") +
llvm::StringRef(" a variable in its environment that ") +
- ::testing::DescribeMatcher<std::pair<Value *, Environment *>>(checks, negation)
- ).str()) {
+ ::testing::DescribeMatcher<std::pair<Value *, Environment *>>(
+ checks, negation))
+ .str()) {
auto MatchResults = match(functionDecl(hasDescendant(nameToVar(name))),
- *output.Target,
- output.ASTCtx);
+ *output.Target, output.ASTCtx);
// FIXME: Use gtest functions
assert(!MatchResults.empty());
@@ -143,12 +139,13 @@ MATCHER_P3(HoldsVariable, name, output, checks,
assert(pointerExpr != nullptr);
const auto *ExprValue = arg.Env.getValue(*pointerExpr);
-
+
if (ExprValue == nullptr) {
return false;
}
- return ExplainMatchResult(checks, std::pair { ExprValue, &arg.Env }, result_listener);
+ return ExplainMatchResult(checks, std::pair{ExprValue, &arg.Env},
+ result_listener);
}
template <typename MatcherFactory>
@@ -166,15 +163,17 @@ void ExpectDataflowResult(llvm::StringRef Code, MatcherFactory Expectations) {
})
.withSetupTest([&InitEnv](AnalysisOutputs &AO) {
assert(InitEnv && "SetupTest ran at an unexpected time");
- ReverseNullModel &Analysis = static_cast<ReverseNullModel&>(AO.Analysis);
+ ReverseNullModel &Analysis =
+ static_cast<ReverseNullModel &>(AO.Analysis);
Analysis.initializeFunctionParameters(AO.CFCtx, *InitEnv);
return llvm::Error::success();
})
.withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
/*VerifyResults=*/
- [&Expectations](const llvm::StringMap<DataflowAnalysisState<
- ReverseNullModel::Lattice>> &Results,
- const AnalysisOutputs &Output) {
+ [&Expectations](
+ const llvm::StringMap<
+ DataflowAnalysisState<ReverseNullModel::Lattice>> &Results,
+ const AnalysisOutputs &Output) {
EXPECT_THAT(Results, Expectations(Output));
}),
llvm::Succeeded());
@@ -194,10 +193,12 @@ TEST(ReverseNullTest, DereferenceTypes) {
)";
ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
return UnorderedElementsAre(
- IsStringMapEntry("p", HoldsVariable("p", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))),
- IsStringMapEntry("q", HoldsVariable("q", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))));
+ IsStringMapEntry(
+ "p", HoldsVariable("p", Output,
+ HasNullabilityState(kBoolFalse, kBoolTrue))),
+ IsStringMapEntry(
+ "q", HoldsVariable("q", Output,
+ HasNullabilityState(kBoolFalse, kBoolTrue))));
});
}
@@ -216,9 +217,11 @@ TEST(ReverseNullTest, ConditionalTypes) {
ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
return UnorderedElementsAre(
IsStringMapEntry("p_true", HoldsVariable("p", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))),
+ HasNullabilityState(
+ kBoolFalse, kBoolTrue))),
IsStringMapEntry("p_false", HoldsVariable("p", Output,
- HasNullabilityState(kBoolTrue, kBoolFalse))));
+ HasNullabilityState(
+ kBoolTrue, kBoolFalse))));
});
}
@@ -248,17 +251,27 @@ TEST(ReverseNullTest, UnrelatedCondition) {
ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
return UnorderedElementsAre(
IsStringMapEntry("p_b_true", HoldsVariable("p", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))),
- IsStringMapEntry("p_b_false", HoldsVariable("p", Output,
- HasNullabilityState(kBoolUnknown, kBoolUnknown))),
- IsStringMapEntry("p_merged", HoldsVariable("p", Output,
- HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+ HasNullabilityState(
+ kBoolFalse, kBoolTrue))),
+ IsStringMapEntry(
+ "p_b_false",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+ IsStringMapEntry(
+ "p_merged",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolUnknown, kBoolUnknown))),
IsStringMapEntry("b_true", HoldsVariable("p", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))),
+ HasNullabilityState(
+ kBoolFalse, kBoolTrue))),
IsStringMapEntry("b_p_true", HoldsVariable("p", Output,
- HasNullabilityState(kBoolInvalid, kBoolInvalid))), // FIXME
- IsStringMapEntry("b_p_false", HoldsVariable("p", Output,
- HasNullabilityState(kBoolInvalid, kBoolInvalid))));
+ HasNullabilityState(
+ kBoolInvalid,
+ kBoolInvalid))), // FIXME
+ IsStringMapEntry(
+ "b_p_false",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolInvalid, kBoolInvalid))));
});
}
@@ -287,17 +300,28 @@ TEST(ReverseNullTest, AssignmentOfCommonValues) {
ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
return UnorderedElementsAre(
IsStringMapEntry("p_malloc", HoldsVariable("p", Output,
- HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
+ HasNullabilityState(
+ kBoolNullptr,
+ kBoolNullptr))), // FIXME
IsStringMapEntry("p_true", HoldsVariable("p", Output,
- HasNullabilityState(kBoolFalse, kBoolTrue))),
+ HasNullabilityState(
+ kBoolFalse, kBoolTrue))),
IsStringMapEntry("p_false", HoldsVariable("p", Output,
- HasNullabilityState(kBoolTrue, kBoolFalse))),
- IsStringMapEntry("p_merge", HoldsVariable("p", Output,
- HasNullabilityState(kBoolUnknown, kBoolUnknown))),
- IsStringMapEntry("p_nullptr", HoldsVariable("p", Output,
- HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
- IsStringMapEntry("p_extern", HoldsVariable("p", Output,
- HasNullabilityState(kBoolUnknown, kBoolUnknown))));
+ HasNullabilityState(
+ kBoolTrue, kBoolFalse))),
+ IsStringMapEntry(
+ "p_merge",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+ IsStringMapEntry(
+ "p_nullptr",
+ HoldsVariable(
+ "p", Output,
+ HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
+ IsStringMapEntry(
+ "p_extern",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolUnknown, kBoolUnknown))));
});
}
@@ -317,9 +341,10 @@ TEST(ReverseNullTest, MergeValues) {
}
)";
ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
- return UnorderedElementsAre(
- IsStringMapEntry("p_merge", HoldsVariable("p", Output,
- HasNullabilityState(kBoolUnknown, kBoolTrue))));
+ return UnorderedElementsAre(IsStringMapEntry(
+ "p_merge",
+ HoldsVariable("p", Output,
+ HasNullabilityState(kBoolUnknown, kBoolTrue))));
});
}
|
2bb84a6
to
159c114
Compare
…tePluginObject After llvm#68052 this function changed from returning a nullptr with `return {};` to returning Expected and hitting `llvm_unreachable` before it could do so. I gather that we're never supposed to call this function, but on Windows we actually do call this function because `interpreter->CreateScriptedProcessInterface()` returns `ScriptedProcessInterface` not `ScriptedProcessPythonInterface`. Likely because `target_sp->GetDebugger().GetScriptInterpreter()` also does not return a Python related class. The previously XFAILed test crashed with: ``` # .---command stderr------------ # | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. # | Stack dump: # | 0. Program arguments: c:\\users\\tcwg\\david.spickett\\build-llvm\\bin\\lldb-test.exe ir-memory-map C:\\Users\\tcwg\\david.spickett\\build-llvm\\tools\\lldb\\test\\Shell\\Expr\\Output\\TestIRMemoryMapWindows.test.tmp C:\\Users\\tcwg\\david.spickett\\llvm-project\\lldb\\test\\Shell\\Expr/Inputs/ir-memory-map-basic # | 1. HandleCommand(command = "run") # | Exception Code: 0xC000001D # | #0 0x00007ff696b5f588 lldb_private::ScriptedProcessInterface::CreatePluginObject(class llvm::StringRef, class lldb_private::ExecutionContext &, class std::shared_ptr<class lldb_private::StructuredData::Dictionary>, class lldb_private::StructuredData::Generic *) C:\Users\tcwg\david.spickett\llvm-project\lldb\include\lldb\Interpreter\Interfaces\ScriptedProcessInterface.h:28:0 # | #1 0x00007ff696b1d808 llvm::Expected<std::shared_ptr<lldb_private::StructuredData::Generic> >::operator bool C:\Users\tcwg\david.spickett\llvm-project\llvm\include\llvm\Support\Error.h:567:0 # | llvm#2 0x00007ff696b1d808 lldb_private::ScriptedProcess::ScriptedProcess(class std::shared_ptr<class lldb_private::Target>, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::ScriptedMetadata const &, class lldb_private::Status &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Process\scripted\ScriptedProcess.cpp:115:0 # | llvm#3 0x00007ff696b1d124 std::shared_ptr<lldb_private::ScriptedProcess>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1478:0 # | llvm#4 0x00007ff696b1d124 lldb_private::ScriptedProcess::CreateInstance(class std::shared_ptr<class lldb_private::Target>, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Process\scripted\ScriptedProcess.cpp:61:0 # | llvm#5 0x00007ff69699c8f4 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0 # | llvm#6 0x00007ff69699c8f4 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0 # | llvm#7 0x00007ff69699c8f4 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0 # | llvm#8 0x00007ff69699c8f4 lldb_private::Process::FindPlugin(class std::shared_ptr<class lldb_private::Target>, class llvm::StringRef, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Process.cpp:396:0 # | llvm#9 0x00007ff6969bd708 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0 # | llvm#10 0x00007ff6969bd708 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0 # | llvm#11 0x00007ff6969bd708 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0 # | llvm#12 0x00007ff6969bd708 lldb_private::Target::CreateProcess(class std::shared_ptr<class lldb_private::Listener>, class llvm::StringRef, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Target.cpp:215:0 # | llvm#13 0x00007ff696b13af0 std::_Ptr_base<lldb_private::Process>::_Ptr_base C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1230:0 # | llvm#14 0x00007ff696b13af0 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1524:0 # | llvm#15 0x00007ff696b13af0 lldb_private::PlatformWindows::DebugProcess(class lldb_private::ProcessLaunchInfo &, class lldb_private::Debugger &, class lldb_private::Target &, class lldb_private::Status &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Platform\Windows\PlatformWindows.cpp:495:0 # | llvm#16 0x00007ff6969cf590 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0 # | llvm#17 0x00007ff6969cf590 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0 # | llvm#18 0x00007ff6969cf590 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0 # | llvm#19 0x00007ff6969cf590 lldb_private::Target::Launch(class lldb_private::ProcessLaunchInfo &, class lldb_private::Stream *) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Target.cpp:3274:0 # | llvm#20 0x00007ff696fff82c CommandObjectProcessLaunch::DoExecute(class lldb_private::Args &, class lldb_private::CommandReturnObject &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Commands\CommandObjectProcess.cpp:258:0 # | llvm#21 0x00007ff696fab6c0 lldb_private::CommandObjectParsed::Execute(char const *, class lldb_private::CommandReturnObject &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Interpreter\CommandObject.cpp:751:0 # `----------------------------- # error: command failed with exit status: 0xc000001d ``` That might be a bug on the Windows side, or an artifact of how our build is setup, but whatever it is, having `CreatePluginObject` return an error and the caller check it, fixes the failing test. The built lldb can run the script command to use Python, but I'm not sure if that means anything.
159c114
to
07e7db8
Compare
9185f98
to
298cc6f
Compare
I will detail my high-level comments first before delving into the nitty-gritty of the source code. At face value, and immediately looking at the very first line in the header, I got slapped in the face with the check's name as it stands right now. I really, really do not like it... If I only go off of the check's name and search Google for reverse null, the first possibly related idea (REVERSE_INULL False Positive “Dereference before NULL check" at Synopsys Coverity Community) in the result list is at the 7th position. Granted, other search engines like DDG or Bing show in 1st or 2nd location the "what is difference between REVERSE_INULL and FORWARD_NULL error in coverity scan(static code analysis)?" at Stack Overflow. It begs the question what does I mean in INULL anyway...??? Note, the only answer here is a guy who admittedly "used to work for Synopsys". Also, even with DDG and Bing, most of the results are completely derailing, unrelated things like The problem here is that to understand from the general name of the check what we are trying to achieve requires either:
Both of these would mean the user is already seasoned in static analysis. They likely are not, especially when it comes to people who just drive Clang-Tidy through things like coc-clangd or CLion. Thus, I would like to say that we should give some consideration to the check's name. I think a longer but more informative name (to everyday developers not knowledgeable about this field) would be preferable to a concise but very internal name. Something along the lines of I think we should consider whether this is truly just bug_prone_ in the first place. Assuming the pointer was indeed null, if you have already dereferenced it by the time you reached the condition, you are definitely not null, because otherwise the program "should have" crashed, right? Or am I missing something and this is a too simple overview? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review for now. Unfortunately, I need to leave my work now, I will likely continue from another location tomorrow, and I do not know whether GitHub saves pending reviews clientside or serverside...
@@ -0,0 +1,126 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✏️ (Style nit: stale empty line.)
// finding invalid dereferences, and unnecessary null-checks. Only a limited | ||
// set of operations are currently recognized. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Nit: It would be beneficial if this "limited set" was explained. What important bits that are meaningfully missing?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be documented.
namespace clang { | ||
namespace tidy { | ||
namespace bugprone { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✏️ (Style nit: LLVM is now in C++17, so nested namespace specifiers namespace clang::tidy::bugprone
could be used instead.)
|
||
constexpr char kBoolTrue[] = "true"; | ||
constexpr char kBoolFalse[] = "false"; | ||
constexpr char kBoolInvalid[] = "truefalse"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Maybe this could spell "invalid" instead. Thinking about truefalse
its not immediately apparent whether this means "true AND false" or "true OR false", and those are not the same things.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
truefalse
was just more convenient, but I will rework the debugBoolValue
function that uses this.
auto ptrToVar() { return declRefExpr(hasType(isAnyPointer())).bind(kVar); } | ||
auto namedPtrToVar(llvm::StringRef name) { | ||
return declRefExpr(allOf(hasType(isAnyPointer()), | ||
hasDeclaration(namedDecl(hasName(name))) | ||
)).bind(kVar); | ||
} | ||
|
||
auto nameToVar(llvm::StringRef name) { | ||
return declRefExpr(hasType(isAnyPointer()), hasDeclaration( | ||
namedDecl(hasName(name)) | ||
)).bind(kVar); | ||
} | ||
|
||
auto voidCastMatcher() { | ||
return stmt(hasDescendant(castExpr(hasCastKind(CK_ToVoid), | ||
hasSourceExpression(ptrToVar()) | ||
))); | ||
} | ||
|
||
auto derefMatcher() { | ||
return traverse(TK_IgnoreUnlessSpelledInSource, | ||
unaryOperator( | ||
hasOperatorName("*"), | ||
hasUnaryOperand(ptrToVar()) | ||
) | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what is the convention for clang/unittests
, but similar constructs in Clang-Tidy are usually expressed in the form of static const auto
objects allocated directly in the TU for the check's implementation. (For matchers that take a parameter, the AST_MATCHER_P
macro creates the appropriate matcher class.)
These factory functions can be simplified:
- For matchers that do not have parameters and do not require imperative logic, a
static const(expr?) auto X = matcher(foo());
object that lives only once can express the needed logic. - For matchers that do require imperative logic,
AST_MATCHER
can create the appropriately named matcher. - Similarly, where parameters are needed,
AST_MATCHER_P
does the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed it does do the same thing. Ultimately I think instead of redefining matchers here, the ones from the ReverseNullModel
itself should be somehow used.
Arena &A = Env.arena(); | ||
|
||
if (auto *RootValue = Env.getValue(*Var)) { | ||
// FIXME: Hack |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the hack here? What happens if the hack is removed, or how should it disappear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the other hack.
if (!IsNull && IsNonnull) { | ||
return {{}, {Var->getBeginLoc()}}; | ||
} | ||
|
||
if (IsNull && !IsNonnull) { | ||
return {{Var->getBeginLoc()}, {}}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the semantics of this return value? At the definition of the type alias, it is only said it's a pair of Loc vectors. How is it special if one part of the pair has elements, and how if the other. Is it possible that BOTH vectors will contain an element?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not documented, and will most likely be changed. One is for checking when the value is definitely not-null, and the other is for checking when the value is definitely null.
.CaseOfCFGStmt<Expr>(nullptrMatcher(), matchNullptrExpr) | ||
.CaseOfCFGStmt<Expr>(addressofMatcher(), matchAddressofExpr) | ||
.CaseOfCFGStmt<Expr>(functionCallMatcher(), matchFunctionCallExpr) | ||
.CaseOfCFGStmt<Expr>(anyPointerMatcher(), matchFunctionCallExpr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is "any pointer" a "function call"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be named the other way around, fixed.
if (!Type->isAnyPointerType()) | ||
return false; // FIXME: Maybe requires true? | ||
|
||
// Alternative attempt: is any branch is true ie. can be a pointer, merging it is also a pointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be a pointer, or can be a null pointer?
Does "can be a pointer" mean that it is not null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be non-null.
default: | ||
llvm_unreachable("all cases covered in switch"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✏️ (My problem with having a default:
here is that this kills -Wswitch
, but I guess because not all branches of the switch actually return we can't really do any better than this...
Moreover, I have an architectural question as I just got to where the various alternative heuristics for calculating stuff are implemented. Could we somehow verify that alternates get the same consistent results somehow? If this is not feasible at low-level (i.e., running each alternate and comparing that they report the same value), then perhaps we should keep the alternates until the end so when the checker is ready for real-life testing on projects, we can run different configurations, and compare the user-facing reports from Tidy? Is this a good idea, even, or am I overthinking it? I see somewhere you mentioned things could simply just not conclude (and hey, Also, a schematic ASCII art for the lattice would be nice to have. (See the "Why are there 5 values?" question. So far, I don't know.) |
@@ -0,0 +1,101 @@ | |||
//===-- ReverseNullModel.h --------------------------------------*- C++ -*-===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you can find this tomorrow, pending reviews are saved serverside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay!
@@ -0,0 +1,723 @@ | |||
//===-- ReverseNullModel.h --------------------------------------*- C++ -*-===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✏️ (Mismatch between file name and what is written in this comment.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the high-level idea is convincing. In general, the presentation and the understandability of the code need to be improved. I did this review only by reading what's in the code without going into your external presentations and documents. I'm happy to look at them now, but the code in itself should still be capable of explaining what is going on and why it is going on.
I've started running the check in the usual CI, and so far, there were no findings or no errors, but in TinyXML's There were some more hangs with the same symptoms, which I killed after letting the jobs stagnate for about 15-30 minutes, on libwebm ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed earlier today, if the checker is known to be crashy or hangy, we should preemptively forbid clangd from exercising it, as shown in this patch: llvm#69427
298cc6f
to
086c092
Compare
4d20f44
to
37d3a78
Compare
…lvm#73463) Despite CWG2497 not being resolved, it is reasonable to expect the following code to compile (and which is supported by other compilers) ```cpp template<typename T> constexpr T f(); constexpr int g() { return f<int>(); } // #1 template<typename T> constexpr T f() { return 123; } int k[g()]; // llvm#2 ``` To that end, we eagerly instantiate all referenced specializations of constexpr functions when they are defined. We maintain a map of (pattern, [instantiations]) independent of `PendingInstantiations` to avoid having to iterate that list after each function definition. We should apply the same logic to constexpr variables, but I wanted to keep the PR small. Fixes llvm#73232
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think the implementation is now much better understandable (also given the fact that this was the 2nd pass over the code). There are some crucial pain points that still need to be improved, however:
- The diagnostic output should be reviewed, especially this "Can we get to 'the' previous dereference?"-esque questions. If not, then at least the Checker's CPP file should contain a comment about this...
- The documentation file for the checker has a lot of spotty parts. I tried to give as many comments as I could. There are some conflated terminologies (like safety?) and in general the flow of the explanation is just not a good angle to ease the user into understanding what is going on. We're doing something rather complex and new here (especially this all path / no path "stuff").
I also couldn't find any discussion about the lattice merging strategies. I can't even find my own comment wrt. this, so this might be a significant Mandela effect, but I do remember saying that we should evaluate which strategy is better (if such a comparison can be drawn, even...) and leave only that in the suggested upstream patch. If such comparisons are not possible, then running this or that strategy should be configurable through a checker option. I would still like to have some sort of evaluation even if we can't pronounce a definite "winner".
//===--- NullCheckAfterDereferenceCheck.cpp - clang-tidy | ||
//--------------------------------===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(✏️ Style nit: Something went wrong here with the formatting, the line became too long and split.)
|
||
using ast_matchers::MatchFinder; | ||
using dataflow::NullCheckAfterDereferenceModel; | ||
using dataflow::ReverseNullDiagnoser; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ The "reverse null" name remained here. This is not inherently a problem! But perhaps it would be worthwhile to think about renaming this to BackpropagatingNull
or ...Nullness
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to NullCheckAfterDereference, since this diagnoser is specific to the checker, not to the framework.
clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst
Outdated
Show resolved
Hide resolved
This check identifies potentially unsafe pointer null-checks, | ||
by finding cases where the pointer cannot be null at the location of the check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐩 Is this true? Why would it be unsafe? The null conditional is redundant. What's unsafe is the unconditional dereference prior to the condition, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the first example, I wanted to give the exact warning the checker provides, so later I can expand on the specific edge-cases. Changed unsafe => bugprone where possible though.
if (Env.flowConditionImplies(Val->formula())) | ||
return SR::True; | ||
|
||
if (Env.flowConditionImplies(Env.arena().makeNot(Val->formula()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's actually neat! But it triggers a follow-up question: Here, you mean "analysis" as in the analysis of a single function (as driven by the helper function within the check's implementation), and not the analysis of an entire T.U., right?
*p = 42; | ||
|
||
if (p) { | ||
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked despite dereferencing it earlier [bugprone-experimental-reverse-null] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we know, or can we know this "earlier" location? Here it would be trivial to give a note:
to line 8 (*p = 42
), but it might not be that trivial in the framework. Is it worthwhile to pursue this, or is it too troublesome and could only be done as a future, separate patch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a past version of the data-flow framework, storing the location of a previous dereference was possible, but currently I don't think it can be implemented. I do want to get this feature working as well, even if not showing all previous dereferences/assignments, at least showing one, but I haven't found a way to do this yet.
clang/include/clang/Analysis/FlowSensitive/Models/NullCheckAfterDereferenceModel.h
Outdated
Show resolved
Hide resolved
clang/include/clang/Analysis/FlowSensitive/Models/NullCheckAfterDereferenceModel.h
Outdated
Show resolved
Hide resolved
clang/lib/Analysis/FlowSensitive/Models/NullCheckAfterDereferenceModel.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Oh yeah I forgot one thing which would end up being in 1288123 locations: in many places the variable names do not follow established LLVM conventions, e.g. lhsResult
instead of LHSResult
. This is more of a tedious nit than anything significant.)
I put the |
Re #1 (comment) The analyses re-run in the CI, and I uploaded the results to the usual location, although the "detection status" diff of the reports had gone wrong due to the rename. Nevertheless, here are the failure statistics. All the tallied failures are due to hung kills. ✅ No crashes! I killed an analysis manually after having observed more than 1 hr of runtime for the file. (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, as the checker is known to hang we should position ourselves with this check immediately in a defensive way and add it to the list of forbidden-by-default checks when running through clangd. Please modify the file in the same way as in this commit: e63ab13.
37d3a78
to
34bdc9b
Compare
Changed the used merge/compare algos to the non-fast ones. Unit tests don't pass yet, but I'm debugging it. |
34bdc9b
to
286d12a
Compare
Given the changes in |
286d12a
to
005aeb4
Compare
It is now the latest version, ready to test. |
(As soon as the commit tree syncs anyways.) |
I fired up b9fba0444d4ffbb35548fa7dd546157a6160c3c7 in the CI. No issues on any of the C projects so far, but I think this was intended that the check is incapable of dealing with C, so yeah... I'll come back with the results and the hangs after C++ analyses succeeded. What I can see now is that |
@Discookie I'm not convinced it will do that without a nudge. Next time you update the patch let's take care and do a separate push to Alright, the analysis concluded over C++ projects. I actually restarted the job (with I observed no crashes at all. So all the failures you can see on the run list are from timeouts. The 30-minute timeout might have been a bit eager, but at least we know that feature works in CodeChecker. This is a per-process timeout, so every time a TU hung for 30 minutes, the Tidy process got terminated. Observing the CI logs (it's In general, most projects succeeded appropriately. Performance was the worst on Qt, where only 172 TUs out of the 1175 succeeded, taking a whopping 34 hours to execute. LLVM was the second worst (it being on the stand isn't surprising, but Qt lapping it, at first glance, is...) with 23:13 runtime and 687 out of the 3089 jobs failing. There are quite some new reports, almost 10 per project, except for LLVM, where there were previously no reports but now there are 69. Potential false positivesHere are some of the interesting findings which I think should be mentioned in the documentation (and preferably also with a test with https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=libwebm_libwebm-1.0.0.27_Discookie_reverse-null&detection-status=New&report-id=3497414&report-hash=e75b81fcd825616e0ec904ffef1c99ac&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Flibwebm%2Fmkvparser.cpp
Dittos
True positivesStrange reports
|
Hm. I thought to check for function calls that return pointers, but not for functions that take pointers as arguments. Will implement. The strange report is not that strange, but I don't know how better to display it without spans. In the constructor a call is made with &(diagObj->...) as its argument, and this explicitly dereferences diagObj before the function starts. |
I might have linked the wrong one there. That one with the ctor was easy to understand I found another one that was strange. Anyway the reports are up on the server, you can go through them one by one. But most of them is the |
The concurrent tests all do a pthread_join at the end, and concurrent_base.py stops after that pthread_join and sanity checks that only 1 thread is running. On macOS, after pthread_join() has completed, there can be an extra thread still running which is completing the details of that task asynchronously; this causes testsuite failures. When this happens, we see the second thread is in ``` frame #0: 0x0000000180ce7700 libsystem_kernel.dylib`__ulock_wake + 8 frame #1: 0x0000000180d25ad4 libsystem_pthread.dylib`_pthread_joiner_wake + 52 frame llvm#2: 0x0000000180d23c18 libsystem_pthread.dylib`_pthread_terminate + 384 frame llvm#3: 0x0000000180d23a98 libsystem_pthread.dylib`_pthread_terminate_invoke + 92 frame llvm#4: 0x0000000180d26740 libsystem_pthread.dylib`_pthread_exit + 112 frame llvm#5: 0x0000000180d26040 libsystem_pthread.dylib`_pthread_start + 148 ``` there are none of the functions from the test file present on this thread. In this patch, instead of counting the number of threads, I iterate over the threads looking for functions from our test file (by name) and only count threads that have at least one of them. It's a lower frequency failure than the darwin kernel bug causing an extra step instruction mach exception when hardware breakpoint/watchpoints are used, but once I fixed that, this came up as the next most common failure for these tests. rdar://110555062
…lvm#80904)" This reverts commit b1ac052. This commit breaks coroutine splitting for non-swift calling convention functions. In this example: ```ll ; ModuleID = 'repro.ll' source_filename = "stdlib/test/runtime/test_llcl.mojo" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @0 = internal constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint (ptr @craSH to i64), i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32 }, ptr @0, i32 0, i32 1) to i64)) to i32), i32 64 } define dso_local void @af_suspend_fn(ptr %0, i64 %1, ptr %2) #0 { ret void } define dso_local void @craSH(ptr %0) #0 { %2 = call token @llvm.coro.id.async(i32 64, i32 8, i32 0, ptr @0) %3 = call ptr @llvm.coro.begin(token %2, ptr null) %4 = getelementptr inbounds { ptr, { ptr, ptr }, i64, { ptr, i1 }, i64, i64 }, ptr poison, i32 0, i32 0 %5 = call ptr @llvm.coro.async.resume() store ptr %5, ptr %4, align 8 %6 = call { ptr, ptr, ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0p0p0s(i32 0, ptr %5, ptr @ctxt_proj_fn, ptr @af_suspend_fn, ptr poison, i64 -1, ptr poison) ret void } define dso_local ptr @ctxt_proj_fn(ptr %0) #0 { ret ptr %0 } ; Function Attrs: nomerge nounwind declare { ptr, ptr, ptr } @llvm.coro.suspend.async.sl_p0p0p0s(i32, ptr, ptr, ...) #1 ; Function Attrs: nounwind declare token @llvm.coro.id.async(i32, i32, i32, ptr) llvm#2 ; Function Attrs: nounwind declare ptr @llvm.coro.begin(token, ptr writeonly) llvm#2 ; Function Attrs: nomerge nounwind declare ptr @llvm.coro.async.resume() #1 attributes #0 = { "target-features"="+adx,+aes,+avx,+avx2,+bmi,+bmi2,+clflushopt,+clwb,+clzero,+crc32,+cx16,+cx8,+f16c,+fma,+fsgsbase,+fxsr,+invpcid,+lzcnt,+mmx,+movbe,+mwaitx,+pclmul,+pku,+popcnt,+prfchw,+rdpid,+rdpru,+rdrnd,+rdseed,+sahf,+sha,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+sse4a,+ssse3,+vaes,+vpclmulqdq,+wbnoinvd,+x87,+xsave,+xsavec,+xsaveopt,+xsaves" } attributes #1 = { nomerge nounwind } attributes llvm#2 = { nounwind } ``` This verifier crashes after the `coro-split` pass with ``` cannot guarantee tail call due to mismatched parameter counts musttail call void @af_suspend_fn(ptr poison, i64 -1, ptr poison) LLVM ERROR: Broken function PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. Stack dump: 0. Program arguments: opt ../../../reduced.ll -O0 #0 0x00007f1d89645c0e __interceptor_backtrace.part.0 /build/gcc-11-XeT9lY/gcc-11-11.4.0/build/x86_64-linux-gnu/libsanitizer/asan/../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4193:28 #1 0x0000556d94d254f7 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Unix/Signals.inc:723:22 llvm#2 0x0000556d94d19a2f llvm::sys::RunSignalHandlers() /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Signals.cpp:105:20 llvm#3 0x0000556d94d1aa42 SignalHandler(int) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Unix/Signals.inc:371:36 llvm#4 0x00007f1d88e42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520) llvm#5 0x00007f1d88e969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76 llvm#6 0x00007f1d88e969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10 llvm#7 0x00007f1d88e969fc pthread_kill ./nptl/pthread_kill.c:89:10 llvm#8 0x00007f1d88e42476 gsignal ./signal/../sysdeps/posix/raise.c:27:6 llvm#9 0x00007f1d88e287f3 abort ./stdlib/abort.c:81:7 llvm#10 0x0000556d8944be01 std::vector<llvm::json::Value, std::allocator<llvm::json::Value>>::size() const /usr/include/c++/11/bits/stl_vector.h:919:40 llvm#11 0x0000556d8944be01 bool std::operator==<llvm::json::Value, std::allocator<llvm::json::Value>>(std::vector<llvm::json::Value, std::allocator<llvm::json::Value>> const&, std::vector<llvm::json::Value, std::allocator<llvm::json::Value>> const&) /usr/include/c++/11/bits/stl_vector.h:1893:23 llvm#12 0x0000556d8944be01 llvm::json::operator==(llvm::json::Array const&, llvm::json::Array const&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/Support/JSON.h:572:69 llvm#13 0x0000556d8944be01 llvm::json::operator==(llvm::json::Value const&, llvm::json::Value const&) (.cold) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/JSON.cpp:204:28 llvm#14 0x0000556d949ed2bd llvm::report_fatal_error(char const*, bool) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/ErrorHandling.cpp:82:70 llvm#15 0x0000556d8e37e876 llvm::SmallVectorBase<unsigned int>::size() const /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:91:32 llvm#16 0x0000556d8e37e876 llvm::SmallVectorTemplateCommon<llvm::DiagnosticInfoOptimizationBase::Argument, void>::end() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:282:41 llvm#17 0x0000556d8e37e876 llvm::SmallVector<llvm::DiagnosticInfoOptimizationBase::Argument, 4u>::~SmallVector() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:1215:24 llvm#18 0x0000556d8e37e876 llvm::DiagnosticInfoOptimizationBase::~DiagnosticInfoOptimizationBase() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:413:7 llvm#19 0x0000556d8e37e876 llvm::DiagnosticInfoIROptimization::~DiagnosticInfoIROptimization() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:622:7 llvm#20 0x0000556d8e37e876 llvm::OptimizationRemark::~OptimizationRemark() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:689:7 llvm#21 0x0000556d8e37e876 operator() /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroSplit.cpp:2213:14 llvm#22 0x0000556d8e37e876 emit<llvm::CoroSplitPass::run(llvm::LazyCallGraph::SCC&, llvm::CGSCCAnalysisManager&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&)::<lambda()> > /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h:83:12 llvm#23 0x0000556d8e37e876 llvm::CoroSplitPass::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroSplit.cpp:2212:13 llvm#24 0x0000556d8c36ecb1 llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::CoroSplitPass, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3 llvm#25 0x0000556d91c1a84f llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Analysis/CGSCCPassManager.cpp:90:12 llvm#26 0x0000556d8c3690d1 llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3 llvm#27 0x0000556d91c2162d llvm::ModuleToPostOrderCGSCCPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Analysis/CGSCCPassManager.cpp:278:18 llvm#28 0x0000556d8c369035 llvm::detail::PassModel<llvm::Module, llvm::ModuleToPostOrderCGSCCPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3 llvm#29 0x0000556d9457abc5 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManager.h:247:20 llvm#30 0x0000556d8e30979e llvm::CoroConditionalWrapper::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroConditionalWrapper.cpp:19:74 llvm#31 0x0000556d8c365755 llvm::detail::PassModel<llvm::Module, llvm::CoroConditionalWrapper, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3 llvm#32 0x0000556d9457abc5 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManager.h:247:20 llvm#33 0x0000556d89818556 llvm::SmallPtrSetImplBase::isSmall() const /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:196:33 llvm#34 0x0000556d89818556 llvm::SmallPtrSetImplBase::~SmallPtrSetImplBase() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:84:17 llvm#35 0x0000556d89818556 llvm::SmallPtrSetImpl<llvm::AnalysisKey*>::~SmallPtrSetImpl() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:321:7 llvm#36 0x0000556d89818556 llvm::SmallPtrSet<llvm::AnalysisKey*, 2u>::~SmallPtrSet() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:427:7 llvm#37 0x0000556d89818556 llvm::PreservedAnalyses::~PreservedAnalyses() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/Analysis.h:109:7 llvm#38 0x0000556d89818556 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/NewPMDriver.cpp:532:10 llvm#39 0x0000556d897e3939 optMain /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/optdriver.cpp:737:27 llvm#40 0x0000556d89455461 main /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/opt.cpp:25:33 llvm#41 0x00007f1d88e29d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16 llvm#42 0x00007f1d88e29e40 call_init ./csu/../csu/libc-start.c:128:20 llvm#43 0x00007f1d88e29e40 __libc_start_main ./csu/../csu/libc-start.c:379:5 llvm#44 0x0000556d897b6335 _start (/home/ubuntu/modular/.derived/third-party/llvm-project/build-relwithdebinfo-asan/bin/opt+0x150c335) Aborted (core dumped)
…ter partial ordering when determining primary template (llvm#82417) Consider the following: ``` struct A { static constexpr bool x = true; }; template<typename T, typename U> void f(T, U) noexcept(T::y); // #1, error: no member named 'y' in 'A' template<typename T, typename U> void f(T, U*) noexcept(T::x); // llvm#2 template<> void f(A, int*) noexcept; // explicit specialization of llvm#2 ``` We currently instantiate the exception specification of all candidate function template specializations when deducting template arguments for an explicit specialization, which results in a error despite `#1` not being selected by partial ordering as the most specialized template. According to [except.spec] p13: > An exception specification is considered to be needed when: > - [...] > - the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function); Assuming that "comparing declarations" means "determining whether the declarations correspond and declare the same entity" (per [basic.scope.scope] p4 and [basic.link] p11.1, respectively), the exception specification does _not_ need to be instantiated until _after_ partial ordering, at which point we determine whether the implicitly instantiated specialization and the explicit specialization declare the same entity (the determination of whether two functions/function templates correspond does not consider the exception specifications). This patch defers the instantiation of the exception specification until a single function template specialization is selected via partial ordering, matching the behavior of GCC, EDG, and MSVC: see https://godbolt.org/z/Ebb6GTcWE.
Alright, I've gone over most of the reports one more time. If something was very hard to understand due to complex code, I skipped it! The false positives stemming from not modelling out ptrs need at least a test case (marked with a Questionable: 1 (Why is the report there at the parameter loc?) 2 (Fuzzy locations here as well) 3 (How can something that is dereferenced only be null???) Strange and not understandable: 1 2 3 4 5 (all from the same function) 6 7 8 (Is this because it is a result of an Some true positives in the LLVM project (albeit older version!): 1 2 3 4 5 These ones might be worthwhile to have some separate patches to fix, which would double as an immediate proof and reasoning for this check! |
Apparently the questionable reports are because of two reasons. First is that the checker does not put note tags on variable declarations, and the second is that the checker can't check if the note tag is lexically before the warning's location in the current system. The first few strange reports are also caused by this. Strange 6, 7, and 8 look to be true positives, as &Obj can never be null yep, and in case 7 the pointer is already checked. Note tags are missing from some operations here as well, more note tag locations will be added later. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[This can be closed.]
Builder alerted me to the failing test, attempt #1 in the blind.
…des (llvm#94453) LSR will generate chains of related instructions with a known increment between them. With SVE, in the case of the test case, this can include increments like 'vscale * 16 + 8'. The idea of this patch is if we have a '+8' increment already calculated in the chain, we can generate a (legal) '+ vscale*16' addressing mode from it, allowing us to use the '[x16, #1, mul vl]' addressing mode instructions. In order to do this we keep track of the known 'bases' when generating chains in GenerateIVChain, checking for each if the accumulated increment expression from the base neatly folds into a legal addressing mode. If they do not we fall back to the existing LeftOverExpr, whether it is legal or not. This is mostly orthogonal to llvm#88124, dealing with the generation of chains as opposed to rest of LSR. The existing vscale addressing mode work has greatly helped compared to the last time I looked at this, allowing us to check that the addressing modes are indeed legal.
…arallel fusion llvm#94391 (llvm#97607)" This reverts commit edbc0e3. Reason for rollback. ASAN complains about this PR: ==4320==ERROR: AddressSanitizer: heap-use-after-free on address 0x502000006cd8 at pc 0x55e2978d63cf bp 0x7ffe6431c2b0 sp 0x7ffe6431c2a8 READ of size 8 at 0x502000006cd8 thread T0 #0 0x55e2978d63ce in map<llvm::MutableArrayRef<mlir::BlockArgument> &, llvm::MutableArrayRef<mlir::BlockArgument>, nullptr> mlir/include/mlir/IR/IRMapping.h:40:11 #1 0x55e2978d63ce in mlir::createFused(mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface, mlir::RewriterBase&, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)>, llvm::function_ref<void (mlir::RewriterBase&, mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface&, mlir::IRMapping)>) mlir/lib/Interfaces/LoopLikeInterface.cpp:156:11 llvm#2 0x55e2952a614b in mlir::fuseIndependentSiblingForLoops(mlir::scf::ForOp, mlir::scf::ForOp, mlir::RewriterBase&) mlir/lib/Dialect/SCF/Utils/Utils.cpp:1398:43 llvm#3 0x55e291480c6f in mlir::transform::LoopFuseSiblingOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp:482:17 llvm#4 0x55e29149ed5e in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::LoopFuseSiblingOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56 llvm#5 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14 llvm#6 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48 llvm#7 0x55e294646a8d in applySequenceBlock(mlir::Block&, mlir::transform::FailurePropagationMode, mlir::transform::TransformState&, mlir::transform::TransformResults&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:1788:15 llvm#8 0x55e29464f927 in mlir::transform::NamedSequenceOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:2155:10 llvm#9 0x55e2945d28ee in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::NamedSequenceOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56 llvm#10 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14 llvm#11 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48 llvm#12 0x55e2974a5fe2 in mlir::transform::applyTransforms(mlir::Operation*, mlir::transform::TransformOpInterface, mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>> const&, mlir::transform::TransformOptions const&, bool) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:2016:16 llvm#13 0x55e2945888d7 in mlir::transform::applyTransformNamedSequence(mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>>, mlir::transform::TransformOpInterface, mlir::ModuleOp, mlir::transform::TransformOptions const&) mlir/lib/Dialect/Transform/Transforms/TransformInterpreterUtils.cpp:234:10 llvm#14 0x55e294582446 in (anonymous namespace)::InterpreterPass::runOnOperation() mlir/lib/Dialect/Transform/Transforms/InterpreterPass.cpp:147:16 llvm#15 0x55e2978e93c6 in operator() mlir/lib/Pass/Pass.cpp:527:17 llvm#16 0x55e2978e93c6 in void llvm::function_ref<void ()>::callback_fn<mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int)::$_1>(long) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#17 0x55e2978e207a in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#18 0x55e2978e207a in executeAction<mlir::PassExecutionAction, mlir::Pass &> mlir/include/mlir/IR/MLIRContext.h:275:7 llvm#19 0x55e2978e207a in mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) mlir/lib/Pass/Pass.cpp:521:21 llvm#20 0x55e2978e5fbf in runPipeline mlir/lib/Pass/Pass.cpp:593:16 llvm#21 0x55e2978e5fbf in mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) mlir/lib/Pass/Pass.cpp:904:10 llvm#22 0x55e2978e5b65 in mlir::PassManager::run(mlir::Operation*) mlir/lib/Pass/Pass.cpp:884:60 llvm#23 0x55e291ebb460 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:408:17 llvm#24 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9 llvm#25 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12 llvm#26 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#27 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#28 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16 llvm#29 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3 llvm#30 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3 llvm#31 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3 llvm#32 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10 llvm#33 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14 llvm#34 0x55e291eb15f8 in mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:605:10 llvm#35 0x55e29130d1be in main mlir/tools/mlir-opt/mlir-opt.cpp:311:33 llvm#36 0x7fbcf3fff3d3 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x613d3) (BuildId: 9a996398ce14a94560b0c642eb4f6e94) llvm#37 0x55e2912365a9 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120 0x502000006cd8 is located 8 bytes inside of 16-byte region [0x502000006cd0,0x502000006ce0) freed by thread T0 here: #0 0x55e29130b7e2 in operator delete(void*, unsigned long) compiler-rt/lib/asan/asan_new_delete.cpp:155:3 #1 0x55e2979eb657 in __libcpp_operator_delete<void *, unsigned long> llvm#2 0x55e2979eb657 in __do_deallocate_handle_size<> llvm#3 0x55e2979eb657 in __libcpp_deallocate llvm#4 0x55e2979eb657 in deallocate llvm#5 0x55e2979eb657 in deallocate llvm#6 0x55e2979eb657 in operator() llvm#7 0x55e2979eb657 in ~vector llvm#8 0x55e2979eb657 in mlir::Block::~Block() mlir/lib/IR/Block.cpp:24:1 llvm#9 0x55e2979ebc17 in deleteNode llvm/include/llvm/ADT/ilist.h:42:39 llvm#10 0x55e2979ebc17 in erase llvm/include/llvm/ADT/ilist.h:205:5 llvm#11 0x55e2979ebc17 in erase llvm/include/llvm/ADT/ilist.h:209:39 llvm#12 0x55e2979ebc17 in mlir::Block::erase() mlir/lib/IR/Block.cpp:67:28 llvm#13 0x55e297aef978 in mlir::RewriterBase::eraseBlock(mlir::Block*) mlir/lib/IR/PatternMatch.cpp:245:10 llvm#14 0x55e297af0563 in mlir::RewriterBase::inlineBlockBefore(mlir::Block*, mlir::Block*, llvm::ilist_iterator<llvm::ilist_detail::node_options<mlir::Operation, false, false, void, false, void>, false, false>, mlir::ValueRange) mlir/lib/IR/PatternMatch.cpp:331:3 llvm#15 0x55e297af06d8 in mlir::RewriterBase::mergeBlocks(mlir::Block*, mlir::Block*, mlir::ValueRange) mlir/lib/IR/PatternMatch.cpp:341:3 llvm#16 0x55e297036608 in mlir::scf::ForOp::replaceWithAdditionalYields(mlir::RewriterBase&, mlir::ValueRange, bool, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)> const&) mlir/lib/Dialect/SCF/IR/SCF.cpp:575:12 llvm#17 0x55e2970673ca in mlir::detail::LoopLikeOpInterfaceInterfaceTraits::Model<mlir::scf::ForOp>::replaceWithAdditionalYields(mlir::detail::LoopLikeOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::RewriterBase&, mlir::ValueRange, bool, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)> const&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Interfaces/LoopLikeInterface.h.inc:658:56 llvm#18 0x55e2978d5feb in replaceWithAdditionalYields blaze-out/k8-opt-asan/bin/mlir/include/mlir/Interfaces/LoopLikeInterface.cpp.inc:105:14 llvm#19 0x55e2978d5feb in mlir::createFused(mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface, mlir::RewriterBase&, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)>, llvm::function_ref<void (mlir::RewriterBase&, mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface&, mlir::IRMapping)>) mlir/lib/Interfaces/LoopLikeInterface.cpp:135:14 llvm#20 0x55e2952a614b in mlir::fuseIndependentSiblingForLoops(mlir::scf::ForOp, mlir::scf::ForOp, mlir::RewriterBase&) mlir/lib/Dialect/SCF/Utils/Utils.cpp:1398:43 llvm#21 0x55e291480c6f in mlir::transform::LoopFuseSiblingOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp:482:17 llvm#22 0x55e29149ed5e in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::LoopFuseSiblingOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56 llvm#23 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14 llvm#24 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48 llvm#25 0x55e294646a8d in applySequenceBlock(mlir::Block&, mlir::transform::FailurePropagationMode, mlir::transform::TransformState&, mlir::transform::TransformResults&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:1788:15 llvm#26 0x55e29464f927 in mlir::transform::NamedSequenceOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:2155:10 llvm#27 0x55e2945d28ee in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::NamedSequenceOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56 llvm#28 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14 llvm#29 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48 llvm#30 0x55e2974a5fe2 in mlir::transform::applyTransforms(mlir::Operation*, mlir::transform::TransformOpInterface, mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>> const&, mlir::transform::TransformOptions const&, bool) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:2016:16 llvm#31 0x55e2945888d7 in mlir::transform::applyTransformNamedSequence(mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>>, mlir::transform::TransformOpInterface, mlir::ModuleOp, mlir::transform::TransformOptions const&) mlir/lib/Dialect/Transform/Transforms/TransformInterpreterUtils.cpp:234:10 llvm#32 0x55e294582446 in (anonymous namespace)::InterpreterPass::runOnOperation() mlir/lib/Dialect/Transform/Transforms/InterpreterPass.cpp:147:16 llvm#33 0x55e2978e93c6 in operator() mlir/lib/Pass/Pass.cpp:527:17 llvm#34 0x55e2978e93c6 in void llvm::function_ref<void ()>::callback_fn<mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int)::$_1>(long) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#35 0x55e2978e207a in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#36 0x55e2978e207a in executeAction<mlir::PassExecutionAction, mlir::Pass &> mlir/include/mlir/IR/MLIRContext.h:275:7 llvm#37 0x55e2978e207a in mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) mlir/lib/Pass/Pass.cpp:521:21 llvm#38 0x55e2978e5fbf in runPipeline mlir/lib/Pass/Pass.cpp:593:16 llvm#39 0x55e2978e5fbf in mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) mlir/lib/Pass/Pass.cpp:904:10 llvm#40 0x55e2978e5b65 in mlir::PassManager::run(mlir::Operation*) mlir/lib/Pass/Pass.cpp:884:60 llvm#41 0x55e291ebb460 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:408:17 llvm#42 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9 llvm#43 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12 llvm#44 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#45 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#46 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16 llvm#47 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3 llvm#48 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3 llvm#49 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3 llvm#50 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10 llvm#51 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14 previously allocated by thread T0 here: #0 0x55e29130ab5d in operator new(unsigned long) compiler-rt/lib/asan/asan_new_delete.cpp:86:3 #1 0x55e2979ed5d4 in __libcpp_operator_new<unsigned long> llvm#2 0x55e2979ed5d4 in __libcpp_allocate llvm#3 0x55e2979ed5d4 in allocate llvm#4 0x55e2979ed5d4 in __allocate_at_least<std::__u::allocator<mlir::BlockArgument> > llvm#5 0x55e2979ed5d4 in __split_buffer llvm#6 0x55e2979ed5d4 in mlir::BlockArgument* std::__u::vector<mlir::BlockArgument, std::__u::allocator<mlir::BlockArgument>>::__push_back_slow_path<mlir::BlockArgument const&>(mlir::BlockArgument const&) llvm#7 0x55e2979ec0f2 in push_back llvm#8 0x55e2979ec0f2 in mlir::Block::addArgument(mlir::Type, mlir::Location) mlir/lib/IR/Block.cpp:154:13 llvm#9 0x55e29796e457 in parseRegionBody mlir/lib/AsmParser/Parser.cpp:2172:34 llvm#10 0x55e29796e457 in (anonymous namespace)::OperationParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:2121:7 llvm#11 0x55e29796b25e in (anonymous namespace)::CustomOpAsmParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1785:16 llvm#12 0x55e297035742 in mlir::scf::ForOp::parse(mlir::OpAsmParser&, mlir::OperationState&) mlir/lib/Dialect/SCF/IR/SCF.cpp:521:14 llvm#13 0x55e291322c18 in llvm::ParseResult llvm::detail::UniqueFunctionBase<llvm::ParseResult, mlir::OpAsmParser&, mlir::OperationState&>::CallImpl<llvm::ParseResult (*)(mlir::OpAsmParser&, mlir::OperationState&)>(void*, mlir::OpAsmParser&, mlir::OperationState&) llvm/include/llvm/ADT/FunctionExtras.h:220:12 llvm#14 0x55e29795bea3 in operator() llvm/include/llvm/ADT/FunctionExtras.h:384:12 llvm#15 0x55e29795bea3 in callback_fn<llvm::unique_function<llvm::ParseResult (mlir::OpAsmParser &, mlir::OperationState &)> > llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#16 0x55e29795bea3 in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#17 0x55e29795bea3 in parseOperation mlir/lib/AsmParser/Parser.cpp:1521:9 llvm#18 0x55e29795bea3 in parseCustomOperation mlir/lib/AsmParser/Parser.cpp:2017:19 llvm#19 0x55e29795bea3 in (anonymous namespace)::OperationParser::parseOperation() mlir/lib/AsmParser/Parser.cpp:1174:10 llvm#20 0x55e297971d20 in parseBlockBody mlir/lib/AsmParser/Parser.cpp:2296:9 llvm#21 0x55e297971d20 in (anonymous namespace)::OperationParser::parseBlock(mlir::Block*&) mlir/lib/AsmParser/Parser.cpp:2226:12 llvm#22 0x55e29796e4f5 in parseRegionBody mlir/lib/AsmParser/Parser.cpp:2184:7 llvm#23 0x55e29796e4f5 in (anonymous namespace)::OperationParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:2121:7 llvm#24 0x55e29796b25e in (anonymous namespace)::CustomOpAsmParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1785:16 llvm#25 0x55e29796b2cf in (anonymous namespace)::CustomOpAsmParser::parseOptionalRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1796:12 llvm#26 0x55e2978d89ff in mlir::function_interface_impl::parseFunctionOp(mlir::OpAsmParser&, mlir::OperationState&, bool, mlir::StringAttr, llvm::function_ref<mlir::Type (mlir::Builder&, llvm::ArrayRef<mlir::Type>, llvm::ArrayRef<mlir::Type>, mlir::function_interface_impl::VariadicFlag, std::__u::basic_string<char, std::__u::char_traits<char>, std::__u::allocator<char>>&)>, mlir::StringAttr, mlir::StringAttr) mlir/lib/Interfaces/FunctionImplementation.cpp:232:14 llvm#27 0x55e2969ba41d in mlir::func::FuncOp::parse(mlir::OpAsmParser&, mlir::OperationState&) mlir/lib/Dialect/Func/IR/FuncOps.cpp:203:10 llvm#28 0x55e291322c18 in llvm::ParseResult llvm::detail::UniqueFunctionBase<llvm::ParseResult, mlir::OpAsmParser&, mlir::OperationState&>::CallImpl<llvm::ParseResult (*)(mlir::OpAsmParser&, mlir::OperationState&)>(void*, mlir::OpAsmParser&, mlir::OperationState&) llvm/include/llvm/ADT/FunctionExtras.h:220:12 llvm#29 0x55e29795bea3 in operator() llvm/include/llvm/ADT/FunctionExtras.h:384:12 llvm#30 0x55e29795bea3 in callback_fn<llvm::unique_function<llvm::ParseResult (mlir::OpAsmParser &, mlir::OperationState &)> > llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#31 0x55e29795bea3 in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#32 0x55e29795bea3 in parseOperation mlir/lib/AsmParser/Parser.cpp:1521:9 llvm#33 0x55e29795bea3 in parseCustomOperation mlir/lib/AsmParser/Parser.cpp:2017:19 llvm#34 0x55e29795bea3 in (anonymous namespace)::OperationParser::parseOperation() mlir/lib/AsmParser/Parser.cpp:1174:10 llvm#35 0x55e297959b78 in parse mlir/lib/AsmParser/Parser.cpp:2725:20 llvm#36 0x55e297959b78 in mlir::parseAsmSourceFile(llvm::SourceMgr const&, mlir::Block*, mlir::ParserConfig const&, mlir::AsmParserState*, mlir::AsmParserCodeCompleteContext*) mlir/lib/AsmParser/Parser.cpp:2785:41 llvm#37 0x55e29790d5c2 in mlir::parseSourceFile(std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::Block*, mlir::ParserConfig const&, mlir::LocationAttr*) mlir/lib/Parser/Parser.cpp:46:10 llvm#38 0x55e291ebbfe2 in parseSourceFile<mlir::ModuleOp, const std::__u::shared_ptr<llvm::SourceMgr> &> mlir/include/mlir/Parser/Parser.h:159:14 llvm#39 0x55e291ebbfe2 in parseSourceFile<mlir::ModuleOp> mlir/include/mlir/Parser/Parser.h:189:10 llvm#40 0x55e291ebbfe2 in mlir::parseSourceFileForTool(std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::ParserConfig const&, bool) mlir/include/mlir/Tools/ParseUtilities.h:31:12 llvm#41 0x55e291ebb263 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:383:33 llvm#42 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9 llvm#43 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12 llvm#44 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12 llvm#45 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12 llvm#46 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16 llvm#47 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3 llvm#48 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3 llvm#49 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3 llvm#50 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10 llvm#51 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14 llvm#52 0x55e291eb15f8 in mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:605:10 llvm#53 0x55e29130d1be in main mlir/tools/mlir-opt/mlir-opt.cpp:311:33 llvm#54 0x7fbcf3fff3d3 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x613d3) (BuildId: 9a996398ce14a94560b0c642eb4f6e94) llvm#55 0x55e2912365a9 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120 SUMMARY: AddressSanitizer: heap-use-after-free mlir/include/mlir/IR/IRMapping.h:40:11 in map<llvm::MutableArrayRef<mlir::BlockArgument> &, llvm::MutableArrayRef<mlir::BlockArgument>, nullptr> Shadow bytes around the buggy address: 0x502000006a00: fa fa 00 fa fa fa 00 00 fa fa 00 fa fa fa 00 fa 0x502000006a80: fa fa 00 fa fa fa 00 00 fa fa 00 00 fa fa 00 00 0x502000006b00: fa fa 00 00 fa fa 00 00 fa fa 00 fa fa fa 00 fa 0x502000006b80: fa fa 00 fa fa fa 00 fa fa fa 00 00 fa fa 00 00 0x502000006c00: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa fd fa =>0x502000006c80: fa fa fd fa fa fa fd fd fa fa fd[fd]fa fa fd fd 0x502000006d00: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa 0x502000006d80: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa 0x502000006e00: fa fa 00 fa fa fa 00 fa fa fa 00 00 fa fa 00 fa 0x502000006e80: fa fa 00 fa fa fa 00 00 fa fa 00 fa fa fa 00 fa 0x502000006f00: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==4320==ABORTING
This test is currently flaky on a local Windows amd64 build. The reason is that it relies on the order of `process.threads` but this order is nondeterministic: If we print lldb's inputs and outputs while running, we can see that the breakpoints are always being set correctly, and always being hit: ```sh runCmd: breakpoint set -f "main.c" -l 2 output: Breakpoint 1: where = a.out`func_inner + 1 at main.c:2:9, address = 0x0000000140001001 runCmd: breakpoint set -f "main.c" -l 7 output: Breakpoint 2: where = a.out`main + 17 at main.c:7:5, address = 0x0000000140001021 runCmd: run output: Process 52328 launched: 'C:\workspace\llvm-project\llvm\build\lldb-test-build.noindex\functionalities\unwind\zeroth_frame\TestZerothFrame.test_dwarf\a.out' (x86_64) Process 52328 stopped * thread #1, stop reason = breakpoint 1.1 frame #0: 0x00007ff68f6b1001 a.out`func_inner at main.c:2:9 1 void func_inner() { -> 2 int a = 1; // Set breakpoint 1 here ^ 3 } 4 5 int main() { 6 func_inner(); 7 return 0; // Set breakpoint 2 here ``` However, sometimes the backtrace printed in this test shows that the process is stopped inside NtWaitForWorkViaWorkerFactory from `ntdll.dll`: ```sh Backtrace at the first breakpoint: frame #0: 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 frame #1: 0x00007ffecc74585e ntdll.dll`RtlClearThreadWorkOnBehalfTicket + 862 frame llvm#2: 0x00007ffecc3e257d kernel32.dll`BaseThreadInitThunk + 29 frame llvm#3: 0x00007ffecc76af28 ntdll.dll`RtlUserThreadStart + 40 ``` When this happens, the test fails with an assertion error that the stopped thread's zeroth frame's current line number does not match the expected line number. This is because the test is looking at the wrong thread: `process.threads[0]`. If we print the list of threads each time the test is run, we notice that threads are sometimes in a different order, within `process.threads`: ```sh Thread 0: thread llvm#4: tid = 0x9c38, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 1: thread llvm#2: tid = 0xa950, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 2: thread #1: tid = 0xab18, 0x00007ff64bc81001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1 Thread 3: thread llvm#3: tid = 0xc514, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 0: thread llvm#3: tid = 0x018c, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 1: thread #1: tid = 0x85c8, 0x00007ff7130c1001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1 Thread 2: thread llvm#2: tid = 0xf344, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 3: thread llvm#4: tid = 0x6a50, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 ``` Use `self.thread()` to consistently select the correct thread, instead. Co-authored-by: kendal <[email protected]>
…izations of function templates to USRGenerator (llvm#98027) Given the following: ``` template<typename T> struct A { void f(int); // #1 template<typename U> void f(U); // llvm#2 template<> void f<int>(int); // llvm#3 }; ``` Clang will generate the same USR for `#1` and `llvm#2`. This patch fixes the issue by including the template arguments of dependent class scope explicit specializations in their USRs.
This patch adds a frame recognizer for Clang's `__builtin_verbose_trap`, which behaves like a `__builtin_trap`, but emits a failure-reason string into debug-info in order for debuggers to display it to a user. The frame recognizer triggers when we encounter a frame with a function name that begins with `__clang_trap_msg`, which is the magic prefix Clang emits into debug-info for verbose traps. Once such frame is encountered we display the frame function name as the `Stop Reason` and display that frame to the user. Example output: ``` (lldb) run warning: a.out was compiled with optimization - stepping may behave oddly; variables may not be available. Process 35942 launched: 'a.out' (arm64) Process 35942 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented frame #1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt] 1 struct Dummy { 2 void func() { -> 3 __builtin_verbose_trap("Misc.", "Function is not implemented"); 4 } 5 }; 6 7 int main() { (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented frame #0: 0x0000000100003fa4 a.out`main [inlined] __clang_trap_msg$Misc.$Function is not implemented$ at verbose_trap.cpp:0 [opt] * frame #1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt] frame llvm#2: 0x0000000100003fa4 a.out`main at verbose_trap.cpp:8:13 [opt] frame llvm#3: 0x0000000189d518b4 dyld`start + 1988 ```
…lvm#104148) `hasOperands` does not always execute matchers in the order they are written. This can cause issue in code using bindings when one operand matcher is relying on a binding set by the other. With this change, the first matcher present in the code is always executed first and any binding it sets are available to the second matcher. Simple example with current version (1 match) and new version (2 matches): ```bash > cat tmp.cpp int a = 13; int b = ((int) a) - a; int c = a - ((int) a); > clang-query tmp.cpp clang-query> set traversal IgnoreUnlessSpelledInSource clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d")))))) Match #1: tmp.cpp:1:1: note: "d" binds here int a = 13; ^~~~~~~~~~ tmp.cpp:2:9: note: "root" binds here int b = ((int)a) - a; ^~~~~~~~~~~~ 1 match. > ./build/bin/clang-query tmp.cpp clang-query> set traversal IgnoreUnlessSpelledInSource clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d")))))) Match #1: tmp.cpp:1:1: note: "d" binds here 1 | int a = 13; | ^~~~~~~~~~ tmp.cpp:2:9: note: "root" binds here 2 | int b = ((int)a) - a; | ^~~~~~~~~~~~ Match llvm#2: tmp.cpp:1:1: note: "d" binds here 1 | int a = 13; | ^~~~~~~~~~ tmp.cpp:3:9: note: "root" binds here 3 | int c = a - ((int)a); | ^~~~~~~~~~~~ 2 matches. ``` If this should be documented or regression tested anywhere please let me know where.
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame llvm#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame llvm#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame llvm#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame llvm#8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame llvm#8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ```
`JITDylibSearchOrderResolver` local variable can be destroyed before completion of all callbacks. Capture it together with `Deps` in `OnEmitted` callback. Original error: ``` ==2035==ERROR: AddressSanitizer: stack-use-after-return on address 0x7bebfa155b70 at pc 0x7ff2a9a88b4a bp 0x7bec08d51980 sp 0x7bec08d51978 READ of size 8 at 0x7bebfa155b70 thread T87 (tf_xla-cpu-llvm) #0 0x7ff2a9a88b49 in operator() llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:58 #1 0x7ff2a9a88b49 in __invoke<(lambda at llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:9) &, const llvm::DenseMap<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib *, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > &> libcxx/include/__type_traits/invoke.h:149:25 llvm#2 0x7ff2a9a88b49 in __call<(lambda at llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:9) &, const llvm::DenseMap<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib *, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > &> libcxx/include/__type_traits/invoke.h:224:5 llvm#3 0x7ff2a9a88b49 in operator() libcxx/include/__functional/function.h:210:12 llvm#4 0x7ff2a9a88b49 in void std::__u::__function::__policy_invoker<void (llvm::DenseMap<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr, ```
…ap (llvm#108825) This attempts to improve user-experience when LLDB stops on a verbose_trap. Currently if a `__builtin_verbose_trap` triggers, we display the first frame above the call to the verbose_trap. So in the newly added test case, we would've previously stopped here: ``` (lldb) run Process 28095 launched: '/Users/michaelbuch/a.out' (arm64) Process 28095 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access frame #1: 0x0000000100003f5c a.out`std::__1::vector<int>::operator[](this=0x000000016fdfebef size=0, (null)=10) at verbose_trap.cpp:6:9 3 template <typename T> 4 struct vector { 5 void operator[](unsigned) { -> 6 __builtin_verbose_trap("Bounds error", "out-of-bounds access"); 7 } 8 }; ``` After this patch, we would stop in the first non-`std` frame: ``` (lldb) run Process 27843 launched: '/Users/michaelbuch/a.out' (arm64) Process 27843 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access frame llvm#2: 0x0000000100003f44 a.out`g() at verbose_trap.cpp:14:5 11 12 void g() { 13 std::vector<int> v; -> 14 v[10]; 15 } 16 ``` rdar://134490328
Random testing found that the Z3 wrapper does not support UnarySymExpr, which was added recently and not included in the original Z3 wrapper. For now, just avoid submitting expressions to Z3 to avoid compiler crashes. Some crash context ... clang -cc1 -analyze -analyzer-checker=core z3-unarysymexpr.c -analyzer-constraints=z3 Unsupported expression to reason about! UNREACHABLE executed at clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h:297! Stack dump: 3. <root>/clang/test/Analysis/z3-unarysymexpr.c:13:7: Error evaluating branch #0 <addr> llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) #1 <addr> llvm::sys::RunSignalHandlers() llvm#8 <addr> clang::ento::SimpleConstraintManager::assumeAux( llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, bool) llvm#9 <addr> clang::ento::SimpleConstraintManager::assume( llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::NonLoc, bool) Co-authored-by: einvbri <[email protected]>
…llvm#94981) This extends default argument deduction to cover class templates as well, applying only to partial ordering, adding to the provisional wording introduced in llvm#89807. This solves some ambuguity introduced in P0522 regarding how template template parameters are partially ordered, and should reduce the negative impact of enabling `-frelaxed-template-template-args` by default. Given the following example: ```C++ template <class T1, class T2 = float> struct A; template <class T3> struct B; template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #1 template <class T6, class T7> struct B<A<T6, T7>>; // llvm#2 template struct B<A<int>>; ``` Prior to P0522, `llvm#2` was picked. Afterwards, this became ambiguous. This patch restores the pre-P0522 behavior, `llvm#2` is picked again.
…ext is not fully initialized (llvm#110481) As this comment around target initialization implies: ``` // This can be NULL if we don't know anything about the architecture or if // the target for an architecture isn't enabled in the llvm/clang that we // built ``` There are cases where we might fail to call `InitBuiltinTypes` when creating the backing `ASTContext` for a `TypeSystemClang`. If that happens, the builtins `QualType`s, e.g., `VoidPtrTy`/`IntTy`/etc., are not initialized and dereferencing them as we do in `GetBuiltinTypeForEncodingAndBitSize` (and other places) will lead to nullptr-dereferences. Example backtrace: ``` (lldb) run Assertion failed: (!isNull() && "Cannot retrieve a NULL type pointer"), function getCommonPtr, file Type.h, line 958. Process 2680 stopped * thread llvm#15, name = '<lldb.process.internal-state(pid=2712)>', stop reason = hit program assert frame llvm#4: 0x000000010cdf3cdc liblldb.20.0.0git.dylib`DWARFASTParserClang::ExtractIntFromFormValue(lldb_private::CompilerType const&, lldb_private::plugin::dwarf::DWARFFormValue const&) const (.cold.1) + liblldb.20.0.0git.dylib`DWARFASTParserClang::ParseObjCMethod(lldb_private::ObjCLanguage::MethodName const&, lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType, ParsedDWARFTypeAttributes , bool) (.cold.1): -> 0x10cdf3cdc <+0>: stp x29, x30, [sp, #-0x10]! 0x10cdf3ce0 <+4>: mov x29, sp 0x10cdf3ce4 <+8>: adrp x0, 545 0x10cdf3ce8 <+12>: add x0, x0, #0xa25 ; "ParseObjCMethod" Target 0: (lldb) stopped. (lldb) bt * thread llvm#15, name = '<lldb.process.internal-state(pid=2712)>', stop reason = hit program assert frame #0: 0x0000000180d08600 libsystem_kernel.dylib`__pthread_kill + 8 frame #1: 0x0000000180d40f50 libsystem_pthread.dylib`pthread_kill + 288 frame llvm#2: 0x0000000180c4d908 libsystem_c.dylib`abort + 128 frame llvm#3: 0x0000000180c4cc1c libsystem_c.dylib`__assert_rtn + 284 * frame llvm#4: 0x000000010cdf3cdc liblldb.20.0.0git.dylib`DWARFASTParserClang::ExtractIntFromFormValue(lldb_private::CompilerType const&, lldb_private::plugin::dwarf::DWARFFormValue const&) const (.cold.1) + frame llvm#5: 0x0000000109d30acc liblldb.20.0.0git.dylib`lldb_private::TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding, unsigned long) + 1188 frame llvm#6: 0x0000000109aaaed4 liblldb.20.0.0git.dylib`DynamicLoaderMacOS::NotifyBreakpointHit(void*, lldb_private::StoppointCallbackContext*, unsigned long long, unsigned long long) + 384 ``` This patch adds a one-time user-visible warning for when we fail to initialize the AST to indicate that initialization went wrong for the given target. Additionally, we add checks for whether one of the `ASTContext` `QualType`s is invalid before dereferencing any builtin types. The warning would look as follows: ``` (lldb) target create "a.out" Current executable set to 'a.out' (arm64). (lldb) b main warning: Failed to initialize builtin ASTContext types for target 'some-unknown-triple'. Printing variables may behave unexpectedly. Breakpoint 1: where = a.out`main + 8 at stepping.cpp:5:14, address = 0x0000000100003f90 ``` rdar://134869779
TITLE: [clang][dataflow] Add null-check after dereference checker
This checker implements a null-pointer analysis checker using the data-flow framework. In particular, the checker finds cases where the pointer's nullability is checked after it has been dereferenced:
It currently recognizes the following operations:
Notable limitations:
The checker only supports C++ due to the limitations of the framework (llvm#65301).
Function calls taking a reference of a pointer are not handled yet.
void external(int *&ref);
The note tag is only displayed at one location, and not all operations are supported or displayed.
More examples and limitations in the checker docs.
Results on open-source projects:
https://codechecker-demo.eastus.cloudapp.azure.com/Default/runs?run=Discookie_reverse-null
The reference-of-ptr limitation leads to a class of false positives across the tested projects (example).
But the checker also found quite a few true positives, on LLVM: 1 2 3 and a couple more, listed here.
This checker corresponds to the the CERT rule EXP34-C, Do not dereference null pointers, example 3.