1 /**
2  * Implementation of alloca() standard C routine.
3  *
4  * Copyright: Copyright Digital Mars 2000 - 2012.
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Authors:   Walter Bright
9  * Source: $(DRUNTIMESRC rt/_alloca.d)
10  */
11 
12 module rt.alloca;
13 
14 version (Posix)
15 {
16     version = alloca;
17 
18     version (OSX)
19         version = Darwin;
20     else version (iOS)
21         version = Darwin;
22     else version (TVOS)
23         version = Darwin;
24     else version (WatchOS)
25         version = Darwin;
26 }
27 else version (CRuntime_Microsoft)
28 {
29     version = alloca;
30 }
31 
32 // Use DMC++'s alloca() for Win32
33 
34 version (alloca)
35 {
36 
37 /+
38 #if DOS386
39 extern size_t _x386_break;
40 #else
41 extern size_t _pastdata;
42 #endif
43 +/
44 
45 /*******************************************
46  * Allocate data from the caller's stack frame.
47  * This is a 'magic' function that needs help from the compiler to
48  * work right, do not change its name, do not call it from other compilers.
49  * Input:
50  *      nbytes  number of bytes to allocate
51  *      ECX     address of variable with # of bytes in locals
52  *              This is adjusted upon return to reflect the additional
53  *              size of the stack frame.
54  * Returns:
55  *      EAX     allocated data, null if stack overflows
56  */
57 
58 extern (C) void* __alloca(int nbytes)
59 {
60   version (D_InlineAsm_X86)
61   {
62     asm
63     {
64         naked                   ;
65         mov     EDX,ECX         ;
66         mov     EAX,4[ESP]      ; // get nbytes
67         push    EBX             ;
68         push    EDI             ;
69         push    ESI             ;
70 
71         add     EAX,15          ;
72         and     EAX,0xFFFFFFF0  ; // round up to 16 byte boundary
73         jnz     Abegin          ;
74         mov     EAX,16          ; // minimum allocation is 16
75     Abegin:
76         mov     ESI,EAX         ; // ESI = nbytes
77         neg     EAX             ;
78         add     EAX,ESP         ; // EAX is now what the new ESP will be.
79         jae     Aoverflow       ;
80     }
81     version (Win32)
82     {
83     asm
84     {
85         // We need to be careful about the guard page
86         // Thus, for every 4k page, touch it to cause the OS to load it in.
87         mov     ECX,EAX         ; // ECX is new location for stack
88         mov     EBX,ESI         ; // EBX is size to "grow" stack
89     L1:
90         test    [ECX+EBX],EBX   ; // bring in page
91         sub     EBX,0x1000      ; // next 4K page down
92         jae     L1              ; // if more pages
93         test    [ECX],EBX       ; // bring in last page
94     }
95     }
96     version (DOS386)
97     {
98     asm
99     {
100         // is ESP off bottom?
101         cmp     EAX,_x386_break ;
102         jbe     Aoverflow       ;
103     }
104     }
105     version (Unix)
106     {
107     asm
108     {
109         cmp     EAX,_pastdata   ;
110         jbe     Aoverflow       ; // Unlikely - ~2 Gbytes under UNIX
111     }
112     }
113     asm
114     {
115         // Copy down to [ESP] the temps on the stack.
116         // The number of temps is (EBP - ESP - locals).
117         mov     ECX,EBP         ;
118         sub     ECX,ESP         ;
119         sub     ECX,[EDX]       ; // ECX = number of temps (bytes) to move.
120         add     [EDX],ESI       ; // adjust locals by nbytes for next call to alloca()
121         mov     ESP,EAX         ; // Set up new stack pointer.
122         add     EAX,ECX         ; // Return value = ESP + temps.
123         mov     EDI,ESP         ; // Destination of copy of temps.
124         add     ESI,ESP         ; // Source of copy.
125         shr     ECX,2           ; // ECX to count of dwords in temps
126                                   // Always at least 4 (nbytes, EIP, ESI,and EDI).
127         rep                     ;
128         movsd                   ;
129         jmp     done            ;
130 
131     Aoverflow:
132         // Overflowed the stack.  Return null
133         xor     EAX,EAX         ;
134 
135     done:
136         pop     ESI             ;
137         pop     EDI             ;
138         pop     EBX             ;
139         ret                     ;
140     }
141   }
142   else version (D_InlineAsm_X86_64)
143   {
144     version (Win64)
145     {
146     asm
147     {
148         /* RCX     nbytes
149          * RDX     address of variable with # of bytes in locals
150          * Must save registers RBX,RDI,RSI,R12..R15
151          */
152         naked                   ;
153         push    RBX             ;
154         push    RDI             ;
155         push    RSI             ;
156         mov     RAX,RCX         ; // get nbytes
157         add     RAX,15          ;
158         and     AL,0xF0         ; // round up to 16 byte boundary
159         test    RAX,RAX         ;
160         jnz     Abegin          ;
161         mov     RAX,16          ; // allow zero bytes allocation
162     Abegin:
163         mov     RSI,RAX         ; // RSI = nbytes
164         neg     RAX             ;
165         add     RAX,RSP         ; // RAX is now what the new RSP will be.
166         jae     Aoverflow       ;
167 
168         // We need to be careful about the guard page
169         // Thus, for every 4k page, touch it to cause the OS to load it in.
170         mov     RCX,RAX         ; // RCX is new location for stack
171         mov     RBX,RSI         ; // RBX is size to "grow" stack
172     L1:
173         test    [RCX+RBX],RBX   ; // bring in page
174         sub     RBX,0x1000      ; // next 4K page down
175         jae     L1              ; // if more pages
176         test    [RCX],RBX       ; // bring in last page
177 
178         // Copy down to [RSP] the temps on the stack.
179         // The number of temps is (RBP - RSP - locals).
180         mov     RCX,RBP         ;
181         sub     RCX,RSP         ;
182         sub     RCX,[RDX]       ; // RCX = number of temps (bytes) to move.
183         add     [RDX],RSI       ; // adjust locals by nbytes for next call to alloca()
184         mov     RSP,RAX         ; // Set up new stack pointer.
185         add     RAX,RCX         ; // Return value = RSP + temps.
186         mov     RDI,RSP         ; // Destination of copy of temps.
187         add     RSI,RSP         ; // Source of copy.
188         shr     RCX,3           ; // RCX to count of qwords in temps
189         rep                     ;
190         movsq                   ;
191         jmp     done            ;
192 
193     Aoverflow:
194         // Overflowed the stack.  Return null
195         xor     RAX,RAX         ;
196 
197     done:
198         pop     RSI             ;
199         pop     RDI             ;
200         pop     RBX             ;
201         ret                     ;
202     }
203     }
204     else
205     {
206     asm
207     {
208         /* Parameter is passed in RDI
209          * Must save registers RBX,R12..R15
210          */
211         naked                   ;
212         mov     RDX,RCX         ;
213         mov     RAX,RDI         ; // get nbytes
214         add     RAX,15          ;
215         and     AL,0xF0         ; // round up to 16 byte boundary
216         test    RAX,RAX         ;
217         jnz     Abegin          ;
218         mov     RAX,16          ; // allow zero bytes allocation
219     Abegin:
220         mov     RSI,RAX         ; // RSI = nbytes
221         neg     RAX             ;
222         add     RAX,RSP         ; // RAX is now what the new RSP will be.
223         jae     Aoverflow       ;
224     }
225     version (Unix)
226     {
227     asm
228     {
229         cmp     RAX,_pastdata   ;
230         jbe     Aoverflow       ; // Unlikely - ~2 Gbytes under UNIX
231     }
232     }
233     asm
234     {
235         // Copy down to [RSP] the temps on the stack.
236         // The number of temps is (RBP - RSP - locals).
237         mov     RCX,RBP         ;
238         sub     RCX,RSP         ;
239         sub     RCX,[RDX]       ; // RCX = number of temps (bytes) to move.
240         add     [RDX],RSI       ; // adjust locals by nbytes for next call to alloca()
241         mov     RSP,RAX         ; // Set up new stack pointer.
242         add     RAX,RCX         ; // Return value = RSP + temps.
243         mov     RDI,RSP         ; // Destination of copy of temps.
244         add     RSI,RSP         ; // Source of copy.
245         shr     RCX,3           ; // RCX to count of qwords in temps
246         rep                     ;
247         movsq                   ;
248         jmp     done            ;
249 
250     Aoverflow:
251         // Overflowed the stack.  Return null
252         xor     RAX,RAX         ;
253 
254     done:
255         ret                     ;
256     }
257     }
258   }
259   else
260         static assert(0);
261 }
262 
263 }