| author | |
| committer | |
| log | 8671e8d6d4e570b104014859eaa66d70a0991217 |
| tree | ded542cd7dda09801d207959136f3492c1f7ab1d |
| parent | 22e7ca5613c32b14043dee4531fe8b180bfc407a |
3 files changed, 189 insertions(+), 51 deletions(-)
src-self-hosted/ir.zig+63-42| ... | ... | @@ -168,7 +168,7 @@ const Analyze = struct { |
| 168 | 168 | } else if (self.decl_table.get(old_inst)) |kv| { |
| 169 | 169 | return kv.value.ptr orelse return error.AnalysisFail; |
| 170 | 170 | } else { |
| 171 | const new_inst = self.analyzeInst(old_inst, null) catch |err| switch (err) { | |
| 171 | const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) { | |
| 172 | 172 | error.AnalysisFail => { |
| 173 | 173 | try self.decl_table.putNoClobber(old_inst, .{ .ptr = null }); |
| 174 | 174 | return error.AnalysisFail; |
| ... | ... | @@ -256,7 +256,14 @@ const Analyze = struct { |
| 256 | 256 | }); |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | fn analyzeInst(self: *Analyze, old_inst: *text.Inst, opt_func: ?*Fn) InnerError!*Inst { | |
| 259 | fn constType(self: *Analyze, src: usize, ty: Type) !*Inst { | |
| 260 | return self.constInst(src, .{ | |
| 261 | .ty = Type.initTag(.@"type"), | |
| 262 | .val = try ty.toValue(&self.arena.allocator), | |
| 263 | }); | |
| 264 | } | |
| 265 | ||
| 266 | fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst { | |
| 260 | 267 | switch (old_inst.tag) { |
| 261 | 268 | .str => { |
| 262 | 269 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| ... | ... | @@ -271,49 +278,63 @@ const Analyze = struct { |
| 271 | 278 | .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 272 | 279 | .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 273 | 280 | .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 274 | .@"fn" => { | |
| 275 | const fn_inst = old_inst.cast(text.Inst.Fn).?; | |
| 276 | const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type); | |
| 277 | ||
| 278 | var new_func: Fn = .{ | |
| 279 | .body = std.ArrayList(*Inst).init(self.allocator), | |
| 280 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | |
| 281 | .fn_index = self.fns.items.len, | |
| 282 | }; | |
| 283 | defer new_func.body.deinit(); | |
| 284 | defer new_func.inst_table.deinit(); | |
| 285 | // Don't hang on to a reference to this when analyzing body instructions, since the memory | |
| 286 | // could become invalid. | |
| 287 | (try self.fns.addOne()).* = .{ | |
| 288 | .analysis_status = .in_progress, | |
| 289 | .body = undefined, | |
| 290 | }; | |
| 291 | ||
| 292 | for (fn_inst.positionals.body.instructions) |src_inst| { | |
| 293 | const new_inst = self.analyzeInst(src_inst, &new_func) catch |err| { | |
| 294 | self.fns.items[new_func.fn_index].analysis_status = .failure; | |
| 295 | return err; | |
| 296 | }; | |
| 297 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | |
| 298 | } | |
| 299 | ||
| 300 | self.fns.items[new_func.fn_index] = .{ | |
| 301 | .analysis_status = .success, | |
| 302 | .body = new_func.body.toOwnedSlice(), | |
| 303 | }; | |
| 304 | ||
| 305 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); | |
| 306 | fn_payload.* = .{ .index = new_func.fn_index }; | |
| 307 | ||
| 308 | return self.constInst(old_inst.src, .{ | |
| 309 | .ty = fn_type, | |
| 310 | .val = Value.initPayload(&fn_payload.base), | |
| 311 | }); | |
| 312 | }, | |
| 281 | .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?), | |
| 313 | 282 | .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 314 | 283 | .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 315 | .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | |
| 284 | .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?), | |
| 285 | } | |
| 286 | } | |
| 287 | ||
| 288 | fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst { | |
| 289 | const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type); | |
| 290 | ||
| 291 | var new_func: Fn = .{ | |
| 292 | .body = std.ArrayList(*Inst).init(self.allocator), | |
| 293 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | |
| 294 | .fn_index = self.fns.items.len, | |
| 295 | }; | |
| 296 | defer new_func.body.deinit(); | |
| 297 | defer new_func.inst_table.deinit(); | |
| 298 | // Don't hang on to a reference to this when analyzing body instructions, since the memory | |
| 299 | // could become invalid. | |
| 300 | (try self.fns.addOne()).* = .{ | |
| 301 | .analysis_status = .in_progress, | |
| 302 | .body = undefined, | |
| 303 | }; | |
| 304 | ||
| 305 | for (fn_inst.positionals.body.instructions) |src_inst| { | |
| 306 | const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| { | |
| 307 | self.fns.items[new_func.fn_index].analysis_status = .failure; | |
| 308 | return err; | |
| 309 | }; | |
| 310 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | |
| 311 | } | |
| 312 | ||
| 313 | self.fns.items[new_func.fn_index] = .{ | |
| 314 | .analysis_status = .success, | |
| 315 | .body = new_func.body.toOwnedSlice(), | |
| 316 | }; | |
| 317 | ||
| 318 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); | |
| 319 | fn_payload.* = .{ .index = new_func.fn_index }; | |
| 320 | ||
| 321 | return self.constInst(fn_inst.base.src, .{ | |
| 322 | .ty = fn_type, | |
| 323 | .val = Value.initPayload(&fn_payload.base), | |
| 324 | }); | |
| 325 | } | |
| 326 | ||
| 327 | fn analyzeInstFnType(self: *Analyze, opt_func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst { | |
| 328 | const return_type = try self.resolveType(opt_func, fntype.positionals.return_type); | |
| 329 | ||
| 330 | if (return_type.zigTypeTag() == .NoReturn and | |
| 331 | fntype.positionals.param_types.len == 0 and | |
| 332 | fntype.kw_args.cc == .Naked) | |
| 333 | { | |
| 334 | return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); | |
| 316 | 335 | } |
| 336 | ||
| 337 | return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{}); | |
| 317 | 338 | } |
| 318 | 339 | |
| 319 | 340 | fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
src-self-hosted/type.zig+49-3| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const Value = @import("value.zig").Value; |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | |
| 4 | 5 | |
| 5 | 6 | /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. |
| 6 | 7 | /// It's important for this struct to be small. |
| ... | ... | @@ -48,6 +49,8 @@ pub const Type = extern union { |
| 48 | 49 | .@"comptime_float" => return .ComptimeFloat, |
| 49 | 50 | .@"noreturn" => return .NoReturn, |
| 50 | 51 | |
| 52 | .fn_naked_noreturn_no_args => return .Fn, | |
| 53 | ||
| 51 | 54 | .array, .array_u8_sentinel_0 => return .Array, |
| 52 | 55 | .single_const_pointer => return .Pointer, |
| 53 | 56 | .const_slice_u8 => return .Pointer, |
| ... | ... | @@ -122,6 +125,7 @@ pub const Type = extern union { |
| 122 | 125 | => return out_stream.writeAll(@tagName(t)), |
| 123 | 126 | |
| 124 | 127 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 128 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | |
| 125 | 129 | |
| 126 | 130 | .array_u8_sentinel_0 => { |
| 127 | 131 | const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise); |
| ... | ... | @@ -144,6 +148,43 @@ pub const Type = extern union { |
| 144 | 148 | } |
| 145 | 149 | } |
| 146 | 150 | |
| 151 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { | |
| 152 | switch (self.tag()) { | |
| 153 | .@"u8" => return Value.initTag(.u8_type), | |
| 154 | .@"i8" => return Value.initTag(.i8_type), | |
| 155 | .@"isize" => return Value.initTag(.isize_type), | |
| 156 | .@"usize" => return Value.initTag(.usize_type), | |
| 157 | .@"c_short" => return Value.initTag(.c_short_type), | |
| 158 | .@"c_ushort" => return Value.initTag(.c_ushort_type), | |
| 159 | .@"c_int" => return Value.initTag(.c_int_type), | |
| 160 | .@"c_uint" => return Value.initTag(.c_uint_type), | |
| 161 | .@"c_long" => return Value.initTag(.c_long_type), | |
| 162 | .@"c_ulong" => return Value.initTag(.c_ulong_type), | |
| 163 | .@"c_longlong" => return Value.initTag(.c_longlong_type), | |
| 164 | .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type), | |
| 165 | .@"c_longdouble" => return Value.initTag(.c_longdouble_type), | |
| 166 | .@"c_void" => return Value.initTag(.c_void_type), | |
| 167 | .@"f16" => return Value.initTag(.f16_type), | |
| 168 | .@"f32" => return Value.initTag(.f32_type), | |
| 169 | .@"f64" => return Value.initTag(.f64_type), | |
| 170 | .@"f128" => return Value.initTag(.f128_type), | |
| 171 | .@"bool" => return Value.initTag(.bool_type), | |
| 172 | .@"void" => return Value.initTag(.void_type), | |
| 173 | .@"type" => return Value.initTag(.type_type), | |
| 174 | .@"anyerror" => return Value.initTag(.anyerror_type), | |
| 175 | .@"comptime_int" => return Value.initTag(.comptime_int_type), | |
| 176 | .@"comptime_float" => return Value.initTag(.comptime_float_type), | |
| 177 | .@"noreturn" => return Value.initTag(.noreturn_type), | |
| 178 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), | |
| 179 | .const_slice_u8 => return Value.initTag(.const_slice_u8_type), | |
| 180 | else => { | |
| 181 | const ty_payload = try allocator.create(Value.Payload.Ty); | |
| 182 | ty_payload.* = .{ .ty = self }; | |
| 183 | return Value.initPayload(&ty_payload.base); | |
| 184 | }, | |
| 185 | } | |
| 186 | } | |
| 187 | ||
| 147 | 188 | pub fn isSinglePointer(self: Type) bool { |
| 148 | 189 | return switch (self.tag()) { |
| 149 | 190 | .@"u8", |
| ... | ... | @@ -174,6 +215,7 @@ pub const Type = extern union { |
| 174 | 215 | .array, |
| 175 | 216 | .array_u8_sentinel_0, |
| 176 | 217 | .const_slice_u8, |
| 218 | .fn_naked_noreturn_no_args, | |
| 177 | 219 | => false, |
| 178 | 220 | |
| 179 | 221 | .single_const_pointer => true, |
| ... | ... | @@ -210,6 +252,7 @@ pub const Type = extern union { |
| 210 | 252 | .array, |
| 211 | 253 | .array_u8_sentinel_0, |
| 212 | 254 | .single_const_pointer, |
| 255 | .fn_naked_noreturn_no_args, | |
| 213 | 256 | => false, |
| 214 | 257 | |
| 215 | 258 | .const_slice_u8 => true, |
| ... | ... | @@ -246,6 +289,7 @@ pub const Type = extern union { |
| 246 | 289 | .@"noreturn", |
| 247 | 290 | .array, |
| 248 | 291 | .array_u8_sentinel_0, |
| 292 | .fn_naked_noreturn_no_args, | |
| 249 | 293 | => unreachable, |
| 250 | 294 | |
| 251 | 295 | .single_const_pointer, .const_slice_u8 => true, |
| ... | ... | @@ -280,6 +324,7 @@ pub const Type = extern union { |
| 280 | 324 | .@"comptime_int", |
| 281 | 325 | .@"comptime_float", |
| 282 | 326 | .@"noreturn", |
| 327 | .fn_naked_noreturn_no_args, | |
| 283 | 328 | => unreachable, |
| 284 | 329 | |
| 285 | 330 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | ... | @@ -296,7 +341,6 @@ pub const Type = extern union { |
| 296 | 341 | /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`. |
| 297 | 342 | pub const Tag = enum { |
| 298 | 343 | // The first section of this enum are tags that require no payload. |
| 299 | const_slice_u8, | |
| 300 | 344 | @"u8", |
| 301 | 345 | @"i8", |
| 302 | 346 | @"isize", |
| ... | ... | @@ -321,14 +365,16 @@ pub const Type = extern union { |
| 321 | 365 | @"anyerror", |
| 322 | 366 | @"comptime_int", |
| 323 | 367 | @"comptime_float", |
| 324 | @"noreturn", // See last_no_payload_tag below. | |
| 368 | @"noreturn", | |
| 369 | fn_naked_noreturn_no_args, | |
| 370 | const_slice_u8, // See last_no_payload_tag below. | |
| 325 | 371 | // After this, the tag requires a payload. |
| 326 | 372 | |
| 327 | 373 | array_u8_sentinel_0, |
| 328 | 374 | array, |
| 329 | 375 | single_const_pointer, |
| 330 | 376 | |
| 331 | pub const last_no_payload_tag = Tag.@"noreturn"; | |
| 377 | pub const last_no_payload_tag = Tag.const_slice_u8; | |
| 332 | 378 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 333 | 379 | }; |
| 334 | 380 |
src-self-hosted/value.zig+77-6| ... | ... | @@ -16,10 +16,34 @@ pub const Value = extern union { |
| 16 | 16 | |
| 17 | 17 | pub const Tag = enum { |
| 18 | 18 | // The first section of this enum are tags that require no payload. |
| 19 | u8_type, | |
| 20 | i8_type, | |
| 21 | isize_type, | |
| 22 | usize_type, | |
| 23 | c_short_type, | |
| 24 | c_ushort_type, | |
| 25 | c_int_type, | |
| 26 | c_uint_type, | |
| 27 | c_long_type, | |
| 28 | c_ulong_type, | |
| 29 | c_longlong_type, | |
| 30 | c_ulonglong_type, | |
| 31 | c_longdouble_type, | |
| 32 | f16_type, | |
| 33 | f32_type, | |
| 34 | f64_type, | |
| 35 | f128_type, | |
| 36 | c_void_type, | |
| 37 | bool_type, | |
| 19 | 38 | void_type, |
| 39 | type_type, | |
| 40 | anyerror_type, | |
| 41 | comptime_int_type, | |
| 42 | comptime_float_type, | |
| 20 | 43 | noreturn_type, |
| 21 | bool_type, | |
| 22 | usize_type, | |
| 44 | fn_naked_noreturn_no_args_type, | |
| 45 | const_slice_u8_type, | |
| 46 | ||
| 23 | 47 | void_value, |
| 24 | 48 | noreturn_value, |
| 25 | 49 | bool_true, |
| ... | ... | @@ -74,10 +98,34 @@ pub const Value = extern union { |
| 74 | 98 | ) !void { |
| 75 | 99 | comptime assert(fmt.len == 0); |
| 76 | 100 | switch (self.tag()) { |
| 101 | .u8_type => return out_stream.writeAll("u8"), | |
| 102 | .i8_type => return out_stream.writeAll("i8"), | |
| 103 | .isize_type => return out_stream.writeAll("isize"), | |
| 104 | .usize_type => return out_stream.writeAll("usize"), | |
| 105 | .c_short_type => return out_stream.writeAll("c_short"), | |
| 106 | .c_ushort_type => return out_stream.writeAll("c_ushort"), | |
| 107 | .c_int_type => return out_stream.writeAll("c_int"), | |
| 108 | .c_uint_type => return out_stream.writeAll("c_uint"), | |
| 109 | .c_long_type => return out_stream.writeAll("c_long"), | |
| 110 | .c_ulong_type => return out_stream.writeAll("c_ulong"), | |
| 111 | .c_longlong_type => return out_stream.writeAll("c_longlong"), | |
| 112 | .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"), | |
| 113 | .c_longdouble_type => return out_stream.writeAll("c_longdouble"), | |
| 114 | .f16_type => return out_stream.writeAll("f16"), | |
| 115 | .f32_type => return out_stream.writeAll("f32"), | |
| 116 | .f64_type => return out_stream.writeAll("f64"), | |
| 117 | .f128_type => return out_stream.writeAll("f128"), | |
| 118 | .c_void_type => return out_stream.writeAll("c_void"), | |
| 119 | .bool_type => return out_stream.writeAll("bool"), | |
| 77 | 120 | .void_type => return out_stream.writeAll("void"), |
| 121 | .type_type => return out_stream.writeAll("type"), | |
| 122 | .anyerror_type => return out_stream.writeAll("anyerror"), | |
| 123 | .comptime_int_type => return out_stream.writeAll("comptime_int"), | |
| 124 | .comptime_float_type => return out_stream.writeAll("comptime_float"), | |
| 78 | 125 | .noreturn_type => return out_stream.writeAll("noreturn"), |
| 79 | .bool_type => return out_stream.writeAll("bool"), | |
| 80 | .usize_type => return out_stream.writeAll("usize"), | |
| 126 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | |
| 127 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), | |
| 128 | ||
| 81 | 129 | .void_value => return out_stream.writeAll("{}"), |
| 82 | 130 | .noreturn_value => return out_stream.writeAll("unreachable"), |
| 83 | 131 | .bool_true => return out_stream.writeAll("true"), |
| ... | ... | @@ -105,10 +153,33 @@ pub const Value = extern union { |
| 105 | 153 | return switch (self.tag()) { |
| 106 | 154 | .ty => self.cast(Payload.Ty).?.ty, |
| 107 | 155 | |
| 156 | .u8_type => Type.initTag(.@"u8"), | |
| 157 | .i8_type => Type.initTag(.@"i8"), | |
| 158 | .isize_type => Type.initTag(.@"isize"), | |
| 159 | .usize_type => Type.initTag(.@"usize"), | |
| 160 | .c_short_type => Type.initTag(.@"c_short"), | |
| 161 | .c_ushort_type => Type.initTag(.@"c_ushort"), | |
| 162 | .c_int_type => Type.initTag(.@"c_int"), | |
| 163 | .c_uint_type => Type.initTag(.@"c_uint"), | |
| 164 | .c_long_type => Type.initTag(.@"c_long"), | |
| 165 | .c_ulong_type => Type.initTag(.@"c_ulong"), | |
| 166 | .c_longlong_type => Type.initTag(.@"c_longlong"), | |
| 167 | .c_ulonglong_type => Type.initTag(.@"c_ulonglong"), | |
| 168 | .c_longdouble_type => Type.initTag(.@"c_longdouble"), | |
| 169 | .f16_type => Type.initTag(.@"f16"), | |
| 170 | .f32_type => Type.initTag(.@"f32"), | |
| 171 | .f64_type => Type.initTag(.@"f64"), | |
| 172 | .f128_type => Type.initTag(.@"f128"), | |
| 173 | .c_void_type => Type.initTag(.@"c_void"), | |
| 174 | .bool_type => Type.initTag(.@"bool"), | |
| 108 | 175 | .void_type => Type.initTag(.@"void"), |
| 176 | .type_type => Type.initTag(.@"type"), | |
| 177 | .anyerror_type => Type.initTag(.@"anyerror"), | |
| 178 | .comptime_int_type => Type.initTag(.@"comptime_int"), | |
| 179 | .comptime_float_type => Type.initTag(.@"comptime_float"), | |
| 109 | 180 | .noreturn_type => Type.initTag(.@"noreturn"), |
| 110 | .bool_type => Type.initTag(.@"bool"), | |
| 111 | .usize_type => Type.initTag(.@"usize"), | |
| 181 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), | |
| 182 | .const_slice_u8_type => Type.initTag(.const_slice_u8), | |
| 112 | 183 | |
| 113 | 184 | .void_value, |
| 114 | 185 | .noreturn_value, |