[alt.msdos.programmer] alloca

ssuhook@rosemary.cs.reading.ac.uk (Roger Neil Hook) (08/18/90)

I am trying to compile GNU bison version 1.10 on a PC running DOS 3.3, using
Turbo C v2.0. The source references a function called 'alloca' to allocate
memory off the stack which is automatically freed when the function calling it
returns.
 
'alloca' is not included in the TurboC library, and is anyway not standard C.
I have come across two sets of assembly sources for a __680x0__ version, but
do not understand precisely how they function, not having much experience in
assembly.

Turbo C describes two methods of calling C functions which it calls 'cdecl'
and 'pascal' conventions. 

I have been trying to work out how to write an 80x86 assembly version of
alloca to work with Turbo C. The problems I have encountered relate to not
understanding what the conventions for calling a function are, and also 
confusion arising from trying to work out what the stack pointer is doing.
To whit: 
   I assume it is possible for a C compiler (on a PC but not necessarily 
   TurboC) to have a function, say A(), which does some stuff which ends
   up with data stored on the stack. Then it calls alloca() which I assume
   modifies the stack pointer to hide the alloca'd block.
 
1) This being the case, how does A find it's stacked data?
2) Also how does the memory get freed?
3) How can I write a version of alloca in 8086 assembly for the PC, and 
   TurboC in particular?

4) Finally, could someone please explain what alloca _should_ do, and also
   what the version below (extracted from a GNU library for the Atari GCC)
   does, especially the last op:

         .text
         .even

.globl   _alloca
_alloca:
       movel   sp@+,a0           ;get return addr
       movel   sp@+,d0           ;get size -- assist in bug fix, add 4 to sp
       addql   #1,d0             ;ensure address even
       andl    #0xFFFFFFFE,d0    ;lop off extra bits
       subl    d0,sp             ;increase stack frame size by that much
       movel   sp,d0             ;set up to return it
       lea     sp@(-4),sp        ;new top of stack (real bug fix here)
       jmp     a0@               ;return by jumping via saved addr


In summary, I am mainly interested in finding out how to get an alloca() for
the PC for Turbo C, and also what it is that alloca() actually does.

My apologies in advance if bits of this aren't relevant to any particular 
group.

Please email me, and I will endeavour to summarize to the net if there is 
enough interest. Thanks,

Alias
JANET: ssuhook@susssys1.rdg.ac.uk 

vu0310@bingvaxu.cc.binghamton.edu (R. Kym Horsell) (08/18/90)

In article <2745@onion.reading.ac.uk> ssuhook@susssys1.cs.reading.ac.uk writes:
\\\
>In summary, I am mainly interested in finding out how to get an alloca() for
>the PC for Turbo C, and also what it is that alloca() actually does.
\\\

Alloca does something *like*

char *alloca(n) {
	n =			/* round up to words */;
	((char*)SP) += n;	/* bump up the stack ptr */
	return (char*)SP-n;	/* return address of stuff on stack */
	}

