xref: /openbsd-src/gnu/usr.bin/texinfo/lib/alloca.c (revision a1acfa9b69ad64eb720639240c8438f11107dc85)
1840175f0Skstailey /* alloca.c -- allocate automatically reclaimed memory
2840175f0Skstailey    (Mostly) portable public-domain implementation -- D A Gwyn
3840175f0Skstailey 
4840175f0Skstailey    This implementation of the PWB library alloca function,
5840175f0Skstailey    which is used to allocate space off the run-time stack so
6840175f0Skstailey    that it is automatically reclaimed upon procedure exit,
7840175f0Skstailey    was inspired by discussions with J. Q. Johnson of Cornell.
8840175f0Skstailey    J.Otto Tennant <jot@cray.com> contributed the Cray support.
9840175f0Skstailey 
10840175f0Skstailey    There are some preprocessor constants that can
11840175f0Skstailey    be defined when compiling for your specific system, for
12840175f0Skstailey    improved efficiency; however, the defaults should be okay.
13840175f0Skstailey 
14840175f0Skstailey    The general concept of this implementation is to keep
15840175f0Skstailey    track of all alloca-allocated blocks, and reclaim any
16840175f0Skstailey    that are found to be deeper in the stack than the current
17840175f0Skstailey    invocation.  This heuristic does not reclaim storage as
18840175f0Skstailey    soon as it becomes invalid, but it will do so eventually.
19840175f0Skstailey 
20840175f0Skstailey    As a special case, alloca(0) reclaims storage without
21840175f0Skstailey    allocating any.  It is a good idea to use alloca(0) in
22840175f0Skstailey    your main control loop, etc. to force garbage collection.  */
23840175f0Skstailey 
24840175f0Skstailey #ifdef HAVE_CONFIG_H
25840175f0Skstailey # include <config.h>
26840175f0Skstailey #endif
27840175f0Skstailey 
28*a1acfa9bSespie #include <alloca.h>
29*a1acfa9bSespie 
30*a1acfa9bSespie #include <string.h>
31*a1acfa9bSespie #include <stdlib.h>
32*a1acfa9bSespie 
33840175f0Skstailey #ifdef emacs
34*a1acfa9bSespie # include "lisp.h"
35840175f0Skstailey # include "blockinput.h"
36*a1acfa9bSespie # ifdef EMACS_FREE
37*a1acfa9bSespie #  undef free
38*a1acfa9bSespie #  define free EMACS_FREE
39*a1acfa9bSespie # endif
40*a1acfa9bSespie #else
41*a1acfa9bSespie # define memory_full() abort ()
42840175f0Skstailey #endif
43840175f0Skstailey 
44840175f0Skstailey /* If compiling with GCC 2, this file's not needed.  */
45840175f0Skstailey #if !defined (__GNUC__) || __GNUC__ < 2
46840175f0Skstailey 
47840175f0Skstailey /* If someone has defined alloca as a macro,
48840175f0Skstailey    there must be some other way alloca is supposed to work.  */
49840175f0Skstailey # ifndef alloca
50840175f0Skstailey 
51840175f0Skstailey #  ifdef emacs
52840175f0Skstailey #   ifdef static
53840175f0Skstailey /* actually, only want this if static is defined as ""
54840175f0Skstailey    -- this is for usg, in which emacs must undefine static
55840175f0Skstailey    in order to make unexec workable
56840175f0Skstailey    */
57840175f0Skstailey #    ifndef STACK_DIRECTION
58840175f0Skstailey you
59840175f0Skstailey lose
60840175f0Skstailey -- must know STACK_DIRECTION at compile-time
61*a1acfa9bSespie /* Using #error here is not wise since this file should work for
62*a1acfa9bSespie    old and obscure compilers.  */
63840175f0Skstailey #    endif /* STACK_DIRECTION undefined */
64840175f0Skstailey #   endif /* static */
65840175f0Skstailey #  endif /* emacs */
66840175f0Skstailey 
67840175f0Skstailey /* If your stack is a linked list of frames, you have to
68840175f0Skstailey    provide an "address metric" ADDRESS_FUNCTION macro.  */
69840175f0Skstailey 
70840175f0Skstailey #  if defined (CRAY) && defined (CRAY_STACKSEG_END)
71840175f0Skstailey long i00afunc ();
72840175f0Skstailey #   define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
73840175f0Skstailey #  else
74840175f0Skstailey #   define ADDRESS_FUNCTION(arg) &(arg)
75840175f0Skstailey #  endif
76840175f0Skstailey 
77840175f0Skstailey /* Define STACK_DIRECTION if you know the direction of stack
78840175f0Skstailey    growth for your system; otherwise it will be automatically
79840175f0Skstailey    deduced at run-time.
80840175f0Skstailey 
81840175f0Skstailey    STACK_DIRECTION > 0 => grows toward higher addresses
82840175f0Skstailey    STACK_DIRECTION < 0 => grows toward lower addresses
83840175f0Skstailey    STACK_DIRECTION = 0 => direction of growth unknown  */
84840175f0Skstailey 
85840175f0Skstailey #  ifndef STACK_DIRECTION
86840175f0Skstailey #   define STACK_DIRECTION	0	/* Direction unknown.  */
87840175f0Skstailey #  endif
88840175f0Skstailey 
89840175f0Skstailey #  if STACK_DIRECTION != 0
90840175f0Skstailey 
91840175f0Skstailey #   define STACK_DIR	STACK_DIRECTION	/* Known at compile-time.  */
92840175f0Skstailey 
93840175f0Skstailey #  else /* STACK_DIRECTION == 0; need run-time code.  */
94840175f0Skstailey 
95840175f0Skstailey static int stack_dir;		/* 1 or -1 once known.  */
96840175f0Skstailey #   define STACK_DIR	stack_dir
97840175f0Skstailey 
98840175f0Skstailey static void
find_stack_direction(void)99*a1acfa9bSespie find_stack_direction (void)
100840175f0Skstailey {
101840175f0Skstailey   static char *addr = NULL;	/* Address of first `dummy', once known.  */
102840175f0Skstailey   auto char dummy;		/* To get stack address.  */
103840175f0Skstailey 
104840175f0Skstailey   if (addr == NULL)
105840175f0Skstailey     {				/* Initial entry.  */
106840175f0Skstailey       addr = ADDRESS_FUNCTION (dummy);
107840175f0Skstailey 
108840175f0Skstailey       find_stack_direction ();	/* Recurse once.  */
109840175f0Skstailey     }
110840175f0Skstailey   else
111840175f0Skstailey     {
112840175f0Skstailey       /* Second entry.  */
113840175f0Skstailey       if (ADDRESS_FUNCTION (dummy) > addr)
114840175f0Skstailey 	stack_dir = 1;		/* Stack grew upward.  */
115840175f0Skstailey       else
116840175f0Skstailey 	stack_dir = -1;		/* Stack grew downward.  */
117840175f0Skstailey     }
118840175f0Skstailey }
119840175f0Skstailey 
120840175f0Skstailey #  endif /* STACK_DIRECTION == 0 */
121840175f0Skstailey 
122840175f0Skstailey /* An "alloca header" is used to:
123840175f0Skstailey    (a) chain together all alloca'ed blocks;
124840175f0Skstailey    (b) keep track of stack depth.
125840175f0Skstailey 
126840175f0Skstailey    It is very important that sizeof(header) agree with malloc
127840175f0Skstailey    alignment chunk size.  The following default should work okay.  */
128840175f0Skstailey 
129840175f0Skstailey #  ifndef	ALIGN_SIZE
130840175f0Skstailey #   define ALIGN_SIZE	sizeof(double)
131840175f0Skstailey #  endif
132840175f0Skstailey 
133840175f0Skstailey typedef union hdr
134840175f0Skstailey {
135840175f0Skstailey   char align[ALIGN_SIZE];	/* To force sizeof(header).  */
136840175f0Skstailey   struct
137840175f0Skstailey     {
138840175f0Skstailey       union hdr *next;		/* For chaining headers.  */
139840175f0Skstailey       char *deep;		/* For stack depth measure.  */
140840175f0Skstailey     } h;
141840175f0Skstailey } header;
142840175f0Skstailey 
143840175f0Skstailey static header *last_alloca_header = NULL;	/* -> last alloca header.  */
144840175f0Skstailey 
145840175f0Skstailey /* Return a pointer to at least SIZE bytes of storage,
146840175f0Skstailey    which will be automatically reclaimed upon exit from
147840175f0Skstailey    the procedure that called alloca.  Originally, this space
148840175f0Skstailey    was supposed to be taken from the current stack frame of the
149840175f0Skstailey    caller, but that method cannot be made to work for some
150840175f0Skstailey    implementations of C, for example under Gould's UTX/32.  */
151840175f0Skstailey 
152*a1acfa9bSespie void *
alloca(size_t size)153*a1acfa9bSespie alloca (size_t size)
154840175f0Skstailey {
155840175f0Skstailey   auto char probe;		/* Probes stack depth: */
156840175f0Skstailey   register char *depth = ADDRESS_FUNCTION (probe);
157840175f0Skstailey 
158840175f0Skstailey #  if STACK_DIRECTION == 0
159840175f0Skstailey   if (STACK_DIR == 0)		/* Unknown growth direction.  */
160840175f0Skstailey     find_stack_direction ();
161840175f0Skstailey #  endif
162840175f0Skstailey 
163840175f0Skstailey   /* Reclaim garbage, defined as all alloca'd storage that
164840175f0Skstailey      was allocated from deeper in the stack than currently.  */
165840175f0Skstailey 
166840175f0Skstailey   {
167840175f0Skstailey     register header *hp;	/* Traverses linked list.  */
168840175f0Skstailey 
169840175f0Skstailey #  ifdef emacs
170840175f0Skstailey     BLOCK_INPUT;
171840175f0Skstailey #  endif
172840175f0Skstailey 
173840175f0Skstailey     for (hp = last_alloca_header; hp != NULL;)
174840175f0Skstailey       if ((STACK_DIR > 0 && hp->h.deep > depth)
175840175f0Skstailey 	  || (STACK_DIR < 0 && hp->h.deep < depth))
176840175f0Skstailey 	{
177840175f0Skstailey 	  register header *np = hp->h.next;
178840175f0Skstailey 
179*a1acfa9bSespie 	  free (hp);		/* Collect garbage.  */
180840175f0Skstailey 
181840175f0Skstailey 	  hp = np;		/* -> next header.  */
182840175f0Skstailey 	}
183840175f0Skstailey       else
184840175f0Skstailey 	break;			/* Rest are not deeper.  */
185840175f0Skstailey 
186840175f0Skstailey     last_alloca_header = hp;	/* -> last valid storage.  */
187840175f0Skstailey 
188840175f0Skstailey #  ifdef emacs
189840175f0Skstailey     UNBLOCK_INPUT;
190840175f0Skstailey #  endif
191840175f0Skstailey   }
192840175f0Skstailey 
193840175f0Skstailey   if (size == 0)
194840175f0Skstailey     return NULL;		/* No allocation required.  */
195840175f0Skstailey 
196840175f0Skstailey   /* Allocate combined header + user data storage.  */
197840175f0Skstailey 
198840175f0Skstailey   {
199840175f0Skstailey     /* Address of header.  */
200*a1acfa9bSespie     register header *new;
201840175f0Skstailey 
202*a1acfa9bSespie     size_t combined_size = sizeof (header) + size;
203*a1acfa9bSespie     if (combined_size < sizeof (header))
204*a1acfa9bSespie       memory_full ();
205840175f0Skstailey 
206*a1acfa9bSespie     new = malloc (combined_size);
207*a1acfa9bSespie 
208*a1acfa9bSespie     if (! new)
209*a1acfa9bSespie       memory_full ();
210*a1acfa9bSespie 
211*a1acfa9bSespie     new->h.next = last_alloca_header;
212*a1acfa9bSespie     new->h.deep = depth;
213*a1acfa9bSespie 
214*a1acfa9bSespie     last_alloca_header = new;
215840175f0Skstailey 
216840175f0Skstailey     /* User storage begins just after header.  */
217840175f0Skstailey 
218*a1acfa9bSespie     return (void *) (new + 1);
219840175f0Skstailey   }
220840175f0Skstailey }
221840175f0Skstailey 
222840175f0Skstailey #  if defined (CRAY) && defined (CRAY_STACKSEG_END)
223840175f0Skstailey 
224840175f0Skstailey #   ifdef DEBUG_I00AFUNC
225840175f0Skstailey #    include <stdio.h>
226840175f0Skstailey #   endif
227840175f0Skstailey 
228840175f0Skstailey #   ifndef CRAY_STACK
229840175f0Skstailey #    define CRAY_STACK
230840175f0Skstailey #    ifndef CRAY2
231840175f0Skstailey /* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
232840175f0Skstailey struct stack_control_header
233840175f0Skstailey   {
234840175f0Skstailey     long shgrow:32;		/* Number of times stack has grown.  */
235840175f0Skstailey     long shaseg:32;		/* Size of increments to stack.  */
236840175f0Skstailey     long shhwm:32;		/* High water mark of stack.  */
237840175f0Skstailey     long shsize:32;		/* Current size of stack (all segments).  */
238840175f0Skstailey   };
239840175f0Skstailey 
240840175f0Skstailey /* The stack segment linkage control information occurs at
241840175f0Skstailey    the high-address end of a stack segment.  (The stack
242840175f0Skstailey    grows from low addresses to high addresses.)  The initial
243840175f0Skstailey    part of the stack segment linkage control information is
244840175f0Skstailey    0200 (octal) words.  This provides for register storage
245840175f0Skstailey    for the routine which overflows the stack.  */
246840175f0Skstailey 
247840175f0Skstailey struct stack_segment_linkage
248840175f0Skstailey   {
249840175f0Skstailey     long ss[0200];		/* 0200 overflow words.  */
250840175f0Skstailey     long sssize:32;		/* Number of words in this segment.  */
251840175f0Skstailey     long ssbase:32;		/* Offset to stack base.  */
252840175f0Skstailey     long:32;
253840175f0Skstailey     long sspseg:32;		/* Offset to linkage control of previous
254840175f0Skstailey 				   segment of stack.  */
255840175f0Skstailey     long:32;
256840175f0Skstailey     long sstcpt:32;		/* Pointer to task common address block.  */
257840175f0Skstailey     long sscsnm;		/* Private control structure number for
258840175f0Skstailey 				   microtasking.  */
259840175f0Skstailey     long ssusr1;		/* Reserved for user.  */
260840175f0Skstailey     long ssusr2;		/* Reserved for user.  */
261840175f0Skstailey     long sstpid;		/* Process ID for pid based multi-tasking.  */
262840175f0Skstailey     long ssgvup;		/* Pointer to multitasking thread giveup.  */
263840175f0Skstailey     long sscray[7];		/* Reserved for Cray Research.  */
264840175f0Skstailey     long ssa0;
265840175f0Skstailey     long ssa1;
266840175f0Skstailey     long ssa2;
267840175f0Skstailey     long ssa3;
268840175f0Skstailey     long ssa4;
269840175f0Skstailey     long ssa5;
270840175f0Skstailey     long ssa6;
271840175f0Skstailey     long ssa7;
272840175f0Skstailey     long sss0;
273840175f0Skstailey     long sss1;
274840175f0Skstailey     long sss2;
275840175f0Skstailey     long sss3;
276840175f0Skstailey     long sss4;
277840175f0Skstailey     long sss5;
278840175f0Skstailey     long sss6;
279840175f0Skstailey     long sss7;
280840175f0Skstailey   };
281840175f0Skstailey 
282840175f0Skstailey #    else /* CRAY2 */
283840175f0Skstailey /* The following structure defines the vector of words
284840175f0Skstailey    returned by the STKSTAT library routine.  */
285840175f0Skstailey struct stk_stat
286840175f0Skstailey   {
287840175f0Skstailey     long now;			/* Current total stack size.  */
288840175f0Skstailey     long maxc;			/* Amount of contiguous space which would
289840175f0Skstailey 				   be required to satisfy the maximum
290840175f0Skstailey 				   stack demand to date.  */
291840175f0Skstailey     long high_water;		/* Stack high-water mark.  */
292840175f0Skstailey     long overflows;		/* Number of stack overflow ($STKOFEN) calls.  */
293840175f0Skstailey     long hits;			/* Number of internal buffer hits.  */
294840175f0Skstailey     long extends;		/* Number of block extensions.  */
295840175f0Skstailey     long stko_mallocs;		/* Block allocations by $STKOFEN.  */
296840175f0Skstailey     long underflows;		/* Number of stack underflow calls ($STKRETN).  */
297840175f0Skstailey     long stko_free;		/* Number of deallocations by $STKRETN.  */
298840175f0Skstailey     long stkm_free;		/* Number of deallocations by $STKMRET.  */
299840175f0Skstailey     long segments;		/* Current number of stack segments.  */
300840175f0Skstailey     long maxs;			/* Maximum number of stack segments so far.  */
301840175f0Skstailey     long pad_size;		/* Stack pad size.  */
302840175f0Skstailey     long current_address;	/* Current stack segment address.  */
303840175f0Skstailey     long current_size;		/* Current stack segment size.  This
304840175f0Skstailey 				   number is actually corrupted by STKSTAT to
305840175f0Skstailey 				   include the fifteen word trailer area.  */
306840175f0Skstailey     long initial_address;	/* Address of initial segment.  */
307840175f0Skstailey     long initial_size;		/* Size of initial segment.  */
308840175f0Skstailey   };
309840175f0Skstailey 
310840175f0Skstailey /* The following structure describes the data structure which trails
311840175f0Skstailey    any stack segment.  I think that the description in 'asdef' is
312840175f0Skstailey    out of date.  I only describe the parts that I am sure about.  */
313840175f0Skstailey 
314840175f0Skstailey struct stk_trailer
315840175f0Skstailey   {
316840175f0Skstailey     long this_address;		/* Address of this block.  */
317840175f0Skstailey     long this_size;		/* Size of this block (does not include
318840175f0Skstailey 				   this trailer).  */
319840175f0Skstailey     long unknown2;
320840175f0Skstailey     long unknown3;
321840175f0Skstailey     long link;			/* Address of trailer block of previous
322840175f0Skstailey 				   segment.  */
323840175f0Skstailey     long unknown5;
324840175f0Skstailey     long unknown6;
325840175f0Skstailey     long unknown7;
326840175f0Skstailey     long unknown8;
327840175f0Skstailey     long unknown9;
328840175f0Skstailey     long unknown10;
329840175f0Skstailey     long unknown11;
330840175f0Skstailey     long unknown12;
331840175f0Skstailey     long unknown13;
332840175f0Skstailey     long unknown14;
333840175f0Skstailey   };
334840175f0Skstailey 
335840175f0Skstailey #    endif /* CRAY2 */
336840175f0Skstailey #   endif /* not CRAY_STACK */
337840175f0Skstailey 
338840175f0Skstailey #   ifdef CRAY2
339840175f0Skstailey /* Determine a "stack measure" for an arbitrary ADDRESS.
340840175f0Skstailey    I doubt that "lint" will like this much.  */
341840175f0Skstailey 
342840175f0Skstailey static long
i00afunc(long * address)343840175f0Skstailey i00afunc (long *address)
344840175f0Skstailey {
345840175f0Skstailey   struct stk_stat status;
346840175f0Skstailey   struct stk_trailer *trailer;
347840175f0Skstailey   long *block, size;
348840175f0Skstailey   long result = 0;
349840175f0Skstailey 
350840175f0Skstailey   /* We want to iterate through all of the segments.  The first
351840175f0Skstailey      step is to get the stack status structure.  We could do this
352840175f0Skstailey      more quickly and more directly, perhaps, by referencing the
353840175f0Skstailey      $LM00 common block, but I know that this works.  */
354840175f0Skstailey 
355840175f0Skstailey   STKSTAT (&status);
356840175f0Skstailey 
357840175f0Skstailey   /* Set up the iteration.  */
358840175f0Skstailey 
359840175f0Skstailey   trailer = (struct stk_trailer *) (status.current_address
360840175f0Skstailey 				    + status.current_size
361840175f0Skstailey 				    - 15);
362840175f0Skstailey 
363840175f0Skstailey   /* There must be at least one stack segment.  Therefore it is
364840175f0Skstailey      a fatal error if "trailer" is null.  */
365840175f0Skstailey 
366840175f0Skstailey   if (trailer == 0)
367840175f0Skstailey     abort ();
368840175f0Skstailey 
369840175f0Skstailey   /* Discard segments that do not contain our argument address.  */
370840175f0Skstailey 
371840175f0Skstailey   while (trailer != 0)
372840175f0Skstailey     {
373840175f0Skstailey       block = (long *) trailer->this_address;
374840175f0Skstailey       size = trailer->this_size;
375840175f0Skstailey       if (block == 0 || size == 0)
376840175f0Skstailey 	abort ();
377840175f0Skstailey       trailer = (struct stk_trailer *) trailer->link;
378840175f0Skstailey       if ((block <= address) && (address < (block + size)))
379840175f0Skstailey 	break;
380840175f0Skstailey     }
381840175f0Skstailey 
382840175f0Skstailey   /* Set the result to the offset in this segment and add the sizes
383840175f0Skstailey      of all predecessor segments.  */
384840175f0Skstailey 
385840175f0Skstailey   result = address - block;
386840175f0Skstailey 
387840175f0Skstailey   if (trailer == 0)
388840175f0Skstailey     {
389840175f0Skstailey       return result;
390840175f0Skstailey     }
391840175f0Skstailey 
392840175f0Skstailey   do
393840175f0Skstailey     {
394840175f0Skstailey       if (trailer->this_size <= 0)
395840175f0Skstailey 	abort ();
396840175f0Skstailey       result += trailer->this_size;
397840175f0Skstailey       trailer = (struct stk_trailer *) trailer->link;
398840175f0Skstailey     }
399840175f0Skstailey   while (trailer != 0);
400840175f0Skstailey 
401840175f0Skstailey   /* We are done.  Note that if you present a bogus address (one
402840175f0Skstailey      not in any segment), you will get a different number back, formed
403840175f0Skstailey      from subtracting the address of the first block.  This is probably
404840175f0Skstailey      not what you want.  */
405840175f0Skstailey 
406840175f0Skstailey   return (result);
407840175f0Skstailey }
408840175f0Skstailey 
409840175f0Skstailey #   else /* not CRAY2 */
410840175f0Skstailey /* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
411840175f0Skstailey    Determine the number of the cell within the stack,
412840175f0Skstailey    given the address of the cell.  The purpose of this
413840175f0Skstailey    routine is to linearize, in some sense, stack addresses
414840175f0Skstailey    for alloca.  */
415840175f0Skstailey 
416840175f0Skstailey static long
i00afunc(long address)417840175f0Skstailey i00afunc (long address)
418840175f0Skstailey {
419840175f0Skstailey   long stkl = 0;
420840175f0Skstailey 
421840175f0Skstailey   long size, pseg, this_segment, stack;
422840175f0Skstailey   long result = 0;
423840175f0Skstailey 
424840175f0Skstailey   struct stack_segment_linkage *ssptr;
425840175f0Skstailey 
426840175f0Skstailey   /* Register B67 contains the address of the end of the
427840175f0Skstailey      current stack segment.  If you (as a subprogram) store
428840175f0Skstailey      your registers on the stack and find that you are past
429840175f0Skstailey      the contents of B67, you have overflowed the segment.
430840175f0Skstailey 
431840175f0Skstailey      B67 also points to the stack segment linkage control
432840175f0Skstailey      area, which is what we are really interested in.  */
433840175f0Skstailey 
434840175f0Skstailey   stkl = CRAY_STACKSEG_END ();
435840175f0Skstailey   ssptr = (struct stack_segment_linkage *) stkl;
436840175f0Skstailey 
437840175f0Skstailey   /* If one subtracts 'size' from the end of the segment,
438840175f0Skstailey      one has the address of the first word of the segment.
439840175f0Skstailey 
440840175f0Skstailey      If this is not the first segment, 'pseg' will be
441840175f0Skstailey      nonzero.  */
442840175f0Skstailey 
443840175f0Skstailey   pseg = ssptr->sspseg;
444840175f0Skstailey   size = ssptr->sssize;
445840175f0Skstailey 
446840175f0Skstailey   this_segment = stkl - size;
447840175f0Skstailey 
448840175f0Skstailey   /* It is possible that calling this routine itself caused
449840175f0Skstailey      a stack overflow.  Discard stack segments which do not
450840175f0Skstailey      contain the target address.  */
451840175f0Skstailey 
452840175f0Skstailey   while (!(this_segment <= address && address <= stkl))
453840175f0Skstailey     {
454840175f0Skstailey #    ifdef DEBUG_I00AFUNC
455840175f0Skstailey       fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
456840175f0Skstailey #    endif
457840175f0Skstailey       if (pseg == 0)
458840175f0Skstailey 	break;
459840175f0Skstailey       stkl = stkl - pseg;
460840175f0Skstailey       ssptr = (struct stack_segment_linkage *) stkl;
461840175f0Skstailey       size = ssptr->sssize;
462840175f0Skstailey       pseg = ssptr->sspseg;
463840175f0Skstailey       this_segment = stkl - size;
464840175f0Skstailey     }
465840175f0Skstailey 
466840175f0Skstailey   result = address - this_segment;
467840175f0Skstailey 
468840175f0Skstailey   /* If you subtract pseg from the current end of the stack,
469840175f0Skstailey      you get the address of the previous stack segment's end.
470840175f0Skstailey      This seems a little convoluted to me, but I'll bet you save
471840175f0Skstailey      a cycle somewhere.  */
472840175f0Skstailey 
473840175f0Skstailey   while (pseg != 0)
474840175f0Skstailey     {
475840175f0Skstailey #    ifdef DEBUG_I00AFUNC
476840175f0Skstailey       fprintf (stderr, "%011o %011o\n", pseg, size);
477840175f0Skstailey #    endif
478840175f0Skstailey       stkl = stkl - pseg;
479840175f0Skstailey       ssptr = (struct stack_segment_linkage *) stkl;
480840175f0Skstailey       size = ssptr->sssize;
481840175f0Skstailey       pseg = ssptr->sspseg;
482840175f0Skstailey       result += size;
483840175f0Skstailey     }
484840175f0Skstailey   return (result);
485840175f0Skstailey }
486840175f0Skstailey 
487840175f0Skstailey #   endif /* not CRAY2 */
488840175f0Skstailey #  endif /* CRAY */
489840175f0Skstailey 
490840175f0Skstailey # endif /* no alloca */
491840175f0Skstailey #endif /* not GCC version 2 */
492