authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-12 02:14:44-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-12 02:14:44-05:00
log445b03384a5ffcace11927aa9dd5f21604527f5c
tree9cab533e7ac95f74d0aad7e986f86d0f7a2c307e
parentf2d601661d286b135293373a83ce1a8628272379

introduce std.heap.ArenaAllocator and std.heap.DirectAllocator

* DirectAllocator does the underlying syscall for every allocation. * ArenaAllocator takes another allocator as an argument and allocates bytes up front, falling back to DirectAllocator with increasingly large allocation sizes, to avoid calling it too often. Then the entire arena can be freed at once. The self hosted parser is updated to take advantage of ArenaAllocator for the AST that it returns. This significantly reduces the complexity of cleanup code. docgen and build runner are updated to use the combination of ArenaAllocator and DirectAllocator instead of IncrementingAllocator, which is now deprecated in favor of FixedBufferAllocator combined with DirectAllocator. The C allocator calls aligned_alloc instead of malloc, in order to respect the alignment parameter. Added asserts in Allocator to ensure that implementors of the interface return slices of the correct size. Fixed a bug in Allocator when you call realloc to grow the allocation.

10 files changed, 395 insertions(+), 130 deletions(-)

doc/docgen.zig+7-4
......@@ -13,10 +13,13 @@ const obj_ext = std.build.Target(std.build.Target.Native).oFileExt();
1313const tmp_dir_name = "docgen_tmp";
1414
1515pub fn main() !void {
16 // TODO use a more general purpose allocator here
17 var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size);
18 defer inc_allocator.deinit();
19 const allocator = &inc_allocator.allocator;
16 var direct_allocator = std.heap.DirectAllocator.init();
17 defer direct_allocator.deinit();
18
19 var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
20 defer arena.deinit();
21
22 const allocator = &arena.allocator;
2023
2124 var args_it = os.args();
2225
src-self-hosted/main.zig+1-1
......@@ -575,7 +575,7 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
575575 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
576576 defer parser.deinit();
577577
578 const tree = try parser.parse();
578 var tree = try parser.parse();
579579 defer tree.deinit();
580580
581581 const baf = try io.BufferedAtomicFile.create(allocator, file_path);
src-self-hosted/module.zig+1-1
......@@ -241,7 +241,7 @@ pub const Module = struct {
241241 var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
242242 defer parser.deinit();
243243
244 const tree = try parser.parse();
244 var tree = try parser.parse();
245245 defer tree.deinit();
246246
247247 var stderr_file = try std.io.getStdErr();
std/c/index.zig+1
......@@ -45,6 +45,7 @@ pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
4545pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
4646pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
4747
48pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
4849pub extern "c" fn malloc(usize) ?&c_void;
4950pub extern "c" fn realloc(&c_void, usize) ?&c_void;
5051pub extern "c" fn free(&c_void) void;
std/heap.zig+280-47
......@@ -17,7 +17,7 @@ var c_allocator_state = Allocator {
1717};
1818
1919fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 {
20 return if (c.malloc(usize(n))) |buf|
20 return if (c.aligned_alloc(alignment, n)) |buf|
2121 @ptrCast(&u8, buf)[0..n]
2222 else
2323 error.OutOfMemory;
......@@ -39,83 +39,279 @@ fn cFree(self: &Allocator, old_mem: []u8) void {
3939 c.free(old_ptr);
4040}
4141
42/// Use this allocator when you want to allocate completely up front and guarantee that individual
43/// allocations will never make syscalls.
4244pub const IncrementingAllocator = struct {
4345 allocator: Allocator,
4446 bytes: []u8,
4547 end_index: usize,
46 heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
48 direct_allocator: DirectAllocator,
49
50 pub fn init(capacity: usize) !IncrementingAllocator {
51 var direct_allocator = DirectAllocator.init();
52 const bytes = try direct_allocator.allocator.alloc(u8, capacity);
53 errdefer direct_allocator.allocator.free(bytes);
54
55 return IncrementingAllocator {
56 .allocator = Allocator {
57 .allocFn = alloc,
58 .reallocFn = realloc,
59 .freeFn = free,
60 },
61 .bytes = bytes,
62 .direct_allocator = direct_allocator,
63 .end_index = 0,
64 };
65 }
66
67 pub fn deinit(self: &IncrementingAllocator) void {
68 self.direct_allocator.allocator.free(self.bytes);
69 self.direct_allocator.deinit();
70 }
71
72 fn reset(self: &IncrementingAllocator) void {
73 self.end_index = 0;
74 }
75
76 fn bytesLeft(self: &const IncrementingAllocator) usize {
77 return self.bytes.len - self.end_index;
78 }
79
80 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
81 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
82 const addr = @ptrToInt(&self.bytes[self.end_index]);
83 const rem = @rem(addr, alignment);
84 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
85 const adjusted_index = self.end_index + march_forward_bytes;
86 const new_end_index = adjusted_index + n;
87 if (new_end_index > self.bytes.len) {
88 return error.OutOfMemory;
89 }
90 const result = self.bytes[adjusted_index .. new_end_index];
91 self.end_index = new_end_index;
92 return result;
93 }
94
95 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
96 if (new_size <= old_mem.len) {
97 return old_mem[0..new_size];
98 } else {
99 const result = try alloc(allocator, new_size, alignment);
100 mem.copy(u8, result, old_mem);
101 return result;
102 }
103 }
104
105 fn free(allocator: &Allocator, bytes: []u8) void {
106 // Do nothing. That's the point of an incrementing allocator.
107 }
108};
109
110/// This allocator makes a syscall directly for every allocation and free.
111pub const DirectAllocator = struct {
112 allocator: Allocator,
113 heap_handle: ?HeapHandle,
114
115 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
116
117 //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
118 //pub const want_safety = switch (builtin.mode) {
119 // builtin.Mode.Debug => true,
120 // builtin.Mode.ReleaseSafe => true,
121 // else => false,
122 //};
123
124 pub fn init() DirectAllocator {
125 return DirectAllocator {
126 .allocator = Allocator {
127 .allocFn = alloc,
128 .reallocFn = realloc,
129 .freeFn = free,
130 },
131 .heap_handle = if (builtin.os == Os.windows) null else {},
132 };
133 }
134
135 pub fn deinit(self: &DirectAllocator) void {
136 switch (builtin.os) {
137 Os.windows => if (self.heap_handle) |heap_handle| {
138 _ = os.windows.HeapDestroy(heap_handle);
139 },
140 else => {},
141 }
142 }
143
144 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
145 const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
47146
48 fn init(capacity: usize) !IncrementingAllocator {
49147 switch (builtin.os) {
50148 Os.linux, Os.macosx, Os.ios => {
149 assert(alignment <= os.page_size);
51150 const p = os.posix;
52 const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
53 p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
151 const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE,
152 p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
54153 if (addr == p.MAP_FAILED) {
55154 return error.OutOfMemory;
56155 }
57 return IncrementingAllocator {
58 .allocator = Allocator {
59 .allocFn = alloc,
60 .reallocFn = realloc,
61 .freeFn = free,
62 },
63 .bytes = @intToPtr(&u8, addr)[0..capacity],
64 .end_index = 0,
65 .heap_handle = {},
156 return @intToPtr(&u8, addr)[0..n];
157 },
158 Os.windows => {
159 const amt = n + alignment + @sizeOf(usize);
160 const heap_handle = self.heap_handle ?? blk: {
161 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;
162 self.heap_handle = hh;
163 break :blk hh;
66164 };
165 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) ?? return error.OutOfMemory;
166 const root_addr = @ptrToInt(ptr);
167 const rem = @rem(root_addr, alignment);
168 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
169 const adjusted_addr = root_addr + march_forward_bytes;
170 const record_addr = adjusted_addr + n;
171 *@intToPtr(&align(1) usize, record_addr) = root_addr;
172 return @intToPtr(&u8, adjusted_addr)[0..n];
173 },
174 else => @compileError("Unsupported OS"),
175 }
176 }
177
178 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
179 const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
180
181 switch (builtin.os) {
182 Os.linux, Os.macosx, Os.ios => {
183 if (new_size <= old_mem.len) {
184 const base_addr = @ptrToInt(old_mem.ptr);
185 const old_addr_end = base_addr + old_mem.len;
186 const new_addr_end = base_addr + new_size;
187 const rem = @rem(new_addr_end, os.page_size);
188 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
189 if (old_addr_end > new_addr_end_rounded) {
190 _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);
191 }
192 return old_mem[0..new_size];
193 }
194
195 const result = try alloc(allocator, new_size, alignment);
196 mem.copy(u8, result, old_mem);
197 return result;
67198 },
68199 Os.windows => {
69 const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
70 const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
71 return IncrementingAllocator {
72 .allocator = Allocator {
73 .allocFn = alloc,
74 .reallocFn = realloc,
75 .freeFn = free,
76 },
77 .bytes = @ptrCast(&u8, ptr)[0..capacity],
78 .end_index = 0,
79 .heap_handle = heap_handle,
200 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
201 const old_record_addr = old_adjusted_addr + old_mem.len;
202 const root_addr = *@intToPtr(&align(1) usize, old_record_addr);
203 const old_ptr = @intToPtr(os.windows.LPVOID, root_addr);
204 const amt = new_size + alignment + @sizeOf(usize);
205 const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: {
206 if (new_size > old_mem.len) return error.OutOfMemory;
207 const new_record_addr = old_record_addr - new_size + old_mem.len;
208 *@intToPtr(&align(1) usize, new_record_addr) = root_addr;
209 return old_mem[0..new_size];
80210 };
211 const offset = old_adjusted_addr - root_addr;
212 const new_root_addr = @ptrToInt(new_ptr);
213 const new_adjusted_addr = new_root_addr + offset;
214 assert(new_adjusted_addr % alignment == 0);
215 const new_record_addr = new_adjusted_addr + new_size;
216 *@intToPtr(&align(1) usize, new_record_addr) = new_root_addr;
217 return @intToPtr(&u8, new_adjusted_addr)[0..new_size];
81218 },
82219 else => @compileError("Unsupported OS"),
83220 }
84221 }
85222
86 fn deinit(self: &IncrementingAllocator) void {
223 fn free(allocator: &Allocator, bytes: []u8) void {
224 const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
225
87226 switch (builtin.os) {
88227 Os.linux, Os.macosx, Os.ios => {
89 _ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
228 _ = os.posix.munmap(bytes.ptr, bytes.len);
90229 },
91230 Os.windows => {
92 _ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
231 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
232 const root_addr = *@intToPtr(&align(1) usize, record_addr);
233 const ptr = @intToPtr(os.windows.LPVOID, root_addr);
234 _ = os.windows.HeapFree(??self.heap_handle, 0, ptr);
93235 },
94236 else => @compileError("Unsupported OS"),
95237 }
96238 }
239};
97240
98 fn reset(self: &IncrementingAllocator) void {
99 self.end_index = 0;
241/// This allocator takes an existing allocator, wraps it, and provides an interface
242/// where you can allocate without freeing, and then free it all together.
243pub const ArenaAllocator = struct {
244 pub allocator: Allocator,
245
246 child_allocator: &Allocator,
247 buffer_list: std.LinkedList([]u8),
248 end_index: usize,
249
250 const BufNode = std.LinkedList([]u8).Node;
251
252 pub fn init(child_allocator: &Allocator) ArenaAllocator {
253 return ArenaAllocator {
254 .allocator = Allocator {
255 .allocFn = alloc,
256 .reallocFn = realloc,
257 .freeFn = free,
258 },
259 .child_allocator = child_allocator,
260 .buffer_list = std.LinkedList([]u8).init(),
261 .end_index = 0,
262 };
100263 }
101264
102 fn bytesLeft(self: &const IncrementingAllocator) usize {
103 return self.bytes.len - self.end_index;
265 pub fn deinit(self: &ArenaAllocator) void {
266 var it = self.buffer_list.first;
267 while (it) |node| {
268 // this has to occur before the free because the free frees node
269 it = node.next;
270
271 self.child_allocator.free(node.data);
272 }
273 }
274
275 fn createNode(self: &ArenaAllocator, prev_len: usize, minimum_size: usize) !&BufNode {
276 const actual_min_size = minimum_size + @sizeOf(BufNode);
277 var len = prev_len;
278 while (true) {
279 len += len / 2;
280 len += os.page_size - @rem(len, os.page_size);
281 if (len >= actual_min_size) break;
282 }
283 const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);
284 const buf_node_slice = ([]BufNode)(buf[0..@sizeOf(BufNode)]);
285 const buf_node = &buf_node_slice[0];
286 *buf_node = BufNode {
287 .data = buf,
288 .prev = null,
289 .next = null,
290 };
291 self.buffer_list.append(buf_node);
292 self.end_index = 0;
293 return buf_node;
104294 }
105295
106296 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
107 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
108 const addr = @ptrToInt(&self.bytes[self.end_index]);
109 const rem = @rem(addr, alignment);
110 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
111 const adjusted_index = self.end_index + march_forward_bytes;
112 const new_end_index = adjusted_index + n;
113 if (new_end_index > self.bytes.len) {
114 return error.OutOfMemory;
297 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
298
299 var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment);
300 while (true) {
301 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
302 const addr = @ptrToInt(&cur_buf[self.end_index]);
303 const rem = @rem(addr, alignment);
304 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
305 const adjusted_index = self.end_index + march_forward_bytes;
306 const new_end_index = adjusted_index + n;
307 if (new_end_index > cur_buf.len) {
308 cur_node = try self.createNode(cur_buf.len, n + alignment);
309 continue;
310 }
311 const result = cur_buf[adjusted_index .. new_end_index];
312 self.end_index = new_end_index;
313 return result;
115314 }
116 const result = self.bytes[adjusted_index .. new_end_index];
117 self.end_index = new_end_index;
118 return result;
119315 }
120316
121317 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
......@@ -128,11 +324,11 @@ pub const IncrementingAllocator = struct {
128324 }
129325 }
130326
131 fn free(allocator: &Allocator, bytes: []u8) void {
132 // Do nothing. That's the point of an incrementing allocator.
133 }
327 fn free(allocator: &Allocator, bytes: []u8) void { }
134328};
135329
330
331
136332test "c_allocator" {
137333 if (builtin.link_libc) {
138334 var slice = c_allocator.alloc(u8, 50) catch return;
......@@ -142,7 +338,7 @@ test "c_allocator" {
142338}
143339
144340test "IncrementingAllocator" {
145 const total_bytes = 100 * 1024 * 1024;
341 const total_bytes = 10 * 1024 * 1024;
146342 var inc_allocator = try IncrementingAllocator.init(total_bytes);
147343 defer inc_allocator.deinit();
148344
......@@ -161,3 +357,40 @@ test "IncrementingAllocator" {
161357 assert(inc_allocator.bytesLeft() == total_bytes);
162358}
163359
360test "DirectAllocator" {
361 var direct_allocator = DirectAllocator.init();
362 defer direct_allocator.deinit();
363
364 const allocator = &direct_allocator.allocator;
365 try testAllocator(allocator);
366}
367
368test "ArenaAllocator" {
369 var direct_allocator = DirectAllocator.init();
370 defer direct_allocator.deinit();
371
372 var arena_allocator = ArenaAllocator.init(&direct_allocator.allocator);
373 defer arena_allocator.deinit();
374
375 try testAllocator(&arena_allocator.allocator);
376}
377
378fn testAllocator(allocator: &mem.Allocator) !void {
379 var slice = try allocator.alloc(&i32, 100);
380
381 for (slice) |*item, i| {
382 *item = try allocator.create(i32);
383 **item = i32(i);
384 }
385
386 for (slice) |item, i| {
387 allocator.destroy(item);
388 }
389
390 slice = try allocator.realloc(&i32, slice, 20000);
391 slice = try allocator.realloc(&i32, slice, 50);
392 slice = try allocator.realloc(&i32, slice, 25);
393 slice = try allocator.realloc(&i32, slice, 10);
394
395 allocator.free(slice);
396}
std/mem.zig+8-4
......@@ -44,6 +44,7 @@ pub const Allocator = struct {
4444 {
4545 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
4646 const byte_slice = try self.allocFn(self, byte_count, alignment);
47 assert(byte_slice.len == byte_count);
4748 // This loop should get optimized out in ReleaseFast mode
4849 for (byte_slice) |*byte| {
4950 *byte = undefined;
......@@ -65,9 +66,12 @@ pub const Allocator = struct {
6566 const old_byte_slice = ([]u8)(old_mem);
6667 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
6768 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
68 // This loop should get optimized out in ReleaseFast mode
69 for (byte_slice[old_byte_slice.len..]) |*byte| {
70 *byte = undefined;
69 assert(byte_slice.len == byte_count);
70 if (n > old_mem.len) {
71 // This loop should get optimized out in ReleaseFast mode
72 for (byte_slice[old_byte_slice.len..]) |*byte| {
73 *byte = undefined;
74 }
7175 }
7276 return ([]T)(@alignCast(alignment, byte_slice));
7377 }
......@@ -94,6 +98,7 @@ pub const Allocator = struct {
9498 const byte_count = @sizeOf(T) * n;
9599
96100 const byte_slice = self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment) catch unreachable;
101 assert(byte_slice.len == byte_count);
97102 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
98103 }
99104
......@@ -151,7 +156,6 @@ pub const FixedBufferAllocator = struct {
151156 fn free(allocator: &Allocator, bytes: []u8) void { }
152157};
153158
154
155159/// Copy all of source into dest at position 0.
156160/// dest.len must be >= source.len.
157161pub fn copy(comptime T: type, dest: []T, source: []const T) void {
std/os/windows/index.zig+17-5
......@@ -22,7 +22,7 @@ pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &H
2222
2323pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR,
2424 lpProcessAttributes: ?&SECURITY_ATTRIBUTES, lpThreadAttributes: ?&SECURITY_ATTRIBUTES, bInheritHandles: BOOL,
25 dwCreationFlags: DWORD, lpEnvironment: ?LPVOID, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA,
25 dwCreationFlags: DWORD, lpEnvironment: ?&c_void, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA,
2626 lpProcessInformation: &PROCESS_INFORMATION) BOOL;
2727
2828pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
......@@ -61,16 +61,24 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
6161
6262pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
6363
64pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
65pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
66pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
67pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) SIZE_T;
68pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) BOOL;
69pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
70pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
71
6472pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
6573
66pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?LPVOID;
74pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?&c_void;
6775
68pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) BOOL;
76pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void) BOOL;
6977
7078pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
7179 dwFlags: DWORD) BOOL;
7280
73pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,
81pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
7482 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
7583 in_out_lpOverlapped: ?&OVERLAPPED) BOOL;
7684
......@@ -201,7 +209,7 @@ pub const VOLUME_NAME_NT = 0x2;
201209
202210pub const SECURITY_ATTRIBUTES = extern struct {
203211 nLength: DWORD,
204 lpSecurityDescriptor: ?LPVOID,
212 lpSecurityDescriptor: ?&c_void,
205213 bInheritHandle: BOOL,
206214};
207215pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
......@@ -296,3 +304,7 @@ pub const MOVEFILE_WRITE_THROUGH = 8;
296304pub const FILE_BEGIN = 0;
297305pub const FILE_CURRENT = 1;
298306pub const FILE_END = 2;
307
308pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
309pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
310pub const HEAP_NO_SERIALIZE = 0x00000001;
std/special/build_runner.zig+10-4
......@@ -12,11 +12,17 @@ const warn = std.debug.warn;
1212pub fn main() !void {
1313 var arg_it = os.args();
1414
15 // TODO use a more general purpose allocator here
16 var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024);
17 defer inc_allocator.deinit();
15 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
16 // one shot program. We don't need to waste time freeing memory and finding places to squish
17 // bytes into. So we free everything all at once at the very end.
1818
19 const allocator = &inc_allocator.allocator;
19 var direct_allocator = std.heap.DirectAllocator.init();
20 defer direct_allocator.deinit();
21
22 var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
23 defer arena.deinit();
24
25 const allocator = &arena.allocator;
2026
2127
2228 // skip my own exe name
std/zig/parser.zig+64-61
......@@ -13,7 +13,7 @@ const io = std.io;
1313const warn = std.debug.warn;
1414
1515pub const Parser = struct {
16 allocator: &mem.Allocator,
16 util_allocator: &mem.Allocator,
1717 tokenizer: &Tokenizer,
1818 put_back_tokens: [2]Token,
1919 put_back_count: usize,
......@@ -21,9 +21,10 @@ pub const Parser = struct {
2121
2222 pub const Tree = struct {
2323 root_node: &ast.NodeRoot,
24 arena_allocator: std.heap.ArenaAllocator,
2425
25 pub fn deinit(self: &const Tree) void {
26 // TODO free the whole arena
26 pub fn deinit(self: &Tree) void {
27 self.arena_allocator.deinit();
2728 }
2829 };
2930
......@@ -33,12 +34,10 @@ pub const Parser = struct {
3334 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
3435 utility_bytes: []align(utility_bytes_align) u8,
3536
36 /// `allocator` should be an arena allocator. Parser never calls free on anything. After you're
37 /// done with a Parser, free the arena. After the arena is freed, no member functions of Parser
38 /// may be called.
37 /// allocator must outlive the returned Parser and all the parse trees you create with it.
3938 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
4039 return Parser {
41 .allocator = allocator,
40 .util_allocator = allocator,
4241 .tokenizer = tokenizer,
4342 .put_back_tokens = undefined,
4443 .put_back_count = 0,
......@@ -48,7 +47,7 @@ pub const Parser = struct {
4847 }
4948
5049 pub fn deinit(self: &Parser) void {
51 self.allocator.free(self.utility_bytes);
50 self.util_allocator.free(self.utility_bytes);
5251 }
5352
5453 const TopLevelDeclCtx = struct {
......@@ -101,8 +100,11 @@ pub const Parser = struct {
101100 var stack = self.initUtilityArrayList(State);
102101 defer self.deinitUtilityArrayList(stack);
103102
104 const root_node = try self.createRoot();
105 // TODO errdefer arena free root node
103 var arena_allocator = std.heap.ArenaAllocator.init(self.util_allocator);
104 errdefer arena_allocator.deinit();
105
106 const arena = &arena_allocator.allocator;
107 const root_node = try self.createRoot(arena);
106108
107109 try stack.append(State.TopLevel);
108110
......@@ -130,7 +132,7 @@ pub const Parser = struct {
130132 stack.append(State { .TopLevelExtern = token }) catch unreachable;
131133 continue;
132134 },
133 Token.Id.Eof => return Tree {.root_node = root_node},
135 Token.Id.Eof => return Tree {.root_node = root_node, .arena_allocator = arena_allocator},
134136 else => {
135137 self.putBackToken(token);
136138 stack.append(State { .TopLevelExtern = null }) catch unreachable;
......@@ -164,7 +166,7 @@ pub const Parser = struct {
164166 Token.Id.Keyword_var, Token.Id.Keyword_const => {
165167 stack.append(State.TopLevel) catch unreachable;
166168 // TODO shouldn't need these casts
167 const var_decl_node = try self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
169 const var_decl_node = try self.createAttachVarDecl(arena, &root_node.decls, ctx.visib_token,
168170 token, (?Token)(null), ctx.extern_token);
169171 try stack.append(State { .VarDecl = var_decl_node });
170172 continue;
......@@ -172,7 +174,7 @@ pub const Parser = struct {
172174 Token.Id.Keyword_fn => {
173175 stack.append(State.TopLevel) catch unreachable;
174176 // TODO shouldn't need these casts
175 const fn_proto = try self.createAttachFnProto(&root_node.decls, token,
177 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
176178 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
177179 try stack.append(State { .FnDef = fn_proto });
178180 try stack.append(State { .FnProto = fn_proto });
......@@ -185,7 +187,7 @@ pub const Parser = struct {
185187 stack.append(State.TopLevel) catch unreachable;
186188 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
187189 // TODO shouldn't need this cast
188 const fn_proto = try self.createAttachFnProto(&root_node.decls, fn_token,
190 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, fn_token,
189191 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
190192 try stack.append(State { .FnDef = fn_proto });
191193 try stack.append(State { .FnProto = fn_proto });
......@@ -253,13 +255,13 @@ pub const Parser = struct {
253255 const token = self.getNextToken();
254256 switch (token.id) {
255257 Token.Id.Keyword_return => {
256 try stack.append(State { .PrefixOp = try self.createPrefixOp(token,
258 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
257259 ast.NodePrefixOp.PrefixOp.Return) });
258260 try stack.append(State.ExpectOperand);
259261 continue;
260262 },
261263 Token.Id.Ampersand => {
262 const prefix_op = try self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
264 const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
263265 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
264266 .align_expr = null,
265267 .bit_offset_start_token = null,
......@@ -275,21 +277,21 @@ pub const Parser = struct {
275277 },
276278 Token.Id.Identifier => {
277279 try stack.append(State {
278 .Operand = &(try self.createIdentifier(token)).base
280 .Operand = &(try self.createIdentifier(arena, token)).base
279281 });
280282 try stack.append(State.AfterOperand);
281283 continue;
282284 },
283285 Token.Id.IntegerLiteral => {
284286 try stack.append(State {
285 .Operand = &(try self.createIntegerLiteral(token)).base
287 .Operand = &(try self.createIntegerLiteral(arena, token)).base
286288 });
287289 try stack.append(State.AfterOperand);
288290 continue;
289291 },
290292 Token.Id.FloatLiteral => {
291293 try stack.append(State {
292 .Operand = &(try self.createFloatLiteral(token)).base
294 .Operand = &(try self.createFloatLiteral(arena, token)).base
293295 });
294296 try stack.append(State.AfterOperand);
295297 continue;
......@@ -306,14 +308,14 @@ pub const Parser = struct {
306308 switch (token.id) {
307309 Token.Id.EqualEqual => {
308310 try stack.append(State {
309 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
311 .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.EqualEqual)
310312 });
311313 try stack.append(State.ExpectOperand);
312314 continue;
313315 },
314316 Token.Id.BangEqual => {
315317 try stack.append(State {
316 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
318 .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BangEqual)
317319 });
318320 try stack.append(State.ExpectOperand);
319321 continue;
......@@ -421,7 +423,7 @@ pub const Parser = struct {
421423 if (token.id == Token.Id.RParen) {
422424 continue;
423425 }
424 const param_decl = try self.createAttachParamDecl(&fn_proto.params);
426 const param_decl = try self.createAttachParamDecl(arena, &fn_proto.params);
425427 if (token.id == Token.Id.Keyword_comptime) {
426428 param_decl.comptime_token = token;
427429 token = self.getNextToken();
......@@ -470,7 +472,7 @@ pub const Parser = struct {
470472 const token = self.getNextToken();
471473 switch(token.id) {
472474 Token.Id.LBrace => {
473 const block = try self.createBlock(token);
475 const block = try self.createBlock(arena, token);
474476 fn_proto.body_node = &block.base;
475477 stack.append(State { .Block = block }) catch unreachable;
476478 continue;
......@@ -504,7 +506,7 @@ pub const Parser = struct {
504506 const mut_token = self.getNextToken();
505507 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
506508 // TODO shouldn't need these casts
507 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
509 const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
508510 mut_token, (?Token)(comptime_token), (?Token)(null));
509511 try stack.append(State { .VarDecl = var_decl });
510512 continue;
......@@ -518,7 +520,7 @@ pub const Parser = struct {
518520 const mut_token = self.getNextToken();
519521 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
520522 // TODO shouldn't need these casts
521 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
523 const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
522524 mut_token, (?Token)(null), (?Token)(null));
523525 try stack.append(State { .VarDecl = var_decl });
524526 continue;
......@@ -541,20 +543,20 @@ pub const Parser = struct {
541543 }
542544 }
543545
544 fn createRoot(self: &Parser) !&ast.NodeRoot {
545 const node = try self.allocator.create(ast.NodeRoot);
546 fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
547 const node = try arena.create(ast.NodeRoot);
546548
547549 *node = ast.NodeRoot {
548550 .base = ast.Node {.id = ast.Node.Id.Root},
549 .decls = ArrayList(&ast.Node).init(self.allocator),
551 .decls = ArrayList(&ast.Node).init(arena),
550552 };
551553 return node;
552554 }
553555
554 fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
555 extern_token: &const ?Token) !&ast.NodeVarDecl
556 fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
557 comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
556558 {
557 const node = try self.allocator.create(ast.NodeVarDecl);
559 const node = try arena.create(ast.NodeVarDecl);
558560
559561 *node = ast.NodeVarDecl {
560562 .base = ast.Node {.id = ast.Node.Id.VarDecl},
......@@ -573,17 +575,17 @@ pub const Parser = struct {
573575 return node;
574576 }
575577
576 fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
578 fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
577579 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
578580 {
579 const node = try self.allocator.create(ast.NodeFnProto);
581 const node = try arena.create(ast.NodeFnProto);
580582
581583 *node = ast.NodeFnProto {
582584 .base = ast.Node {.id = ast.Node.Id.FnProto},
583585 .visib_token = *visib_token,
584586 .name_token = null,
585587 .fn_token = *fn_token,
586 .params = ArrayList(&ast.Node).init(self.allocator),
588 .params = ArrayList(&ast.Node).init(arena),
587589 .return_type = undefined,
588590 .var_args_token = null,
589591 .extern_token = *extern_token,
......@@ -596,8 +598,8 @@ pub const Parser = struct {
596598 return node;
597599 }
598600
599 fn createParamDecl(self: &Parser) !&ast.NodeParamDecl {
600 const node = try self.allocator.create(ast.NodeParamDecl);
601 fn createParamDecl(self: &Parser, arena: &mem.Allocator) !&ast.NodeParamDecl {
602 const node = try arena.create(ast.NodeParamDecl);
601603
602604 *node = ast.NodeParamDecl {
603605 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
......@@ -610,20 +612,20 @@ pub const Parser = struct {
610612 return node;
611613 }
612614
613 fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock {
614 const node = try self.allocator.create(ast.NodeBlock);
615 fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
616 const node = try arena.create(ast.NodeBlock);
615617
616618 *node = ast.NodeBlock {
617619 .base = ast.Node {.id = ast.Node.Id.Block},
618620 .begin_token = *begin_token,
619621 .end_token = undefined,
620 .statements = ArrayList(&ast.Node).init(self.allocator),
622 .statements = ArrayList(&ast.Node).init(arena),
621623 };
622624 return node;
623625 }
624626
625 fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
626 const node = try self.allocator.create(ast.NodeInfixOp);
627 fn createInfixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
628 const node = try arena.create(ast.NodeInfixOp);
627629
628630 *node = ast.NodeInfixOp {
629631 .base = ast.Node {.id = ast.Node.Id.InfixOp},
......@@ -635,8 +637,8 @@ pub const Parser = struct {
635637 return node;
636638 }
637639
638 fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
639 const node = try self.allocator.create(ast.NodePrefixOp);
640 fn createPrefixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
641 const node = try arena.create(ast.NodePrefixOp);
640642
641643 *node = ast.NodePrefixOp {
642644 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
......@@ -647,8 +649,8 @@ pub const Parser = struct {
647649 return node;
648650 }
649651
650 fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier {
651 const node = try self.allocator.create(ast.NodeIdentifier);
652 fn createIdentifier(self: &Parser, arena: &mem.Allocator, name_token: &const Token) !&ast.NodeIdentifier {
653 const node = try arena.create(ast.NodeIdentifier);
652654
653655 *node = ast.NodeIdentifier {
654656 .base = ast.Node {.id = ast.Node.Id.Identifier},
......@@ -657,8 +659,8 @@ pub const Parser = struct {
657659 return node;
658660 }
659661
660 fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral {
661 const node = try self.allocator.create(ast.NodeIntegerLiteral);
662 fn createIntegerLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeIntegerLiteral {
663 const node = try arena.create(ast.NodeIntegerLiteral);
662664
663665 *node = ast.NodeIntegerLiteral {
664666 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
......@@ -667,8 +669,8 @@ pub const Parser = struct {
667669 return node;
668670 }
669671
670 fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral {
671 const node = try self.allocator.create(ast.NodeFloatLiteral);
672 fn createFloatLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeFloatLiteral {
673 const node = try arena.create(ast.NodeFloatLiteral);
672674
673675 *node = ast.NodeFloatLiteral {
674676 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
......@@ -677,31 +679,32 @@ pub const Parser = struct {
677679 return node;
678680 }
679681
680 fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
681 const node = try self.createIdentifier(name_token);
682 fn createAttachIdentifier(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
683 const node = try self.createIdentifier(arena, name_token);
682684 try dest_ptr.store(&node.base);
683685 return node;
684686 }
685687
686 fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
687 const node = try self.createParamDecl();
688 fn createAttachParamDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
689 const node = try self.createParamDecl(arena);
688690 try list.append(&node.base);
689691 return node;
690692 }
691693
692 fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
694 fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
693695 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
694696 inline_token: &const ?Token) !&ast.NodeFnProto
695697 {
696 const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
698 const node = try self.createFnProto(arena, fn_token, extern_token, cc_token, visib_token, inline_token);
697699 try list.append(&node.base);
698700 return node;
699701 }
700702
701 fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
702 mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
703 fn createAttachVarDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
704 visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
705 extern_token: &const ?Token) !&ast.NodeVarDecl
703706 {
704 const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
707 const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token);
705708 try list.append(&node.base);
706709 return node;
707710 }
......@@ -1018,10 +1021,10 @@ pub const Parser = struct {
10181021
10191022 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
10201023 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1021 self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1024 self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
10221025 const typed_slice = ([]T)(self.utility_bytes);
10231026 return ArrayList(T) {
1024 .allocator = self.allocator,
1027 .allocator = self.util_allocator,
10251028 .items = typed_slice,
10261029 .len = 0,
10271030 };
......@@ -1043,7 +1046,7 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
10431046 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
10441047 defer parser.deinit();
10451048
1046 const tree = try parser.parse();
1049 var tree = try parser.parse();
10471050 defer tree.deinit();
10481051
10491052 var buffer = try std.Buffer.initSize(allocator, 0);
test/standalone/brace_expansion/main.zig+6-3
......@@ -182,10 +182,13 @@ pub fn main() !void {
182182 var stdin_file = try io.getStdIn();
183183 var stdout_file = try io.getStdOut();
184184
185 var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024);
186 defer inc_allocator.deinit();
185 var direct_allocator = std.heap.DirectAllocator.init();
186 defer direct_allocator.deinit();
187187
188 global_allocator = &inc_allocator.allocator;
188 var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
189 defer arena.deinit();
190
191 global_allocator = &arena.allocator;
189192
190193 var stdin_buf = try Buffer.initSize(global_allocator, 0);
191194 defer stdin_buf.deinit();