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