Bug in printf with long long handling
pedward
Posts: 1,642
As a simple test, I modified the hello.c as follows:
The result is garbage for the second value. Now, %x only handles longs, but it appears that the stack is not properly managed by the printf function.
What happens:
String is pushed onto stack
Low long of n is pushed
High long of n is pushed
Low long of n is pushed
High long of n is pushed
printf is called
Here's the PASM listing:
The observed behavior is the low long of n is displayed with first %x, but for the second %x it is gibberish. The expected behavior is that the low long of n should be displayed with spaces separating.
As a test, I replaced the second n with 0 and got the expected result, 4 items are placed on the stack in that instance.
If I just put n as the argument, I get similar gibberish, so it's consuming both longs.
My original intent was to print the high long using this syntax:
#include <stdio.h> #include <propeller.h> int main(void) { long long n = 1; printf("Hello World\n"); while(1) { waitcnt(CLKFREQ/10+CNT); printf("Hello World %x %x\n", n, n); n+=0x0FFFFFFF; } return 0; }
The result is garbage for the second value. Now, %x only handles longs, but it appears that the stack is not properly managed by the printf function.
What happens:
String is pushed onto stack
Low long of n is pushed
High long of n is pushed
Low long of n is pushed
High long of n is pushed
printf is called
Here's the PASM listing:
17:hello.c **** printf("Hello World %x %x\n", n, n); 55 .loc 1 17 0 discriminator 1 56 0068 009C3C08 wrlong .LC4, sp 57 006c 0000BCA0 mov r7, sp 58 0070 0400FC80 add r7, #4 59 0074 0000BCA0 mov r6, r14 60 0078 0800FC84 sub r6, #8 61 007c 0000BC08 rdlong r6, r6 62 0080 00003C08 wrlong r6, r7 63 0084 0400FC80 add r7, #4 64 0088 0000BCA0 mov r6, r14 65 008c 0400FC84 sub r6, #4 66 0090 0000BC08 rdlong r6, r6 67 0094 00003C08 wrlong r6, r7 68 0098 0000BCA0 mov r7, sp 69 009c 0C00FC80 add r7, #12 70 00a0 0000BCA0 mov r6, r14 71 00a4 0800FC84 sub r6, #8 72 00a8 0000BC08 rdlong r6, r6 73 00ac 00003C08 wrlong r6, r7 74 00b0 0400FC80 add r7, #4 75 00b4 0000BCA0 mov r6, r14 76 00b8 0400FC84 sub r6, #4 77 00bc 0000BC08 rdlong r6, r6 78 00c0 00003C08 wrlong r6, r7 79 00c4 0000FC5C jmpret lr,#_printf
The observed behavior is the low long of n is displayed with first %x, but for the second %x it is gibberish. The expected behavior is that the low long of n should be displayed with spaces separating.
As a test, I replaced the second n with 0 and got the expected result, 4 items are placed on the stack in that instance.
If I just put n as the argument, I get similar gibberish, so it's consuming both longs.
My original intent was to print the high long using this syntax:
printf("Hello World %x %x\n", *(&n+4), n);
Comments
As Dave said, %llx can be used to print a long long. If you really want to print the two halves separately, use casts and do something like:
If you look at the assembly listing, you will see that GCC didn't do any magic, it did as it should and passed 5 longs on the stack. The problem is in printf handling the passed variables.
I tested with the "not" simple printf, and it does the same thing.
If I pass "n, 0" as the parameters, the first invocation of printf prints "1 0", then each subsequent invocation prints garbage for the second argument.
The output for ersmiths suggestion results in garbage in the "(long) (n>>32)" position.
The code above shows quite clearly that there is some screwy stuff going on with printf and the stack. I would bet that the stack will keep growing until the program crashes.
When I changed to LMM and "standard printf" the results were as expected.
That said, there is still a bug.
In COG MM, using simple printf, when casted to a long and shifted, the results are not as expected.
In LMM, using simple printf, when casted to a long and shifted, the results are as expected.
Clearly there is something wrong with simple printf in COG MM.
I note that in your assembly code there's a "jmpret lr, #_printf". That implies that you're compiling for the COG memory model, which is pretty significant, because it uses a different library. That library only has the simple printf; in fact it's a stripped down version of the full printf. So 64 bits aren't handled.
http://propgcc.googlecode.com/hg/doc/Library.html#printf
I tried your same syntax in the hello.c program and it still doesn't work properly.
I would appreciate it if people would try to replicate my findings with the code I provided above, instead of telling me 5 different ways of trying to do it otherwise. My syntax isn't wrong, the compiled code is not working correctly. I want to fix or document this problem, not ignore it.
The first step in fixing something is to replicate the result, given the input. If the input is insufficient to replicate the result, then goto 0.
I have narrowed it down to simple printf not behaving correctly in COG mode. The "(unsigned int)(n>>32)" syntax DOES NOT work in the hello.c example I cited above.
I'm not trying to be contrary, I'm trying to get to the bottom of what I see as a bug, because it doesn't pass the simplest of examples.
This was prompted by the 64bit math thread, I wanted to simply demonstrate 64bit variable handling, and it has failed.
I agree. I've tried all the examples here and the only ones i can get to work are:
Eric's simple_printf LMM example, and peward's non simple_printf LMM example.
These were all run with simpleide on linux (pita not being able to simply disable output).
Here are some output samples:
SimpleIDE Eric's example:
peward example with no simple_printf on LMM
peward example with simple_printf on LMM
peward example with COG mode
Where is the source to the simple printf located? I'm assuming that there are multiple libraries for it, one for COG MM and one for xMMy?
I tried compiling your sample program (the first one in the thread) exactly as given, and it worked for me. The code was:
I don't have simple IDE. The command line I used to compile was:
and the output was:
Note that the code as given is not legal C, since we're passing 2 long longs in to printf but the format string is told to expect only unsigned ints. So in theory the results could be undefined and I just happen to be getting the right answer where you are not. We could try modifying the code to be strictly C compliant, which would be:
Does that work for you?
and then loads it with something like:
But this requires that .LC1 must be in hub memory in order to work properly, and it looks like in -mcog mode the compiler puts it into cog memory.
It should just emit directly in -mcog mode. I'll look into it.
I was able to initialize a 64bit value and shift it to print the upper and lower longs. I'm in the process of rebuilding the compiler from scratch, my local repo was dorked and I had to re-clone the repo. I got half way through building the tools and it bombed out on the library. Oops, bombed out again, off to make another post...
Thanks for bringing this issue to our attention.