Skip to content

ExprEngine: Handle pointer assignments in loops without bailout#2949

Closed
jubnzv wants to merge 3 commits into
cppcheck-opensource:mainfrom
jubnzv:exprengine-fix-redundant-bailout
Closed

ExprEngine: Handle pointer assignments in loops without bailout#2949
jubnzv wants to merge 3 commits into
cppcheck-opensource:mainfrom
jubnzv:exprengine-fix-redundant-bailout

Conversation

@jubnzv

@jubnzv jubnzv commented Dec 14, 2020

Copy link
Copy Markdown
Contributor

The following source code:

void foo(int *a)
{
  while (1)
    *a = 42;
  *a == 42;
}

Before this commit generates a redundant "bailout" value on the assigment:
15:12: 0:memory:{a=($2,[$1],[:]=?,[0]=bailout,[0]=42)}

After this commit:
15:12: 0:memory:{a=($2,[$1],[:]=?,[0]=42)}

The added tests are redundant and can't detect this error, but they increase the number of similar cases to handle possible regressions in the future.

The following source code:

```
void foo(int *a)
{
  while (1)
    *a = 42;
  *a == 42;
}
```

Before this commit generates a redundant "bailout" value on the assigment:
15:12: 0:memory:{a=($2,[$1],[:]=?,[0]=bailout,[0]=42)}

After this commit:
15:12: 0:memory:{a=($2,[$1],[:]=?,[0]=42)}

The added tests are redundant and can't detect this error, but they
increase the number of similar cases to handle possible regressions in
the future.
@jubnzv jubnzv changed the title ExprEngine: Remove redundant bailout value from pointer assignment ExprEngine: Remove redundant bailout value from the pointer assignment Dec 14, 2020

@danmar danmar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

great work! It is not easy to understand this code.

Comment thread lib/exprengine.cpp Outdated
Comment thread lib/exprengine.cpp
Comment thread test/testexprengine.cpp Outdated
Comment thread lib/exprengine.cpp
@jubnzv jubnzv changed the title ExprEngine: Remove redundant bailout value from the pointer assignment ExprEngine: Handle pointer assignments in loops without bailout Dec 15, 2020
@jubnzv

jubnzv commented Dec 15, 2020

Copy link
Copy Markdown
Contributor Author

I'm not sure how this CI error can be related to my commits. Other github actions are green.

@danmar

danmar commented Dec 15, 2020

Copy link
Copy Markdown
Collaborator

sorry I caused that CI error .. I've tried to fix it now with ecfabbc

@danmar

danmar commented Dec 15, 2020

Copy link
Copy Markdown
Collaborator

After some more thinking.. I think it makes sense to assign a bailout value. I think we want to have something like this:

    while (1) {

        int x = 24;
0:memory:{a=($2,[$1],[:]=?,[0]=bailout) x=24}

        *a = 42;
0:memory:{a=($2,[$1],[:]=?,[0]=42) x=24}

    }

could you look at ArrayValue::assign.. if there is a matching index then it should be removed.

@danmar danmar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would suggest that we drop this work path.. I changed my mind and don't think assigning a BailoutValue is bad. The assignment happens before the loop is executed.

Comment thread lib/exprengine.cpp
changedVariables.insert(varid);
auto oldValue = data.getValue(varid, nullptr, nullptr);
if (oldValue && oldValue->isUninit())
call(data.callbacks, varToken, oldValue, &data);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We are not trying to execute the loop yet. We should call callbacks later when the loop is executed. We also do not know if the variable will still have the oldValue when this code is reached (it will if this is unconditionally executed).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Then why do we call them in variable assignments here?

Comment thread lib/exprengine.cpp
// Unknown pointer, array or struct
auto arrayValue = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(val);
assert(arrayValue);
data.assignValue(tok2, varid, val);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

as far as I see this assignment is redundant.. it assigns the same value as we already have.

@jubnzv

jubnzv commented Dec 15, 2020

Copy link
Copy Markdown
Contributor Author

The assignment happens before the loop is executed.

That is, you suggest to replace bailout values later, when we assign a new value?

But how do we know that the loop has been executed? Do we want generate two equations after the loop, like for conditional statements?

For example:

int foo(int *arr) {
// memory:{arr=($2,[$1],[:]=?,[0]=bailout) a=2 b=$3}
    int b = 24;
    while (1) {
        *arr = 0;
// memory:{arr=($2,[$1],[:]=?,[0]=0) a=2 b=24}
    }
// memory:{arr=($2,[$1],[:]=?,[0]=0) b=24} constraints:{1}
// memory:{arr=($3,[$1],[:]=?,[0]=bailout,) b=24} constraints:{(1)==(0)}
}

Something like this, right?

@danmar

danmar commented Dec 15, 2020

Copy link
Copy Markdown
Collaborator

I expanded a bit..

void foo(int *a, int x) {

    *a = 12;
0:memory:{a=($2,[$1],[:]=?,[0]=12) x=$3}

    while (x < 10) {

        x = 24;
0:memory:{a=($2,[$1],[:]=?,[0]=bailout) x=24} constraints:{$3 < 10}

        *a = 42;
0:memory:{a=($2,[$1],[:]=?,[0]=42) x=24} constraints:{$3 < 10}
    }

0:memory:{a=($2,[$1],[:]=?,[0]=12) x=$3} constraints:{$3 >= 10}
0:memory:{a=($2,[$1],[:]=?,[0]=42) x=24} constraints:{$3 < 10}
    return *a;
}

EDIT: I thought it was unfortunate that your code before the while loop had a bailout value. I guess your example is right but it's not clear where the "bailout" after the loop comes from.

@danmar

danmar commented Dec 15, 2020

Copy link
Copy Markdown
Collaborator

One more thing.. if the loop condition is 1 then we should not split the execution path. The execution will obviously not skip the loop then. Please see how "alwaysTrue" is calculated in the handling of if.

@danmar

danmar commented Dec 15, 2020

Copy link
Copy Markdown
Collaborator

It feels like we can find more and more issues :-) it's never ending.

I've tried to look into merging "same" execution paths this evening to speedup analysis.. I have a work in progress will see if I can finish that.

@jubnzv jubnzv closed this Dec 16, 2020
@danmar

danmar commented Dec 16, 2020

Copy link
Copy Markdown
Collaborator

Please see how "alwaysTrue" is calculated in the handling of if.

Maybe we should create helper functions isAlwaysTrue and isAlwaysFalse. It is misleading that isTrue returns true whenever the value could be true, not sure how to rename it to make it less misleading..

@jubnzv

jubnzv commented Dec 16, 2020

Copy link
Copy Markdown
Contributor Author

Maybe we should create helper functions isAlwaysTrue and isAlwaysFalse

Yes, I implemented them while working on condition branches for the loops right now. This will take some more time.

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