Skip to content

Commit fd35e8e

Browse files
SC llvm teamSC llvm team
authored andcommitted
Merged main:11e482c4a32b into amd-gfx:176390732d3e
Local branch amd-gfx 1763907 Merged main:9cd774d1e49f into amd-gfx:557a5107ab62 Remote branch main 11e482c RegAllocGreedy: Add dummy priority advisor for writing MIR tests (llvm#121207)
2 parents 1763907 + 11e482c commit fd35e8e

File tree

30 files changed

+922
-319
lines changed

30 files changed

+922
-319
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,13 @@ New features
11571157
Crash and bug fixes
11581158
^^^^^^^^^^^^^^^^^^^
11591159

1160+
- In loops where the loop condition is opaque (i.e. the analyzer cannot
1161+
determine whether it's true or false), the analyzer will no longer assume
1162+
execution paths that perform more that two iterations. These unjustified
1163+
assumptions caused false positive reports (e.g. 100+ out-of-bounds reports in
1164+
the FFMPEG codebase) in loops where the programmer intended only two or three
1165+
steps but the analyzer wasn't able to understand that the loop is limited.
1166+
11601167
Improvements
11611168
^^^^^^^^^^^^
11621169

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ class CoreEngine {
126126
ExplodedNode *generateCallExitBeginNode(ExplodedNode *N,
127127
const ReturnStmt *RS);
128128

129+
/// Helper function called by `HandleBranch()`. If the currently handled
130+
/// branch corresponds to a loop, this returns the number of already
131+
/// completed iterations in that loop, otherwise the return value is
132+
/// `std::nullopt`. Note that this counts _all_ earlier iterations, including
133+
/// ones that were performed within an earlier iteration of an outer loop.
134+
std::optional<unsigned> getCompletedIterationCount(const CFGBlock *B,
135+
ExplodedNode *Pred) const;
136+
129137
public:
130138
/// Construct a CoreEngine object to analyze the provided CFG.
131139
CoreEngine(ExprEngine &exprengine,

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,14 @@ class ExprEngine {
321321
NodeBuilderWithSinks &nodeBuilder,
322322
ExplodedNode *Pred);
323323

324-
/// ProcessBranch - Called by CoreEngine. Used to generate successor
325-
/// nodes by processing the 'effects' of a branch condition.
326-
void processBranch(const Stmt *Condition,
327-
NodeBuilderContext& BuilderCtx,
328-
ExplodedNode *Pred,
329-
ExplodedNodeSet &Dst,
330-
const CFGBlock *DstT,
331-
const CFGBlock *DstF);
324+
/// ProcessBranch - Called by CoreEngine. Used to generate successor nodes by
325+
/// processing the 'effects' of a branch condition. If the branch condition
326+
/// is a loop condition, IterationsCompletedInLoop is the number of completed
327+
/// iterations (otherwise it's std::nullopt).
328+
void processBranch(const Stmt *Condition, NodeBuilderContext &BuilderCtx,
329+
ExplodedNode *Pred, ExplodedNodeSet &Dst,
330+
const CFGBlock *DstT, const CFGBlock *DstF,
331+
std::optional<unsigned> IterationsCompletedInLoop);
332332

333333
/// Called by CoreEngine.
334334
/// Used to generate successor nodes for temporary destructors depending
@@ -588,6 +588,8 @@ class ExprEngine {
588588
void evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
589589
const Expr *Ex);
590590

591+
bool didEagerlyAssumeBifurcateAt(ProgramStateRef State, const Expr *Ex) const;
592+
591593
static std::pair<const ProgramPointTag *, const ProgramPointTag *>
592594
getEagerlyAssumeBifurcationTags();
593595

clang/lib/Driver/ToolChains/OHOS.cpp

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
#include "llvm/ProfileData/InstrProf.h"
2020
#include "llvm/Support/FileSystem.h"
2121
#include "llvm/Support/Path.h"
22-
#include "llvm/Support/ScopedPrinter.h"
2322
#include "llvm/Support/VirtualFileSystem.h"
23+
#include "llvm/Support/ScopedPrinter.h"
2424

2525
using namespace clang::driver;
2626
using namespace clang::driver::toolchains;
@@ -58,9 +58,11 @@ static bool findOHOSMuslMultilibs(const Driver &D,
5858
return false;
5959
}
6060

61-
static bool findOHOSMultilibs(const Driver &D, const ToolChain &TC,
62-
const llvm::Triple &TargetTriple, StringRef Path,
63-
const ArgList &Args, DetectedMultilibs &Result) {
61+
static bool findOHOSMultilibs(const Driver &D,
62+
const ToolChain &TC,
63+
const llvm::Triple &TargetTriple,
64+
StringRef Path, const ArgList &Args,
65+
DetectedMultilibs &Result) {
6466
Multilib::flags_list Flags;
6567
bool IsA7 = false;
6668
if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ))
@@ -170,7 +172,8 @@ OHOS::OHOS(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
170172
Paths);
171173
}
172174

173-
ToolChain::RuntimeLibType OHOS::GetRuntimeLibType(const ArgList &Args) const {
175+
ToolChain::RuntimeLibType OHOS::GetRuntimeLibType(
176+
const ArgList &Args) const {
174177
if (Arg *A = Args.getLastArg(clang::driver::options::OPT_rtlib_EQ)) {
175178
StringRef Value = A->getValue();
176179
if (Value != "compiler-rt")
@@ -181,19 +184,20 @@ ToolChain::RuntimeLibType OHOS::GetRuntimeLibType(const ArgList &Args) const {
181184
return ToolChain::RLT_CompilerRT;
182185
}
183186

184-
ToolChain::CXXStdlibType OHOS::GetCXXStdlibType(const ArgList &Args) const {
187+
ToolChain::CXXStdlibType
188+
OHOS::GetCXXStdlibType(const ArgList &Args) const {
185189
if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) {
186190
StringRef Value = A->getValue();
187191
if (Value != "libc++")
188192
getDriver().Diag(diag::err_drv_invalid_stdlib_name)
189-
<< A->getAsString(Args);
193+
<< A->getAsString(Args);
190194
}
191195

192196
return ToolChain::CST_Libcxx;
193197
}
194198

195199
void OHOS::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
196-
ArgStringList &CC1Args) const {
200+
ArgStringList &CC1Args) const {
197201
const Driver &D = getDriver();
198202
const llvm::Triple &Triple = getTriple();
199203
std::string SysRoot = computeSysRoot();
@@ -254,7 +258,7 @@ void OHOS::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
254258
}
255259

256260
void OHOS::AddCXXStdlibLibArgs(const ArgList &Args,
257-
ArgStringList &CmdArgs) const {
261+
ArgStringList &CmdArgs) const {
258262
switch (GetCXXStdlibType(Args)) {
259263
case ToolChain::CST_Libcxx:
260264
CmdArgs.push_back("-lc++");
@@ -287,8 +291,7 @@ ToolChain::path_list OHOS::getRuntimePaths() const {
287291

288292
// First try the triple passed to driver as --target=<triple>.
289293
P.assign(D.ResourceDir);
290-
llvm::sys::path::append(P, "lib", D.getTargetTriple(),
291-
SelectedMultilib.gccSuffix());
294+
llvm::sys::path::append(P, "lib", D.getTargetTriple(), SelectedMultilib.gccSuffix());
292295
Paths.push_back(P.c_str());
293296

294297
// Second try the normalized triple.
@@ -337,20 +340,26 @@ std::string OHOS::getDynamicLinker(const ArgList &Args) const {
337340

338341
std::string OHOS::getCompilerRT(const ArgList &Args, StringRef Component,
339342
FileType Type) const {
340-
std::string CRTBasename =
341-
buildCompilerRTBasename(Args, Component, Type, /*AddArch=*/false);
342-
343343
SmallString<128> Path(getDriver().ResourceDir);
344344
llvm::sys::path::append(Path, "lib", getMultiarchTriple(getTriple()),
345-
SelectedMultilib.gccSuffix(), CRTBasename);
346-
if (getVFS().exists(Path))
347-
return std::string(Path);
348-
349-
std::string NewPath = ToolChain::getCompilerRT(Args, Component, Type);
350-
if (getVFS().exists(NewPath))
351-
return NewPath;
352-
353-
return std::string(Path);
345+
SelectedMultilib.gccSuffix());
346+
const char *Prefix =
347+
Type == ToolChain::FT_Object ? "" : "lib";
348+
const char *Suffix;
349+
switch (Type) {
350+
case ToolChain::FT_Object:
351+
Suffix = ".o";
352+
break;
353+
case ToolChain::FT_Static:
354+
Suffix = ".a";
355+
break;
356+
case ToolChain::FT_Shared:
357+
Suffix = ".so";
358+
break;
359+
}
360+
llvm::sys::path::append(
361+
Path, Prefix + Twine("clang_rt.") + Component + Suffix);
362+
return static_cast<std::string>(Path.str());
354363
}
355364

356365
void OHOS::addExtraOpts(llvm::opt::ArgStringList &CmdArgs) const {
@@ -387,7 +396,7 @@ SanitizerMask OHOS::getSupportedSanitizers() const {
387396

388397
// TODO: Make a base class for Linux and OHOS and move this there.
389398
void OHOS::addProfileRTLibs(const llvm::opt::ArgList &Args,
390-
llvm::opt::ArgStringList &CmdArgs) const {
399+
llvm::opt::ArgStringList &CmdArgs) const {
391400
// Add linker option -u__llvm_profile_runtime to cause runtime
392401
// initialization module to be linked in.
393402
if (needsProfileRT(Args))
@@ -404,8 +413,7 @@ ToolChain::path_list OHOS::getArchSpecificLibPaths() const {
404413
return Paths;
405414
}
406415

407-
ToolChain::UnwindLibType
408-
OHOS::GetUnwindLibType(const llvm::opt::ArgList &Args) const {
416+
ToolChain::UnwindLibType OHOS::GetUnwindLibType(const llvm::opt::ArgList &Args) const {
409417
if (Args.getLastArg(options::OPT_unwindlib_EQ))
410418
return Generic_ELF::GetUnwindLibType(Args);
411419
return GetDefaultUnwindLibType();

clang/lib/StaticAnalyzer/Core/CoreEngine.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,8 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
444444
NodeBuilderContext Ctx(*this, B, Pred);
445445
ExplodedNodeSet Dst;
446446
ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
447-
*(B->succ_begin() + 1));
447+
*(B->succ_begin() + 1),
448+
getCompletedIterationCount(B, Pred));
448449
// Enqueue the new frontier onto the worklist.
449450
enqueue(Dst);
450451
}
@@ -591,6 +592,30 @@ ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N,
591592
return isNew ? Node : nullptr;
592593
}
593594

595+
std::optional<unsigned>
596+
CoreEngine::getCompletedIterationCount(const CFGBlock *B,
597+
ExplodedNode *Pred) const {
598+
const LocationContext *LC = Pred->getLocationContext();
599+
BlockCounter Counter = WList->getBlockCounter();
600+
unsigned BlockCount =
601+
Counter.getNumVisited(LC->getStackFrame(), B->getBlockID());
602+
603+
const Stmt *Term = B->getTerminatorStmt();
604+
if (isa<ForStmt, WhileStmt, CXXForRangeStmt>(Term)) {
605+
assert(BlockCount >= 1 &&
606+
"Block count of currently analyzed block must be >= 1");
607+
return BlockCount - 1;
608+
}
609+
if (isa<DoStmt>(Term)) {
610+
// In a do-while loop one iteration happens before the first evaluation of
611+
// the loop condition, so we don't subtract one.
612+
return BlockCount;
613+
}
614+
// ObjCForCollectionStmt is skipped intentionally because the current
615+
// application of the iteration counts is not relevant for it.
616+
return std::nullopt;
617+
}
618+
594619
void CoreEngine::enqueue(ExplodedNodeSet &Set) {
595620
for (const auto I : Set)
596621
WList->enqueue(I);

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,12 +2760,10 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) {
27602760
return State->assume(V);
27612761
}
27622762

2763-
void ExprEngine::processBranch(const Stmt *Condition,
2764-
NodeBuilderContext& BldCtx,
2765-
ExplodedNode *Pred,
2766-
ExplodedNodeSet &Dst,
2767-
const CFGBlock *DstT,
2768-
const CFGBlock *DstF) {
2763+
void ExprEngine::processBranch(
2764+
const Stmt *Condition, NodeBuilderContext &BldCtx, ExplodedNode *Pred,
2765+
ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF,
2766+
std::optional<unsigned> IterationsCompletedInLoop) {
27692767
assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
27702768
"CXXBindTemporaryExprs are handled by processBindTemporary.");
27712769
const LocationContext *LCtx = Pred->getLocationContext();
@@ -2808,8 +2806,35 @@ void ExprEngine::processBranch(const Stmt *Condition,
28082806
if (StTrue && StFalse)
28092807
assert(!isa<ObjCForCollectionStmt>(Condition));
28102808

2811-
if (StTrue)
2812-
Builder.generateNode(StTrue, true, PredN);
2809+
if (StTrue) {
2810+
// If we are processing a loop condition where two iterations have
2811+
// already been completed and the false branch is also feasible, then
2812+
// don't assume a third iteration because it is a redundant execution
2813+
// path (unlikely to be different from earlier loop exits) and can cause
2814+
// false positives if e.g. the loop iterates over a two-element structure
2815+
// with an opaque condition.
2816+
//
2817+
// The iteration count "2" is hardcoded because it's the natural limit:
2818+
// * the fact that the programmer wrote a loop (and not just an `if`)
2819+
// implies that they thought that the loop body might be executed twice;
2820+
// * however, there are situations where the programmer knows that there
2821+
// are at most two iterations but writes a loop that appears to be
2822+
// generic, because there is no special syntax for "loop with at most
2823+
// two iterations". (This pattern is common in FFMPEG and appears in
2824+
// many other projects as well.)
2825+
bool CompletedTwoIterations = IterationsCompletedInLoop.value_or(0) >= 2;
2826+
bool FalseAlsoFeasible =
2827+
StFalse ||
2828+
didEagerlyAssumeBifurcateAt(PrevState, dyn_cast<Expr>(Condition));
2829+
bool SkipTrueBranch = CompletedTwoIterations && FalseAlsoFeasible;
2830+
2831+
// FIXME: This "don't assume third iteration" heuristic partially
2832+
// conflicts with the widen-loop analysis option (which is off by
2833+
// default). If we intend to support and stabilize the loop widening,
2834+
// we must ensure that it 'plays nicely' with this logic.
2835+
if (!SkipTrueBranch || AMgr.options.ShouldWidenLoops)
2836+
Builder.generateNode(StTrue, true, PredN);
2837+
}
28132838

28142839
if (StFalse)
28152840
Builder.generateNode(StFalse, false, PredN);
@@ -3731,6 +3756,12 @@ ExprEngine::getEagerlyAssumeBifurcationTags() {
37313756
return std::make_pair(&TrueTag, &FalseTag);
37323757
}
37333758

3759+
/// If the last EagerlyAssume attempt was successful (i.e. the true and false
3760+
/// cases were both feasible), this state trait stores the expression where it
3761+
/// happened; otherwise this holds nullptr.
3762+
REGISTER_TRAIT_WITH_PROGRAMSTATE(LastEagerlyAssumeExprIfSuccessful,
3763+
const Expr *)
3764+
37343765
void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
37353766
ExplodedNodeSet &Src,
37363767
const Expr *Ex) {
@@ -3746,13 +3777,19 @@ void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
37463777
}
37473778

37483779
ProgramStateRef State = Pred->getState();
3780+
State = State->set<LastEagerlyAssumeExprIfSuccessful>(nullptr);
37493781
SVal V = State->getSVal(Ex, Pred->getLocationContext());
37503782
std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
37513783
if (SEV && SEV->isExpression()) {
37523784
const auto &[TrueTag, FalseTag] = getEagerlyAssumeBifurcationTags();
37533785

37543786
auto [StateTrue, StateFalse] = State->assume(*SEV);
37553787

3788+
if (StateTrue && StateFalse) {
3789+
StateTrue = StateTrue->set<LastEagerlyAssumeExprIfSuccessful>(Ex);
3790+
StateFalse = StateFalse->set<LastEagerlyAssumeExprIfSuccessful>(Ex);
3791+
}
3792+
37563793
// First assume that the condition is true.
37573794
if (StateTrue) {
37583795
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
@@ -3770,6 +3807,11 @@ void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
37703807
}
37713808
}
37723809

3810+
bool ExprEngine::didEagerlyAssumeBifurcateAt(ProgramStateRef State,
3811+
const Expr *Ex) const {
3812+
return Ex && State->get<LastEagerlyAssumeExprIfSuccessful>() == Ex;
3813+
}
3814+
37733815
void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
37743816
ExplodedNodeSet &Dst) {
37753817
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);

0 commit comments

Comments
 (0)