Hello SDCC team,
Found this in some of my code:
uint32_t global; void func(void) { uint8_t i=18; uint8_t bit; bit = (BOOL)((global & (((uint32_t)1) << i)) && TRUE); if(bit) { while(1); }
Compiling this gives: "unreachable code"
The reason is the "i" variable which is only 8 bit. However, as the "1" is already casted, this should not matter.
What is worse, if the "i" variable is not a constant (e.g. comes from a function call), the code can build, but bad code is emitted. All bits above 8 are broken.
I think there are some general rules for casts that do not work here.
Thanks,
/pedro
I should probably mention i only tried this for the STM8 target.
And BOOL=uint8_t and TRUE=1
I did not see any bugs and will explain it in details.
First,
The expression bit = (BOOL)((global & (((uint32_t)1) << i)) && TRUE) is optimized by copy propagation and constant folding, so it becomes bit = 0, and the code changes to
Of course the while(1) is unreachable.
The second case,
Though upper bytes are omitted in calculation of the variable bit, but they are correctly processed in bit32.
This is also due to some earlier arch-independant optimization. Since omitting upper bytes does not affect final result.
Sorry, a typo. It should be constant propagation and constant folding above.
I suggest you build your program with
sdcc a.c -c -mstm8 --dump-ast --dump-i-code
and check what happens in each optimization step.
Ben,
I think Peter is right and this really is a bug.
Changes into:
Then it should upcast to uint32_t and change into:
And thus:
OK. I will check it later.
Yes, it is exactly the missing upcast that causes this IMHO.
This should not be fixed by adding the upcast in the frontend as that will only create a bigger register pressure. All useful shift values fit in a uint8_t type. All optimizations and all backends should be smart enough to handle this special case.
OK i think i figured out whats wrong here (i hope) :-)
It was not the missing upcast of "i" that caused the problem, but a simple bug in the AST tree generator. It cannot be fixed in the back-end as the bug is not where we thought it was.
Consider the simpler code:
This yields the following AST:
Note the the constant "1" has been recorded as being "unsigned-char literal". At this point the optimizers will later (of-course) make dog-food of it and optimize it away. No matter what the backend does, the code is dead at this point, and all bits above bit 7 must and will be dead.
The reason it fails is that "((unsigned long)1)" is a CAST op which is then replaced completely with a new entry. This happens in "case CAST:" at "SDCCast" line 4317. The comment also states " if the right is a literal replace the tree". This is all fine. It creates a new tree element which becomes the left side of the "LEFT_OP" element later.
Now the code does this:
It expects the getLeftResultType() to get the left result type of the newly replaced "left". This is where the bug is. getLeftResultType() does not treat SHIFT ops correctly and just returns the type of the current tree entry which is wrong. Now "unsigned char" is returned and not the "unsigned long" as was intended. getLeftResultType() simply does not work for this type.
A small patch to fixup getLeftResultType() makes it all work.
The reason it does not fail with variable entries is that this does not cause the tree replacement, so the type is calculated around a "CAST" op instead which does work in getLeftResultType().
Here is a patch with tests that fixes it up.
It does not AFAIK increase the register pressure for other cases other than this special case where more registers (for long instead of char) might be need, which is completely valid as the calculation would fail badly (and does) without it.
Please review/apply/reject.
Thanks,
/pedro
Please not that the example code above in itself does not make sense. Its only to debug the AST problem. For the code to make sense it looks like this:
This makes setting "bit" possible. The bug in the tree generator is however just a visible with any of them.
I disagree with your conclusion here. There is a huge difference between the last two example codes.
equals
But that is not the case for
and
Exactly. I know. That is why i wrote the extra comment. The first and second case gives a completely different result, and the bug is only visible to the user in the second. I just used these examples for debugging as the initial steps and the problematic AST generation is the same.
Sorry for the confusion. I did not think straight when i posted it as it allowed me to debug the problem in simpler code, but does not expose the bug to the user. I know.
Use the test case to view the bug properly and forget the other examples:
Without the fix this fails because of the "1" never getting cast properly. With the fix this works and the AST tree is now the same, but showing the constant as "unsigned long" as it should be.
Please review the patch if possible.
Thanks,
/pedro
Also, i am not sure why you write:
Quote:
equals
They are not the same as "i" is also a char type. Only the top one will work. Either "i" has to be unsigned long or the cast needs to be (unsigned long). Something has to promote the result to "unsigned long", or the expression will fail. I think this is in its essence what this bug is about.
edit: this does however depend on what "bit" is. If its also char, then you are right....
Thanks,
/pedro
Last edit: Peter Dons Tychsen 2016-01-24
Peter,
I'm glad you found out yourself that the wrong example depends on the type of bit. You made it an unsigned char and the fact that you name it bit does not turn it into a bool. Had you changed its type I would not have complained.
Maarten
All tests seem to be OK with this change.
Do we have regression tests for all the cases discussed here?
Philipp
There really is only one case here. I again apolagize for confusing with my other examples. Please disregard them. I am not aware of any other case with problems.
There is only one case, one test case and one fix (in this report).
Any bugs that look like this, but are not this should go in another report.
Thanks,
/pedro
Last edit: Peter Dons Tychsen 2016-01-24
Dear Peter,
Thanks for helping improving SDCC recently.
I would like to explain something to you,
You may get "warnings" and "non-fatal internal compiler errors" while during the test, however we asset if it is OK by if there are "failures" or not. If there is no "failure" then we can commit.
sdcc test.c -c --dump-ast
You can see the 0xa000 is also casted to 0x00 as a char value.
Last edit: Ben Shi 2016-01-25
I have not found the root cause of this bug. But I guess taking a deep look into the function decorateType() might be necessary.
Dear Peter,
Besides all regression test cases pass, space/time efficiency is also a concern before commit to SVN. A large increment in bytes / ticks cost in the regression test is not acceptable.
Hi Ben,
That is why i sent in a patch containing a test case and wrote "All tests seem to be OK with this change". I did not use a magic wand to state this :-). I ran all tests on Linux and Cygwin.
I will double check the results for this before/after the change.
No, that is a completely different case. The cast to "char" causes right propagation of the type "char" so all the elements in (q + 0xa000) are downgraded. That is not the case in my example.
Have you read the analysis of "getLeftResultType()" i tried to make above? I might very well have missed something (likely :-)), but something is wrong with either this functions comments or its implementation. The comments state that its purpose is to return the type of left side, but it does not do this for all types including the "shift opererators". Either the function fails or its comments do.
Thanks,
/pedro