@@ -344,7 +344,20 @@ static bool isBasicForLoop(const Token* tok)
344344 return true ;
345345}
346346
347- static void programMemoryParseCondition (ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings& settings, bool then)
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 )
348361{
349362 auto eval = [&](const Token* t) -> std::vector<MathLib::bigint> {
350363 if (!t)
@@ -368,7 +381,7 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
368381 return ;
369382 if (!truevalue.isIntValue ())
370383 return ;
371- if (endTok && findExpressionChanged ( vartok, tok->next (), endTok, settings))
384+ if (endTok && cachedFindExpressionChanged (cache, vartok, tok->next (), endTok, settings))
372385 return ;
373386 const bool impossible = (tok->str () == " ==" && !then) || (tok->str () == " !=" && then);
374387 const ValueFlow::Value& v = then ? truevalue : falsevalue;
@@ -377,26 +390,26 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
377390 if (containerTok)
378391 pm.setContainerSizeValue (containerTok, v.intvalue , !impossible);
379392 } else if (Token::simpleMatch (tok, " !" )) {
380- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, !then);
393+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, !then, cache );
381394 } else if (then && Token::simpleMatch (tok, " &&" )) {
382- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then);
383- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then);
395+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
396+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
384397 } else if (!then && Token::simpleMatch (tok, " ||" )) {
385- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then);
386- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then);
398+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
399+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
387400 } else if (Token::Match (tok, " &&|%oror%" )) {
388401 std::vector<MathLib::bigint> lhs = eval (tok->astOperand1 ());
389402 std::vector<MathLib::bigint> rhs = eval (tok->astOperand2 ());
390403 if (lhs.empty () || rhs.empty ()) {
391404 if (frontIs (lhs, !then))
392- programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then);
405+ programMemoryParseCondition (pm, tok->astOperand2 (), endTok, settings, then, cache );
393406 else if (frontIs (rhs, !then))
394- programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then);
407+ programMemoryParseCondition (pm, tok->astOperand1 (), endTok, settings, then, cache );
395408 else
396409 pm.setIntValue (tok, 0 , then);
397410 }
398411 } else if (tok && tok->exprId () > 0 ) {
399- if (endTok && findExpressionChanged ( tok, tok->next (), endTok, settings))
412+ if (endTok && cachedFindExpressionChanged (cache, tok, tok->next (), endTok, settings))
400413 return ;
401414 pm.setIntValue (tok, 0 , then);
402415 const Token* containerTok = settings.library .getContainerFromYield (tok, Library::Container::Yield::EMPTY );
@@ -405,14 +418,14 @@ static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, con
405418 }
406419}
407420
408- static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings& settings)
421+ static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings& settings, ProgramMemoryState::ChangedCache* cache )
409422{
410423 if (!scope)
411424 return ;
412425 if (!scope->isLocal ())
413426 return ;
414427 assert (scope != scope->nestedIn );
415- fillProgramMemoryFromConditions (pm, scope->nestedIn , endTok, settings);
428+ fillProgramMemoryFromConditions (pm, scope->nestedIn , endTok, settings, cache );
416429 if (scope->type == ScopeType::eIf || scope->type == ScopeType::eWhile || scope->type == ScopeType::eElse || scope->type == ScopeType::eFor) {
417430 const Token* condTok = getCondTokFromEnd (scope->bodyEnd );
418431 if (!condTok)
@@ -421,13 +434,13 @@ static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scop
421434 bool error = false ;
422435 execute (condTok, pm, &result, &error, settings);
423436 if (error)
424- programMemoryParseCondition (pm, condTok, endTok, settings, scope->type != ScopeType::eElse);
437+ programMemoryParseCondition (pm, condTok, endTok, settings, scope->type != ScopeType::eElse, cache );
425438 }
426439}
427440
428- static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Token* tok, const Settings& settings)
441+ static void fillProgramMemoryFromConditions (ProgramMemory& pm, const Token* tok, const Settings& settings, ProgramMemoryState::ChangedCache* cache = nullptr )
429442{
430- fillProgramMemoryFromConditions (pm, tok->scope (), tok, settings);
443+ fillProgramMemoryFromConditions (pm, tok->scope (), tok, settings, cache );
431444}
432445
433446static void fillProgramMemoryFromAssignments (ProgramMemory& pm, const Token* tok, const Settings& settings, const ProgramMemory& state, const ProgramMemory::Map& vars)
@@ -536,7 +549,7 @@ void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& va
536549{
537550 ProgramMemory local = state;
538551 addVars (local, vars);
539- fillProgramMemoryFromConditions (local, tok, settings);
552+ fillProgramMemoryFromConditions (local, tok, settings, changedCache. get () );
540553 ProgramMemory pm;
541554 fillProgramMemoryFromAssignments (pm, tok, settings, local, vars);
542555 local.replace (std::move (pm));
@@ -567,11 +580,7 @@ void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty, const To
567580const Token* ProgramMemoryState::findExpressionChanged (const Token* expr, const Token* start, const Token* tok) const
568581{
569582 // Memoized structural pre-filter (a superset) gates the expensive dead-code-aware walk.
570- const auto key = std::make_tuple (expr, start, tok);
571- auto it = changedCache->find (key);
572- if (it == changedCache->end ())
573- it = changedCache->emplace (key, ::findExpressionChanged (expr, start, tok, settings)).first ;
574- if (!it->second )
583+ if (!cachedFindExpressionChanged (changedCache.get (), expr, start, tok, settings))
575584 return nullptr ;
576585 auto eval = [&](const Token* cond) -> std::vector<MathLib::bigint> {
577586 ProgramMemory pm2 = state;
0 commit comments