diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 12f902a2ff..ca68e64bd5 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -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) { diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 8ba0311eaf..bea6aa75e3 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -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; }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 14edfeea92..8d3aa5daf2 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -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,