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 else version (CRuntime_Microsoft)
19 {
20     version = alloca;
21 }
22 
23 // Use DMC++'s alloca() for Win32
24 
25 version (alloca)
26 {
27 
28 /*******************************************
29  * Allocate data from the caller's stack frame.
30  * This is a 'magic' function that needs help from the compiler to
31  * work right, do not change its name, do not call it from other compilers.
32  * Input:
33  *      nbytes  number of bytes to allocate
34  *      ECX     address of variable with # of bytes in locals
35  *              This is adjusted upon return to reflect the additional
36  *              size of the stack frame.
37  * Returns:
38  *      EAX     allocated data, null if stack overflows
39  */
40 
41 extern (C) void* __alloca(int nbytes)
42 {
43   version (D_InlineAsm_X86)
44   {
45     asm
46     {
47         naked                   ;
48         mov     EDX,ECX         ;
49         mov     EAX,4[ESP]      ; // get nbytes
50         push    EBX             ;
51         push    EDI             ;
52         push    ESI             ;
53 
54         add     EAX,15          ;
55         and     EAX,0xFFFFFFF0  ; // round up to 16 byte boundary
56         jnz     Abegin          ;
57         mov     EAX,16          ; // minimum allocation is 16
58     Abegin:
59         mov     ESI,EAX         ; // ESI = nbytes
60         neg     EAX             ;
61         add     EAX,ESP         ; // EAX is now what the new ESP will be.
62         jae     Aoverflow       ;
63     }
64     version (Win32)
65     {
66     asm
67     {
68         // We need to be careful about the guard page
69         // Thus, for every 4k page, touch it to cause the OS to load it in.
70         mov     ECX,EAX         ; // ECX is new location for stack
71         mov     EBX,ESI         ; // EBX is size to "grow" stack
72     L1:
73         test    [ECX+EBX],EBX   ; // bring in page
74         sub     EBX,0x1000      ; // next 4K page down
75         jae     L1              ; // if more pages
76         test    [ECX],EBX       ; // bring in last page
77     }
78     }
79     asm
80     {
81         // Copy down to [ESP] the temps on the stack.
82         // The number of temps is (EBP - ESP - locals).
83         mov     ECX,EBP         ;
84         sub     ECX,ESP         ;
85         sub     ECX,[EDX]       ; // ECX = number of temps (bytes) to move.
86         add     [EDX],ESI       ; // adjust locals by nbytes for next call to alloca()
87         mov     ESP,EAX         ; // Set up new stack pointer.
88         add     EAX,ECX         ; // Return value = ESP + temps.
89         mov     EDI,ESP         ; // Destination of copy of temps.
90         add     ESI,ESP         ; // Source of copy.
91         shr     ECX,2           ; // ECX to count of dwords in temps
92                                   // Always at least 4 (nbytes, EIP, ESI,and EDI).
93         rep                     ;
94         movsd                   ;
95         jmp     done            ;
96 
97     Aoverflow:
98         // Overflowed the stack.  Return null
99         xor     EAX,EAX         ;
100 
101     done:
102         pop     ESI             ;
103         pop     EDI             ;
104         pop     EBX             ;
105         ret                     ;
106     }
107   }
108   else version (D_InlineAsm_X86_64)
109   {
110     version (Win64)
111     {
112     asm
113     {
114         /* RCX     nbytes
115          * RDX     address of variable with # of bytes in locals
116          * Must save registers RBX,RDI,RSI,R12..R15
117          */
118         naked                   ;
119         push    RBX             ;
120         push    RDI             ;
121         push    RSI             ;
122         mov     RAX,RCX         ; // get nbytes
123         add     RAX,15          ;
124         and     AL,0xF0         ; // round up to 16 byte boundary
125         test    RAX,RAX         ;
126         jnz     Abegin          ;
127         mov     RAX,16          ; // allow zero bytes allocation
128     Abegin:
129         mov     RSI,RAX         ; // RSI = nbytes
130         neg     RAX             ;
131         add     RAX,RSP         ; // RAX is now what the new RSP will be.
132         jae     Aoverflow       ;
133 
134         // We need to be careful about the guard page
135         // Thus, for every 4k page, touch it to cause the OS to load it in.
136         mov     RCX,RAX         ; // RCX is new location for stack
137         mov     RBX,RSI         ; // RBX is size to "grow" stack
138     L1:
139         test    [RCX+RBX],RBX   ; // bring in page
140         sub     RBX,0x1000      ; // next 4K page down
141         jae     L1              ; // if more pages
142         test    [RCX],RBX       ; // bring in last page
143 
144         // Copy down to [RSP] the temps on the stack.
145         // The number of temps is (RBP - RSP - locals).
146         mov     RCX,RBP         ;
147         sub     RCX,RSP         ;
148         sub     RCX,[RDX]       ; // RCX = number of temps (bytes) to move.
149         add     [RDX],RSI       ; // adjust locals by nbytes for next call to alloca()
150         mov     RSP,RAX         ; // Set up new stack pointer.
151         add     RAX,RCX         ; // Return value = RSP + temps.
152         mov     RDI,RSP         ; // Destination of copy of temps.
153         add     RSI,RSP         ; // Source of copy.
154         shr     RCX,3           ; // RCX to count of qwords in temps
155         rep                     ;
156         movsq                   ;
157         jmp     done            ;
158 
159     Aoverflow:
160         // Overflowed the stack.  Return null
161         xor     RAX,RAX         ;
162 
163     done:
164         pop     RSI             ;
165         pop     RDI             ;
166         pop     RBX             ;
167         ret                     ;
168     }
169     }
170     else
171     {
172     asm
173     {
174         /* Parameter is passed in RDI
175          * Must save registers RBX,R12..R15
176          */
177         naked                   ;
178         mov     RDX,RCX         ;
179         mov     RAX,RDI         ; // get nbytes
180         add     RAX,15          ;
181         and     AL,0xF0         ; // round up to 16 byte boundary
182         test    RAX,RAX         ;
183         jnz     Abegin          ;
184         mov     RAX,16          ; // allow zero bytes allocation
185     Abegin:
186         mov     RSI,RAX         ; // RSI = nbytes
187         neg     RAX             ;
188         add     RAX,RSP         ; // RAX is now what the new RSP will be.
189         jae     Aoverflow       ;
190 
191         // Copy down to [RSP] the temps on the stack.
192         // The number of temps is (RBP - RSP - locals).
193         mov     RCX,RBP         ;
194         sub     RCX,RSP         ;
195         sub     RCX,[RDX]       ; // RCX = number of temps (bytes) to move.
196         add     [RDX],RSI       ; // adjust locals by nbytes for next call to alloca()
197         mov     RSP,RAX         ; // Set up new stack pointer.
198         add     RAX,RCX         ; // Return value = RSP + temps.
199         mov     RDI,RSP         ; // Destination of copy of temps.
200         add     RSI,RSP         ; // Source of copy.
201         shr     RCX,3           ; // RCX to count of qwords in temps
202         rep                     ;
203         movsq                   ;
204         jmp     done            ;
205 
206     Aoverflow:
207         // Overflowed the stack.  Return null
208         xor     RAX,RAX         ;
209 
210     done:
211         ret                     ;
212     }
213     }
214   }
215   else
216         static assert(0);
217 }
218 
219 }