authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 20:04:11-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 20:04:11-04:00
logded6e0326d8965de8763806593b008c9c28d5508
treecfc0bb9cc9407aca7bdf230859c1e5978fbb60b7
parent1f3eeb5443ada8dbe9226f1bcc5322fd89c20d3e

ir: rendering skeleton


3 files changed, 182 insertions(+), 30 deletions(-)

src-self-hosted/ir.zig+109-3
...@@ -33,6 +33,14 @@ pub const Inst = struct {...@@ -33,6 +33,14 @@ pub const Inst = struct {
33 };33 };
34 }34 }
3535
36 pub fn cast(base: *Inst, comptime T: type) ?*T {
37 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
38 if (base.tag != expected_tag)
39 return null;
40
41 return @fieldParentPtr(T, "base", base);
42 }
43
36 /// This struct owns the `Value` memory. When the struct is deallocated,44 /// This struct owns the `Value` memory. When the struct is deallocated,
37 /// so is the `Value`. The value of a constant must be copied into45 /// so is the `Value`. The value of a constant must be copied into
38 /// a memory location for the value to survive after a const instruction.46 /// a memory location for the value to survive after a const instruction.
...@@ -130,6 +138,92 @@ pub const ErrorMsg = struct {...@@ -130,6 +138,92 @@ pub const ErrorMsg = struct {
130pub const Tree = struct {138pub const Tree = struct {
131 decls: []*Inst,139 decls: []*Inst,
132 errors: []ErrorMsg,140 errors: []ErrorMsg,
141
142 pub fn deinit(self: *Tree) void {
143 // TODO resource deallocation
144 self.* = undefined;
145 }
146
147 /// This is a debugging utility for rendering the tree to stderr.
148 pub fn dump(self: Tree) void {
149 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
150 }
151
152 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body });
153
154 pub fn writeToStream(self: Tree, allocator: *Allocator, stream: var) !void {
155 // First, build a map of *Inst to @ or % indexes
156 var inst_table = InstPtrTable.init(allocator);
157 defer inst_table.deinit();
158
159 try inst_table.ensureCapacity(self.decls.len);
160
161 for (self.decls) |decl, decl_i| {
162 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
163
164 if (decl.cast(Inst.Fn)) |fn_inst| {
165 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
166 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
167 }
168 }
169 }
170
171 for (self.decls) |decl, i| {
172 try stream.print("@{} = ", .{i});
173 try self.writeInstToStream(stream, decl, &inst_table);
174 }
175 }
176
177 fn writeInstToStream(self: Tree, stream: var, decl: *Inst, inst_table: *const InstPtrTable) !void {
178 // TODO I tried implementing this with an inline for loop and hit a compiler bug
179 switch (decl.tag) {
180 .constant => return self.writeInstToStreamGeneric(stream, .constant, decl, inst_table),
181 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
182 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
183 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
184 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
185 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
186 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
187 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
188 }
189 }
190
191 fn writeInstToStreamGeneric(
192 self: Tree,
193 stream: var,
194 comptime inst_tag: Inst.Tag,
195 base: *Inst,
196 inst_table: *const InstPtrTable,
197 ) !void {
198 const SpecificInst = Inst.TagToType(inst_tag);
199 const inst = @fieldParentPtr(SpecificInst, "base", base);
200 const Positionals = @TypeOf(inst.positionals);
201 try stream.writeAll(@tagName(inst_tag) ++ "(");
202 inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| {
203 if (i != 0) {
204 try stream.writeAll(", ");
205 }
206 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
207 }
208 try stream.writeAll(")\n");
209 }
210
211 pub fn writeParamToStream(self: Tree, stream: var, param: var, inst_table: *const InstPtrTable) !void {
212 switch (@TypeOf(param)) {
213 Value => {
214 try stream.print("{}", .{param});
215 },
216 *Inst => {
217 const info = inst_table.getValue(param).?;
218 const prefix = if (info.fn_body == null) "@" else "%";
219 try stream.print("{}{}", .{ prefix, info.index });
220 },
221 Inst.Fn.Body => {
222 try stream.print("(fn body)", .{});
223 },
224 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
225 }
226 }
133};227};
134228
135const ParseContext = struct {229const ParseContext = struct {
...@@ -278,6 +372,7 @@ fn parseInstructionGeneric(...@@ -278,6 +372,7 @@ fn parseInstructionGeneric(
278 body_ctx: ?*BodyContext,372 body_ctx: ?*BodyContext,
279) !*Inst {373) !*Inst {
280 const inst_specific = try ctx.allocator.create(InstType);374 const inst_specific = try ctx.allocator.create(InstType);
375 inst_specific.base = std.meta.fieldInfo(InstType, "base").default_value.?;
281376
282 if (@hasField(InstType, "ty")) {377 if (@hasField(InstType, "ty")) {
283 inst_specific.ty = opt_type orelse {378 inst_specific.ty = opt_type orelse {
...@@ -286,7 +381,7 @@ fn parseInstructionGeneric(...@@ -286,7 +381,7 @@ fn parseInstructionGeneric(
286 }381 }
287382
288 const Positionals = @TypeOf(inst_specific.positionals);383 const Positionals = @TypeOf(inst_specific.positionals);
289 inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| {384 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
290 if (ctx.source[ctx.i] == ',') {385 if (ctx.source[ctx.i] == ',') {
291 ctx.i += 1;386 ctx.i += 1;
292 skipSpace(ctx);387 skipSpace(ctx);
...@@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {...@@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {
379 const local_ref = switch (ctx.source[ctx.i]) {474 const local_ref = switch (ctx.source[ctx.i]) {
380 '@' => false,475 '@' => false,
381 '%' => true,476 '%' => true,
382 '"' => return parseStringLiteralConst(ctx, null),477 '"' => {
478 const str_lit_inst = try parseStringLiteralConst(ctx, null);
479 try ctx.decls.append(str_lit_inst);
480 return str_lit_inst;
481 },
383 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),482 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
384 };483 };
385 const map = if (local_ref)484 const map = if (local_ref)
...@@ -538,7 +637,9 @@ pub fn main() anyerror!void {...@@ -538,7 +637,9 @@ pub fn main() anyerror!void {
538637
539 const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32));638 const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32));
540639
541 const tree = try parse(allocator, source);640 var tree = try parse(allocator, source);
641 defer tree.deinit();
642
542 if (tree.errors.len != 0) {643 if (tree.errors.len != 0) {
543 for (tree.errors) |err_msg| {644 for (tree.errors) |err_msg| {
544 const loc = findLineColumn(source, err_msg.byte_offset);645 const loc = findLineColumn(source, err_msg.byte_offset);
...@@ -547,6 +648,11 @@ pub fn main() anyerror!void {...@@ -547,6 +648,11 @@ pub fn main() anyerror!void {
547 if (debug_error_trace) return error.ParseFailure;648 if (debug_error_trace) return error.ParseFailure;
548 std.process.exit(1);649 std.process.exit(1);
549 }650 }
651
652 tree.dump();
653
654 //const new_tree = try semanticallyAnalyze(tree);
655 //defer new_tree.deinit();
550}656}
551657
552fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {658fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
src-self-hosted/type.zig+30-20
...@@ -36,7 +36,7 @@ pub const Type = extern union {...@@ -36,7 +36,7 @@ pub const Type = extern union {
3636
37 pub fn tag(self: Type) Tag {37 pub fn tag(self: Type) Tag {
38 if (self.tag_if_small_enough < Tag.no_payload_count) {38 if (self.tag_if_small_enough < Tag.no_payload_count) {
39 return @intToEnum(self.tag_if_small_enough);39 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
40 } else {40 } else {
41 return self.ptr_otherwise.tag;41 return self.ptr_otherwise.tag;
42 }42 }
...@@ -49,21 +49,31 @@ pub const Type = extern union {...@@ -49,21 +49,31 @@ pub const Type = extern union {
49 out_stream: var,49 out_stream: var,
50 ) !void {50 ) !void {
51 comptime assert(fmt.len == 0);51 comptime assert(fmt.len == 0);
52 switch (self.tag()) {52 var ty = self;
53 .int_u8 => return out_stream.writeAll("u8"),53 while (true) {
54 .int_usize => return out_stream.writeAll("usize"),54 switch (ty.tag()) {
55 .array_u8_sentinel_0 => {55 .no_return => return out_stream.writeAll("noreturn"),
56 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise);56 .int_comptime => return out_stream.writeAll("comptime_int"),
57 return out_stream.print("[{}:0]u8", .{payload.len});57 .int_u8 => return out_stream.writeAll("u8"),
58 },58 .int_usize => return out_stream.writeAll("usize"),
59 .array => {59 .array_u8_sentinel_0 => {
60 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);60 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
61 return out_stream.print("[{}]{}", .{ payload.len, payload.elem_type });61 return out_stream.print("[{}:0]u8", .{payload.len});
62 },62 },
63 .single_const_pointer => {63 .array => {
64 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);64 const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
65 return out_stream.print("*const {}", .{payload.pointee_type});65 try out_stream.print("[{}]", .{payload.len});
66 },66 ty = payload.elem_type;
67 continue;
68 },
69 .single_const_pointer => {
70 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
71 try out_stream.writeAll("*const ");
72 ty = payload.pointee_type;
73 continue;
74 },
75 }
76 unreachable;
67 }77 }
68 }78 }
6979
...@@ -78,15 +88,15 @@ pub const Type = extern union {...@@ -78,15 +88,15 @@ pub const Type = extern union {
78 no_return,88 no_return,
79 int_comptime,89 int_comptime,
80 int_u8,90 int_u8,
81 int_usize,91 int_usize, // See last_no_payload_tag below.
82 // Bump this when adding items above.
83 pub const last_no_payload_tag = Tag.int_usize;
84 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
85 // After this, the tag requires a payload.92 // After this, the tag requires a payload.
8693
87 array_u8_sentinel_0,94 array_u8_sentinel_0,
88 array,95 array,
89 single_const_pointer,96 single_const_pointer,
97
98 pub const last_no_payload_tag = Tag.int_usize;
99 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
90 };100 };
91101
92 pub const Payload = struct {102 pub const Payload = struct {
src-self-hosted/value.zig+43-7
...@@ -23,10 +23,7 @@ pub const Value = extern union {...@@ -23,10 +23,7 @@ pub const Value = extern union {
23 void_value,23 void_value,
24 noreturn_value,24 noreturn_value,
25 bool_true,25 bool_true,
26 bool_false,26 bool_false, // See last_no_payload_tag below.
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.27 // After this, the tag requires a payload.
3128
32 ty,29 ty,
...@@ -35,6 +32,9 @@ pub const Value = extern union {...@@ -35,6 +32,9 @@ pub const Value = extern union {
35 function,32 function,
36 ref,33 ref,
37 bytes,34 bytes,
35
36 pub const last_no_payload_tag = Tag.bool_false;
37 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
38 };38 };
3939
40 pub fn initTag(comptime small_tag: Tag) Value {40 pub fn initTag(comptime small_tag: Tag) Value {
...@@ -49,12 +49,48 @@ pub const Value = extern union {...@@ -49,12 +49,48 @@ pub const Value = extern union {
4949
50 pub fn tag(self: Value) Tag {50 pub fn tag(self: Value) Tag {
51 if (self.tag_if_small_enough < Tag.no_payload_count) {51 if (self.tag_if_small_enough < Tag.no_payload_count) {
52 return @intToEnum(self.tag_if_small_enough);52 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
53 } else {53 } else {
54 return self.ptr_otherwise.tag;54 return self.ptr_otherwise.tag;
55 }55 }
56 }56 }
5757
58 pub fn cast(self: Value, comptime T: type) ?*T {
59 if (self.tag_if_small_enough < Tag.no_payload_count)
60 return null;
61
62 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
63 if (self.ptr_otherwise.tag != expected_tag)
64 return null;
65
66 return @fieldParentPtr(T, "base", self.ptr_otherwise);
67 }
68
69 pub fn format(
70 self: Value,
71 comptime fmt: []const u8,
72 options: std.fmt.FormatOptions,
73 out_stream: var,
74 ) !void {
75 comptime assert(fmt.len == 0);
76 switch (self.tag()) {
77 .void_type => return out_stream.writeAll("void"),
78 .noreturn_type => return out_stream.writeAll("noreturn"),
79 .bool_type => return out_stream.writeAll("bool"),
80 .usize_type => return out_stream.writeAll("usize"),
81 .void_value => return out_stream.writeAll("{}"),
82 .noreturn_value => return out_stream.writeAll("unreachable"),
83 .bool_true => return out_stream.writeAll("true"),
84 .bool_false => return out_stream.writeAll("false"),
85 .ty => return self.cast(Payload.Ty).?.ty.format("", options, out_stream),
86 .int_u64 => return std.fmt.formatIntValue(self.cast(Payload.Int_u64).?.int, "", options, out_stream),
87 .int_i64 => return std.fmt.formatIntValue(self.cast(Payload.Int_i64).?.int, "", options, out_stream),
88 .function => return out_stream.writeAll("(function)"),
89 .ref => return out_stream.writeAll("(ref)"),
90 .bytes => return out_stream.writeAll("(bytes)"),
91 }
92 }
93
58 /// This type is not copyable since it may contain pointers to its inner data.94 /// This type is not copyable since it may contain pointers to its inner data.
59 pub const Payload = struct {95 pub const Payload = struct {
60 tag: Tag,96 tag: Tag,
...@@ -94,8 +130,8 @@ pub const Value = extern union {...@@ -94,8 +130,8 @@ pub const Value = extern union {
94 };130 };
95131
96 pub const Ty = struct {132 pub const Ty = struct {
97 base: Payload = Payload{ .tag = .fully_qualified_type },133 base: Payload = Payload{ .tag = .ty },
98 ptr: *Type,134 ty: Type,
99 };135 };
100 };136 };
101};137};