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 }