250 bytes of RAM lost on stack in 1.6.11?

I found the following puzzle in 1.6.11 with an Uno.

If you have a single call to a function with up to about 250 bytes of local storage, the memory can be lost on the stack.

Here is a test program. fcn() is not special, just complicated enough so the compiler does not optimize it away.

#define DIM_A 128
// function with local array
void fcn() {
  int a[DIM_A];
  for (int i = 0; i < DIM_A; i++) {
    a[i] = i;
  }
  int s = 0;
  for (int i = 0; i < DIM_A; i++) {
    s += a[i];
  }
  if (s < 10000) return;
  Serial.println(s);
}

void setup() {
  fcn();
//  fcn();
  Serial.begin(9600);
  Serial.print(F("SP setup "));
  Serial.println(SP);
  Serial.print(F("RAMEND   "));
  Serial.println(RAMEND);
}

bool done = false;
void loop() {
  if (done) return;
  done = true;
  Serial.print(F("SP loop  "));
  Serial.println(SP);
}

If you run this program the output is:

SP setup 2043
RAMEND 2303
SP loop 2043

So the stack pointer in loop() is more than 250 byte from RAMEND.

If you remove the // from the second fcn() call in setup the output is:

SP setup 2301
RAMEND 2303
SP loop 2301

So now the stack pointer in loop() is only two bytes different than RAMEND.

If you increase DIM_A to 200 with a single fcn() call you get this result.

SP setup 2301
RAMEND 2303
SP loop 2301

This must be some sort of optimization problem. It’s really ugly.

This bug is causing some programs that use SdFat to crash. rename() in SdFat has about 200 bytes of local storage.

Many Uno programs that use an SD are close to the edge with RAM so programs that were working are crashing.

I get the same results as you from your test program with Arduino IDE 1.6.11/Arduino AVR Boards 1.6.13.

Using Arduino IDE 1.6.11, if you do Tools > Board > Boards Manager > Arduino AVR Boards > 1.6.11 > Install > Close the output changes to:

SP setup 2299
RAMEND   2303
SP loop  2299

So, as you'd expect, it's not an IDE version specific phenomenon. The avr-gcc version was updated in Arduino AVR Boards 1.6.12 from 4.8.1-arduino5 to 4.9.2-atmel3.5.3-arduino2. The same version is used in the Arduino AVR Boards 1.6.13 included with Arduino IDE 1.6.11. The new compiler version supports LTO, which they have enabled.

If you reinstall Arduino AVR Boards 1.6.13 but remove the LTO flags from platform.txt by changing:

compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects
compiler.c.elf.flags={compiler.warning_flags} -Os -flto -fuse-linker-plugin -Wl,--gc-sections
compiler.c.elf.cmd=avr-gcc
compiler.S.flags=-c -g -x assembler-with-cpp -flto
compiler.cpp.cmd=avr-g++
compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto

to:

compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -MMD
compiler.c.elf.flags={compiler.warning_flags} -Os -Wl,--gc-sections
compiler.c.elf.cmd=avr-gcc
compiler.S.flags=-c -g -x assembler-with-cpp
compiler.cpp.cmd=avr-g++
compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD

You get the same output as with Arduino AVR Boards 1.6.11:

SP setup 2299
RAMEND   2303
SP loop  2299

So that tells me the difference is caused, not by the new compiler version, but the LTO.

I suspected something like LTO.

I just used exactly the setup that was giving a user trouble.

It's hard pin down because if you comment out some code to isolate the problem, some other function that had multiple calls pops up when there is only one call to it.

The linker has smashed main, setup, and loop into one function. The array is not purged when loop is called because it is still essentially in scope.