authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-18 19:41:45-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 19:31:50-04:00
log018daa028e2705a6a5d7621cccd277a490f17646
tree07ce402383481308045308d69e1cb7ba1e43ab23
parentf92ccf365be1e4dad74f6ac08503832ed2d7f805

rework types and values data layout


3 files changed, 201 insertions(+), 1088 deletions(-)

src-self-hosted/ir.zig+41-6
......@@ -2,6 +2,7 @@ const std = @import("std");
22const mem = std.mem;
33const Allocator = std.mem.Allocator;
44const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;
56const assert = std.debug.assert;
67
78pub const Inst = struct {
......@@ -17,6 +18,7 @@ pub const Inst = struct {
1718 Fn,
1819 };
1920
21 /// These names are used for the IR text format.
2022 pub const Tag = enum {
2123 constant,
2224 ptrtoint,
......@@ -32,9 +34,11 @@ pub const Inst = struct {
3234 /// a memory location for the value to survive after a const instruction.
3335 pub const Constant = struct {
3436 base: Inst = Inst{ .tag = .constant },
35 value: *Value,
37 ty: Type,
3638
37 positionals: struct {},
39 positionals: struct {
40 value: Value,
41 },
3842 kw_args: struct {},
3943 };
4044
......@@ -241,6 +245,31 @@ fn parseInstructionGeneric(ctx: *ParseContext, comptime fn_name: []const u8, com
241245}
242246
243247fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T {
248 if (@typeInfo(T) == .Enum) {
249 const start = ctx.i;
250 while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
251 ' ', '\n', ',', ')' => {
252 const enum_name = ctx.source[start..ctx.i];
253 ctx.i += 1;
254 return std.meta.stringToEnum(T, enum_name) orelse {
255 return parseError(ctx, "tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
256 };
257 },
258 else => continue,
259 };
260 return parseError(ctx, "unexpected EOF in enum parameter", .{});
261 }
262 switch (T) {
263 Inst.Fn.Body => {
264 var instructions = std.ArrayList(*Inst).init(ctx.allocator);
265 try requireEatBytes(ctx, "{");
266 return T{
267 .instructions = instructions.toOwnedSlice(),
268 };
269 },
270 Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),
271 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
272 }
244273 return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)});
245274}
246275
......@@ -261,12 +290,18 @@ fn parseStringLiteralConst(ctx: *ParseContext) !*Inst {
261290 },
262291 else => |e| return e,
263292 };
264 const bytes_val = try ctx.allocator.create(Value.Bytes);
265 bytes_val.* = .{ .data = parsed };
293 const bytes_payload = try ctx.allocator.create(Value.Payload.Bytes);
294 errdefer ctx.allocator.destroy(bytes_payload);
295 bytes_payload.* = .{ .data = parsed };
296
297 const ty_payload = try ctx.allocator.create(Type.Payload.Array_u8_Sentinel0);
298 errdefer ctx.allocator.destroy(ty_payload);
299 ty_payload.* = .{ .len = parsed.len };
300
266301 const const_inst = try ctx.allocator.create(Inst.Constant);
267302 const_inst.* = .{
268 .value = &bytes_val.base,
269 .positionals = .{},
303 .ty = Type.initPayload(&ty_payload.base),
304 .positionals = .{ .value = Value.initPayload(&bytes_payload.base) },
270305 .kw_args = .{},
271306 };
272307 return &const_inst.base;
src-self-hosted/type.zig+85-1048
......@@ -1,1075 +1,112 @@
11const std = @import("std");
2const builtin = std.builtin;
3const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;
52const Value = @import("value.zig").Value;
6const llvm = @import("llvm.zig");
7const event = std.event;
8const Allocator = std.mem.Allocator;
93const assert = std.debug.assert;
104
11pub const Type = struct {
12 base: Value,
13 id: Id,
14 name: []const u8,
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
18
19 pub const Id = builtin.TypeId;
20
21 pub fn destroy(base: *Type, comp: *Compilation) void {
22 switch (base.id) {
23 .Struct => @fieldParentPtr(Struct, "base", base).destroy(comp),
24 .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
25 .Type => @fieldParentPtr(MetaType, "base", base).destroy(comp),
26 .Void => @fieldParentPtr(Void, "base", base).destroy(comp),
27 .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
28 .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
29 .Int => @fieldParentPtr(Int, "base", base).destroy(comp),
30 .Float => @fieldParentPtr(Float, "base", base).destroy(comp),
31 .Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp),
32 .Array => @fieldParentPtr(Array, "base", base).destroy(comp),
33 .ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp),
34 .ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp),
35 .EnumLiteral => @fieldParentPtr(EnumLiteral, "base", base).destroy(comp),
36 .Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp),
37 .Null => @fieldParentPtr(Null, "base", base).destroy(comp),
38 .Optional => @fieldParentPtr(Optional, "base", base).destroy(comp),
39 .ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp),
40 .ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp),
41 .Enum => @fieldParentPtr(Enum, "base", base).destroy(comp),
42 .Union => @fieldParentPtr(Union, "base", base).destroy(comp),
43 .BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp),
44 .Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
45 .Frame => @fieldParentPtr(Frame, "base", base).destroy(comp),
46 .AnyFrame => @fieldParentPtr(AnyFrame, "base", base).destroy(comp),
47 .Vector => @fieldParentPtr(Vector, "base", base).destroy(comp),
5/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
6/// It's important for this struct to be small.
7/// It is not copyable since it may contain references to its inner data.
8/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
9/// of obtaining a lock on a global type table, as well as making the
10/// garbage collection bookkeeping simpler.
11/// This union takes advantage of the fact that the first page of memory
12/// is unmapped, giving us 4096 possible enum tags that have no payload.
13pub const Type = extern union {
14 /// If the tag value is less than Tag.no_payload_count, then no pointer
15 /// dereference is needed.
16 tag_if_small_enough: usize,
17 ptr_otherwise: *Payload,
18
19 pub fn zigTypeTag(self: Type) std.builtin.TypeId {
20 switch (self.tag()) {
21 .int_u8, .int_usize => return .Int,
22 .array_u8, .array_u8_sentinel_0 => return .Array,
23 .single_const_pointer => return .Pointer,
4824 }
4925 }
5026
51 pub fn getLlvmType(
52 base: *Type,
53 allocator: *Allocator,
54 llvm_context: *llvm.Context,
55 ) error{OutOfMemory}!*llvm.Type {
56 switch (base.id) {
57 .Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
58 .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
59 .Type => unreachable,
60 .Void => unreachable,
61 .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
62 .NoReturn => unreachable,
63 .Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
64 .Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
65 .Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
66 .Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
67 .ComptimeFloat => unreachable,
68 .ComptimeInt => unreachable,
69 .EnumLiteral => unreachable,
70 .Undefined => unreachable,
71 .Null => unreachable,
72 .Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
73 .ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
74 .ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
75 .Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
76 .Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
77 .BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
78 .Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
79 .Frame => return @fieldParentPtr(Frame, "base", base).getLlvmType(allocator, llvm_context),
80 .AnyFrame => return @fieldParentPtr(AnyFrame, "base", base).getLlvmType(allocator, llvm_context),
81 .Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context),
82 }
27 pub fn initTag(comptime tag: Tag) Type {
28 comptime assert(@enumToInt(tag) < Tag.no_payload_count);
29 return .{ .tag_if_small_enough = @enumToInt(tag) };
8330 }
8431
85 pub fn handleIsPtr(base: *Type) bool {
86 switch (base.id) {
87 .Type,
88 .ComptimeFloat,
89 .ComptimeInt,
90 .EnumLiteral,
91 .Undefined,
92 .Null,
93 .BoundFn,
94 .Opaque,
95 => unreachable,
96
97 .NoReturn,
98 .Void,
99 .Bool,
100 .Int,
101 .Float,
102 .Pointer,
103 .ErrorSet,
104 .Enum,
105 .Fn,
106 .Frame,
107 .AnyFrame,
108 .Vector,
109 => return false,
110
111 .Struct => @panic("TODO"),
112 .Array => @panic("TODO"),
113 .Optional => @panic("TODO"),
114 .ErrorUnion => @panic("TODO"),
115 .Union => @panic("TODO"),
116 }
32 pub fn initPayload(payload: *Payload) Type {
33 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
34 return .{ .ptr_otherwise = payload };
11735 }
11836
119 pub fn hasBits(base: *Type) bool {
120 switch (base.id) {
121 .Type,
122 .ComptimeFloat,
123 .ComptimeInt,
124 .EnumLiteral,
125 .Undefined,
126 .Null,
127 .BoundFn,
128 .Opaque,
129 => unreachable,
130
131 .Void,
132 .NoReturn,
133 => return false,
134
135 .Bool,
136 .Int,
137 .Float,
138 .Fn,
139 .Frame,
140 .AnyFrame,
141 .Vector,
142 => return true,
143
144 .Pointer => {
145 const ptr_type = @fieldParentPtr(Pointer, "base", base);
146 return ptr_type.key.child_type.hasBits();
147 },
148
149 .ErrorSet => @panic("TODO"),
150 .Enum => @panic("TODO"),
151 .Struct => @panic("TODO"),
152 .Array => @panic("TODO"),
153 .Optional => @panic("TODO"),
154 .ErrorUnion => @panic("TODO"),
155 .Union => @panic("TODO"),
37 pub fn tag(self: Type) Tag {
38 if (self.tag_if_small_enough < Tag.no_payload_count) {
39 return @intToEnum(self.tag_if_small_enough);
40 } else {
41 return self.ptr_otherwise.tag;
15642 }
15743 }
15844
159 pub fn cast(base: *Type, comptime T: type) ?*T {
160 if (base.id != @field(Id, @typeName(T))) return null;
161 return @fieldParentPtr(T, "base", base);
162 }
163
164 pub fn dump(base: *const Type) void {
165 std.debug.warn("{}", .{@tagName(base.id)});
166 }
167
168 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
169 base.* = Type{
170 .base = Value{
171 .id = .Type,
172 .typ = &MetaType.get(comp).base,
173 .ref_count = std.atomic.Int(usize).init(1),
45 pub fn format(
46 self: Type,
47 comptime fmt: []const u8,
48 options: std.fmt.FormatOptions,
49 out_stream: var,
50 ) !void {
51 comptime assert(fmt.len == 0);
52 switch (self.tag()) {
53 .int_u8 => return out_stream.writeAll("u8"),
54 .int_usize => return out_stream.writeAll("usize"),
55 .array_u8_sentinel_0 => {
56 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise);
57 return out_stream.print("[{}:0]u8", .{payload.len});
58 },
59 .array => {
60 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
61 return out_stream.print("[{}]{}", .{ payload.len, payload.elem_type });
62 },
63 .single_const_pointer => {
64 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
65 return out_stream.print("*const {}", .{payload.pointee_type});
17466 },
175 .id = id,
176 .name = name,
177 .abi_alignment = AbiAlignment.init(),
178 };
179 }
180
181 /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
182 /// Otherwise, this one will grab one from the pool and then release it.
183 pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
184 if (base.abi_alignment.start()) |ptr| return ptr.*;
185
186 {
187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.zig_compiler);
189
190 const llvm_context = held.node.data;
191
192 base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
19367 }
194 base.abi_alignment.resolve();
195 return base.abi_alignment.data;
19668 }
19769
198 /// If you have an llvm conext handy, you can use it here.
199 pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
200 if (base.abi_alignment.start()) |ptr| return ptr.*;
201
202 base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
203 base.abi_alignment.resolve();
204 return base.abi_alignment.data;
205 }
206
207 /// Lower level function that does the work. See getAbiAlignment.
208 fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
209 const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
210 return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
211 }
212
213 pub const Struct = struct {
214 base: Type,
215 decls: *Scope.Decls,
216
217 pub fn destroy(self: *Struct, comp: *Compilation) void {
218 comp.gpa().destroy(self);
219 }
220
221 pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
222 @panic("TODO");
223 }
224 };
225
226 pub const Fn = struct {
227 base: Type,
228 key: Key,
229 non_key: NonKey,
230 garbage_node: std.atomic.Stack(*Fn).Node,
231
232 pub const Kind = enum {
233 Normal,
234 Generic,
235 };
236
237 pub const NonKey = union {
238 Normal: Normal,
239 Generic: void,
240
241 pub const Normal = struct {
242 variable_list: std.ArrayList(*Scope.Var),
243 };
244 };
245
246 pub const Key = struct {
247 data: Data,
248 alignment: ?u32,
249
250 pub const Data = union(Kind) {
251 Generic: Generic,
252 Normal: Normal,
253 };
254
255 pub const Normal = struct {
256 params: []Param,
257 return_type: *Type,
258 is_var_args: bool,
259 cc: CallingConvention,
260 };
261
262 pub const Generic = struct {
263 param_count: usize,
264 cc: CallingConvention,
265 };
266
267 pub fn hash(self: *const Key) u32 {
268 var result: u32 = 0;
269 result +%= hashAny(self.alignment, 0);
270 switch (self.data) {
271 .Generic => |generic| {
272 result +%= hashAny(generic.param_count, 1);
273 result +%= hashAny(generic.cc, 3);
274 },
275 .Normal => |normal| {
276 result +%= hashAny(normal.return_type, 4);
277 result +%= hashAny(normal.is_var_args, 5);
278 result +%= hashAny(normal.cc, 6);
279 for (normal.params) |param| {
280 result +%= hashAny(param.is_noalias, 7);
281 result +%= hashAny(param.typ, 8);
282 }
283 },
284 }
285 return result;
286 }
287
288 pub fn eql(self: *const Key, other: *const Key) bool {
289 if ((self.alignment == null) != (other.alignment == null)) return false;
290 if (self.alignment) |self_align| {
291 if (self_align != other.alignment.?) return false;
292 }
293 if (@as(@TagType(Data), self.data) != @as(@TagType(Data), other.data)) return false;
294 switch (self.data) {
295 .Generic => |*self_generic| {
296 const other_generic = &other.data.Generic;
297 if (self_generic.param_count != other_generic.param_count) return false;
298 if (self_generic.cc != other_generic.cc) return false;
299 },
300 .Normal => |*self_normal| {
301 const other_normal = &other.data.Normal;
302 if (self_normal.cc != other_normal.cc) return false;
303 if (self_normal.is_var_args != other_normal.is_var_args) return false;
304 if (self_normal.return_type != other_normal.return_type) return false;
305 for (self_normal.params) |*self_param, i| {
306 const other_param = &other_normal.params[i];
307 if (self_param.is_noalias != other_param.is_noalias) return false;
308 if (self_param.typ != other_param.typ) return false;
309 }
310 },
311 }
312 return true;
313 }
314
315 pub fn deref(key: Key, comp: *Compilation) void {
316 switch (key.data) {
317 .Generic => {},
318 .Normal => |normal| {
319 normal.return_type.base.deref(comp);
320 for (normal.params) |param| {
321 param.typ.base.deref(comp);
322 }
323 },
324 }
325 }
326
327 pub fn ref(key: Key) void {
328 switch (key.data) {
329 .Generic => {},
330 .Normal => |normal| {
331 normal.return_type.base.ref();
332 for (normal.params) |param| {
333 param.typ.base.ref();
334 }
335 },
336 }
337 }
338 };
339
340 const CallingConvention = builtin.CallingConvention;
341
342 pub const Param = struct {
343 is_noalias: bool,
344 typ: *Type,
345 };
346
347 fn ccFnTypeStr(cc: CallingConvention) []const u8 {
348 return switch (cc) {
349 .Unspecified => "",
350 .C => "extern ",
351 .Cold => "coldcc ",
352 .Naked => "nakedcc ",
353 .Stdcall => "stdcallcc ",
354 .Async => "async ",
355 else => unreachable,
356 };
357 }
358
359 pub fn paramCount(self: *Fn) usize {
360 return switch (self.key.data) {
361 .Generic => |generic| generic.param_count,
362 .Normal => |normal| normal.params.len,
363 };
364 }
365
366 /// takes ownership of key.Normal.params on success
367 pub fn get(comp: *Compilation, key: Key) !*Fn {
368 {
369 const held = comp.fn_type_table.acquire();
370 defer held.release();
371
372 if (held.value.get(&key)) |entry| {
373 entry.value.base.base.ref();
374 return entry.value;
375 }
376 }
377
378 key.ref();
379 errdefer key.deref(comp);
380
381 const self = try comp.gpa().create(Fn);
382 self.* = Fn{
383 .base = undefined,
384 .key = key,
385 .non_key = undefined,
386 .garbage_node = undefined,
387 };
388 errdefer comp.gpa().destroy(self);
389
390 var name_buf = std.ArrayList(u8).init(comp.gpa());
391 defer name_buf.deinit();
392
393 const name_stream = name_buf.outStream();
394
395 switch (key.data) {
396 .Generic => |generic| {
397 self.non_key = NonKey{ .Generic = {} };
398 const cc_str = ccFnTypeStr(generic.cc);
399 try name_stream.print("{}fn(", .{cc_str});
400 var param_i: usize = 0;
401 while (param_i < generic.param_count) : (param_i += 1) {
402 const arg = if (param_i == 0) "var" else ", var";
403 try name_stream.write(arg);
404 }
405 try name_stream.write(")");
406 if (key.alignment) |alignment| {
407 try name_stream.print(" align({})", .{alignment});
408 }
409 try name_stream.write(" var");
410 },
411 .Normal => |normal| {
412 self.non_key = NonKey{
413 .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) },
414 };
415 const cc_str = ccFnTypeStr(normal.cc);
416 try name_stream.print("{}fn(", .{cc_str});
417 for (normal.params) |param, i| {
418 if (i != 0) try name_stream.write(", ");
419 if (param.is_noalias) try name_stream.write("noalias ");
420 try name_stream.write(param.typ.name);
421 }
422 if (normal.is_var_args) {
423 if (normal.params.len != 0) try name_stream.write(", ");
424 try name_stream.write("...");
425 }
426 try name_stream.write(")");
427 if (key.alignment) |alignment| {
428 try name_stream.print(" align({})", .{alignment});
429 }
430 try name_stream.print(" {}", .{normal.return_type.name});
431 },
432 }
433
434 self.base.init(comp, .Fn, name_buf.toOwnedSlice());
435
436 {
437 const held = comp.fn_type_table.acquire();
438 defer held.release();
439
440 _ = try held.value.put(&self.key, self);
441 }
442 return self;
443 }
444
445 pub fn destroy(self: *Fn, comp: *Compilation) void {
446 self.key.deref(comp);
447 switch (self.key.data) {
448 .Generic => {},
449 .Normal => {
450 self.non_key.Normal.variable_list.deinit();
451 },
452 }
453 comp.gpa().destroy(self);
454 }
455
456 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
457 const normal = &self.key.data.Normal;
458 const llvm_return_type = switch (normal.return_type.id) {
459 .Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
460 else => try normal.return_type.getLlvmType(allocator, llvm_context),
461 };
462 const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len);
463 defer allocator.free(llvm_param_types);
464 for (llvm_param_types) |*llvm_param_type, i| {
465 llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
466 }
467
468 return llvm.FunctionType(
469 llvm_return_type,
470 llvm_param_types.ptr,
471 @intCast(c_uint, llvm_param_types.len),
472 @boolToInt(normal.is_var_args),
473 ) orelse error.OutOfMemory;
474 }
475 };
476
477 pub const MetaType = struct {
478 base: Type,
479 value: *Type,
480
481 /// Adds 1 reference to the resulting type
482 pub fn get(comp: *Compilation) *MetaType {
483 comp.meta_type.base.base.ref();
484 return comp.meta_type;
485 }
486
487 pub fn destroy(self: *MetaType, comp: *Compilation) void {
488 comp.gpa().destroy(self);
489 }
490 };
491
492 pub const Void = struct {
493 base: Type,
494
495 /// Adds 1 reference to the resulting type
496 pub fn get(comp: *Compilation) *Void {
497 comp.void_type.base.base.ref();
498 return comp.void_type;
499 }
500
501 pub fn destroy(self: *Void, comp: *Compilation) void {
502 comp.gpa().destroy(self);
503 }
504 };
505
506 pub const Bool = struct {
507 base: Type,
508
509 /// Adds 1 reference to the resulting type
510 pub fn get(comp: *Compilation) *Bool {
511 comp.bool_type.base.base.ref();
512 return comp.bool_type;
513 }
514
515 pub fn destroy(self: *Bool, comp: *Compilation) void {
516 comp.gpa().destroy(self);
517 }
518
519 pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
520 @panic("TODO");
521 }
522 };
523
524 pub const NoReturn = struct {
525 base: Type,
526
527 /// Adds 1 reference to the resulting type
528 pub fn get(comp: *Compilation) *NoReturn {
529 comp.noreturn_type.base.base.ref();
530 return comp.noreturn_type;
531 }
532
533 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
534 comp.gpa().destroy(self);
535 }
536 };
537
538 pub const Int = struct {
539 base: Type,
540 key: Key,
541 garbage_node: std.atomic.Stack(*Int).Node,
542
543 pub const Key = struct {
544 bit_count: u32,
545 is_signed: bool,
546
547 pub fn hash(self: *const Key) u32 {
548 var result: u32 = 0;
549 result +%= hashAny(self.is_signed, 0);
550 result +%= hashAny(self.bit_count, 1);
551 return result;
552 }
553
554 pub fn eql(self: *const Key, other: *const Key) bool {
555 return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
556 }
557 };
558
559 pub fn get_u8(comp: *Compilation) *Int {
560 comp.u8_type.base.base.ref();
561 return comp.u8_type;
562 }
563
564 pub fn get(comp: *Compilation, key: Key) !*Int {
565 {
566 const held = comp.int_type_table.acquire();
567 defer held.release();
568
569 if (held.value.get(&key)) |entry| {
570 entry.value.base.base.ref();
571 return entry.value;
572 }
573 }
574
575 const self = try comp.gpa().create(Int);
576 self.* = Int{
577 .base = undefined,
578 .key = key,
579 .garbage_node = undefined,
580 };
581 errdefer comp.gpa().destroy(self);
582
583 const u_or_i = "ui"[@boolToInt(key.is_signed)];
584 const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count });
585 errdefer comp.gpa().free(name);
586
587 self.base.init(comp, .Int, name);
588
589 {
590 const held = comp.int_type_table.acquire();
591 defer held.release();
592
593 _ = try held.value.put(&self.key, self);
594 }
595 return self;
596 }
597
598 pub fn destroy(self: *Int, comp: *Compilation) void {
599 self.garbage_node = std.atomic.Stack(*Int).Node{
600 .data = self,
601 .next = undefined,
602 };
603 comp.registerGarbage(Int, &self.garbage_node);
604 }
605
606 pub fn gcDestroy(self: *Int, comp: *Compilation) void {
607 {
608 const held = comp.int_type_table.acquire();
609 defer held.release();
610
611 _ = held.value.remove(&self.key).?;
612 }
613 // we allocated the name
614 comp.gpa().free(self.base.name);
615 comp.gpa().destroy(self);
616 }
617
618 pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
619 return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
620 }
621 };
622
623 pub const Float = struct {
624 base: Type,
625
626 pub fn destroy(self: *Float, comp: *Compilation) void {
627 comp.gpa().destroy(self);
628 }
629
630 pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
631 @panic("TODO");
632 }
633 };
634 pub const Pointer = struct {
635 base: Type,
636 key: Key,
637 garbage_node: std.atomic.Stack(*Pointer).Node,
638
639 pub const Key = struct {
640 child_type: *Type,
641 mut: Mut,
642 vol: Vol,
643 size: Size,
644 alignment: Align,
645
646 pub fn hash(self: *const Key) u32 {
647 var result: u32 = 0;
648 result +%= switch (self.alignment) {
649 .Abi => 0xf201c090,
650 .Override => |x| hashAny(x, 0),
651 };
652 result +%= hashAny(self.child_type, 1);
653 result +%= hashAny(self.mut, 2);
654 result +%= hashAny(self.vol, 3);
655 result +%= hashAny(self.size, 4);
656 return result;
657 }
658
659 pub fn eql(self: *const Key, other: *const Key) bool {
660 if (self.child_type != other.child_type or
661 self.mut != other.mut or
662 self.vol != other.vol or
663 self.size != other.size or
664 @as(@TagType(Align), self.alignment) != @as(@TagType(Align), other.alignment))
665 {
666 return false;
667 }
668 switch (self.alignment) {
669 .Abi => return true,
670 .Override => |x| return x == other.alignment.Override,
671 }
672 }
70 /// This enum does not directly correspond to `std.builtin.TypeId` because
71 /// it has extra enum tags in it, as a way of using less memory. For example,
72 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
73 /// but with different alignment values, in this data structure they are represented
74 /// with different enum tags, because the the former requires more payload data than the latter.
75 /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
76 pub const Tag = enum {
77 // The first section of this enum are tags that require no payload.
78 int_u8,
79 int_usize,
80 // Bump this when adding items above.
81 pub const last_no_payload_tag = Tag.int_usize;
82 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
83 // After this, the tag requires a payload.
84
85 array_u8_sentinel_0,
86 array,
87 single_const_pointer,
88 };
89
90 pub const Payload = struct {
91 tag: Tag,
92
93 pub const Array_u8_Sentinel0 = struct {
94 base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
95
96 len: u64,
67397 };
67498
675 pub const Mut = enum {
676 Mut,
677 Const,
678 };
679
680 pub const Vol = enum {
681 Non,
682 Volatile,
683 };
99 pub const Array = struct {
100 base: Payload = Payload{ .tag = .array },
684101
685 pub const Align = union(enum) {
686 Abi,
687 Override: u32,
102 elem_type: Type,
103 len: u64,
688104 };
689105
690 pub const Size = builtin.TypeInfo.Pointer.Size;
691
692 pub fn destroy(self: *Pointer, comp: *Compilation) void {
693 self.garbage_node = std.atomic.Stack(*Pointer).Node{
694 .data = self,
695 .next = undefined,
696 };
697 comp.registerGarbage(Pointer, &self.garbage_node);
698 }
699
700 pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {
701 {
702 const held = comp.ptr_type_table.acquire();
703 defer held.release();
704
705 _ = held.value.remove(&self.key).?;
706 }
707 self.key.child_type.base.deref(comp);
708 comp.gpa().destroy(self);
709 }
710
711 pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
712 switch (self.key.alignment) {
713 .Abi => return self.key.child_type.getAbiAlignment(comp),
714 .Override => |alignment| return alignment,
715 }
716 }
717
718 pub fn get(
719 comp: *Compilation,
720 key: Key,
721 ) !*Pointer {
722 var normal_key = key;
723 switch (key.alignment) {
724 .Abi => {},
725 .Override => |alignment| {
726 // TODO https://github.com/ziglang/zig/issues/3190
727 var align_spill = alignment;
728 const abi_align = try key.child_type.getAbiAlignment(comp);
729 if (abi_align == align_spill) {
730 normal_key.alignment = .Abi;
731 }
732 },
733 }
734 {
735 const held = comp.ptr_type_table.acquire();
736 defer held.release();
737
738 if (held.value.get(&normal_key)) |entry| {
739 entry.value.base.base.ref();
740 return entry.value;
741 }
742 }
106 pub const SingleConstPointer = struct {
107 base: Payload = Payload{ .tag = .single_const_pointer },
743108
744 const self = try comp.gpa().create(Pointer);
745 self.* = Pointer{
746 .base = undefined,
747 .key = normal_key,
748 .garbage_node = undefined,
749 };
750 errdefer comp.gpa().destroy(self);
751
752 const size_str = switch (self.key.size) {
753 .One => "*",
754 .Many => "[*]",
755 .Slice => "[]",
756 .C => "[*c]",
757 };
758 const mut_str = switch (self.key.mut) {
759 .Const => "const ",
760 .Mut => "",
761 };
762 const vol_str = switch (self.key.vol) {
763 .Volatile => "volatile ",
764 .Non => "",
765 };
766 const name = switch (self.key.alignment) {
767 .Abi => try std.fmt.allocPrint(comp.gpa(), "{}{}{}{}", .{
768 size_str,
769 mut_str,
770 vol_str,
771 self.key.child_type.name,
772 }),
773 .Override => |alignment| try std.fmt.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{
774 size_str,
775 alignment,
776 mut_str,
777 vol_str,
778 self.key.child_type.name,
779 }),
780 };
781 errdefer comp.gpa().free(name);
782
783 self.base.init(comp, .Pointer, name);
784
785 {
786 const held = comp.ptr_type_table.acquire();
787 defer held.release();
788
789 _ = try held.value.put(&self.key, self);
790 }
791 return self;
792 }
793
794 pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
795 const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
796 return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
797 }
798 };
799
800 pub const Array = struct {
801 base: Type,
802 key: Key,
803 garbage_node: std.atomic.Stack(*Array).Node,
804
805 pub const Key = struct {
806 elem_type: *Type,
807 len: usize,
808
809 pub fn hash(self: *const Key) u32 {
810 var result: u32 = 0;
811 result +%= hashAny(self.elem_type, 0);
812 result +%= hashAny(self.len, 1);
813 return result;
814 }
815
816 pub fn eql(self: *const Key, other: *const Key) bool {
817 return self.elem_type == other.elem_type and self.len == other.len;
818 }
109 pointee_type: Type,
819110 };
820
821 pub fn destroy(self: *Array, comp: *Compilation) void {
822 self.key.elem_type.base.deref(comp);
823 comp.gpa().destroy(self);
824 }
825
826 pub fn get(comp: *Compilation, key: Key) !*Array {
827 key.elem_type.base.ref();
828 errdefer key.elem_type.base.deref(comp);
829
830 {
831 const held = comp.array_type_table.acquire();
832 defer held.release();
833
834 if (held.value.get(&key)) |entry| {
835 entry.value.base.base.ref();
836 return entry.value;
837 }
838 }
839
840 const self = try comp.gpa().create(Array);
841 self.* = Array{
842 .base = undefined,
843 .key = key,
844 .garbage_node = undefined,
845 };
846 errdefer comp.gpa().destroy(self);
847
848 const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name });
849 errdefer comp.gpa().free(name);
850
851 self.base.init(comp, .Array, name);
852
853 {
854 const held = comp.array_type_table.acquire();
855 defer held.release();
856
857 _ = try held.value.put(&self.key, self);
858 }
859 return self;
860 }
861
862 pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
863 const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
864 return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
865 }
866 };
867
868 pub const Vector = struct {
869 base: Type,
870
871 pub fn destroy(self: *Vector, comp: *Compilation) void {
872 comp.gpa().destroy(self);
873 }
874
875 pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
876 @panic("TODO");
877 }
878 };
879
880 pub const ComptimeFloat = struct {
881 base: Type,
882
883 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
884 comp.gpa().destroy(self);
885 }
886 };
887
888 pub const ComptimeInt = struct {
889 base: Type,
890
891 /// Adds 1 reference to the resulting type
892 pub fn get(comp: *Compilation) *ComptimeInt {
893 comp.comptime_int_type.base.base.ref();
894 return comp.comptime_int_type;
895 }
896
897 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
898 comp.gpa().destroy(self);
899 }
900 };
901
902 pub const EnumLiteral = struct {
903 base: Type,
904
905 /// Adds 1 reference to the resulting type
906 pub fn get(comp: *Compilation) *EnumLiteral {
907 comp.comptime_int_type.base.base.ref();
908 return comp.comptime_int_type;
909 }
910
911 pub fn destroy(self: *EnumLiteral, comp: *Compilation) void {
912 comp.gpa().destroy(self);
913 }
914 };
915
916 pub const Undefined = struct {
917 base: Type,
918
919 pub fn destroy(self: *Undefined, comp: *Compilation) void {
920 comp.gpa().destroy(self);
921 }
922 };
923
924 pub const Null = struct {
925 base: Type,
926
927 pub fn destroy(self: *Null, comp: *Compilation) void {
928 comp.gpa().destroy(self);
929 }
930 };
931
932 pub const Optional = struct {
933 base: Type,
934
935 pub fn destroy(self: *Optional, comp: *Compilation) void {
936 comp.gpa().destroy(self);
937 }
938
939 pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
940 @panic("TODO");
941 }
942 };
943
944 pub const ErrorUnion = struct {
945 base: Type,
946
947 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
948 comp.gpa().destroy(self);
949 }
950
951 pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
952 @panic("TODO");
953 }
954 };
955
956 pub const ErrorSet = struct {
957 base: Type,
958
959 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
960 comp.gpa().destroy(self);
961 }
962
963 pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
964 @panic("TODO");
965 }
966 };
967
968 pub const Enum = struct {
969 base: Type,
970
971 pub fn destroy(self: *Enum, comp: *Compilation) void {
972 comp.gpa().destroy(self);
973 }
974
975 pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
976 @panic("TODO");
977 }
978 };
979
980 pub const Union = struct {
981 base: Type,
982
983 pub fn destroy(self: *Union, comp: *Compilation) void {
984 comp.gpa().destroy(self);
985 }
986
987 pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
988 @panic("TODO");
989 }
990 };
991
992 pub const BoundFn = struct {
993 base: Type,
994
995 pub fn destroy(self: *BoundFn, comp: *Compilation) void {
996 comp.gpa().destroy(self);
997 }
998
999 pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1000 @panic("TODO");
1001 }
1002 };
1003
1004 pub const Opaque = struct {
1005 base: Type,
1006
1007 pub fn destroy(self: *Opaque, comp: *Compilation) void {
1008 comp.gpa().destroy(self);
1009 }
1010
1011 pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1012 @panic("TODO");
1013 }
1014 };
1015
1016 pub const Frame = struct {
1017 base: Type,
1018
1019 pub fn destroy(self: *Frame, comp: *Compilation) void {
1020 comp.gpa().destroy(self);
1021 }
1022
1023 pub fn getLlvmType(self: *Frame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1024 @panic("TODO");
1025 }
1026 };
1027
1028 pub const AnyFrame = struct {
1029 base: Type,
1030
1031 pub fn destroy(self: *AnyFrame, comp: *Compilation) void {
1032 comp.gpa().destroy(self);
1033 }
1034
1035 pub fn getLlvmType(self: *AnyFrame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1036 @panic("TODO");
1037 }
1038111 };
1039112};
1040
1041fn hashAny(x: var, comptime seed: u64) u32 {
1042 switch (@typeInfo(@TypeOf(x))) {
1043 .Int => |info| {
1044 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1045 const unsigned_x = @bitCast(std.meta.IntType(false, info.bits), x);
1046 if (info.bits <= 32) {
1047 return @as(u32, unsigned_x) *% comptime rng.random.scalar(u32);
1048 } else {
1049 return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@TypeOf(unsigned_x)));
1050 }
1051 },
1052 .Pointer => |info| {
1053 switch (info.size) {
1054 .One => return hashAny(@ptrToInt(x), seed),
1055 .Many => @compileError("implement hash function"),
1056 .Slice => @compileError("implement hash function"),
1057 .C => unreachable,
1058 }
1059 },
1060 .Enum => return hashAny(@enumToInt(x), seed),
1061 .Bool => {
1062 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1063 const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
1064 return vals[@boolToInt(x)];
1065 },
1066 .Optional => {
1067 if (x) |non_opt| {
1068 return hashAny(non_opt, seed);
1069 } else {
1070 return hashAny(@as(u32, 1), seed);
1071 }
1072 },
1073 else => @compileError("implement hash function for " ++ @typeName(@TypeOf(x))),
1074 }
1075}
src-self-hosted/value.zig+75-34
......@@ -1,26 +1,35 @@
11const std = @import("std");
2const Type = @import("type.zig").Type;
3const log2 = std.math.log2;
4const assert = std.debug.assert;
25
36/// This is the raw data, with no bookkeeping, no memory awareness,
47/// no de-duplication, and no type system awareness.
58/// It's important for this struct to be small.
6/// It is not copyable since it may contain references to its inner data.
7pub const Value = struct {
8 tag: Tag,
9/// This union takes advantage of the fact that the first page of memory
10/// is unmapped, giving us 4096 possible enum tags that have no payload.
11pub const Value = extern union {
12 /// If the tag value is less than Tag.no_payload_count, then no pointer
13 /// dereference is needed.
14 tag_if_small_enough: usize,
15 ptr_otherwise: *Payload,
916
1017 pub const Tag = enum {
18 // The first section of this enum are tags that require no payload.
1119 void_type,
1220 noreturn_type,
1321 bool_type,
1422 usize_type,
15
1623 void_value,
1724 noreturn_value,
1825 bool_true,
1926 bool_false,
27 // Bump this when adding items above.
28 pub const last_no_payload_tag = Tag.bool_false;
29 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
30 // After this, the tag requires a payload.
2031
21 array_sentinel_0_u8_type,
22 single_const_ptr_type,
23
32 ty,
2433 int_u64,
2534 int_i64,
2635 function,
......@@ -28,44 +37,76 @@ pub const Value = struct {
2837 bytes,
2938 };
3039
31 pub const Int_u64 = struct {
32 base: Value = Value{ .tag = .int_u64 },
33 int: u64,
34 };
40 pub fn initTag(comptime tag: Tag) Value {
41 comptime assert(@enumToInt(tag) < Tag.no_payload_count);
42 return .{ .tag_if_small_enough = @enumToInt(tag) };
43 }
3544
36 pub const Int_i64 = struct {
37 base: Value = Value{ .tag = .int_i64 },
38 int: i64,
39 };
45 pub fn initPayload(payload: *Payload) Value {
46 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
47 return .{ .ptr_otherwise = payload };
48 }
4049
41 pub const Function = struct {
42 base: Value = Value{ .tag = .function },
43 };
50 pub fn tag(self: Value) Tag {
51 if (self.tag_if_small_enough < Tag.no_payload_count) {
52 return @intToEnum(self.tag_if_small_enough);
53 } else {
54 return self.ptr_otherwise.tag;
55 }
56 }
4457
45 pub const ArraySentinel0_u8_Type = struct {
46 base: Value = Value{ .tag = .array_sentinel_0_u8_type },
47 len: u64,
48 };
58 /// This type is not copyable since it may contain pointers to its inner data.
59 pub const Payload = struct {
60 tag: Tag,
4961
50 pub const SingleConstPtrType = struct {
51 base: Value = Value{ .tag = .single_const_ptr_type },
52 elem_type: *Value,
53 };
62 pub const Int_u64 = struct {
63 base: Payload = Payload{ .tag = .int_u64 },
64 int: u64,
65 };
5466
55 pub const Ref = struct {
56 base: Value = Value{ .tag = .ref },
57 pointee: *MemoryCell,
58 };
67 pub const Int_i64 = struct {
68 base: Payload = Payload{ .tag = .int_i64 },
69 int: i64,
70 };
71
72 pub const Function = struct {
73 base: Payload = Payload{ .tag = .function },
74 };
75
76 pub const ArraySentinel0_u8_Type = struct {
77 base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
78 len: u64,
79 };
80
81 pub const SingleConstPtrType = struct {
82 base: Payload = Payload{ .tag = .single_const_ptr_type },
83 elem_type: *Type,
84 };
85
86 pub const Ref = struct {
87 base: Payload = Payload{ .tag = .ref },
88 pointee: *MemoryCell,
89 };
90
91 pub const Bytes = struct {
92 base: Payload = Payload{ .tag = .bytes },
93 data: []u8,
94 };
5995
60 pub const Bytes = struct {
61 base: Value = Value{ .tag = .bytes },
62 data: []u8,
96 pub const Ty = struct {
97 base: Payload = Payload{ .tag = .fully_qualified_type },
98 ptr: *Type,
99 };
63100 };
64101};
65102
103/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
104/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
105/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
106/// a root.
66107pub const MemoryCell = struct {
67108 parent: Parent,
68 contents: *Value,
109 contents: Value,
69110
70111 pub const Parent = union(enum) {
71112 none,