0

I'm playing around with context switching, and my first idea is to play with SP and see how it changes. However, Sp doesn't do what I expect. The sketch is below. I'm using an Uno R3. What I expect to see is that SP should first print out some value, then it should be 4 integers further along (4x2 bytes?) when printed the second time. But that doesn't happen, and both times I see the exact same value for SP. What could the problem be?

    void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);

  Serial.print("X = ");
  Serial.println(SP,HEX);
}
void loop() {
  // put your main code here, to run repeatedly:
Serial.print("BEFORE: ");
Serial.println(SP,HEX);
int y = 15;
int z = y;
int g = z + y;
int dsf = z-y;
  Serial.println(dsf+g); //this should make these variables DO something...
  Serial.print(" After = ");
  Serial.println(SP,HEX);
  delay(1000);
}
  • 1
    Why do you assume that the stack will change? The AVR core has 32 registers. Plenty to store little temporary variables like those. Add to that the fact that the compiler optimizes lots of things out that it doesn't need, and you end up with almost nothing. – Majenko Oct 18 '18 at 16:53
  • Valid point. However, a similar sketch that actually called a function, whose sole role it was to print out SP, still had the SP not changing. If I understand the function calls (perhaps I don't) then at least the return address should be on the stack at that point, shouldn't it? – Michael Stachowsky Oct 18 '18 at 16:55
  • 1
    You should examine the compiled assembly code to see what it's actually doing. – Majenko Oct 18 '18 at 16:56
  • You should also read this: gcc.gnu.org/wiki/avr-gcc - especially the calling convention and frame layout sections – Majenko Oct 18 '18 at 16:57
  • 1
    @MichaelStachowsky That is the spirit! Here is a link that might provide some insight. github.com/vancegroup-mirrors/avr-libc/blob/master/avr-libc/… Writing that in C/C++ would provide an extra challenge :) – Mikael Patel Oct 18 '18 at 17:56
2

It's impossible to second-guess the compiler like that. There's a couple of things you need to realise:

  • The compiler will optimize out any variables that just get assigned to other variables as part of calculations and have no further use
  • The AVR CPU has 32 registers. More than enough to not need to use RAM to store values for a simple sketch like that.

Making the variables volatile will certainly help, since that forces the compiler to place them in memory. However you really should examine the assembly code the compiler produces to know exactly what it's doing.

3

Another way the compiler can optimize is to compile small or single-use functions inline. What you expected would get wrapped inside a call and return sequence, may not.

Inserting __attribute__ ((noinline)) ahead of the function definition can suppress that optimization:

__attribute__ ((noinline)) void foo(int bar)
{
   // do something
}

suppresses the inlining for function foo(). Inlining has frustrated my investigations more than once, so I created this easier to remember macro. (The keyword is just __NOINLINE; the #else-clause is there so that, umm, "someone", doesn't unwittingly try to use the "__attribute..." declarator with another compiler that doesn't recognize it).

// Don't compile this function inline
//   '__NOINLINE void foo(void){ ... }'
#ifdef __GNUC__
#define __NOINLINE  __attribute__ ((noinline))
#else
#define NOINLINE error 'NOINLINE' not defined for this compiler
#endif
  • Even small multi-use functions might be inlined. – Nick Gammon Oct 18 '18 at 23:21
  • I noticed that gcc is way more prone to inlining since the Arduino IDE started giving it the -flto compiler option. Before that point, it would not inline any non-trivial non-static function. – Edgar Bonet Oct 19 '18 at 7:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.