Go Down

Topic: uint32_t weirdness (Read 3984 times) previous topic - next topic

chucktodd

Agreed, the results are unexpected. The surprising thing is that when not initializing "a", it appears that the array is still indexed correctly, that is the correct character appears to be retrieved.

The unexpected behavior probably has to do with how the compiler optimizes the code with regard to initialized versus uninitialized indices.

When I suspect an optimization problem, I turn it off.  That is required anyway when debugging, because the compiler can put optimized code anywhere it "wants" as long as the end result is correct.
I agree with what you are saying, Somehow the compiler is changing the loop to an incorrect form just because 'a' was never initialized.  

It is probably allocating 'a' to a register that is colliding with the 'b' value, maybe it is re-initializing 'b' every time through the loop.

Is there anyway I can get a LST of of the optimized code? or a reverse assembly of the HEX?

Chuck.
Currently built mega http server, Now converting it to ESP32.

cattledog

Quote
maybe it is re-initializing 'b' every time through the loop.
additional compiler weirdness

if b is declared static uint32_t the function works even when "a" and "c" are not initialized.

Delta_G

#17
Sep 08, 2016, 02:29 am Last Edit: Sep 08, 2016, 02:30 am by Delta_G
Objdump time.  See what it's being compiled into.
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

chucktodd

Why not print a since that value seems to be the source of at least some part of the problem.
Here is the debug printing out 'a'

htoli("64")=
 after inc a=1 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 after inc a=2 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0

returned value of htoli("64")=0
htoli("64")=
 after inc a=1 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 after inc a=2 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0
returned value of htoli(buf)=0


new code with debug print out of 'a'

Code: [Select]

uint32_t htoli(const char inbuf[]){
uint32_t b=0;
uint16_t a;
uint8_t c;
Serial.print("htoli(\"");
Serial.print(inbuf);
Serial.print("\")=\n");
bool err=false;
while((c=inbuf[a++])&&!err){
 Serial.print(" after inc a=");
 Serial.print(a,DEC);
 c = toupper(c);
 Serial.print(" c(chr)=");
 Serial.print((char)c);

 if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))){
 Serial.print(" b=");
 Serial.print(b,DEC);
 Serial.print(" ");

 b=b*16;
 Serial.print(" after mul b=");
 Serial.print(b,DEC);
 Serial.print(" c(dec)=");

  c= c-48;
 if(c>9)c=c-7;
 b = b + c;
 Serial.print(c,DEC);
 Serial.print(", after add (b=b+c) b=");
 Serial.print(b,DEC);
 Serial.print("\n");

 }
 else {
 Serial.print("error? ");
 err=true;
 }
 }

Serial.print("result =");
Serial.println(b,DEC);

return b;
}


'a' looks to be incrementing correctly, but 'b' is still being re-initialized every time through the loop.

Chuck.
Currently built mega http server, Now converting it to ESP32.

chucktodd

Objdump time.  See what it's being compiled into.
Can you point me somewhere that will instruct me on how to use Objdump, and where to acquire it?

Is it included with the tool chain, install with 1.6.5?

Chuck.
Currently built mega http server, Now converting it to ESP32.

chucktodd

additional compiler weirdness

if b is declared static uint32_t the function works even when "a" and "c" are not initialized.
I think this is point towards the compiler flagging 'b' as an uninitialized internal loop variable.  It thinks it is being instantiated every loop. 

And the compiler is helpfully zeroing it out at the top.

Chuck.
Currently built mega http server, Now converting it to ESP32.

jremington

Quote
Somehow the compiler is changing the loop to an incorrect form just because 'a' was never initialized.  
Incorrect only from the point of view of looking at the intermediate results, which are not as expected. The end result is correct when a is initialized.

This is why you MUST turn off optimization when debugging. I'll bet you get different results with optimization off.

cattledog

Quote
I think this is point towards the compiler flagging 'b' as an uninitialized internal loop variable.
It is initialized within the function, but not within the while loop.

It its declared as a global variable uint32_t b = 0; The function also works properly. I would have thought that declaring and initializing b within the function would have covered it for the while loop, but I think I just learned something about scope.

chucktodd

It is initialized within the function, but not within the while loop.

