←back to thread

327 points AareyBaba | 9 comments | | HN request time: 0.001s | source | bottom
Show context
anonymousiam ◴[] No.46184727[source]
The same is true for the software that runs many satellites. Use of the STL is prohibited.

The main issue is mission assurance. Using the stack or the heap means your variables aren't always at the same memory address. This can be bad if a particular memory cell has failed. If every variable has a fixed address, and one of those addresses goes bad, a patch can be loaded to move that address and the mission can continue.

replies(7): >>46185566 #>>46187219 #>>46188652 #>>46189307 #>>46190613 #>>46191199 #>>46196185 #
1. quietbritishjim ◴[] No.46190613[source]
> Using the stack or the heap means your variables aren't always at the same memory address.

Your mention of STL makes it sound like you're talking about C++. But I don't know of any C++ compiler that lets you completely avoid use of the stack, even if you disable the usual suspects (RTTI and exceptions). Sure, you'd have to avoid local variables, defined within a function's body (or at block scope), but that's nowhere near enough.

* The compiler would need to statically allocate space for every function's parameters and return address. That's actually how early compilers did work, but today it would be inefficient because there are surely so many functions defined in a program's binary compared to the number being executed at any given time. (Edit: I suppose you already need the actual code for those functions, so maybe allocating room for their parameters is not so bad.)

* It would also mean that recursion would not work, even mutual recursion (so you'd need runtime checks because this would be hard to detect at compile/link time), although I suspect this is less of a problem than it sounds, but I'm not aware of a C++ compiler that supports it.

* You'd also need to avoid creating any temporary variables at all e.g. y = a + b + c would not be allowed if a,b,c are non-trivial types. (y = a + b would be OK because the temporary could be constructed directly into y's footprint, or stored temporarily in the return space of the relevant operator+(), which again would be statically allocated).

Is that really what you meant? I suspect not, but without all that your point about avoiding the stack doesn't make any sense.

replies(1): >>46191224 #
2. RealityVoid ◴[] No.46191224[source]
Your points are correct, but recursion is banned anyway in safety critical applications. The main issue is determinism. The fact you have to use the stack for call stacks is correct OP seems misinformed.
replies(1): >>46191738 #
3. adrian_b ◴[] No.46191738[source]
You have to use the stack for procedure calls on x86/x86-64 CPUs, where the hardware enforces this.

In most other surviving CPU ISAs the return address is saved in a register and it is easy to arrange in a compiler to use only procedure arguments that are passed in registers, the only price being paid for this being a reasonable upper limit for the number of parameters of a function, e.g. 12 or 24, depending on the number of general-purpose registers (e.g. 16 or 32). For the very rare case when a programmer would want more parameters, some of them should be grouped into a structure.

With this convention, which normally should not be a problem, there is no need for a call stack. There can be software managed stacks, which can be used even for implementing recursion, when that is desired.

The use of static memory for passing function arguments was necessary only in the very early computers, which were starved in registers.

replies(2): >>46192009 #>>46196117 #
4. RealityVoid ◴[] No.46192009{3}[source]
I believe it's possible to do what you've described, but I am not aware of any compiler that does this.

What do you get by doing it like this?

Also, in your described structure, how do you handle nested function calls? I'm sure there exists a convoluted scheme that does this, but not sure with the current call assumptions.

You also lose ABI compatibility with a bunch of stuff.

And regardless, I mostly program in Risc-v and ARM -most compiles like to pass arguments on the registers, but use the stack anyway for local context.

replies(1): >>46192143 #
5. jcalvinowens ◴[] No.46192143{4}[source]
You can do it on x86 too, just use jmp instead of call and invent your own arbitrary register scheme for it. This x86 program has no stack: https://github.com/jcalvinowens/asmhttpd

I don't think it's too hard to imagine a compiler that does that, although it would obviously be very limited in functionality (nesting would be disallowed as you note, or I guess limited to the number of registers you're willing to waste on it...).

6. 0xffff2 ◴[] No.46196117{3}[source]
I honestly can't tell if you know a lot more than me or a lot less than me about how computers work... A couple of honest questions:

1. Where do you save the current value of the return address register before calling a function?

2. When parameters are "grouped into a structure" and the structure is passed as an argument to a function, where do you store that structure?

replies(2): >>46196454 #>>46199713 #
7. RealityVoid ◴[] No.46196454{4}[source]
Not OP, but presumably, the answers are:

1) You don't... hence, my question about no nested function calls. If you push it anywhere else, you can call it whatever you want, but you just re-invented the stack. I _guess_ you could do some wierd stuff to technically not get a stack, but... again, it's wierd. And for what, again?

2) Some fixed address. If you have for example:

```c

typeRealBigStructure foo;

void baz(typeRealBigStructure * struct){

    // Do whatever to struct
}

void bar(void){

  baz(&foo);
}

```

The foo will probably end up in the BSS and will take up that space for the whole lifetime of the program. That's not the heap, not the stack, just... a fixed location in memory where the linker placed it.

I guess on big PC's stuff is very dynamic and you use malloc for a lot of stuff, but in embedded C, it's a very common pattern.

replies(1): >>46196579 #
8. 0xffff2 ◴[] No.46196579{5}[source]
Ah, you're right, the struct case is actually pretty straightforward (especially since recursion is likely forbidden anyway), I just have trouble contorting my brain to such a different viewpoint.
9. quietbritishjim ◴[] No.46199713{4}[source]
The sibling comment already answered your question, but just to add: As I mentioned earlier, this was actually how old programming languages worked. Famously(ish), Dijkstra secretly snuck recursive functions into the ALGOL 60 standard, thus forcing compiler authors to use a stack!

https://news.ycombinator.com/item?id=10131664