I.e. it just bumps up the stack by n bytes (usually word alignining
the result) and returns the address of the new stack section.
Since the SP is restore by C exit stuff from a `frame ptr register'
(typically, anyway) this memory is deallocated at the end of the function.

Note I've assumed the stack grows up (not a normal thing these days).
Also we can't *actually* write alloca() as above even when you can
refer to the SP directly since its exit will deallocate the
space just allocated -- you should think of alloca() as written
above as a macro.

To solve your problem with proting Bison -- why not use
(a)	malloc/alloc and just forget deallocating it, or
(b)	malloc/alloc and use an explicit free at the end of
	the function its used it (might as well catch *all*
	returns from the function while you're about it).

Ive written a fairly complete Yacc/Lex for AT's and didn't
both about memory leakage. After a PDP-11 there was more memory
than I new what to do with (ahhh...many years ago now).

Hope this's been some help,
-Kym Horsell

trier@cwlim.CWRU.EDU (Stephen C. Trier) (08/22/90)

Here's a version of alloca I use.  It's my clone of a version from some
piece of PD software I saw a year or two ago.

        #define alloca(A) ((_SP -= ((A)+1) & 0xFFFE), (void far *)(((unsigned long)_SS << 16) | _SP))

The idea is to subtract the size A from the stack pointer, making sure
it's word-aligned.  (This is the (_SP -= ((A)+1) & 0xFFFE) part.)  Then,
since alloca returns a pointer to the allocated space, the macro must
kludge together a far pointer from the stack segment and stack pointer.

I should add the disclaimer that I'm not entirely sure that this works.
It's my own version of someone else's, but I changed it a bit.  (I don't
remember what the original looked like.)

Hope this helps!

-- 
Stephen Trier                              Case Western Reserve University
Home: sct@seldon.clv.oh.us                 Information Network Services
Work: trier@cwlim.ins.cwru.edu
   I may work for the University, but that doesn't mean I speak for them.

sidney@saturn.ucsc.edu (Sidney Markowitz ) (08/23/90)

trier@po.CWRU.Edu writes:
>Here's a version of alloca I use.  It's my clone of a version from some
>piece of PD software I saw a year or two ago.

[For anyone who still hasn't gotten the idea, alloca is supposed to
allocate storage (like malloc) which is automatically freed upon exit
from the calling procedure (as if they are on the stack like local
variables).]

I haven't checked it out thoroughly in TC 2.0, but in TC++ nothing
that actually puts the alloca'd storage on the stack is going to work.
For one thing, the compiler may save a value on the stack before the
call to alloca, then try to pop it after the call, not realizing that
alloca has changed the stack. Also, if you look at the assembler code
generated for a function that has 0, 1 or 2 words of local variables,
you will see that no stack space is reserved for them (SI and DI
registers are used instead) and the compiler assumes that it doesn't
need a mov sp,bp instruction to restore the stack on exit. This
assumption is not true if alloca has pushed stuff on the stack
expecting it to be cleaned up on exit. Basically, you can't have a
real alloca without support from the compiler.

There's a portable alloca written in C that I've seen with some FSF
software. It simulates the effect of alloca by calling malloc and
maintaining a linked list of pointers to the allocated blocks, along
with the value of SP at the time of each allocation. Whenever alloca
is called, it goes through the linked list, freeing all blocks for
which the saved SP is smaller (deeper in the stack) than the current
one. So alloca'd storage is not freed at once upon exit from the
routine that is calling alloca, but instead is freed approximately the
next time alloca is called.

If there is interest, I can post the code. But I suggest rewriting the
program to use malloc and free instead of alloca.

-- sidney markowitz <sidney@saturn.ucsc.edu>

dale@NCoast.ORG (Dale Smith) (08/23/90)

In article <1990Aug22.032243.18214@usenet.ins.cwru.edu> trier@po.CWRU.Edu writes:
>Here's a version of alloca I use.  It's my clone of a version from some
>piece of PD software I saw a year or two ago.
>
>        #define alloca(A) ((_SP -= ((A)+1) & 0xFFFE), (void far *)(((unsigned long)_SS << 16) | _SP))
>
>The idea is to subtract the size A from the stack pointer, making sure
>it's word-aligned.  (This is the (_SP -= ((A)+1) & 0xFFFE) part.)  Then,
>since alloca returns a pointer to the allocated space, the macro must
>kludge together a far pointer from the stack segment and stack pointer.
>
>I should add the disclaimer that I'm not entirely sure that this works.
>It's my own version of someone else's, but I changed it a bit.  (I don't
>remember what the original looked like.)
>
>Hope this helps!
>
>-- 
>Stephen Trier                              Case Western Reserve University
>Home: sct@seldon.clv.oh.us                 Information Network Services
>Work: trier@cwlim.ins.cwru.edu
>   I may work for the University, but that doesn't mean I speak for them.

I believe I saw something like that in an early port of bison.  It's
really cute but will trash register variables.

Someone posted an alloca for the 386 which I hacked to fit Turbo C.
I also took a peek at a disassembly of the Microsoft C 5.1 alloca().

Here it is, have fun.

------ Snip it or somethin' ------------------------------
; alloca.asm
; Allocate from the stack for Turbo C 2.0
; I have not tried this this TC++ 1.0

; void *alloca(size_t size);

; Beware of using alloca in functions without arguments
; and -k- in tiny, small, and medium models.  The stack has a
; possibility of growing.

    ifdef __TINY__
   .model   tiny
elseifdef __SMALL__
   .model   small
elseifdef __MEDIUM__
   .model   medium
elseifdef __COMPACT__
   .model   compact
elseifdef __LARGE__
   .model   large
elseifdef __HUGE__
   .model   huge
else
   .model small
endif


if @DataSize EQ 0
	.data
	extrn	___brklvl:word
endif

	.code
	public	_alloca
_alloca	proc

;; Pop return address into (es:) cx and size into ax
	pop	cx		; return address
if @CodeSize
	pop	es		; return segment
endif
	pop	ax		; size

;; Make sure that ax is greater than zero, and is a multiple of two.
	cmp	ax,0
	jl	error
	inc	ax
	and	ax,not 1

;; Figure new stack pointer, check for negative and less than brk.
	mov	bx,sp
	sub	bx,ax
	jb	error		; below zero
if @DataSize EQ 0		; tiny, small, medium
	cmp	bx,___brklvl
	jb	error		; below the brk
endif
	xchg	sp,bx
	mov	ax,sp


;; Copy the (hypothetical) register variables.
ifdef __MEDIUM__
	push	ss:6[bx]	; return segment(medium)
endif
	push	ss:4[bx]	; return offset(med,sm,tiny) or ds(huge)
	push	ss:2[bx]	; di (all)
	push	ss:[bx]		; si (all)

if @DataSize
	mov	dx,ss
endif

exit:
;; Put something on the stack so that our caller can pop it off.
	push	ax

;; Return to the the caller.
if @CodeSize
	push	es
	push	cx
	ret
else
	jmp	cx
endif

error:
	xor	ax,ax
if @DataSize
	cwd
endif
	jmp	exit

	endp
	end
-------------- Ok, that's it -----------------------------------------

dale
-- 
 Dale P. Smith
 dale@ncoast.org
 uunet!usenet.ins.cwru.edu!ncoast!dale

pruss@ria.ccs.uwo.ca (Alex Pruss) (08/28/90)

In article <1990Aug22.032243.18214@usenet.ins.cwru.edu> trier@po.CWRU.Edu writes:
>Here's a version of alloca I use.  It's my clone of a version from some
>piece of PD software I saw a year or two ago.
>
>        #define alloca(A) ((_SP -= ((A)+1) & 0xFFFE), (void far *)(((unsigned long)_SS << 16) | _SP))

This looks surprisingly like the original of my alloca().  Unfortunately it
doesn't work.  (Not at all!--my version didn't either--it took many hours
of painful debugging to find what was wrong.)  You see it returns SS:SP.
However, next time a function call, push, or interrupt comes along, a word
gets stored at SS:SP which is part of the allocated area.  Thus a minimal
fix is to change it to:

#define alloca(A) ((_SP -= ((A)+1) & 0xFFFE), (void far *)(((unsigned long)_SS << 16) | _SP+2))

Unfortunately this fails in the case that the function has stored DS/SI/DI
on the stack.  Furthermore one must beware that the calling function may not
have a proper stack frame.

>I should add the disclaimer that I'm not entirely sure that this works.
>It's my own version of someone else's, but I changed it a bit.  (I don't
>remember what the original looked like.)
It was less elegant in the forcing of A to be even, and it too didn't work.
It included also a { char dummy; dummy=dummy; } type of thing to force a
stack frame.

Anyways, the latest WORKING (I hope...  I tested it...) version I've just posted
to alt.sources.  (The whole thing with docs and uuencoded object files is
13Kb, so I didn't want to post it here).

pruss@uwo.ca // internet
pruss@uwovax // bitnet
pruss@ria    // uucp
//\\ The SCHSPH Guy //\\