It its declared as a global variable uint32_t b = 0; The function also works properly. I would have thought that declaring and initializing b within the function would have covered it for the while loop, but I think I just learned something about scope.
Cattledog,

  You were able to duplicate this behavior?  That's great!

  The code it is part of a 7k line project.  I was worried that it was something about exceeding 64k of code that triggered this problem.  My current app compiles to 66,620 bytes with 4,252 bytes of RAM used.

Thanks for taking the time to duplicate it.

Chuck.
Currently built mega http server, Now converting it to ESP32.

pert

I was worried that it was something about exceeding 64k of code that triggered this problem.
Then why not make a minimal test sketch to check? Something like this:
Code: [Select]
void setup() {
  Serial.begin(115200);
  htoli("800");
}

void loop() {}


uint32_t htoli(const char inbuf[]) {
  uint32_t b = 0;
  uint16_t a;
  uint8_t c;
  Serial.print("htoli(\"");
  Serial.print(inbuf);
  Serial.print("\")=\n");
  bool err = false;
  while ((c = inbuf[a++]) && !err) {
    c = toupper(c);
    Serial.print(" c(chr)=");
    Serial.print((char)c);
    if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F'))) {
      Serial.print(" b=");
      Serial.print(b, DEC);
      Serial.print(" ");
      b = b * 16;
      Serial.print(" after mul b=");
      Serial.print(b, DEC);
      Serial.print(" c(dec)=");
      c = c - 48;
      if (c > 9)c = c - 7;
      b = b + c;
      Serial.print(c, DEC);
      Serial.print(", after add (b=b+c) b=");
      Serial.print(b, DEC);
      Serial.print("\n");
    }
    else {
      Serial.print("error? ");
      err = true;
    }
  }
  //if(err) b=0;
  Serial.print("result =");
  Serial.println(b, DEC);
  return b;
}

I get the same results as you.

Can you point me somewhere that will instruct me on how to use Objdump
avr-objdump --help

Is it included with the tool chain, install with 1.6.5?
Yes, it's at {Arduino IDE installation folder}/hardware/tools/avr/bin

When I run the command:
Code: [Select]
avr-objdump -I"E:\Stuff\misc\electronics\arduino\temp\htoli" -d -S -j .text htoli.cpp.elf > disassembly.txt
on that simplified sketch compiled with Arduino IDE 1.6.5-r5/Arduino AVR Boards 1.6.8(because it sounds like you're using Arduino IDE 1.6.5) I get the attached output file. The -I"E:\Stuff\misc\electronics\arduino\temp\htoli" is the sketch folder and is necessary to get the inline source. This was fixed in Arduino IDE 1.6.6 but seems like it may have been broken again in recent versions because I can't get the inline source to work with Arduino IDE 1.6.11 even with the sketch folder specified.

htoli.cpp.elf is located in the build folder. You can find the location of the build folder by turning on File > Preferences > Show verbose output during: > compilation and looking at the console output, for example in my case:
Code: [Select]
C:\Program Files (x86)\arduino-1.6.5-r5\hardware\tools\avr/bin/avr-objcopy -O ihex -R .eeprom C:\Users\per\AppData\Local\Temp\build5698988197269744591.tmp/htoli.cpp.elf C:\Users\per\AppData\Local\Temp\build5698988197269744591.tmp/htoli.cpp.hex

PaulMurrayCbr

Quote
c= c-48;
  if(c>9)c=c-7;
I always prefer

b = (b * 16)  + (c<='9' ? c-'0' : c-'A'+10);

But as to what's stomping over b in between iterations of you loop - I have no idea.
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

el_supremo

I've got a disassembled output with and without the initialization of 'a'. When comparing the two, is obvious that when 'a' is not initialized a chunk of the code in the loop is gone and therefore the compiler somehow optimized it into oblivion. But at the moment I can't see why it does it.

Pete
Don't send me technical questions via Private Message.

chucktodd

Then why not make a minimal test sketch to check? Something like this:
Code: [Select]
void setup() {
  Serial.begin(115200);
  htoli("800");
}

void loop() {}


uint32_t htoli(const char inbuf[]) {
  uint32_t b = 0;
  uint16_t a;
  uint8_t c;
  Serial.print("htoli(\"");
  Serial.print(inbuf);
  Serial.print("\")=\n");
  bool err = false;
  while ((c = inbuf[a++]) && !err) {
    c = toupper(c);
    Serial.print(" c(chr)=");
    Serial.print((char)c);
    if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F'))) {
      Serial.print(" b=");
      Serial.print(b, DEC);
      Serial.print(" ");
      b = b * 16;
      Serial.print(" after mul b=");
      Serial.print(b, DEC);
      Serial.print(" c(dec)=");
      c = c - 48;
      if (c > 9)c = c - 7;
      b = b + c;
      Serial.print(c, DEC);
      Serial.print(", after add (b=b+c) b=");
      Serial.print(b, DEC);
      Serial.print("\n");
    }
    else {
      Serial.print("error? ");
      err = true;
    }
  }
  //if(err) b=0;
  Serial.print("result =");
  Serial.println(b, DEC);
  return b;
}

