Stack overflow instantiating struct with array members


#1

Yo! I’ve run into a rather strange problem trying to instantiate a struct with array members, where a stack overflow occurs. I’m using rustc 1.2.0-nightly (0cc99f9cc 2015-05-17) (built 2015-05-17). Here’s what the struct looks like:

pub struct Apu {
    ram: [u8; RAM_LEN],
    ipl_rom: [u8; IPL_ROM_LEN],

    smp: Option<Smp>,

    timers: [Timer; 3],

    left_output_buffer: [i16; BUFFER_LEN],
    right_output_buffer: [i16; BUFFER_LEN],
    //overflow_buffer: RingBuffer,

    is_ipl_rom_enabled: bool,
    dsp_reg_address: u8
}


impl Apu {
    pub fn new() -> Apu {
        Apu {
            ram: [0; RAM_LEN],
            ipl_rom: [0; IPL_ROM_LEN],

            smp: None,

            timers: [Timer::new(256), Timer::new(256), Timer::new(32)],

            left_output_buffer: [0; BUFFER_LEN],
            right_output_buffer: [0; BUFFER_LEN],
            //overflow_buffer: RingBuffer::new(),

            is_ipl_rom_enabled: true,
            dsp_reg_address: 0
        }
    }

// Lots more impl stuff, unrelated

and I instantiate it like so:

let mut apu = Apu::new();

when running this, it panics with the following output:

thread '<main>' has overflowed its stack

None of the fixed-size arrays are larger than 64000 elements or so, but when I comment them out, the code works. What’s MORE strange is I tried to isolate the code with this example:

const THE_SIZE: usize = 65536;

struct Lol {
    member1: [u8; THE_SIZE],
    member2: [u8; THE_SIZE],
    member3: [u8; THE_SIZE],
    member4: [u8; THE_SIZE],

    some_other_member: bool
}

impl Lol {
    fn new() -> Lol {
        Lol {
            member1: [0; THE_SIZE],
            member2: [0; THE_SIZE],
            member3: [0; THE_SIZE],
            member4: [0; THE_SIZE],

            some_other_member: true
        }
    }
}

fn main() {
    let mut lol = Lol::new();

    println!("this works perfectly");
}

but I can’t seem to get it to break in the same way.

The full code for my breaking project can be found here (latest master):

and the exact command to run it that causes the bug is as follows:

cargo run -- test/ferris-nu.spc test/smashit.spc

again, I’ve tried to isolate the problem in much simpler test cases, but to no avail. It seems the problem only occurs when I have a certain amount array members in the struct, and doesn’t seem to occur if I do things like comment out the rest of the function in which the struct is initialized. Very very strange happenings here.

Any help is appreciated!


#2

After playing with it a bit more, the answer is of course super stupid simple :smile:

The struct is actually simply too big for the stack. I played around with stuffing more stuff in the simpler example:

const THE_SIZE: usize = 65536;

struct Lol {
    member1: [u8; THE_SIZE],
    member2: [u8; THE_SIZE],
    member3: [u8; THE_SIZE],
    member4: [u8; THE_SIZE],
    member5: [u8; THE_SIZE],
    member6: [u8; THE_SIZE],
    member7: [u8; THE_SIZE],
    member8: [u8; THE_SIZE],
}

impl Lol {
    fn new() -> Lol {
        Lol {
            member1: [0; THE_SIZE],
            member2: [0; THE_SIZE],
            member3: [0; THE_SIZE],
            member4: [0; THE_SIZE],
            member5: [0; THE_SIZE],
            member6: [0; THE_SIZE],
            member7: [0; THE_SIZE],
            member8: [0; THE_SIZE],
        }
    }
}

fn main() {
    let mut lol = Lol::new();

    println!("now it breaks");
}

of course it breaks as expected.

In this case the size of the struct is 64kb * 8 = 512kb, which is too big for the stack. This is just a very simple reminder of how small the stack actually can be, d’oh :blush: been a LONG time since I ran into something like this :smiley: I guess you could say I’m a but RUST’y…


#3

FWIW, I tried the simple example again using Box’s around each member and instantiating with Box::new([…]), like so:

const THE_SIZE: usize = 65536;

struct Lol {
    member1: Box<[u8; THE_SIZE]>,
    member2: Box<[u8; THE_SIZE]>,
    member3: Box<[u8; THE_SIZE]>,
    member4: Box<[u8; THE_SIZE]>,
    member5: Box<[u8; THE_SIZE]>,
    member6: Box<[u8; THE_SIZE]>,
    member7: Box<[u8; THE_SIZE]>,
    member8: Box<[u8; THE_SIZE]>,
}

impl Lol {
    fn new() -> Lol {
        Lol {
            member1: Box::new([0; THE_SIZE]),
            member2: Box::new([0; THE_SIZE]),
            member3: Box::new([0; THE_SIZE]),
            member4: Box::new([0; THE_SIZE]),
            member5: Box::new([0; THE_SIZE]),
            member6: Box::new([0; THE_SIZE]),
            member7: Box::new([0; THE_SIZE]),
            member8: Box::new([0; THE_SIZE]),
        }
    }
}

fn main() {
    let mut lol = Lol::new();

    println!("still breaks");
}

and ran into the same problem. According to this answer on Stack Overflow (link de-linked because I received a “Sorry, new users can only put 2 links in a post.” error otherwise):

http://stackoverflow.com/questions/30242770/how-to-allocate-arrays-on-the-heap-in-rust-1-0-beta

this is because when you call Box::new, the parameter is still created on the stack, then passed to the box on the heap. While the answer on SO suggests using a Vec instead of an array, it seems like a very roundabout way of doing this. Are there any plans to support heap-allocating fixed-size arrays (or any other kind of objects, for that matter) directly in Rust? Or is that going to stay the best option?

Again, any help is much appreciated :slight_smile:


#4

We’re going to get the box syntax in stable some day. For now you can use it on nightly. Using box x instead of Box::new(x) will prevent the additional stack allocation. Also you can try what happens with optimizations activated, maybe the stack alloc gets optimized out in stable.


#6

The box syntax works perfectly. Thank you!