Commit ded6e0326d
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,
};
};
};