I get the same results as you.
avr-objdump --help
Yes, it's at {Arduino IDE installation folder}/hardware/tools/avr/bin

When I run the command:
Code: [Select]
avr-objdump -I"E:\Stuff\misc\electronics\arduino\temp\htoli" -d -S -j .text htoli.cpp.elf > disassembly.txt
on that simplified sketch compiled with Arduino IDE 1.6.5-r5/Arduino AVR Boards 1.6.8(because it sounds like you're using Arduino IDE 1.6.5) I get the attached output file. The -I"E:\Stuff\misc\electronics\arduino\temp\htoli" is the sketch folder and is necessary to get the inline source. This was fixed in Arduino IDE 1.6.6 but seems like it may have been broken again in recent versions because I can't get the inline source to work with Arduino IDE 1.6.11 even with the sketch folder specified.

htoli.cpp.elf is located in the build folder. You can find the location of the build folder by turning on File > Preferences > Show verbose output during: > compilation and looking at the console output, for example in my case:
Code: [Select]
C:\Program Files (x86)\arduino-1.6.5-r5\hardware\tools\avr/bin/avr-objcopy -O ihex -R .eeprom C:\Users\per\AppData\Local\Temp\build5698988197269744591.tmp/htoli.cpp.elf C:\Users\per\AppData\Local\Temp\build5698988197269744591.tmp/htoli.cpp.hex
Thanks for the work,  I walked, (ok, staggered) through this listing, nowhere in this listing is the code to actually add 'c' to 'b', the Print(b) and Print(c) both reference the same register(usually R16, sometime R24), and there is no multiply or left shift.  The code is defective.

What do we do from Here?

Chuck.
Currently built mega http server, Now converting it to ESP32.

cattledog

Quote
What do we do from Here?
It seems to me that the best solution is to declare a b and c within the function.
Code: [Select]
uint32_t b = 0;
  uint16_t a  = 0;
  uint8_t c = 0 ;


Declaring b as static within the function or declaring b as global (with a and c uninitialized) also lead to the function working properly, but those solutions seem a little farther out of the box.

The compiler may have a mind of its own, and it may be hard to understand why it does what it does, but I don't think it will change what it does. I can't envision that any possible changes to the compiler would change the way it treats declared and initialized variables within a function.


PaulMurrayCbr

#29
Sep 08, 2016, 07:08 am Last Edit: Sep 08, 2016, 07:44 am by PaulMurrayCbr
What do we do from Here?
Just code it up the easy way.

Code: [Select]

uint32_t htoli(char *p){
  uint32_t n = 0;

  for(;;) {
    if(*p>='0' && *p <='9') n = n*16 + (*p-'0');
    else if(*p>='A' && *p <='F') n = n*16 + (*p-'A'+10);
    else if(*p>='a' && *p <='f') n = n*16 + (*p-'a'+10);
    else return n; // this catches the nul terminator
    p++;
  }
}


Or, if you prefer:

Code: [Select]

uint32_t htoli(char *p){
  uint32_t n = 0;
  uint8_t ch;

  while(ch = *p++) {
    if(ch>='0' && ch <='9') ch = ch - '0';
    else if(ch>='A' && ch <='F') ch = ch - 'A' + 10;
    else if(ch>='a' && ch <='f') ch = ch - 'a' + 10;
    else break;
    n = n * 16 + ch;
  }
  return n;
}


http://paulmurraycbr.github.io/ArduinoTheOOWay.html

Go Up