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 {
3333 };
3434 }
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
3644 /// This struct owns the `Value` memory. When the struct is deallocated,
3745 /// so is the `Value`. The value of a constant must be copied into
3846 /// a memory location for the value to survive after a const instruction.
......@@ -130,6 +138,92 @@ pub const ErrorMsg = struct {
130138pub const Tree = struct {
131139 decls: []*Inst,
132140 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 }
133227};
134228
135229const ParseContext = struct {
......@@ -278,6 +372,7 @@ fn parseInstructionGeneric(
278372 body_ctx: ?*BodyContext,
279373) !*Inst {
280374 const inst_specific = try ctx.allocator.create(InstType);
375 inst_specific.base = std.meta.fieldInfo(InstType, "base").default_value.?;
281376
282377 if (@hasField(InstType, "ty")) {
283378 inst_specific.ty = opt_type orelse {
......@@ -286,7 +381,7 @@ fn parseInstructionGeneric(
286381 }
287382
288383 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| {
290385 if (ctx.source[ctx.i] == ',') {
291386 ctx.i += 1;
292387 skipSpace(ctx);
......@@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {
379474 const local_ref = switch (ctx.source[ctx.i]) {
380475 '@' => false,
381476 '%' => 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 },
383482 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
384483 };
385484 const map = if (local_ref)
......@@ -538,7 +637,9 @@ pub fn main() anyerror!void {
538637
539638 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
542643 if (tree.errors.len != 0) {
543644 for (tree.errors) |err_msg| {
544645 const loc = findLineColumn(source, err_msg.byte_offset);
......@@ -547,6 +648,11 @@ pub fn main() anyerror!void {
547648 if (debug_error_trace) return error.ParseFailure;
548649 std.process.exit(1);
549650 }
651
652 tree.dump();
653
654 //const new_tree = try semanticallyAnalyze(tree);
655 //defer new_tree.deinit();
550656}
551657
552658fn 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 {
3636
3737 pub fn tag(self: Type) Tag {
3838 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));
4040 } else {
4141 return self.ptr_otherwise.tag;
4242 }
......@@ -49,21 +49,31 @@ pub const Type = extern union {
4949 out_stream: var,
5050 ) !void {
5151 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});
66 },
52 var ty = self;
53 while (true) {
54 switch (ty.tag()) {
55 .no_return => return out_stream.writeAll("noreturn"),
56 .int_comptime => return out_stream.writeAll("comptime_int"),
57 .int_u8 => return out_stream.writeAll("u8"),
58 .int_usize => return out_stream.writeAll("usize"),
59 .array_u8_sentinel_0 => {
60 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
61 return out_stream.print("[{}:0]u8", .{payload.len});
62 },
63 .array => {
64 const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
65 try out_stream.print("[{}]", .{payload.len});
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;
6777 }
6878 }
6979
......@@ -78,15 +88,15 @@ pub const Type = extern union {
7888 no_return,
7989 int_comptime,
8090 int_u8,
81 int_usize,
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;
91 int_usize, // See last_no_payload_tag below.
8592 // After this, the tag requires a payload.
8693
8794 array_u8_sentinel_0,
8895 array,
8996 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;
90100 };
91101
92102 pub const Payload = struct {
src-self-hosted/value.zig+43-7
......@@ -23,10 +23,7 @@ pub const Value = extern union {
2323 void_value,
2424 noreturn_value,
2525 bool_true,
26 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;
26 bool_false, // See last_no_payload_tag below.
3027 // After this, the tag requires a payload.
3128
3229 ty,
......@@ -35,6 +32,9 @@ pub const Value = extern union {
3532 function,
3633 ref,
3734 bytes,
35
36 pub const last_no_payload_tag = Tag.bool_false;
37 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
3838 };
3939
4040 pub fn initTag(comptime small_tag: Tag) Value {
......@@ -49,12 +49,48 @@ pub const Value = extern union {
4949
5050 pub fn tag(self: Value) Tag {
5151 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));
5353 } else {
5454 return self.ptr_otherwise.tag;
5555 }
5656 }
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
5894 /// This type is not copyable since it may contain pointers to its inner data.
5995 pub const Payload = struct {
6096 tag: Tag,
......@@ -94,8 +130,8 @@ pub const Value = extern union {
94130 };
95131
96132 pub const Ty = struct {
97 base: Payload = Payload{ .tag = .fully_qualified_type },
98 ptr: *Type,
133 base: Payload = Payload{ .tag = .ty },
134 ty: Type,
99135 };
100136 };
101137};