Commit ded6e0326d

Andrew Kelley <andrew@ziglang.org>
2020-04-20 02:04:11
ir: rendering skeleton
1 parent 1f3eeb5
Changed files (3)
src-self-hosted
src-self-hosted/ir.zig
@@ -33,6 +33,14 @@ pub const Inst = struct {
         };
     }
 
+    pub fn cast(base: *Inst, comptime T: type) ?*T {
+        const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
+        if (base.tag != expected_tag)
+            return null;
+
+        return @fieldParentPtr(T, "base", base);
+    }
+
     /// This struct owns the `Value` memory. When the struct is deallocated,
     /// so is the `Value`. The value of a constant must be copied into
     /// a memory location for the value to survive after a const instruction.
@@ -130,6 +138,92 @@ pub const ErrorMsg = struct {
 pub const Tree = struct {
     decls: []*Inst,
     errors: []ErrorMsg,
+
+    pub fn deinit(self: *Tree) void {
+        // TODO resource deallocation
+        self.* = undefined;
+    }
+
+    /// This is a debugging utility for rendering the tree to stderr.
+    pub fn dump(self: Tree) void {
+        self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
+    }
+
+    const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body });
+
+    pub fn writeToStream(self: Tree, allocator: *Allocator, stream: var) !void {
+        // First, build a map of *Inst to @ or % indexes
+        var inst_table = InstPtrTable.init(allocator);
+        defer inst_table.deinit();
+
+        try inst_table.ensureCapacity(self.decls.len);
+
+        for (self.decls) |decl, decl_i| {
+            try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
+
+            if (decl.cast(Inst.Fn)) |fn_inst| {
+                for (fn_inst.positionals.body.instructions) |inst, inst_i| {
+                    try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
+                }
+            }
+        }
+
+        for (self.decls) |decl, i| {
+            try stream.print("@{} = ", .{i});
+            try self.writeInstToStream(stream, decl, &inst_table);
+        }
+    }
+
+    fn writeInstToStream(self: Tree, stream: var, decl: *Inst, inst_table: *const InstPtrTable) !void {
+        // TODO I tried implementing this with an inline for loop and hit a compiler bug
+        switch (decl.tag) {
+            .constant => return self.writeInstToStreamGeneric(stream, .constant, decl, inst_table),
+            .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
+            .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
+            .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
+            .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
+            .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
+            .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
+            .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
+        }
+    }
+
+    fn writeInstToStreamGeneric(
+        self: Tree,
+        stream: var,
+        comptime inst_tag: Inst.Tag,
+        base: *Inst,
+        inst_table: *const InstPtrTable,
+    ) !void {
+        const SpecificInst = Inst.TagToType(inst_tag);
+        const inst = @fieldParentPtr(SpecificInst, "base", base);
+        const Positionals = @TypeOf(inst.positionals);
+        try stream.writeAll(@tagName(inst_tag) ++ "(");
+        inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| {
+            if (i != 0) {
+                try stream.writeAll(", ");
+            }
+            try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
+        }
+        try stream.writeAll(")\n");
+    }
+
+    pub fn writeParamToStream(self: Tree, stream: var, param: var, inst_table: *const InstPtrTable) !void {
+        switch (@TypeOf(param)) {
+            Value => {
+                try stream.print("{}", .{param});
+            },
+            *Inst => {
+                const info = inst_table.getValue(param).?;
+                const prefix = if (info.fn_body == null) "@" else "%";
+                try stream.print("{}{}", .{ prefix, info.index });
+            },
+            Inst.Fn.Body => {
+                try stream.print("(fn body)", .{});
+            },
+            else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
+        }
+    }
 };
 
 const ParseContext = struct {
@@ -278,6 +372,7 @@ fn parseInstructionGeneric(
     body_ctx: ?*BodyContext,
 ) !*Inst {
     const inst_specific = try ctx.allocator.create(InstType);
+    inst_specific.base = std.meta.fieldInfo(InstType, "base").default_value.?;
 
     if (@hasField(InstType, "ty")) {
         inst_specific.ty = opt_type orelse {
@@ -286,7 +381,7 @@ fn parseInstructionGeneric(
     }
 
     const Positionals = @TypeOf(inst_specific.positionals);
-    inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| {
+    inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
         if (ctx.source[ctx.i] == ',') {
             ctx.i += 1;
             skipSpace(ctx);
@@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {
     const local_ref = switch (ctx.source[ctx.i]) {
         '@' => false,
         '%' => true,
-        '"' => return parseStringLiteralConst(ctx, null),
+        '"' => {
+            const str_lit_inst = try parseStringLiteralConst(ctx, null);
+            try ctx.decls.append(str_lit_inst);
+            return str_lit_inst;
+        },
         else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
     };
     const map = if (local_ref)
@@ -538,7 +637,9 @@ pub fn main() anyerror!void {
 
     const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32));
 
-    const tree = try parse(allocator, source);
+    var tree = try parse(allocator, source);
+    defer tree.deinit();
+
     if (tree.errors.len != 0) {
         for (tree.errors) |err_msg| {
             const loc = findLineColumn(source, err_msg.byte_offset);
@@ -547,6 +648,11 @@ pub fn main() anyerror!void {
         if (debug_error_trace) return error.ParseFailure;
         std.process.exit(1);
     }
+
+    tree.dump();
+
+    //const new_tree = try semanticallyAnalyze(tree);
+    //defer new_tree.deinit();
 }
 
 fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
src-self-hosted/type.zig
@@ -36,7 +36,7 @@ pub const Type = extern union {
 
     pub fn tag(self: Type) Tag {
         if (self.tag_if_small_enough < Tag.no_payload_count) {
-            return @intToEnum(self.tag_if_small_enough);
+            return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
         } else {
             return self.ptr_otherwise.tag;
         }
@@ -49,21 +49,31 @@ pub const Type = extern union {
         out_stream: var,
     ) !void {
         comptime assert(fmt.len == 0);
-        switch (self.tag()) {
-            .int_u8 => return out_stream.writeAll("u8"),
-            .int_usize => return out_stream.writeAll("usize"),
-            .array_u8_sentinel_0 => {
-                const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise);
-                return out_stream.print("[{}:0]u8", .{payload.len});
-            },
-            .array => {
-                const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
-                return out_stream.print("[{}]{}", .{ payload.len, payload.elem_type });
-            },
-            .single_const_pointer => {
-                const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
-                return out_stream.print("*const {}", .{payload.pointee_type});
-            },
+        var ty = self;
+        while (true) {
+            switch (ty.tag()) {
+                .no_return => return out_stream.writeAll("noreturn"),
+                .int_comptime => return out_stream.writeAll("comptime_int"),
+                .int_u8 => return out_stream.writeAll("u8"),
+                .int_usize => return out_stream.writeAll("usize"),
+                .array_u8_sentinel_0 => {
+                    const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
+                    return out_stream.print("[{}:0]u8", .{payload.len});
+                },
+                .array => {
+                    const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
+                    try out_stream.print("[{}]", .{payload.len});
+                    ty = payload.elem_type;
+                    continue;
+                },
+                .single_const_pointer => {
+                    const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
+                    try out_stream.writeAll("*const ");
+                    ty = payload.pointee_type;
+                    continue;
+                },
+            }
+            unreachable;
         }
     }
 
@@ -78,15 +88,15 @@ pub const Type = extern union {
         no_return,
         int_comptime,
         int_u8,
-        int_usize,
-        // Bump this when adding items above.
-        pub const last_no_payload_tag = Tag.int_usize;
-        pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
+        int_usize, // See last_no_payload_tag below.
         // After this, the tag requires a payload.
 
         array_u8_sentinel_0,
         array,
         single_const_pointer,
+
+        pub const last_no_payload_tag = Tag.int_usize;
+        pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
     };
 
     pub const Payload = struct {
src-self-hosted/value.zig
@@ -23,10 +23,7 @@ pub const Value = extern union {
         void_value,
         noreturn_value,
         bool_true,
-        bool_false,
-        // Bump this when adding items above.
-        pub const last_no_payload_tag = Tag.bool_false;
-        pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
+        bool_false, // See last_no_payload_tag below.
         // After this, the tag requires a payload.
 
         ty,
@@ -35,6 +32,9 @@ pub const Value = extern union {
         function,
         ref,
         bytes,
+
+        pub const last_no_payload_tag = Tag.bool_false;
+        pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
     };
 
     pub fn initTag(comptime small_tag: Tag) Value {
@@ -49,12 +49,48 @@ pub const Value = extern union {
 
     pub fn tag(self: Value) Tag {
         if (self.tag_if_small_enough < Tag.no_payload_count) {
-            return @intToEnum(self.tag_if_small_enough);
+            return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
         } else {
             return self.ptr_otherwise.tag;
         }
     }
 
+    pub fn cast(self: Value, comptime T: type) ?*T {
+        if (self.tag_if_small_enough < Tag.no_payload_count)
+            return null;
+
+        const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
+        if (self.ptr_otherwise.tag != expected_tag)
+            return null;
+
+        return @fieldParentPtr(T, "base", self.ptr_otherwise);
+    }
+
+    pub fn format(
+        self: Value,
+        comptime fmt: []const u8,
+        options: std.fmt.FormatOptions,
+        out_stream: var,
+    ) !void {
+        comptime assert(fmt.len == 0);
+        switch (self.tag()) {
+            .void_type => return out_stream.writeAll("void"),
+            .noreturn_type => return out_stream.writeAll("noreturn"),
+            .bool_type => return out_stream.writeAll("bool"),
+            .usize_type => return out_stream.writeAll("usize"),
+            .void_value => return out_stream.writeAll("{}"),
+            .noreturn_value => return out_stream.writeAll("unreachable"),
+            .bool_true => return out_stream.writeAll("true"),
+            .bool_false => return out_stream.writeAll("false"),
+            .ty => return self.cast(Payload.Ty).?.ty.format("", options, out_stream),
+            .int_u64 => return std.fmt.formatIntValue(self.cast(Payload.Int_u64).?.int, "", options, out_stream),
+            .int_i64 => return std.fmt.formatIntValue(self.cast(Payload.Int_i64).?.int, "", options, out_stream),
+            .function => return out_stream.writeAll("(function)"),
+            .ref => return out_stream.writeAll("(ref)"),
+            .bytes => return out_stream.writeAll("(bytes)"),
+        }
+    }
+
     /// This type is not copyable since it may contain pointers to its inner data.
     pub const Payload = struct {
         tag: Tag,
@@ -94,8 +130,8 @@ pub const Value = extern union {
         };
 
         pub const Ty = struct {
-            base: Payload = Payload{ .tag = .fully_qualified_type },
-            ptr: *Type,
+            base: Payload = Payload{ .tag = .ty },
+            ty: Type,
         };
     };
 };