The C166 compiler generates incorrect code when using __inline functions with bit return type. Test source code:
bit bitvar1 = 1; bit bitvar2; __inline bit test() { return bitvar1; } void main() { bitvar2 = test(); }
9: void main() 10: { 0000000A 4A00F000 BMOV R0.0,0xFD00.0 11: bitvar2 = test(); 0000000E 4AF00001 BMOV 0xFD00.1,R0.0 12: } 00000012 CB00 RET
; line 4: __inline bit test() ; line 5: { ; line 6: return bitvar1; BMOV R4.0,bitvar1 ; line 7: } RET ; line 8: ; line 9: void main() ; line 10: { ; line 11: bitvar2 = test(); CALL test BMOV bitvar2,R4.0 ; line 12: } RET
Hi Mike, this is interesting. My compiler V4.27 generates this disassembly: ; FUNCTION test (BEGIN RMASK = @0x0010) ; SOURCE LINE # 90 ; SOURCE LINE # 92 0000 4A00F400 R BMOV R4.0,bitvar1 ; SOURCE LINE # 93 0004 DB00 RETS ; FUNCTION test (END RMASK = @0x0010) ; FUNCTION main (BEGIN RMASK = @0x4000) ; SOURCE LINE # 95 ; SOURCE LINE # 97 ; SOURCE LINE # 92 ; SOURCE LINE # 97 0006 4A000000 R BMOV bitvar2,bitvar1 ; SOURCE LINE # 98 000A DB00 RETS ; FUNCTION main (END RMASK = @0x4000) The code should be correct, but the test-FUNCTION is dead code. May be this is different because I use the "MEDIUM" memory model? Anyway, Keil pretends that the C166 Compiler is capable of handling inline-functions, but this is not true. In 99% the __inline keyword is ignored. Particularly when you look at the example code in the release notes to C166 V4.25. When you compile this code with V4.27 you get totally different results, nothing is "inline". Holger
Hi Holger, That's strange, I couldn't get the compiler to generate the code that you posted. Anyway, how did you get that disassembly? Did you use the simulator or did you get the compiler to generate .SRC file? I'm asking this because it seems like the compiler generates different code with SRC option and without it. In particular, it seems that it ignores the __inline keyword when compiling with SRC. As for dead code, maybe it's left there just in case anyone needs the address of the function. The compiler cannot know if anyone needs it since references to the function can be in other modules. Besides, if you decide to use inline functions, you should be prepared for bigger code size anyway. - mike
Just found this: The bug doesn't show with this code at optimization level 7 (common tail merging.) It shows at optimization level 6 (constant propagation) which is default when a new project is created. - mike
Hi Mike, I took the first disassembly from the .LST-file, because I generally use the "pragma CODE" option, where the generated assembler code is included at the end of the .LST-file. I used the command line compiler "C166.EXE". Now I made a second try on this and replaced the "pragma CODE" with "pragma SRC": The generated code is indeed different. You seem to be right with your suspicion that the __inline keyword is ignored with "SRC". (I haven't made good experiences with the "SRC"-option and try to avoid it if possible.) So here's the generated code. I could only see marginal differences between optimization levels 6 and 7, both codes are correct: With OPTIMIZE 7,SPEED: test PROC FAR PUBLIC test ; FUNCTION test (BEGIN RMASK = @0x8000) ; line 94: { ; line 95: bitvar2 = bitvar1; BMOV bitvar2,bitvar1 ; line 96: } RETS ; FUNCTION test (END RMASK = @0x8000) test ENDP ; line 99: void main (void) main PROC FAR GLOBAL main ; FUNCTION main (BEGIN RMASK = @0x8000) ; line 100: { ; line 101: test(); JMP test ; line 102: } ; FUNCTION main (END RMASK = @0x8000) main ENDP And with OPTIMIZE 6,SPEED: test PROC FAR PUBLIC test ; FUNCTION test (BEGIN RMASK = @0x8000) ; line 94: { ; line 95: bitvar2 = bitvar1; BMOV bitvar2,bitvar1 ; line 96: } RETS ; FUNCTION test (END RMASK = @0x8000) test ENDP ; line 99: void main (void) main PROC FAR GLOBAL main ; FUNCTION main (BEGIN RMASK = @0x8000) ; line 100: { ; line 101: test(); CALL test ; line 102: } RETS ; FUNCTION main (END RMASK = @0x8000) main ENDP The differences result from "common tail merging", but anyway both compiles are correct. Maybe there is also a difference between the compiler embedded in uVision2 and the command line compiler? I have not yet tried this example with uVision2. Holger.
Hi Holger, To make things as simple as possible I tried this in command line:
\Keil\C166\BIN\C166 test.c code
; FUNCTION test (BEGIN RMASK = @0x0010) ; SOURCE LINE # 3 ; SOURCE LINE # 5 0000 4A00F400 R BMOV R4.0,bitvar1 ; SOURCE LINE # 6 0004 CB00 RET ; FUNCTION test (END RMASK = @0x0010) ; FUNCTION main (BEGIN RMASK = @0x4000) ; SOURCE LINE # 7 ; SOURCE LINE # 9 ; SOURCE LINE # 5 0006 4A00F000 R BMOV Rvb?1?test,bitvar1 ; SOURCE LINE # 9 000A 4AF00000 R BMOV bitvar2,Rvb?1?test ; SOURCE LINE # 10 000E CB00 RET ; FUNCTION main (END RMASK = @0x4000)
Hi mike, sorry, I think in my last post I used a different source code. So let's try again with this source code: bit bitvar1 = 1; bit bitvar2; __inline bit test() { return bitvar1; } void main() { bitvar2 = test(); } Compiling this with "code"-option delivers the same result as you have shown. So we're on the same line. The "Rvb?1?test" is a static bit which is reserved in bit-addressable memory. So the code should be correct, R0 is not changed. The only "bad" thing is: the main()-function is not reentrant, because it uses a static variable. For example: replace "bitvar1" by a port input pin, and rename main() into "read_pin()", then call this function from different interrupts. You will get confusing results! Compiling with "src"-option is also correct, even though the __inline keyword is ignored: ; line 4: __inline bit test() test PROC NEAR PUBLIC test ; FUNCTION test (BEGIN RMASK = @0x0010) ; line 5: { ; line 6: return bitvar1; BMOV R4.0,bitvar1 ; line 7: } RET ; FUNCTION test (END RMASK = @0x0010) test ENDP ; line 8: ; line 9: void main() main PROC NEAR GLOBAL main ; FUNCTION main (BEGIN RMASK = @0x0010) ; line 10: { ; line 11: bitvar2 = test(); CALL test BMOV bitvar2,R4.0 ; line 12: } RET ; FUNCTION main (END RMASK = @0x0010) main ENDP This code is reentrant! Holger.
Hi Mike, hi Holger I was reading this interesting fact and if I have time I will try out this here. Using the V427 too. Why I answer is the RvB?1?test,bitvar Mike concerns of. I play for some demonstration purposes with µVision2 Linker/Locator. This seems to be a codesegment you can see in map-file. ( Hope that I avoid to making angry Mike, he seems very competent on the C166 area ) I think it means Return virtually Bit or more likely Return value Bit with a size of one bit from code section or module "test" and parameter is "bitvar" what would be identically with that, what Mike wrote. Hope this can help you to better understand the code, since some new features are not well documented. "Sorry" again for interrupting this dialog, I will observe your results and try a test on friday. Stefan
The "Rvb?1?test" is a static bit which is reserved in bit-addressable memory. Can I see some evidence to support this, please?:-) I've checked all the listing files in the project and I didn't find such a name. It only shows in the compiler listing. Besides, the disassembly window in the simulator is still giving this:
9: void main() 10: { 0000000A 4A00F000 BMOV R0.0,0xFD00.0 11: bitvar2 = test(); 0000000E 4AF00001 BMOV 0xFD00.1,R0.0
Hi Mike, Hi Stefan, I've again analysed our little problem. Mike, you are right, the variable "Rvb?1?test" is indeed replaced by R0.0. This is done by the linker. To proof this, I've generated an absolute binary file from the linker output file, and disassembled the crucial part: 4A16 F0F0 -> BMOV R0.0,FD2C.F ; = Rvb?1?test,bitvar1 4AF0 160E -> BMOV FD2C.E,R0.0 ; = bitvar2,Rvb?1?test This is actually a bug! You can report it to Keil. The bug does not appear if you use OPTIMIZE(7,SPEED): ; FUNCTION main (BEGIN RMASK = @0x4000) 0006 4A000000 R BMOV bitvar2,bitvar1 000A CB00 RET ; FUNCTION main (END RMASK = @0x4000) Holger.
Hi Holger, hi Mike, thanks for find this bug. I did the test on friday but only find that what Mike has posted as first message on 28.05.03. I tried out with different optimization levels but got a identical result. So it was good idea to disassemble and find the detail in the binary. You are excellent guys. Stefan. (This to find would not have been an easy thing ;-} Thanks again )
Hi Holger, hi Mike, this is known by KEIL and will be solved in the next release. Stefan
Hi, Stefan, thanks for find this bug No problem. But does it mean that I don't have to report this bug to Keil anymore? I mean, with your interest in the bug, it sounds like you work for Keil. If not, how do I report a bug? Do I write an email to support.intl@keil.com? There is a couple more bugs I found some time ago. One of them was in the implementation of scanf and the other was in RTX166 tiny. So while I am at it, I could as well verify that the bugs are still there and report them too. - mike