Skip to content
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

added (optional) lazy execution of ValueFlow #4521

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

firewave
Copy link
Collaborator

No description provided.


std::function<void()> mValuesFunc;
mutable bool mValuesSet;
mutable bool mInSetValues;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two variables can be moved into the mValuesFunc.

Copy link
Contributor

@pfultz2 pfultz2 Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do something similar to what we do for memoize:

template<class F>
static std::function<void()> once(F f)
{
    bool ran = false;
    return [=]() mutable {
        if (ran)
            return;
        ran = true;
        f();
    };
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was very hacky. I just tried to get rid what I wanted it to do.

These two variables can be moved into the mValuesFunc.

Good point.

We could do something similar to what we do for [memoize]

Will take a look. I know there are patterns for this but I rarely use them so I keep forgetting them.

}

// cppcheck-suppress unusedFunction
const ValueFlow::Value* Token::getContainerSizeValue(const MathLib::bigint val) const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

Copy link
Collaborator Author

@firewave firewave Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept it to avoid the function calls (which obviously won't do much) since some of these might be called loooooots of times. I haven't profiled any of this yet.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not call it "redundant" but "unnecessary". Still needs to be profiled.

@@ -2388,24 +2385,24 @@ const ValueFlow::Value* Token::getMaxValue(bool condition, MathLib::bigint path)

const ValueFlow::Value* Token::getMovedValue() const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

}

const ValueFlow::Value* Token::getMaxValue(bool condition, MathLib::bigint path) const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

}

const ValueFlow::Value* Token::getValue(const MathLib::bigint val) const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

@@ -2340,39 +2337,39 @@ bool Token::hasKnownSymbolicValue(const Token* tok) const
{
if (tok->exprId() == 0)
return false;
return mImpl->mValues &&
std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) {
return !values().empty() &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

return value.isKnown() && value.isIntValue();
});
}

bool Token::hasKnownValue() const
{
return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown));
return !values().empty() && std::any_of(values().begin(), values().end(), std::mem_fn(&ValueFlow::Value::isKnown));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

@@ -2316,22 +2314,21 @@ std::shared_ptr<ScopeInfo2> Token::scopeInfo() const

bool Token::hasKnownIntValue() const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

@@ -1868,12 +1866,12 @@ const Token *Token::getValueTokenMinStrSize(const Settings *settings, MathLib::b

const Token *Token::getValueTokenMaxStrLength() const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

@@ -1847,12 +1845,12 @@ const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int ar

const Token *Token::getValueTokenMinStrSize(const Settings *settings, MathLib::bigint* path) const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

return !v.isImpossible() && v.isIntValue() && v.intvalue >= val;
});
}

const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const
{
if (!mImpl->mValues || !settings)
if (values().empty() || !settings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

return !v.isImpossible() && v.isIntValue() && v.intvalue <= val;
});
}

const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const
{
if (!mImpl->mValues)
if (values().empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values().empty() is redundant here.

@pfultz2
Copy link
Contributor

pfultz2 commented Oct 4, 2022

I think its a lot cleaner to use values() instead of mImpl->mValues. However, !mImp->mValues is not the equivalent of !values.empty(). If we use values() then the checks for null can be removed.

@firewave
Copy link
Collaborator Author

firewave commented Oct 4, 2022

I think its a lot cleaner to use values() instead of mImpl->mValues.

That was my takeaway as well after I "cracked" this and saw only one method needs to access the raw pointers - which is quite nice. I will extract the encapsulation and cleanup parts of this into a separate PR but it still needs some work (see below).

However, !mImp->mValues is not the equivalent of !values.empty(). If we use values() then the checks for null can be removed.

I am aware of that. I already fixed up a few mistakes I made in earlier revisions.

But it is still very much WIP...

I still have to profile this with valueflow enabled and disabled.

Also the lazy execution doesn't save anything at all.
Only when used in conjunction with #4362 it will actually do something since there's currently no way to control specific checks and even the special ones don't do that as evident by the above PR and several tickets I filed. This is a bigger thing and quite a can of worms but I have some ideas on how to incrementally address this to enhance the control and get rid of my hacks used for profiling and the CI.
I was hoping that maybe some checks can be adjusted to "delay" usage of the vaueflow data so we could profit in more cases but without proper control about what check is run it won't do much in the end.

There's also some tests failing with the lazy execution. But I guess they just lack the lazy execution hook. Still it is interesting and maybe we should make valueflow more explicit in tests so we know what depends on it. It might also help with determining how to increase the test coverage. But I haven't looked into this at all.

@firewave
Copy link
Collaborator Author

firewave commented Aug 8, 2023

This should "help" (as in executing less code) with tests which use the Tokenizer but do not rely on the ValueFlow. That might also fix the issue I hit in #5299.

@firewave
Copy link
Collaborator Author

I will try to pull out the wrapper function for values so this can finally progress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants