why this program is not crashing

D

Dave Thompson

Jack Klein wrote:
What "stack"? On some platforms, the four arguments in that printf()
call would all be passed in registers. And the return address in a
dedicated register of its own. No "stack" need apply.

Every platform I've seen uses the stack for printf, because it's the
most efficient way to implement va_next - va_start can create a hidden
pointer to the args on the stack, and va_next can do *hidden_ptr++ and
some masking.

If the args were in registers, you'd need either a need a wierd
addressing mode like this

mov Rdest, Registers[Rcount] ; mov the register number Rcount into
Rdest

I've never seen a architectures that provides this addressing mode. As

The PDP-10 did, sort of. It has the 16 GPRs (also) addressable as
"memory" locations 0-15. (Actually the initial design simply used low
memory as registers and hardware registers was officially an "add-on",
but was so obviously needed that it became standard.) Since it is
otherwise a (nearly) strict reg-mem architecture, this is how you do
reg-reg operations. It also produced a minor fad of writing inner
loops that could be moved into and executed from the registers much
faster, in days before (CPU) caches.

I have heard of other, some more modern, (mostly?) low-end things like
microcontrollers that use (fixed?) memory areas for registers in the
first place, with the same result that they are addressable.
a HW guy it looks hard to implement. You could do two loads from the
register file, one to read Rcount and one to read Registers[Rcount], or
you could have add _lots_ of read ports to the register file.
Or a dedicated (nonfile) register-selecting register.
On a normal chip, the C compiler could use self modifying code to
synthesise it - <snip>
But self modifying code is slow because it flushes pipelines, and
causes problems if you have a read only code page. It's also worth

Both of which can be fixed by generating code elsewhere and jump to it
and back, or call it and return. This may instead require some hackery
with separate (and not sufficiently coordinated) code and dat caches.
gcc at least on some targets does this on the stack for nested
functions, although some (many?) people now like the idea of
prohibiting execute from the stack to "fix" (partially) buffer-overrun
bugs and exploits, so you might have to do heap/data instead.

Or a dispatch into a table of fixed moves:
MOV wantedreg, rx
JMP getreg+8*rx # or CALL
next:

getreg: MOV R0, ry
JMP next # or RET
getreg+8: MOV R1, ry
# etc. etc.
# assumes total <= 8 addr units, else adjust constant factor
# or make it an indirect JMP/CALL through a table of pointers

Or an ISA could explicitly provide for this like the S/360 et seq
"EXecute" instruction although I don't know any recent one that does.
pointing out that both of these solutions will break if you try to pass
more arguments to printf than you have usuable argument registers.
E.g. consider a printf on a Risc chip which allows 4 registers to pass
arguments. One call to printf with 5 arguments would require a second
copy of the function with the stack based calling convention.
As would a stored pointer (value) to printf, which might later be
called with any number of arguments. Up to the environmental limit,
supposedly at least 127.

Or a hybrid -- up to N in registers and rest in memory. Which I
believe is what all the (RISC) args-in-registers conventions actually
do. This "just" makes va_next more complicated.
So even on Risc machines where the normal calling convention is to pass
args in registers, variadic functions still pass the args to printf on
the stack. It's probably fast too, because the top of the stack is

Or have the call convention 'N in register' but leaving 'blank' space
on the stack, so the callee can, if vararg or if it wants to spill the
arg registers for any reason of its own, store there.
pretty much guaranteed to be in a cache or write buffer when the
variadic function calls va_next. Then again, if you want speed, you'd

Nit: executes va_next. It's not a real function so can't be called.
make the function non variadic and declare it INLINE with inline
#defined to be something appropriate for the compiler
(http://www.greenend.org.uk/rjk/2003/03/inline.html)

- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top