@@ -344,20 +344,8 @@ static bool isBasicForLoop(const Token* tok)
344344 return true ;
345345}
346346
347- // findExpressionChanged() is a pure structural query, so memoize it via the program-state cache,
348- // keyed by (expr, start, end). This dominates the cost of building program memory from conditions.
349- static const Token* cachedFindExpressionChanged (ProgramMemoryState::ChangedCache* cache, const Token* expr, const Token* start, const Token* end, const Settings& settings)
350- {
351- if (!cache)
352- return findExpressionChanged (expr, start, end, settings);
353- const auto key = std::make_tuple (expr, start, end);
354- const auto it = cache->find (key);
355- if (it != cache->end ())
356- return it->second ;
357- return cache->emplace (key, findExpressionChanged (expr, start, end, settings)).first ->second ;
358- }
359-
360- static void programMemoryParseCondition (ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings& settings, bool then, ProgramMemoryState::ChangedCache* cache = nullptr )
347+ // findChanged: optional cached findExpressionChanged (see ProgramMemoryState::FindChangedFn).
348+ static void programMemoryParseCondition (ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings& settings, bool then, const ProgramMemoryState::FindChangedFn& findChanged = {})
361349{
362350 auto eval = [&](const Token* t) -> std::vector<MathLib::bigint> {
363351 if (!t)
@@ -371,6 +359,10 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
371359 return {result};
372360 return std::vector<MathLib::bigint>{};
373361 };
362+ // Use the cached closure if given, else compute directly.
363+ auto changed = [&](const Token* e, const Token* s, const Token* en) -> const Token* {
364+ return findChanged ? findChanged (e, s, en) : findExpressionChanged (e, s, en, settings);
365+ };
374366 if (Token::Match (tok, " ==|>=|<=|<|>|!=" )) {
375367 ValueFlow::Value truevalue;
376368 ValueFlow::Value falsevalue;
@@ -381,7 +373,7 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
381373 return ;
382374 if (!truevalue.isIntValue ())
383375 return ;
384- if (endTok && cachedFindExpressionChanged (cache, vartok, tok->next (), endTok, settings ))
376+ if (endTok && changed ( vartok, tok->next (), endTok))
385377 return ;
386378 const bool impossible = (tok->str () == " ==" && !then) || (tok->str () == " !=" && then);
387379 const ValueFlow::Value& v = then ? truevalue : falsevalue;
@@ -390,26 +382,26 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
390382 if (containerTok)
391383 pm.setContainerSizeValue (containerTok, v.intvalue , !impossible);
392384 } else if (Token::simpleMatch (tok, " !" )) {
393- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, !then, cache );
385+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, !then, findChanged );
394386 } else if (then && Token::simpleMatch (tok, " &&" )) {
395- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
396- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
387+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, findChanged );
388+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, findChanged );
397389 } else if (!then && Token::simpleMatch (tok, " ||" )) {
398- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
399- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
390+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, findChanged );
391+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, findChanged );
400392 } else if (Token::Match (tok, " &&|%oror%" )) {
401393 std::vector<MathLib::bigint> lhs = eval (tok->astOperand1 ());
402394 std::vector<MathLib::bigint> rhs = eval (tok->astOperand2 ());
403395 if (lhs.empty () || rhs.empty ()) {
404396 if (frontIs (lhs, !then))
405- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
397+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, findChanged );
406398 else if (frontIs (rhs, !then))
407- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
399+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, findChanged );
408400 else
409401 pm.setIntValue (tok, 0 , then);
410402 }
411403 } else if (tok && tok->exprId () > 0 ) {
412- if (endTok && cachedFindExpressionChanged (cache, tok, tok->next (), endTok, settings ))
404+ if (endTok && changed ( tok, tok->next (), endTok))
413405 return ;
414406 pm.setIntValue (tok, 0 , then);
415407 const Token* containerTok = settings.library .getContainerFromYield (tok, Library::Container::Yield::EMPTY );
@@ -418,14 +410,14 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
418410 }
419411}
420412
421- static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings& settings, ProgramMemoryState::ChangedCache* cache )
413+ static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings& settings, const ProgramMemoryState::FindChangedFn& findChanged )
422414{
423415 if (!scope)
424416 return ;
425417 if (!scope->isLocal ())
426418 return ;
427419 assert (scope != scope->nestedIn );
428- fillProgramMemoryFromConditions (pm, scope->nestedIn , endTok, settings, cache );
420+ fillProgramMemoryFromConditions (pm, scope->nestedIn , endTok, settings, findChanged );
429421 if (scope->type == ScopeType::eIf || scope->type == ScopeType::eWhile || scope->type == ScopeType::eElse || scope->type == ScopeType::eFor) {
430422 const Token* condTok = getCondTokFromEnd (scope->bodyEnd );
431423 if (!condTok)
@@ -434,13 +426,13 @@ static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scop
434426 bool error = false ;
435427 execute (condTok, pm, &result, &error, settings);
436428 if (error)
437- programMemoryParseCondition (pm, condTok, endTok, settings, scope->type != ScopeType::eElse, cache );
429+ programMemoryParseCondition (pm, condTok, endTok, settings, scope->type != ScopeType::eElse, findChanged );
438430 }
439431}
440432
441- static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Token* tok, const Settings& settings, ProgramMemoryState::ChangedCache* cache = nullptr )
433+ static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Token* tok, const Settings& settings, const ProgramMemoryState::FindChangedFn& findChanged = {} )
442434{
443- fillProgramMemoryFromConditions (pm, tok->scope (), tok, settings, cache );
435+ fillProgramMemoryFromConditions (pm, tok->scope (), tok, settings, findChanged );
444436}
445437
446438static void fillProgramMemoryFromAssignments (ProgramMemory& pm, const Token* tok, const Settings& settings, const ProgramMemory& state, const ProgramMemory::Map& vars)
@@ -549,7 +541,7 @@ void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& va
549541{
550542 ProgramMemory local = state;
551543 addVars (local, vars);
552- fillProgramMemoryFromConditions (local, tok, settings, changedCache. get ( ));
544+ fillProgramMemoryFromConditions (local, tok, settings, getCachedFindExpressionChanged ( /* skipDeadCode */ false ));
553545 ProgramMemory pm;
554546 fillProgramMemoryFromAssignments (pm, tok, settings, local, vars);
555547 local.replace (std::move (pm));
@@ -577,29 +569,38 @@ void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty, const To
577569 replace (std::move (pm), origin);
578570}
579571
580- const Token* ProgramMemoryState::findExpressionChanged ( const Token* expr, const Token* start, const Token* tok ) const
572+ ProgramMemoryState::FindChangedFn ProgramMemoryState::getCachedFindExpressionChanged ( bool skipDeadCode ) const
581573{
582- // Memoized structural pre-filter (a superset) gates the expensive dead-code-aware walk.
583- if (!cachedFindExpressionChanged (changedCache.get (), expr, start, tok, settings))
584- return nullptr ;
585- auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> {
586- ProgramMemory pm2 = state;
587- const auto result = execute (cond, pm2, settings);
588- if (isTrue (result))
589- return {1 };
590- if (isFalse (result))
591- return {0 };
592- return {};
574+ // The structural findExpressionChanged() is pure, so memoize it in changedCache (never
575+ // invalidated). skipDeadCode gates it as a pre-filter for the dead-code walk, which needs state.
576+ return [cache = changedCache, sp = &settings, statePtr = &state, skipDeadCode](const Token* expr, const Token* start, const Token* end) -> const Token* {
577+ const auto key = std::make_tuple (expr, start, end);
578+ const auto it = cache->find (key);
579+ const Token* modified = (it != cache->end ())
580+ ? it->second
581+ : cache->emplace (key, findExpressionChanged (expr, start, end, *sp)).first ->second ;
582+ if (!skipDeadCode || !modified)
583+ return modified;
584+ auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> {
585+ ProgramMemory pm2 = *statePtr;
586+ const auto result = execute (cond, pm2, *sp);
587+ if (isTrue (result))
588+ return {1 };
589+ if (isFalse (result))
590+ return {0 };
591+ return {};
592+ };
593+ return findExpressionChangedSkipDeadCode (expr, start, end, *sp, eval);
593594 };
594- return ::findExpressionChangedSkipDeadCode (expr, start, tok, settings, eval);
595595}
596596
597597void ProgramMemoryState::removeModifiedVars (const Token* tok)
598598{
599+ const auto findChanged = getCachedFindExpressionChanged (/* skipDeadCode*/ true );
599600 state.erase_if ([&](const ExprIdToken& e) {
600601 const Token* start = origins[e.getExpressionId ()];
601602 const Token* expr = e.tok ;
602- const bool changed = !expr || findExpressionChanged (expr, start, tok);
603+ const bool changed = !expr || findChanged (expr, start, tok);
603604 if (changed)
604605 origins.erase (e.getExpressionId ());
605606 return changed;
0 commit comments