ir: rendering skeleton

This commit is contained in:
Andrew Kelley 2020-04-19 20:04:11 -04:00
parent 1f3eeb5443
commit ded6e0326d
3 changed files with 182 additions and 30 deletions

View File

@ -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 } {

View File

@ -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,22 +49,32 @@ pub const Type = extern union {
out_stream: var,
) !void {
comptime assert(fmt.len == 0);
switch (self.tag()) {
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", self.ptr_otherwise);
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", self.ptr_otherwise);
return out_stream.print("[{}]{}", .{ payload.len, payload.elem_type });
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", self.ptr_otherwise);
return out_stream.print("*const {}", .{payload.pointee_type});
const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
try out_stream.writeAll("*const ");
ty = payload.pointee_type;
continue;
},
}
unreachable;
}
}
/// This enum does not directly correspond to `std.builtin.TypeId` because
@ -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 {

View File

@ -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,
};
};
};