ir: analyze fntype instruction

This commit is contained in:
Andrew Kelley
2020-04-21 17:06:09 -04:00
parent 22e7ca5613
commit 8671e8d6d4
3 changed files with 192 additions and 54 deletions

View File

@@ -168,7 +168,7 @@ const Analyze = struct {
} else if (self.decl_table.get(old_inst)) |kv| {
return kv.value.ptr orelse return error.AnalysisFail;
} else {
const new_inst = self.analyzeInst(old_inst, null) catch |err| switch (err) {
const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) {
error.AnalysisFail => {
try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
return error.AnalysisFail;
@@ -256,7 +256,14 @@ const Analyze = struct {
});
}
fn analyzeInst(self: *Analyze, old_inst: *text.Inst, opt_func: ?*Fn) InnerError!*Inst {
fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
return self.constInst(src, .{
.ty = Type.initTag(.@"type"),
.val = try ty.toValue(&self.arena.allocator),
});
}
fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
switch (old_inst.tag) {
.str => {
// We can use this reference because Inst.Const's Value is arena-allocated.
@@ -271,51 +278,65 @@ const Analyze = struct {
.as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.@"fn" => {
const fn_inst = old_inst.cast(text.Inst.Fn).?;
const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
var new_func: Fn = .{
.body = std.ArrayList(*Inst).init(self.allocator),
.inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
.fn_index = self.fns.items.len,
};
defer new_func.body.deinit();
defer new_func.inst_table.deinit();
// Don't hang on to a reference to this when analyzing body instructions, since the memory
// could become invalid.
(try self.fns.addOne()).* = .{
.analysis_status = .in_progress,
.body = undefined,
};
for (fn_inst.positionals.body.instructions) |src_inst| {
const new_inst = self.analyzeInst(src_inst, &new_func) catch |err| {
self.fns.items[new_func.fn_index].analysis_status = .failure;
return err;
};
try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
}
self.fns.items[new_func.fn_index] = .{
.analysis_status = .success,
.body = new_func.body.toOwnedSlice(),
};
const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
fn_payload.* = .{ .index = new_func.fn_index };
return self.constInst(old_inst.src, .{
.ty = fn_type,
.val = Value.initPayload(&fn_payload.base),
});
},
.@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
.@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
}
}
fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
var new_func: Fn = .{
.body = std.ArrayList(*Inst).init(self.allocator),
.inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
.fn_index = self.fns.items.len,
};
defer new_func.body.deinit();
defer new_func.inst_table.deinit();
// Don't hang on to a reference to this when analyzing body instructions, since the memory
// could become invalid.
(try self.fns.addOne()).* = .{
.analysis_status = .in_progress,
.body = undefined,
};
for (fn_inst.positionals.body.instructions) |src_inst| {
const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
self.fns.items[new_func.fn_index].analysis_status = .failure;
return err;
};
try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
}
self.fns.items[new_func.fn_index] = .{
.analysis_status = .success,
.body = new_func.body.toOwnedSlice(),
};
const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
fn_payload.* = .{ .index = new_func.fn_index };
return self.constInst(fn_inst.base.src, .{
.ty = fn_type,
.val = Value.initPayload(&fn_payload.base),
});
}
fn analyzeInstFnType(self: *Analyze, opt_func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
const return_type = try self.resolveType(opt_func, fntype.positionals.return_type);
if (return_type.zigTypeTag() == .NoReturn and
fntype.positionals.param_types.len == 0 and
fntype.kw_args.cc == .Naked)
{
return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
}
return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
}
fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
if (in_memory_result == .ok) {

View File

@@ -1,6 +1,7 @@
const std = @import("std");
const Value = @import("value.zig").Value;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
/// It's important for this struct to be small.
@@ -48,6 +49,8 @@ pub const Type = extern union {
.@"comptime_float" => return .ComptimeFloat,
.@"noreturn" => return .NoReturn,
.fn_naked_noreturn_no_args => return .Fn,
.array, .array_u8_sentinel_0 => return .Array,
.single_const_pointer => return .Pointer,
.const_slice_u8 => return .Pointer,
@@ -122,6 +125,7 @@ pub const Type = extern union {
=> return out_stream.writeAll(@tagName(t)),
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.array_u8_sentinel_0 => {
const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
@@ -144,6 +148,43 @@ pub const Type = extern union {
}
}
pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
switch (self.tag()) {
.@"u8" => return Value.initTag(.u8_type),
.@"i8" => return Value.initTag(.i8_type),
.@"isize" => return Value.initTag(.isize_type),
.@"usize" => return Value.initTag(.usize_type),
.@"c_short" => return Value.initTag(.c_short_type),
.@"c_ushort" => return Value.initTag(.c_ushort_type),
.@"c_int" => return Value.initTag(.c_int_type),
.@"c_uint" => return Value.initTag(.c_uint_type),
.@"c_long" => return Value.initTag(.c_long_type),
.@"c_ulong" => return Value.initTag(.c_ulong_type),
.@"c_longlong" => return Value.initTag(.c_longlong_type),
.@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
.@"c_longdouble" => return Value.initTag(.c_longdouble_type),
.@"c_void" => return Value.initTag(.c_void_type),
.@"f16" => return Value.initTag(.f16_type),
.@"f32" => return Value.initTag(.f32_type),
.@"f64" => return Value.initTag(.f64_type),
.@"f128" => return Value.initTag(.f128_type),
.@"bool" => return Value.initTag(.bool_type),
.@"void" => return Value.initTag(.void_type),
.@"type" => return Value.initTag(.type_type),
.@"anyerror" => return Value.initTag(.anyerror_type),
.@"comptime_int" => return Value.initTag(.comptime_int_type),
.@"comptime_float" => return Value.initTag(.comptime_float_type),
.@"noreturn" => return Value.initTag(.noreturn_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
else => {
const ty_payload = try allocator.create(Value.Payload.Ty);
ty_payload.* = .{ .ty = self };
return Value.initPayload(&ty_payload.base);
},
}
}
pub fn isSinglePointer(self: Type) bool {
return switch (self.tag()) {
.@"u8",
@@ -174,6 +215,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.const_slice_u8,
.fn_naked_noreturn_no_args,
=> false,
.single_const_pointer => true,
@@ -210,6 +252,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.single_const_pointer,
.fn_naked_noreturn_no_args,
=> false,
.const_slice_u8 => true,
@@ -246,6 +289,7 @@ pub const Type = extern union {
.@"noreturn",
.array,
.array_u8_sentinel_0,
.fn_naked_noreturn_no_args,
=> unreachable,
.single_const_pointer, .const_slice_u8 => true,
@@ -280,6 +324,7 @@ pub const Type = extern union {
.@"comptime_int",
.@"comptime_float",
.@"noreturn",
.fn_naked_noreturn_no_args,
=> unreachable,
.array => self.cast(Payload.Array).?.elem_type,
@@ -296,7 +341,6 @@ pub const Type = extern union {
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
const_slice_u8,
@"u8",
@"i8",
@"isize",
@@ -321,14 +365,16 @@ pub const Type = extern union {
@"anyerror",
@"comptime_int",
@"comptime_float",
@"noreturn", // See last_no_payload_tag below.
@"noreturn",
fn_naked_noreturn_no_args,
const_slice_u8, // 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.@"noreturn";
pub const last_no_payload_tag = Tag.const_slice_u8;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
};

View File

@@ -16,10 +16,34 @@ pub const Value = extern union {
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
void_type,
noreturn_type,
bool_type,
u8_type,
i8_type,
isize_type,
usize_type,
c_short_type,
c_ushort_type,
c_int_type,
c_uint_type,
c_long_type,
c_ulong_type,
c_longlong_type,
c_ulonglong_type,
c_longdouble_type,
f16_type,
f32_type,
f64_type,
f128_type,
c_void_type,
bool_type,
void_type,
type_type,
anyerror_type,
comptime_int_type,
comptime_float_type,
noreturn_type,
fn_naked_noreturn_no_args_type,
const_slice_u8_type,
void_value,
noreturn_value,
bool_true,
@@ -74,10 +98,34 @@ pub const Value = extern union {
) !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"),
.u8_type => return out_stream.writeAll("u8"),
.i8_type => return out_stream.writeAll("i8"),
.isize_type => return out_stream.writeAll("isize"),
.usize_type => return out_stream.writeAll("usize"),
.c_short_type => return out_stream.writeAll("c_short"),
.c_ushort_type => return out_stream.writeAll("c_ushort"),
.c_int_type => return out_stream.writeAll("c_int"),
.c_uint_type => return out_stream.writeAll("c_uint"),
.c_long_type => return out_stream.writeAll("c_long"),
.c_ulong_type => return out_stream.writeAll("c_ulong"),
.c_longlong_type => return out_stream.writeAll("c_longlong"),
.c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
.c_longdouble_type => return out_stream.writeAll("c_longdouble"),
.f16_type => return out_stream.writeAll("f16"),
.f32_type => return out_stream.writeAll("f32"),
.f64_type => return out_stream.writeAll("f64"),
.f128_type => return out_stream.writeAll("f128"),
.c_void_type => return out_stream.writeAll("c_void"),
.bool_type => return out_stream.writeAll("bool"),
.void_type => return out_stream.writeAll("void"),
.type_type => return out_stream.writeAll("type"),
.anyerror_type => return out_stream.writeAll("anyerror"),
.comptime_int_type => return out_stream.writeAll("comptime_int"),
.comptime_float_type => return out_stream.writeAll("comptime_float"),
.noreturn_type => return out_stream.writeAll("noreturn"),
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.const_slice_u8_type => return out_stream.writeAll("[]const u8"),
.void_value => return out_stream.writeAll("{}"),
.noreturn_value => return out_stream.writeAll("unreachable"),
.bool_true => return out_stream.writeAll("true"),
@@ -105,10 +153,33 @@ pub const Value = extern union {
return switch (self.tag()) {
.ty => self.cast(Payload.Ty).?.ty,
.void_type => Type.initTag(.@"void"),
.noreturn_type => Type.initTag(.@"noreturn"),
.bool_type => Type.initTag(.@"bool"),
.u8_type => Type.initTag(.@"u8"),
.i8_type => Type.initTag(.@"i8"),
.isize_type => Type.initTag(.@"isize"),
.usize_type => Type.initTag(.@"usize"),
.c_short_type => Type.initTag(.@"c_short"),
.c_ushort_type => Type.initTag(.@"c_ushort"),
.c_int_type => Type.initTag(.@"c_int"),
.c_uint_type => Type.initTag(.@"c_uint"),
.c_long_type => Type.initTag(.@"c_long"),
.c_ulong_type => Type.initTag(.@"c_ulong"),
.c_longlong_type => Type.initTag(.@"c_longlong"),
.c_ulonglong_type => Type.initTag(.@"c_ulonglong"),
.c_longdouble_type => Type.initTag(.@"c_longdouble"),
.f16_type => Type.initTag(.@"f16"),
.f32_type => Type.initTag(.@"f32"),
.f64_type => Type.initTag(.@"f64"),
.f128_type => Type.initTag(.@"f128"),
.c_void_type => Type.initTag(.@"c_void"),
.bool_type => Type.initTag(.@"bool"),
.void_type => Type.initTag(.@"void"),
.type_type => Type.initTag(.@"type"),
.anyerror_type => Type.initTag(.@"anyerror"),
.comptime_int_type => Type.initTag(.@"comptime_int"),
.comptime_float_type => Type.initTag(.@"comptime_float"),
.noreturn_type => Type.initTag(.@"noreturn"),
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
.const_slice_u8_type => Type.initTag(.const_slice_u8),
.void_value,
.noreturn_value,