uint32_t weirdness

Delta_G:
It didn't change the code generated. a had some garbage value left over in it when it was created and not initialized. Since it wasn't 0, it simply wasn't reading the bytes you thought it would.

No,
as the debug output shown, the value of 'c' was changing in the expected manner. First it was the character '8' then the character '0', then the character '0'.

Whatever value 'a' had, (I think it started at zero), it was indexing through the char[] correctly. If it was pointed somewhere else,

htoli("800")=
c(chr)=8 b=0  after mul b=0 c(dec)=8, after add (b=b+c) b=8
c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
result =0

the c(chr)= value would show something else. Between the c(chr) and c(dec) the code subtracted 48 and conditionally another 7 if necessary.

This exposes an edge condition, I want to know were the edge is before I place my feet. :slight_smile:

Chuck.

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.

Delta_G:
It doesn't. You weren't reading where you thought you were. Imagine that a started out with a 1 in it. You'd never see the 8 and you'd parse the two 0's and get 0 as an answer. You weren't reading in the "800". You were probably reading somewhere off in who knows what part of memory.

To prove it, go back to your old code and print a so you can see what value it was getting. Now go and use that value to initialize a and see that you get the exact same wrong result. What if a was getting the initial value of 64? What character was in inbuf[64]? That's the character being parsed, not your "800"

Debug results with 'a' initialized:

htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=6  after mul b=96 c(dec)=4, after add (b=b+c) b=100
result =100
returned value of htoli("64")=100
htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=6  after mul b=96 c(dec)=4, after add (b=b+c) b=100
result =100
returned value of htoli("buf")=100

the calling code:

char buf[10];
uint32_t bigValue = htoli("64");
Serial.print("returned value of htoli(\"64\")=");
Serial.println(bigValue,DEC);
sprintf(buf,"64");
bigValue = htoli(buf);
Serial.print("returned value of htoli(\"buf\")=");
Serial.println(bigValue,DEC);

Debug output with the initialization code for 'a' removed:

htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 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")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 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

The code that produces this error:

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;
 }
 }

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

return b;
}

This is a reproducible error. Something is wrong.

Chuck.

Why not print a since that value seems to be the source of at least some part of the problem.

jremington:
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.

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.

Objdump time. See what it's being compiled into.

Delta_G:
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'

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.

Delta_G:
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.

cattledog:
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.

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.

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.

cattledog:
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.

chucktodd:
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:

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.

chucktodd:
Can you point me somewhere that will instruct me on how to use Objdump

avr-objdump --help

chucktodd:
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:

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:

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

disassembly.txt (55.1 KB)

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.

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

pert:
Then why not make a minimal test sketch to check? Something like this:

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:


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:


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.

What do we do from Here?

It seems to me that the best solution is to declare a b and c within the function.

 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.

chucktodd:
What do we do from Here?

Just code it up the easy way.

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:

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;
}

What do we do from Here?

Based on my understanding of the thread (uninitialised variable a causing the issue), just initialise a. Or do I see that too simplistic?

Interesting problem though.

Note:
I loaded the code in VS2012 C++ and it throws an error on the uninitialised variable a.