aboutsummaryrefslogtreecommitdiff
path: root/src/zir.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/zir.zig')
-rw-r--r--src/zir.zig1610
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,