authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-01-30 14:56:36+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-03-08 00:33:56+02:00
log0a7be71bc2e58a5375ceed0b1b9850bd33717a0b
tree37036778f4688684c92e843e4d5468edc04edd86
parentcfc19eace71c92ecd7e138db6d961271a1b6c126
signature Commit is signed but in an unrecognized format.

stage2 cbe: non pointer optionals


6 files changed, 163 insertions(+), 46 deletions(-)

lib/std/hash_map.zig+24-21
......@@ -50,20 +50,20 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
5050}
5151
5252pub fn AutoHashMap(comptime K: type, comptime V: type) type {
53 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage);
53 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage);
5454}
5555
5656pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type {
57 return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage);
57 return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage);
5858}
5959
6060/// Builtin hashmap for strings as keys.
6161pub fn StringHashMap(comptime V: type) type {
62 return HashMap([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage);
62 return HashMap([]const u8, V, hashString, eqlString, default_max_load_percentage);
6363}
6464
6565pub fn StringHashMapUnmanaged(comptime V: type) type {
66 return HashMapUnmanaged([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage);
66 return HashMapUnmanaged([]const u8, V, hashString, eqlString, default_max_load_percentage);
6767}
6868
6969pub fn eqlString(a: []const u8, b: []const u8) bool {
......@@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 {
7474 return std.hash.Wyhash.hash(0, s);
7575}
7676
77pub const DefaultMaxLoadPercentage = 80;
77/// Deprecated use `default_max_load_percentage`
78pub const DefaultMaxLoadPercentage = default_max_load_percentage;
79
80pub const default_max_load_percentage = 80;
7881
7982/// General purpose hash table.
8083/// No order is guaranteed and any modification invalidates live iterators.
......@@ -89,13 +92,13 @@ pub fn HashMap(
8992 comptime V: type,
9093 comptime hashFn: fn (key: K) u64,
9194 comptime eqlFn: fn (a: K, b: K) bool,
92 comptime MaxLoadPercentage: u64,
95 comptime max_load_percentage: u64,
9396) type {
9497 return struct {
9598 unmanaged: Unmanaged,
9699 allocator: *Allocator,
97100
98 pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, MaxLoadPercentage);
101 pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, max_load_percentage);
99102 pub const Entry = Unmanaged.Entry;
100103 pub const Hash = Unmanaged.Hash;
101104 pub const Iterator = Unmanaged.Iterator;
......@@ -251,9 +254,9 @@ pub fn HashMapUnmanaged(
251254 comptime V: type,
252255 hashFn: fn (key: K) u64,
253256 eqlFn: fn (a: K, b: K) bool,
254 comptime MaxLoadPercentage: u64,
257 comptime max_load_percentage: u64,
255258) type {
256 comptime assert(MaxLoadPercentage > 0 and MaxLoadPercentage < 100);
259 comptime assert(max_load_percentage > 0 and max_load_percentage < 100);
257260
258261 return struct {
259262 const Self = @This();
......@@ -274,12 +277,12 @@ pub fn HashMapUnmanaged(
274277 // Having a countdown to grow reduces the number of instructions to
275278 // execute when determining if the hashmap has enough capacity already.
276279 /// Number of available slots before a grow is needed to satisfy the
277 /// `MaxLoadPercentage`.
280 /// `max_load_percentage`.
278281 available: Size = 0,
279282
280283 // This is purely empirical and not a /very smart magic constant™/.
281284 /// Capacity of the first grow when bootstrapping the hashmap.
282 const MinimalCapacity = 8;
285 const minimal_capacity = 8;
283286
284287 // This hashmap is specially designed for sizes that fit in a u32.
285288 const Size = u32;
......@@ -382,7 +385,7 @@ pub fn HashMapUnmanaged(
382385 found_existing: bool,
383386 };
384387
385 pub const Managed = HashMap(K, V, hashFn, eqlFn, MaxLoadPercentage);
388 pub const Managed = HashMap(K, V, hashFn, eqlFn, max_load_percentage);
386389
387390 pub fn promote(self: Self, allocator: *Allocator) Managed {
388391 return .{
......@@ -392,7 +395,7 @@ pub fn HashMapUnmanaged(
392395 }
393396
394397 fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool {
395 return size * 100 < MaxLoadPercentage * cap;
398 return size * 100 < max_load_percentage * cap;
396399 }
397400
398401 pub fn init(allocator: *Allocator) Self {
......@@ -425,7 +428,7 @@ pub fn HashMapUnmanaged(
425428 }
426429
427430 fn capacityForSize(size: Size) Size {
428 var new_cap = @truncate(u32, (@as(u64, size) * 100) / MaxLoadPercentage + 1);
431 var new_cap = @truncate(u32, (@as(u64, size) * 100) / max_load_percentage + 1);
429432 new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable;
430433 return new_cap;
431434 }
......@@ -439,7 +442,7 @@ pub fn HashMapUnmanaged(
439442 if (self.metadata) |_| {
440443 self.initMetadatas();
441444 self.size = 0;
442 self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100);
445 self.available = @truncate(u32, (self.capacity() * max_load_percentage) / 100);
443446 }
444447 }
445448
......@@ -712,9 +715,9 @@ pub fn HashMapUnmanaged(
712715 }
713716
714717 // This counts the number of occupied slots, used + tombstones, which is
715 // what has to stay under the MaxLoadPercentage of capacity.
718 // what has to stay under the max_load_percentage of capacity.
716719 fn load(self: *const Self) Size {
717 const max_load = (self.capacity() * MaxLoadPercentage) / 100;
720 const max_load = (self.capacity() * max_load_percentage) / 100;
718721 assert(max_load >= self.available);
719722 return @truncate(Size, max_load - self.available);
720723 }
......@@ -733,7 +736,7 @@ pub fn HashMapUnmanaged(
733736 const new_cap = capacityForSize(self.size);
734737 try other.allocate(allocator, new_cap);
735738 other.initMetadatas();
736 other.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100);
739 other.available = @truncate(u32, (new_cap * max_load_percentage) / 100);
737740
738741 var i: Size = 0;
739742 var metadata = self.metadata.?;
......@@ -751,7 +754,7 @@ pub fn HashMapUnmanaged(
751754 }
752755
753756 fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void {
754 const new_cap = std.math.max(new_capacity, MinimalCapacity);
757 const new_cap = std.math.max(new_capacity, minimal_capacity);
755758 assert(new_cap > self.capacity());
756759 assert(std.math.isPowerOfTwo(new_cap));
757760
......@@ -759,7 +762,7 @@ pub fn HashMapUnmanaged(
759762 defer map.deinit(allocator);
760763 try map.allocate(allocator, new_cap);
761764 map.initMetadatas();
762 map.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100);
765 map.available = @truncate(u32, (new_cap * max_load_percentage) / 100);
763766
764767 if (self.size != 0) {
765768 const old_capacity = self.capacity();
......@@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" {
943946
944947 try map.put(0, 0);
945948 expectEqual(map.count(), 1);
946 expectEqual(map.capacity(), @TypeOf(map).Unmanaged.MinimalCapacity);
949 expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity);
947950
948951 try map.ensureCapacity(65);
949952 expectEqual(map.count(), 1);
src/Compilation.zig+2
......@@ -1653,6 +1653,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
16531653 .error_msg = null,
16541654 .decl = decl,
16551655 .fwd_decl = fwd_decl.toManaged(module.gpa),
1656 // we don't want to emit optionals and error unions to headers since they have no ABI
1657 .typedefs = undefined,
16561658 };
16571659 defer dg.fwd_decl.deinit();
16581660
src/codegen/c.zig+70-8
......@@ -32,6 +32,34 @@ pub const CValue = union(enum) {
3232};
3333
3434pub const CValueMap = std.AutoHashMap(*Inst, CValue);
35pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
36
37fn formatTypeAsCIdentifier(
38 data: Type,
39 comptime fmt: []const u8,
40 options: std.fmt.FormatOptions,
41 writer: anytype,
42) !void {
43 var buffer = [1]u8{0} ** 128;
44 // We don't care if it gets cut off, it's still more unique than a number
45 var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer;
46
47 for (buf) |c, i| {
48 switch (c) {
49 0 => return writer.writeAll(buf[0..i]),
50 'a'...'z', 'A'...'Z', '_', '$' => {},
51 '0'...'9' => if (i == 0) {
52 buf[i] = '_';
53 },
54 else => buf[i] = '_',
55 }
56 }
57 return writer.writeAll(buf);
58}
59
60pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {
61 return .{ .data = t };
62}
3563
3664/// This data is available when outputting .c code for a Module.
3765/// It is not available when generating .h file.
......@@ -115,6 +143,7 @@ pub const DeclGen = struct {
115143 decl: *Decl,
116144 fwd_decl: std.ArrayList(u8),
117145 error_msg: ?*Module.ErrorMsg,
146 typedefs: TypedefMap,
118147
119148 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
120149 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{
......@@ -325,22 +354,55 @@ pub const DeclGen = struct {
325354 const child_type = t.optionalChild(&opt_buf);
326355 if (t.isPtrLikeOptional()) {
327356 return dg.renderType(w, child_type);
357 } else if (dg.typedefs.get(t)) |some| {
358 return w.writeAll(some.name);
328359 }
329360
330 // TODO this needs to be typedeffed since different structs are different types.
331 try w.writeAll("struct { ");
332 try dg.renderType(w, child_type);
333 try w.writeAll(" payload; bool is_null; }");
361 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
362 defer buffer.deinit();
363 const bw = buffer.writer();
364
365 try bw.writeAll("typedef struct { ");
366 try dg.renderType(bw, child_type);
367 try bw.writeAll(" payload; bool is_null; } ");
368 const name_index = buffer.items.len;
369 try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)});
370
371 const rendered = buffer.toOwnedSlice();
372 errdefer dg.typedefs.allocator.free(rendered);
373 const name = rendered[name_index .. rendered.len - 2];
374
375 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
376 try w.writeAll(name);
377 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
334378 },
335379 .ErrorSet => {
336380 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);
337381 try w.writeAll("uint16_t");
338382 },
339383 .ErrorUnion => {
340 // TODO this needs to be typedeffed since different structs are different types.
341 try w.writeAll("struct { ");
342 try dg.renderType(w, t.errorUnionChild());
343 try w.writeAll(" payload; uint16_t error; }");
384 if (dg.typedefs.get(t)) |some| {
385 return w.writeAll(some.name);
386 }
387 const child_type = t.errorUnionChild();
388
389 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
390 defer buffer.deinit();
391 const bw = buffer.writer();
392
393 try bw.writeAll("typedef struct { ");
394 try dg.renderType(bw, t.errorUnionChild());
395 try bw.writeAll(" payload; uint16_t error; } ");
396 const name_index = buffer.items.len;
397 try bw.print("zig_err_union_{s}_t;\n", .{typeToCIdentifier(child_type)});
398
399 const rendered = buffer.toOwnedSlice();
400 errdefer dg.typedefs.allocator.free(rendered);
401 const name = rendered[name_index .. rendered.len - 2];
402
403 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
404 try w.writeAll(name);
405 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
344406 },
345407 .Null, .Undefined => unreachable, // must be const or comptime
346408 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
src/link/C.zig+54-15
......@@ -9,6 +9,7 @@ const codegen = @import("../codegen/c.zig");
99const link = @import("../link.zig");
1010const trace = @import("../tracy.zig").trace;
1111const C = @This();
12const Type = @import("../type.zig").Type;
1213
1314pub const base_tag: link.File.Tag = .c;
1415pub const zig_h = @embedFile("C/zig.h");
......@@ -28,9 +29,11 @@ pub const DeclBlock = struct {
2829/// Per-function data.
2930pub const FnBlock = struct {
3031 fwd_decl: std.ArrayListUnmanaged(u8),
32 typedefs: codegen.TypedefMap.Unmanaged,
3133
3234 pub const empty: FnBlock = .{
3335 .fwd_decl = .{},
36 .typedefs = .{},
3437 };
3538};
3639
......@@ -74,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
7477pub fn freeDecl(self: *C, decl: *Module.Decl) void {
7578 decl.link.c.code.deinit(self.base.allocator);
7679 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
80 var it = decl.fn_link.c.typedefs.iterator();
81 while (it.next()) |some| {
82 self.base.allocator.free(some.value.rendered);
83 }
84 decl.fn_link.c.typedefs.deinit(self.base.allocator);
7785}
7886
7987pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
......@@ -81,8 +89,10 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
8189 defer tracy.end();
8290
8391 const fwd_decl = &decl.fn_link.c.fwd_decl;
92 const typedefs = &decl.fn_link.c.typedefs;
8493 const code = &decl.link.c.code;
8594 fwd_decl.shrinkRetainingCapacity(0);
95 typedefs.clearRetainingCapacity();
8696 code.shrinkRetainingCapacity(0);
8797
8898 var object: codegen.Object = .{
......@@ -91,6 +101,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
91101 .error_msg = null,
92102 .decl = decl,
93103 .fwd_decl = fwd_decl.toManaged(module.gpa),
104 .typedefs = typedefs.promote(module.gpa),
94105 },
95106 .gpa = module.gpa,
96107 .code = code.toManaged(module.gpa),
......@@ -98,9 +109,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
98109 .indent_writer = undefined, // set later so we can get a pointer to object.code
99110 };
100111 object.indent_writer = .{ .underlying_writer = object.code.writer() };
101 defer object.value_map.deinit();
102 defer object.code.deinit();
103 defer object.dg.fwd_decl.deinit();
112 defer {
113 object.value_map.deinit();
114 object.code.deinit();
115 object.dg.fwd_decl.deinit();
116 var it = object.dg.typedefs.iterator();
117 while (it.next()) |some| {
118 module.gpa.free(some.value.rendered);
119 }
120 object.dg.typedefs.deinit();
121 }
104122
105123 codegen.genDecl(&object) catch |err| switch (err) {
106124 error.AnalysisFail => {
......@@ -111,6 +129,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
111129 };
112130
113131 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
132 typedefs.* = object.dg.typedefs.unmanaged;
133 object.dg.typedefs.unmanaged = .{};
114134 code.* = object.code.moveToUnmanaged();
115135
116136 // Free excess allocated memory for this Decl.
......@@ -142,7 +162,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
142162 defer all_buffers.deinit();
143163
144164 // This is at least enough until we get to the function bodies without error handling.
145 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
165 try all_buffers.ensureCapacity(module.decl_table.count() + 2);
146166
147167 var file_size: u64 = zig_h.len;
148168 all_buffers.appendAssumeCapacity(.{
......@@ -150,22 +170,25 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
150170 .iov_len = zig_h.len,
151171 });
152172
153 var error_defs_buf = std.ArrayList(u8).init(comp.gpa);
154 defer error_defs_buf.deinit();
173 var err_typedef_buf = std.ArrayList(u8).init(comp.gpa);
174 defer err_typedef_buf.deinit();
175 const err_typedef_writer = err_typedef_buf.writer();
176 const err_typedef_item = all_buffers.addOneAssumeCapacity();
155177
156 var it = module.global_error_set.iterator();
157 while (it.next()) |entry| {
158 try error_defs_buf.writer().print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
178 render_errors: {
179 if (module.global_error_set.size == 0) break :render_errors;
180 var it = module.global_error_set.iterator();
181 while (it.next()) |entry| {
182 try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
183 }
184 try err_typedef_writer.writeByte('\n');
159185 }
160 try error_defs_buf.writer().writeByte('\n');
161 all_buffers.appendAssumeCapacity(.{
162 .iov_base = error_defs_buf.items.ptr,
163 .iov_len = error_defs_buf.items.len,
164 });
165186
166187 var fn_count: usize = 0;
188 var typedefs = std.HashMap(Type, []const u8, Type.hash, Type.eql, std.hash_map.default_max_load_percentage).init(comp.gpa);
189 defer typedefs.deinit();
167190
168 // Forward decls and non-functions first.
191 // Typedefs, forward decls and non-functions first.
169192 // TODO: performance investigation: would keeping a list of Decls that we should
170193 // generate, rather than querying here, be faster?
171194 for (module.decl_table.items()) |kv| {
......@@ -174,6 +197,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
174197 .most_recent => |tvm| {
175198 const buf = buf: {
176199 if (tvm.typed_value.val.castTag(.function)) |_| {
200 var it = decl.fn_link.c.typedefs.iterator();
201 while (it.next()) |new| {
202 if (typedefs.get(new.key)) |previous| {
203 try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value.name });
204 } else {
205 try typedefs.ensureCapacity(typedefs.capacity() + 1);
206 try err_typedef_writer.writeAll(new.value.rendered);
207 typedefs.putAssumeCapacityNoClobber(new.key, new.value.name);
208 }
209 }
177210 fn_count += 1;
178211 break :buf decl.fn_link.c.fwd_decl.items;
179212 } else {
......@@ -190,6 +223,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
190223 }
191224 }
192225
226 err_typedef_item.* = .{
227 .iov_base = err_typedef_buf.items.ptr,
228 .iov_len = err_typedef_buf.items.len,
229 };
230 file_size += err_typedef_buf.items.len;
231
193232 // Now the function bodies.
194233 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
195234 for (module.decl_table.items()) |kv| {
src/test.zig+1-2
......@@ -868,11 +868,10 @@ pub const TestContext = struct {
868868 std.testing.zig_exe_path,
869869 "run",
870870 "-cflags",
871 "-std=c89",
871 "-std=c99",
872872 "-pedantic",
873873 "-Werror",
874874 "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875
875 "-Wno-declaration-after-statement",
876875 "--",
877876 "-lc",
878877 exe_path,
test/stage2/cbe.zig+12
......@@ -258,6 +258,18 @@ pub fn addCases(ctx: *TestContext) !void {
258258 \\ return count - 5;
259259 \\}
260260 , "");
261
262 // Same with non pointer optionals
263 case.addCompareOutput(
264 \\export fn main() c_int {
265 \\ var count: c_int = 0;
266 \\ var opt_ptr: ?c_int = count;
267 \\ while (opt_ptr) |_| : (count += 1) {
268 \\ if (count == 4) opt_ptr = null;
269 \\ }
270 \\ return count - 5;
271 \\}
272 , "");
261273 }
262274 ctx.c("empty start function", linux_x64,
263275 \\export fn _start() noreturn {