diff options
Diffstat (limited to 'src/zir.zig')
| -rw-r--r-- | src/zir.zig | 1610 |
1 files changed, 27 insertions, 1583 deletions
diff --git a/src/zir.zig b/src/zir.zig index 0ccb09efac..0e7b3a3520 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -12,17 +12,6 @@ const TypedValue = @import("TypedValue.zig"); const ir = @import("ir.zig"); const IrModule = @import("Module.zig"); -/// This struct is relevent only for the ZIR Module text format. It is not used for -/// semantic analysis of Zig source code. -pub const Decl = struct { - name: []const u8, - - /// Hash of slice into the source of the part after the = and before the next instruction. - contents_hash: std.zig.SrcHash, - - inst: *Inst, -}; - /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for /// in-memory, analyzed instructions with types and values. /// We use a table to map these instruction to their respective semantically analyzed @@ -141,15 +130,12 @@ pub const Inst = struct { container_field, /// Declares the beginning of a statement. Used for debug info. dbg_stmt, - /// Represents a pointer to a global decl by name. + /// Represents a pointer to a global decl. declref, /// Represents a pointer to a global decl by string name. declref_str, - /// The syntax `@foo` is equivalent to `declval("foo")`. - /// declval is equivalent to declref followed by deref. + /// Equivalent to a declref followed by deref. declval, - /// Same as declval but the parameter is a `*Module.Decl` rather than a name. - declval_in_module, /// Load the value from a pointer. deref, /// Arithmetic division. Asserts no integer overflow. @@ -419,7 +405,6 @@ pub const Inst = struct { .declref => DeclRef, .declref_str => DeclRefStr, .declval => DeclVal, - .declval_in_module => DeclValInModule, .coerce_result_block_ptr => CoerceResultBlockPtr, .compilelog => CompileLog, .loop => Loop, @@ -496,7 +481,6 @@ pub const Inst = struct { .declref, .declref_str, .declval, - .declval_in_module, .deref, .div, .elemptr, @@ -650,7 +634,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - body: Module.Body, + body: Body, }, kw_args: struct {}, }; @@ -705,7 +689,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - name: []const u8, + decl: *IrModule.Decl, }, kw_args: struct {}, }; @@ -725,16 +709,6 @@ pub const Inst = struct { base: Inst, positionals: struct { - name: []const u8, - }, - kw_args: struct {}, - }; - - pub const DeclValInModule = struct { - pub const base_tag = Tag.declval_in_module; - base: Inst, - - positionals: struct { decl: *IrModule.Decl, }, kw_args: struct {}, @@ -758,10 +732,7 @@ pub const Inst = struct { positionals: struct { to_log: []*Inst, }, - kw_args: struct { - /// If we have seen it already so don't make another error - seen: bool = false, - }, + kw_args: struct {}, }; pub const Const = struct { @@ -799,7 +770,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - body: Module.Body, + body: Body, }, kw_args: struct {}, }; @@ -838,7 +809,7 @@ pub const Inst = struct { positionals: struct { fn_type: *Inst, - body: Module.Body, + body: Body, }, kw_args: struct { is_inline: bool = false, @@ -998,8 +969,8 @@ pub const Inst = struct { positionals: struct { condition: *Inst, - then_body: Module.Body, - else_body: Module.Body, + then_body: Body, + else_body: Body, }, kw_args: struct {}, }; @@ -1078,7 +1049,7 @@ pub const Inst = struct { /// List of all individual items and ranges items: []*Inst, cases: []Case, - else_body: Module.Body, + else_body: Body, }, kw_args: struct { /// Pointer to first range if such exists. @@ -1092,7 +1063,7 @@ pub const Inst = struct { pub const Case = struct { item: *Inst, - body: Module.Body, + body: Body, }; }; pub const TypeOfPeer = struct { @@ -1192,6 +1163,10 @@ pub const ErrorMsg = struct { msg: []const u8, }; +pub const Body = struct { + instructions: []*Inst, +}; + pub const Module = struct { decls: []*Decl, arena: std.heap.ArenaAllocator, @@ -1199,6 +1174,15 @@ pub const Module = struct { metadata: std.AutoHashMap(*Inst, MetaData), body_metadata: std.AutoHashMap(*Body, BodyMetaData), + pub const Decl = struct { + name: []const u8, + + /// Hash of slice into the source of the part after the = and before the next instruction. + contents_hash: std.zig.SrcHash, + + inst: *Inst, + }; + pub const MetaData = struct { deaths: ir.Inst.DeathsInt, addr: usize, @@ -1208,10 +1192,6 @@ pub const Module = struct { deaths: []*Inst, }; - pub const Body = struct { - instructions: []*Inst, - }; - pub fn deinit(self: *Module, allocator: *Allocator) void { self.metadata.deinit(); self.body_metadata.deinit(); @@ -1369,7 +1349,7 @@ const Writer = struct { } try stream.writeByte(']'); }, - Module.Body => { + Body => { try stream.writeAll("{\n"); if (self.module.body_metadata.get(param_ptr)) |metadata| { if (metadata.deaths.len > 0) { @@ -1468,8 +1448,6 @@ const Writer = struct { try stream.print("@{s}", .{info.name}); } } else if (inst.cast(Inst.DeclVal)) |decl_val| { - try stream.print("@{s}", .{decl_val.positionals.name}); - } else if (inst.cast(Inst.DeclValInModule)) |decl_val| { try stream.print("@{s}", .{decl_val.positionals.decl.name}); } else { // This should be unreachable in theory, but since ZIR is used for debugging the compiler @@ -1479,502 +1457,6 @@ const Writer = struct { } }; -pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module { - var global_name_map = std.StringHashMap(*Inst).init(allocator); - defer global_name_map.deinit(); - - var parser: Parser = .{ - .allocator = allocator, - .arena = std.heap.ArenaAllocator.init(allocator), - .i = 0, - .source = source, - .global_name_map = &global_name_map, - .decls = .{}, - .unnamed_index = 0, - .block_table = std.StringHashMap(*Inst.Block).init(allocator), - .loop_table = std.StringHashMap(*Inst.Loop).init(allocator), - }; - defer parser.block_table.deinit(); - defer parser.loop_table.deinit(); - errdefer parser.arena.deinit(); - - parser.parseRoot() catch |err| switch (err) { - error.ParseFailure => { - assert(parser.error_msg != null); - }, - else => |e| return e, - }; - - return Module{ - .decls = parser.decls.toOwnedSlice(allocator), - .arena = parser.arena, - .error_msg = parser.error_msg, - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), - }; -} - -const Parser = struct { - allocator: *Allocator, - arena: std.heap.ArenaAllocator, - i: usize, - source: [:0]const u8, - decls: std.ArrayListUnmanaged(*Decl), - global_name_map: *std.StringHashMap(*Inst), - error_msg: ?ErrorMsg = null, - unnamed_index: usize, - block_table: std.StringHashMap(*Inst.Block), - loop_table: std.StringHashMap(*Inst.Loop), - - const Body = struct { - instructions: std.ArrayList(*Inst), - name_map: *std.StringHashMap(*Inst), - }; - - fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body { - var name_map = std.StringHashMap(*Inst).init(self.allocator); - defer name_map.deinit(); - - var body_context = Body{ - .instructions = std.ArrayList(*Inst).init(self.allocator), - .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map, - }; - defer body_context.instructions.deinit(); - - try requireEatBytes(self, "{"); - skipSpace(self); - - while (true) : (self.i += 1) switch (self.source[self.i]) { - ';' => _ = try skipToAndOver(self, '\n'), - '%' => { - self.i += 1; - const ident = try skipToAndOver(self, ' '); - skipSpace(self); - try requireEatBytes(self, "="); - skipSpace(self); - const decl = try parseInstruction(self, &body_context, ident); - const ident_index = body_context.instructions.items.len; - if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{s}'", .{ident}); - } - try body_context.instructions.append(decl.inst); - continue; - }, - ' ', '\n' => continue, - '}' => { - self.i += 1; - break; - }, - else => |byte| return self.failByte(byte), - }; - - // Move the instructions to the arena - const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); - mem.copy(*Inst, instrs, body_context.instructions.items); - return Module.Body{ .instructions = instrs }; - } - - fn parseStringLiteral(self: *Parser) ![]u8 { - const start = self.i; - try self.requireEatBytes("\""); - - while (true) : (self.i += 1) switch (self.source[self.i]) { - '"' => { - self.i += 1; - const span = self.source[start..self.i]; - var bad_index: usize = undefined; - const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) { - error.InvalidCharacter => { - self.i = start + bad_index; - const bad_byte = self.source[self.i]; - return self.fail("invalid string literal character: '{c}'\n", .{bad_byte}); - }, - else => |e| return e, - }; - return parsed; - }, - '\\' => { - self.i += 1; - continue; - }, - 0 => return self.failByte(0), - else => continue, - }; - } - - fn parseIntegerLiteral(self: *Parser) !BigIntConst { - const start = self.i; - if (self.source[self.i] == '-') self.i += 1; - while (true) : (self.i += 1) switch (self.source[self.i]) { - '0'...'9' => continue, - else => break, - }; - const number_text = self.source[start..self.i]; - const base = 10; - // TODO reuse the same array list for this - const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len); - const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len); - defer self.allocator.free(limbs_buffer); - const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len); - const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len); - var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) { - error.InvalidCharacter => { - self.i = start; - return self.fail("invalid digit in integer literal", .{}); - }, - }; - return result.toConst(); - } - - fn parseRoot(self: *Parser) !void { - // The IR format is designed so that it can be tokenized and parsed at the same time. - while (true) { - switch (self.source[self.i]) { - ';' => _ = try skipToAndOver(self, '\n'), - '@' => { - self.i += 1; - const ident = try skipToAndOver(self, ' '); - skipSpace(self); - try requireEatBytes(self, "="); - skipSpace(self); - const decl = try parseInstruction(self, null, ident); - const ident_index = self.decls.items.len; - if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{s}'", .{ident}); - } - try self.decls.append(self.allocator, decl); - }, - ' ', '\n' => self.i += 1, - 0 => break, - else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), - } - } - } - - fn eatByte(self: *Parser, byte: u8) bool { - if (self.source[self.i] != byte) return false; - self.i += 1; - return true; - } - - fn skipSpace(self: *Parser) void { - while (self.source[self.i] == ' ' or self.source[self.i] == '\n') { - self.i += 1; - } - } - - fn requireEatBytes(self: *Parser, bytes: []const u8) !void { - const start = self.i; - for (bytes) |byte| { - if (self.source[self.i] != byte) { - self.i = start; - return self.fail("expected '{s}'", .{bytes}); - } - self.i += 1; - } - } - - fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 { - const start_i = self.i; - while (self.source[self.i] != 0) : (self.i += 1) { - if (self.source[self.i] == byte) { - const result = self.source[start_i..self.i]; - self.i += 1; - return result; - } - } - return self.fail("unexpected EOF", .{}); - } - - /// ParseFailure is an internal error code; handled in `parse`. - const InnerError = error{ ParseFailure, OutOfMemory }; - - fn failByte(self: *Parser, byte: u8) InnerError { - if (byte == 0) { - return self.fail("unexpected EOF", .{}); - } else { - return self.fail("unexpected byte: '{c}'", .{byte}); - } - } - - fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError { - @setCold(true); - self.error_msg = ErrorMsg{ - .byte_offset = self.i, - .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args), - }; - return error.ParseFailure; - } - - fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl { - const contents_start = self.i; - const fn_name = try skipToAndOver(self, '('); - inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { - if (mem.eql(u8, field.name, fn_name)) { - const tag = @field(Inst.Tag, field.name); - return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start); - } - } - return self.fail("unknown instruction '{s}'", .{fn_name}); - } - - fn parseInstructionGeneric( - self: *Parser, - comptime fn_name: []const u8, - comptime InstType: type, - tag: Inst.Tag, - body_ctx: ?*Body, - inst_name: []const u8, - contents_start: usize, - ) InnerError!*Decl { - const inst_specific = try self.arena.allocator.create(InstType); - inst_specific.base = .{ - .src = self.i, - .tag = tag, - }; - - if (InstType == Inst.Block) { - try self.block_table.put(inst_name, inst_specific); - } else if (InstType == Inst.Loop) { - try self.loop_table.put(inst_name, inst_specific); - } - - if (@hasField(InstType, "ty")) { - inst_specific.ty = opt_type orelse { - return self.fail("instruction '" ++ fn_name ++ "' requires type", .{}); - }; - } - - const Positionals = @TypeOf(inst_specific.positionals); - inline for (@typeInfo(Positionals).Struct.fields) |arg_field| { - if (self.source[self.i] == ',') { - self.i += 1; - skipSpace(self); - } else if (self.source[self.i] == ')') { - return self.fail("expected positional parameter '{s}'", .{arg_field.name}); - } - @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric( - self, - arg_field.field_type, - body_ctx, - ); - skipSpace(self); - } - - const KW_Args = @TypeOf(inst_specific.kw_args); - inst_specific.kw_args = .{}; // assign defaults - skipSpace(self); - while (eatByte(self, ',')) { - skipSpace(self); - const name = try skipToAndOver(self, '='); - inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { - const field_name = arg_field.name; - if (mem.eql(u8, name, field_name)) { - const NonOptional = switch (@typeInfo(arg_field.field_type)) { - .Optional => |info| info.child, - else => arg_field.field_type, - }; - @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx); - break; - } - } else { - return self.fail("unrecognized keyword parameter: '{s}'", .{name}); - } - skipSpace(self); - } - try requireEatBytes(self, ")"); - - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = inst_name, - .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]), - .inst = &inst_specific.base, - }; - - return decl; - } - - fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T { - if (@typeInfo(T) == .Enum) { - const start = self.i; - while (true) : (self.i += 1) switch (self.source[self.i]) { - ' ', '\n', ',', ')' => { - const enum_name = self.source[start..self.i]; - return std.meta.stringToEnum(T, enum_name) orelse { - return self.fail("tag '{s}' not a member of enum '{s}'", .{ enum_name, @typeName(T) }); - }; - }, - 0 => return self.failByte(0), - else => continue, - }; - } - switch (T) { - Module.Body => return parseBody(self, body_ctx), - bool => { - const bool_value = switch (self.source[self.i]) { - '0' => false, - '1' => true, - else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}), - }; - self.i += 1; - return bool_value; - }, - []*Inst => { - try requireEatBytes(self, "["); - skipSpace(self); - if (eatByte(self, ']')) return &[0]*Inst{}; - - var instructions = std.ArrayList(*Inst).init(&self.arena.allocator); - while (true) { - skipSpace(self); - try instructions.append(try parseParameterInst(self, body_ctx)); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - try requireEatBytes(self, "]"); - return instructions.toOwnedSlice(); - }, - *Inst => return parseParameterInst(self, body_ctx), - []u8, []const u8 => return self.parseStringLiteral(), - BigIntConst => return self.parseIntegerLiteral(), - usize => { - const big_int = try self.parseIntegerLiteral(); - return big_int.to(usize) catch |err| return self.fail("integer literal: {s}", .{@errorName(err)}); - }, - TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), - *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), - *Inst.Block => { - const name = try self.parseStringLiteral(); - return self.block_table.get(name).?; - }, - *Inst.Loop => { - const name = try self.parseStringLiteral(); - return self.loop_table.get(name).?; - }, - [][]const u8 => { - try requireEatBytes(self, "["); - skipSpace(self); - if (eatByte(self, ']')) return &[0][]const u8{}; - - var strings = std.ArrayList([]const u8).init(&self.arena.allocator); - while (true) { - skipSpace(self); - try strings.append(try self.parseStringLiteral()); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - try requireEatBytes(self, "]"); - return strings.toOwnedSlice(); - }, - []Inst.SwitchBr.Case => { - try requireEatBytes(self, "{"); - skipSpace(self); - if (eatByte(self, '}')) return &[0]Inst.SwitchBr.Case{}; - - var cases = std.ArrayList(Inst.SwitchBr.Case).init(&self.arena.allocator); - while (true) { - const cur = try cases.addOne(); - skipSpace(self); - cur.item = try self.parseParameterGeneric(*Inst, body_ctx); - skipSpace(self); - try requireEatBytes(self, "=>"); - cur.body = try self.parseBody(body_ctx); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - skipSpace(self); - try requireEatBytes(self, "}"); - return cases.toOwnedSlice(); - }, - else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), - } - return self.fail("TODO parse parameter {s}", .{@typeName(T)}); - } - - fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst { - const local_ref = switch (self.source[self.i]) { - '@' => false, - '%' => true, - else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), - }; - const map = if (local_ref) - if (body_ctx) |bc| - bc.name_map - else - return self.fail("referencing a % instruction in global scope", .{}) - else - self.global_name_map; - - self.i += 1; - const name_start = self.i; - while (true) : (self.i += 1) switch (self.source[self.i]) { - 0, ' ', '\n', ',', ')', ']' => break, - else => continue, - }; - const ident = self.source[name_start..self.i]; - return map.get(ident) orelse { - const bad_name = self.source[name_start - 1 .. self.i]; - const src = name_start - 1; - if (local_ref) { - self.i = src; - return self.fail("unrecognized identifier: {s}", .{bad_name}); - } else { - const declval = try self.arena.allocator.create(Inst.DeclVal); - declval.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclVal.base_tag, - }, - .positionals = .{ .name = ident }, - .kw_args = .{}, - }; - return &declval.base; - } - }; - } - - fn generateName(self: *Parser) ![]u8 { - const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.unnamed_index}); - self.unnamed_index += 1; - return result; - } -}; - -pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { - var ctx: EmitZIR = .{ - .allocator = allocator, - .decls = .{}, - .arena = std.heap.ArenaAllocator.init(allocator), - .old_module = old_module, - .next_auto_name = 0, - .names = std.StringArrayHashMap(void).init(allocator), - .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), - .indent = 0, - .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), - .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), - }; - errdefer ctx.metadata.deinit(); - errdefer ctx.body_metadata.deinit(); - defer ctx.block_table.deinit(); - defer ctx.loop_table.deinit(); - defer ctx.decls.deinit(allocator); - defer ctx.names.deinit(); - defer ctx.primitive_table.deinit(); - errdefer ctx.arena.deinit(); - - try ctx.emit(); - - return Module{ - .decls = ctx.decls.toOwnedSlice(allocator), - .arena = ctx.arena, - .metadata = ctx.metadata, - .body_metadata = ctx.body_metadata, - }; -} - /// For debugging purposes, prints a function representation to stderr. pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { const allocator = old_module.gpa; @@ -2374,1052 +1856,14 @@ const DumpTzir = struct { } }; -const EmitZIR = struct { - allocator: *Allocator, - arena: std.heap.ArenaAllocator, - old_module: *const IrModule, - decls: std.ArrayListUnmanaged(*Decl), - names: std.StringArrayHashMap(void), - next_auto_name: usize, - primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl), - indent: usize, - block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block), - loop_table: std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop), - metadata: std.AutoHashMap(*Inst, Module.MetaData), - body_metadata: std.AutoHashMap(*Module.Body, Module.BodyMetaData), - - fn emit(self: *EmitZIR) !void { - // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced - // by the hash table. - var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); - defer src_decls.deinit(); - try src_decls.ensureCapacity(self.old_module.decl_table.items().len); - try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len); - try self.names.ensureCapacity(self.old_module.decl_table.items().len); - - for (self.old_module.decl_table.items()) |entry| { - const decl = entry.value; - src_decls.appendAssumeCapacity(decl); - self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); - } - std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { - fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { - return a.src_index < b.src_index; - } - }).lessThan); - - // Emit all the decls. - for (src_decls.items) |ir_decl| { - switch (ir_decl.analysis) { - .unreferenced => continue, - - .complete => {}, - .codegen_failure => {}, // We still can emit the ZIR. - .codegen_failure_retryable => {}, // We still can emit the ZIR. - - .in_progress => unreachable, - .outdated => unreachable, - - .sema_failure, - .sema_failure_retryable, - .dependency_failure, - => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| { - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = ir_decl.src(), - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, err_msg.msg); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = ir_decl.src(), - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = err_msg.msg, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = mem.spanZ(ir_decl.name), - .contents_hash = undefined, - .inst = &fail_inst.base, - }; - try self.decls.append(self.allocator, decl); - continue; - }, - } - if (self.old_module.export_owners.get(ir_decl)) |exports| { - for (exports) |module_export| { - const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); - const export_inst = try self.arena.allocator.create(Inst.Export); - export_inst.* = .{ - .base = .{ - .src = module_export.src, - .tag = Inst.Export.base_tag, - }, - .positionals = .{ - .symbol_name = symbol_name.inst, - .decl_name = mem.spanZ(module_export.exported_decl.name), - }, - .kw_args = .{}, - }; - _ = try self.emitUnnamedDecl(&export_inst.base); - } - } - const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value); - new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); - } - } - - const ZirBody = struct { - inst_table: *std.AutoHashMap(*ir.Inst, *Inst), - instructions: *std.ArrayList(*Inst), - }; - - fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst { - if (inst.cast(ir.Inst.Constant)) |const_inst| { - const new_inst = if (const_inst.val.castTag(.function)) |func_pl| blk: { - const owner_decl = func_pl.data.owner_decl; - break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); - } else if (const_inst.val.castTag(.decl_ref)) |declref| blk: { - const decl_ref = try self.emitDeclRef(inst.src, declref.data); - try new_body.instructions.append(decl_ref); - break :blk decl_ref; - } else if (const_inst.val.castTag(.variable)) |var_pl| blk: { - const owner_decl = var_pl.data.owner_decl; - break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); - } else blk: { - break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst; - }; - _ = try new_body.inst_table.put(inst, new_inst); - return new_inst; - } else { - return new_body.inst_table.get(inst).?; - } - } - - fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst { - const declval = try self.arena.allocator.create(Inst.DeclVal); - declval.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclVal.base_tag, - }, - .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) }, - .kw_args = .{}, - }; - return &declval.base; - } - - fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl { - const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); - const int_inst = try self.arena.allocator.create(Inst.Int); - int_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Int.base_tag, - }, - .positionals = .{ - .int = val.toBigInt(big_int_space), - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&int_inst.base); - } - - fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst { - const declref_inst = try self.arena.allocator.create(Inst.DeclRef); - declref_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclRef.base_tag, - }, - .positionals = .{ - .name = mem.spanZ(module_decl.name), - }, - .kw_args = .{}, - }; - return &declref_inst.base; - } - - fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl { - var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator); - defer inst_table.deinit(); - - var instructions = std.ArrayList(*Inst).init(self.allocator); - defer instructions.deinit(); - - switch (module_fn.state) { - .queued => unreachable, - .in_progress => unreachable, - .inline_only => unreachable, - .success => { - try self.emitBody(module_fn.body, &inst_table, &instructions); - }, - .sema_failure => { - const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = src, - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, err_msg.msg); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = msg_str, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - try instructions.append(&fail_inst.base); - }, - .dependency_failure => { - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = src, - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, "depends on another failed Decl"); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = msg_str, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - try instructions.append(&fail_inst.base); - }, - } - - const fn_type = try self.emitType(src, ty); - - const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); - mem.copy(*Inst, arena_instrs, instructions.items); - - const fn_inst = try self.arena.allocator.create(Inst.Fn); - fn_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Fn.base_tag, - }, - .positionals = .{ - .fn_type = fn_type.inst, - .body = .{ .instructions = arena_instrs }, - }, - .kw_args = .{ - .is_inline = module_fn.state == .inline_only, - }, - }; - return self.emitUnnamedDecl(&fn_inst.base); - } - - fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl { - const allocator = &self.arena.allocator; - if (typed_value.val.castTag(.decl_ref)) |decl_ref| { - const decl = decl_ref.data; - return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl)); - } else if (typed_value.val.castTag(.variable)) |variable| { - return self.emitTypedValue(src, .{ - .ty = typed_value.ty, - .val = variable.data.init, - }); - } - if (typed_value.val.isUndef()) { - const as_inst = try self.arena.allocator.create(Inst.BinOp); - as_inst.* = .{ - .base = .{ - .tag = .as, - .src = src, - }, - .positionals = .{ - .lhs = (try self.emitType(src, typed_value.ty)).inst, - .rhs = (try self.emitPrimitive(src, .@"undefined")).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&as_inst.base); - } - switch (typed_value.ty.zigTypeTag()) { - .Pointer => { - const ptr_elem_type = typed_value.ty.elemType(); - switch (ptr_elem_type.zigTypeTag()) { - .Array => { - // TODO more checks to make sure this can be emitted as a string literal - //const array_elem_type = ptr_elem_type.elemType(); - //if (array_elem_type.eql(Type.initTag(.u8)) and - // ptr_elem_type.hasSentinel(Value.initTag(.zero))) - //{ - //} - const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { - error.AnalysisFail => unreachable, - else => |e| return e, - }; - return self.emitStringLiteral(src, bytes); - }, - else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {s}", .{@tagName(t)}), - } - }, - .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val), - .Int => { - const as_inst = try self.arena.allocator.create(Inst.BinOp); - as_inst.* = .{ - .base = .{ - .tag = .as, - .src = src, - }, - .positionals = .{ - .lhs = (try self.emitType(src, typed_value.ty)).inst, - .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&as_inst.base); - }, - .Type => { - const ty = try typed_value.val.toType(&self.arena.allocator); - return self.emitType(src, ty); - }, - .Fn => { - const module_fn = typed_value.val.castTag(.function).?.data; - return self.emitFn(module_fn, src, typed_value.ty); - }, - .Array => { - // TODO more checks to make sure this can be emitted as a string literal - //const array_elem_type = ptr_elem_type.elemType(); - //if (array_elem_type.eql(Type.initTag(.u8)) and - // ptr_elem_type.hasSentinel(Value.initTag(.zero))) - //{ - //} - const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { - error.AnalysisFail => unreachable, - else => |e| return e, - }; - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = bytes, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&str_inst.base); - }, - .Void => return self.emitPrimitive(src, .void_value), - .Bool => if (typed_value.val.toBool()) - return self.emitPrimitive(src, .@"true") - else - return self.emitPrimitive(src, .@"false"), - .EnumLiteral => { - const enum_literal = typed_value.val.castTag(.enum_literal).?; - const inst = try self.arena.allocator.create(Inst.Str); - inst.* = .{ - .base = .{ - .src = src, - .tag = .enum_literal, - }, - .positionals = .{ - .bytes = enum_literal.data, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - }, - else => |t| std.debug.panic("TODO implement emitTypedValue for {s}", .{@tagName(t)}), - } - } - - fn emitNoOp(self: *EmitZIR, src: usize, old_inst: *ir.Inst.NoOp, tag: Inst.Tag) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.NoOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{}, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitUnOp( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.UnOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.UnOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitBinOp( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.BinOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.BinOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .lhs = try self.resolveInst(new_body, old_inst.lhs), - .rhs = try self.resolveInst(new_body, old_inst.rhs), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitCast( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.UnOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.BinOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst, - .rhs = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitBody( - self: *EmitZIR, - body: ir.Body, - inst_table: *std.AutoHashMap(*ir.Inst, *Inst), - instructions: *std.ArrayList(*Inst), - ) Allocator.Error!void { - const new_body = ZirBody{ - .inst_table = inst_table, - .instructions = instructions, - }; - for (body.instructions) |inst| { - const new_inst = switch (inst.tag) { - .constant => unreachable, // excluded from function bodies - - .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint), - .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck), - .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid), - .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt), - - .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot), - .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"), - .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint), - .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull), - .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), - .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr), - .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), - .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), - .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), - .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as), - - .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), - .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), - .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store), - .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt), - .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte), - .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq), - .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte), - .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), - .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), - .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland), - .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor), - .bitand => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitand).?, .bitand), - .bitor => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitor).?, .bitor), - .xor => try self.emitBinOp(inst.src, new_body, inst.castTag(.xor).?, .xor), - - .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast), - .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast), - .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast), - - .alloc => blk: { - const new_inst = try self.arena.allocator.create(Inst.UnOp); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = .alloc, - }, - .positionals = .{ - .operand = (try self.emitType(inst.src, inst.ty)).inst, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .arg => blk: { - const old_inst = inst.castTag(.arg).?; - const new_inst = try self.arena.allocator.create(Inst.Arg); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = .arg, - }, - .positionals = .{ - .name = try self.arena.allocator.dupe(u8, mem.spanZ(old_inst.name)), - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .block => blk: { - const old_inst = inst.castTag(.block).?; - const new_inst = try self.arena.allocator.create(Inst.Block); - - try self.block_table.put(old_inst, new_inst); - - var block_body = std.ArrayList(*Inst).init(self.allocator); - defer block_body.deinit(); - - try self.emitBody(old_inst.body, inst_table, &block_body); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Block.base_tag, - }, - .positionals = .{ - .body = .{ .instructions = block_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - - break :blk &new_inst.base; - }, - - .loop => blk: { - const old_inst = inst.castTag(.loop).?; - const new_inst = try self.arena.allocator.create(Inst.Loop); - - try self.loop_table.put(old_inst, new_inst); - - var loop_body = std.ArrayList(*Inst).init(self.allocator); - defer loop_body.deinit(); - - try self.emitBody(old_inst.body, inst_table, &loop_body); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Loop.base_tag, - }, - .positionals = .{ - .body = .{ .instructions = loop_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - - break :blk &new_inst.base; - }, - - .brvoid => blk: { - const old_inst = inst.cast(ir.Inst.BrVoid).?; - const new_block = self.block_table.get(old_inst.block).?; - const new_inst = try self.arena.allocator.create(Inst.BreakVoid); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.BreakVoid.base_tag, - }, - .positionals = .{ - .block = new_block, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .br => blk: { - const old_inst = inst.castTag(.br).?; - const new_block = self.block_table.get(old_inst.block).?; - const new_inst = try self.arena.allocator.create(Inst.Break); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Break.base_tag, - }, - .positionals = .{ - .block = new_block, - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .call => blk: { - const old_inst = inst.castTag(.call).?; - const new_inst = try self.arena.allocator.create(Inst.Call); - - const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len); - for (args) |*elem, i| { - elem.* = try self.resolveInst(new_body, old_inst.args[i]); - } - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Call.base_tag, - }, - .positionals = .{ - .func = try self.resolveInst(new_body, old_inst.func), - .args = args, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .assembly => blk: { - const old_inst = inst.castTag(.assembly).?; - const new_inst = try self.arena.allocator.create(Inst.Asm); - - const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len); - for (inputs) |*elem, i| { - elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst; - } - - const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len); - for (clobbers) |*elem, i| { - elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst; - } - - const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len); - for (args) |*elem, i| { - elem.* = try self.resolveInst(new_body, old_inst.args[i]); - } - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Asm.base_tag, - }, - .positionals = .{ - .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst, - .return_type = (try self.emitType(inst.src, inst.ty)).inst, - }, - .kw_args = .{ - .@"volatile" = old_inst.is_volatile, - .output = if (old_inst.output) |o| - (try self.emitStringLiteral(inst.src, o)).inst - else - null, - .inputs = inputs, - .clobbers = clobbers, - .args = args, - }, - }; - break :blk &new_inst.base; - }, - - .condbr => blk: { - const old_inst = inst.castTag(.condbr).?; - - var then_body = std.ArrayList(*Inst).init(self.allocator); - var else_body = std.ArrayList(*Inst).init(self.allocator); - - defer then_body.deinit(); - defer else_body.deinit(); - - const then_deaths = try self.arena.allocator.alloc(*Inst, old_inst.thenDeaths().len); - const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len); - - for (old_inst.thenDeaths()) |death, i| { - then_deaths[i] = try self.resolveInst(new_body, death); - } - for (old_inst.elseDeaths()) |death, i| { - else_deaths[i] = try self.resolveInst(new_body, death); - } - - try self.emitBody(old_inst.then_body, inst_table, &then_body); - try self.emitBody(old_inst.else_body, inst_table, &else_body); - - const new_inst = try self.arena.allocator.create(Inst.CondBr); - - try self.body_metadata.put(&new_inst.positionals.then_body, .{ .deaths = then_deaths }); - try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths }); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.CondBr.base_tag, - }, - .positionals = .{ - .condition = try self.resolveInst(new_body, old_inst.condition), - .then_body = .{ .instructions = then_body.toOwnedSlice() }, - .else_body = .{ .instructions = else_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - .switchbr => blk: { - const old_inst = inst.castTag(.switchbr).?; - const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len); - const new_inst = try self.arena.allocator.create(Inst.SwitchBr); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.SwitchBr.base_tag, - }, - .positionals = .{ - .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr), - .cases = cases, - .items = &[_]*Inst{}, // TODO this should actually be populated - .else_body = undefined, // populated below - }, - .kw_args = .{}, - }; - - var body_tmp = std.ArrayList(*Inst).init(self.allocator); - defer body_tmp.deinit(); - - for (old_inst.cases) |*case, i| { - body_tmp.items.len = 0; - - const case_deaths = try self.arena.allocator.alloc(*Inst, old_inst.caseDeaths(i).len); - for (old_inst.caseDeaths(i)) |death, j| { - case_deaths[j] = try self.resolveInst(new_body, death); - } - try self.body_metadata.put(&cases[i].body, .{ .deaths = case_deaths }); - - try self.emitBody(case.body, inst_table, &body_tmp); - const item = (try self.emitTypedValue(inst.src, .{ - .ty = old_inst.target_ptr.ty.elemType(), - .val = case.item, - })).inst; - - cases[i] = .{ - .item = item, - .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) }, - }; - } - { // else - const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len); - for (old_inst.elseDeaths()) |death, j| { - else_deaths[j] = try self.resolveInst(new_body, death); - } - try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths }); - - body_tmp.items.len = 0; - try self.emitBody(old_inst.else_body, inst_table, &body_tmp); - new_inst.positionals.else_body = .{ - .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items), - }; - } - - break :blk &new_inst.base; - }, - .varptr => @panic("TODO"), - }; - try self.metadata.put(new_inst, .{ - .deaths = inst.deaths, - .addr = @ptrToInt(inst), - }); - try instructions.append(new_inst); - try inst_table.put(inst, new_inst); - } - } - - fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl { - switch (ty.tag()) { - .i8 => return self.emitPrimitive(src, .i8), - .u8 => return self.emitPrimitive(src, .u8), - .i16 => return self.emitPrimitive(src, .i16), - .u16 => return self.emitPrimitive(src, .u16), - .i32 => return self.emitPrimitive(src, .i32), - .u32 => return self.emitPrimitive(src, .u32), - .i64 => return self.emitPrimitive(src, .i64), - .u64 => return self.emitPrimitive(src, .u64), - .isize => return self.emitPrimitive(src, .isize), - .usize => return self.emitPrimitive(src, .usize), - .c_short => return self.emitPrimitive(src, .c_short), - .c_ushort => return self.emitPrimitive(src, .c_ushort), - .c_int => return self.emitPrimitive(src, .c_int), - .c_uint => return self.emitPrimitive(src, .c_uint), - .c_long => return self.emitPrimitive(src, .c_long), - .c_ulong => return self.emitPrimitive(src, .c_ulong), - .c_longlong => return self.emitPrimitive(src, .c_longlong), - .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong), - .c_longdouble => return self.emitPrimitive(src, .c_longdouble), - .c_void => return self.emitPrimitive(src, .c_void), - .f16 => return self.emitPrimitive(src, .f16), - .f32 => return self.emitPrimitive(src, .f32), - .f64 => return self.emitPrimitive(src, .f64), - .f128 => return self.emitPrimitive(src, .f128), - .anyerror => return self.emitPrimitive(src, .anyerror), - else => switch (ty.zigTypeTag()) { - .Bool => return self.emitPrimitive(src, .bool), - .Void => return self.emitPrimitive(src, .void), - .NoReturn => return self.emitPrimitive(src, .noreturn), - .Type => return self.emitPrimitive(src, .type), - .ComptimeInt => return self.emitPrimitive(src, .comptime_int), - .ComptimeFloat => return self.emitPrimitive(src, .comptime_float), - .Fn => { - const param_types = try self.allocator.alloc(Type, ty.fnParamLen()); - defer self.allocator.free(param_types); - - ty.fnParamTypes(param_types); - const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len); - for (param_types) |param_type, i| { - emitted_params[i] = (try self.emitType(src, param_type)).inst; - } - - const fntype_inst = try self.arena.allocator.create(Inst.FnType); - fntype_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.FnType.base_tag, - }, - .positionals = .{ - .param_types = emitted_params, - .return_type = (try self.emitType(src, ty.fnReturnType())).inst, - }, - .kw_args = .{ - .cc = ty.fnCallingConvention(), - }, - }; - return self.emitUnnamedDecl(&fntype_inst.base); - }, - .Int => { - const info = ty.intInfo(self.old_module.getTarget()); - const signed = try self.emitPrimitive(src, switch (info.signedness) { - .signed => .@"true", - .unsigned => .@"false", - }); - const bits_val = try Value.Tag.int_u64.create(&self.arena.allocator, info.bits); - const bits = try self.emitComptimeIntVal(src, bits_val); - const inttype_inst = try self.arena.allocator.create(Inst.IntType); - inttype_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.IntType.base_tag, - }, - .positionals = .{ - .signed = signed.inst, - .bits = bits.inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inttype_inst.base); - }, - .Pointer => { - if (ty.isSinglePointer()) { - const inst = try self.arena.allocator.create(Inst.UnOp); - const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type; - inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .operand = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - } else { - std.debug.panic("TODO implement emitType for {}", .{ty}); - } - }, - .Optional => { - var buf: Type.Payload.ElemType = undefined; - const inst = try self.arena.allocator.create(Inst.UnOp); - inst.* = .{ - .base = .{ - .src = src, - .tag = .optional_type, - }, - .positionals = .{ - .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - }, - .Array => { - var len_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = ty.arrayLen(), - }; - const len = Value.initPayload(&len_pl.base); - - const inst = if (ty.sentinel()) |sentinel| blk: { - const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel); - inst.* = .{ - .base = .{ - .src = src, - .tag = .array_type, - }, - .positionals = .{ - .len = (try self.emitTypedValue(src, .{ - .ty = Type.initTag(.usize), - .val = len, - })).inst, - .sentinel = (try self.emitTypedValue(src, .{ - .ty = ty.elemType(), - .val = sentinel, - })).inst, - .elem_type = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - break :blk &inst.base; - } else blk: { - const inst = try self.arena.allocator.create(Inst.BinOp); - inst.* = .{ - .base = .{ - .src = src, - .tag = .array_type, - }, - .positionals = .{ - .lhs = (try self.emitTypedValue(src, .{ - .ty = Type.initTag(.usize), - .val = len, - })).inst, - .rhs = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - break :blk &inst.base; - }; - return self.emitUnnamedDecl(inst); - }, - else => std.debug.panic("TODO implement emitType for {}", .{ty}), - }, - } - } - - fn autoName(self: *EmitZIR) ![]u8 { - while (true) { - const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.next_auto_name}); - self.next_auto_name += 1; - const gop = try self.names.getOrPut(proposed_name); - if (!gop.found_existing) { - gop.entry.value = {}; - return proposed_name; - } - } - } - - fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl { - const gop = try self.primitive_table.getOrPut(tag); - if (!gop.found_existing) { - const primitive_inst = try self.arena.allocator.create(Inst.Primitive); - primitive_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Primitive.base_tag, - }, - .positionals = .{ - .tag = tag, - }, - .kw_args = .{}, - }; - gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base); - } - return gop.entry.value; - } - - fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = str, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&str_inst.base); - } - - fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl { - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = try self.autoName(), - .contents_hash = undefined, - .inst = inst, - }; - try self.decls.append(self.allocator, decl); - return decl; - } -}; - /// For debugging purposes, like dumpFn but for unanalyzed zir blocks pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8, instructions: []*Inst) !void { var fib = std.heap.FixedBufferAllocator.init(&[_]u8{}); var module = Module{ - .decls = &[_]*Decl{}, + .decls = &[_]*Module.Decl{}, .arena = std.heap.ArenaAllocator.init(&fib.allocator), .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(&fib.allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(&fib.allocator), + .body_metadata = std.AutoHashMap(*Body, Module.BodyMetaData).init(&fib.allocator), }; var write = Writer{ .module = &module, |
