diff --git a/src/AstGen.zig b/src/AstGen.zig index 8bd71b822805c4777f53eea817aec7ec68254445..bea4df82ce9628e671d943682fc5a4e9c8cb03e7 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -15,7 +15,7 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged; const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); -const zir = @import("zir.zig"); +const Zir = @import("Zir.zig"); const Module = @import("Module.zig"); const trace = @import("tracy.zig").trace; const Scope = Module.Scope; @@ -25,12 +25,12 @@ const Decl = Module.Decl; const LazySrcLoc = Module.LazySrcLoc; const BuiltinFn = @import("BuiltinFn.zig"); -instructions: std.MultiArrayList(zir.Inst) = .{}, +instructions: std.MultiArrayList(Zir.Inst) = .{}, string_bytes: ArrayListUnmanaged(u8) = .{}, extra: ArrayListUnmanaged(u32) = .{}, -/// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert -/// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. -ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, +/// The end of special indexes. `Zir.Inst.Ref` subtracts against this number to convert +/// to `Zir.Inst.Index`. The default here is correct if there are 0 parameters. +ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len, mod: *Module, decl: *Decl, arena: *Allocator, @@ -65,24 +65,24 @@ pub fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { inline for (fields) |field| { astgen.extra.appendAssumeCapacity(switch (field.field_type) { u32 => @field(extra, field.name), - zir.Inst.Ref => @enumToInt(@field(extra, field.name)), + Zir.Inst.Ref => @enumToInt(@field(extra, field.name)), else => @compileError("bad field type"), }); } return result; } -pub fn appendRefs(astgen: *AstGen, refs: []const zir.Inst.Ref) !void { +pub fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void { const coerced = @bitCast([]const u32, refs); return astgen.extra.appendSlice(astgen.mod.gpa, coerced); } -pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const zir.Inst.Ref) void { +pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void { const coerced = @bitCast([]const u32, refs); astgen.extra.appendSliceAssumeCapacity(coerced); } -pub fn refIsNoReturn(astgen: AstGen, inst_ref: zir.Inst.Ref) bool { +pub fn refIsNoReturn(astgen: AstGen, inst_ref: Zir.Inst.Ref) bool { if (inst_ref == .unreachable_value) return true; if (astgen.refToIndex(inst_ref)) |inst_index| { return astgen.instructions.items(.tag)[inst_index].isNoReturn(); @@ -90,11 +90,11 @@ pub fn refIsNoReturn(astgen: AstGen, inst_ref: zir.Inst.Ref) bool { return false; } -pub fn indexToRef(astgen: AstGen, inst: zir.Inst.Index) zir.Inst.Ref { - return @intToEnum(zir.Inst.Ref, astgen.ref_start_index + inst); +pub fn indexToRef(astgen: AstGen, inst: Zir.Inst.Index) Zir.Inst.Ref { + return @intToEnum(Zir.Inst.Ref, astgen.ref_start_index + inst); } -pub fn refToIndex(astgen: AstGen, inst: zir.Inst.Ref) ?zir.Inst.Index { +pub fn refToIndex(astgen: AstGen, inst: Zir.Inst.Ref) ?Zir.Inst.Index { const ref_int = @enumToInt(inst); if (ref_int >= astgen.ref_start_index) { return ref_int - astgen.ref_start_index; @@ -124,16 +124,16 @@ pub const ResultLoc = union(enum) { /// may be treated as `none` instead. none_or_ref, /// The expression will be coerced into this type, but it will be evaluated as an rvalue. - ty: zir.Inst.Ref, + ty: Zir.Inst.Ref, /// The expression must store its result into this typed pointer. The result instruction /// from the expression must be ignored. - ptr: zir.Inst.Ref, + ptr: Zir.Inst.Ref, /// The expression must store its result into this allocation, which has an inferred type. /// The result instruction from the expression must be ignored. /// Always an instruction with tag `alloc_inferred`. - inferred_ptr: zir.Inst.Ref, + inferred_ptr: Zir.Inst.Ref, /// There is a pointer for the expression to store its result into, however, its type - /// is inferred based on peer type resolution for a `zir.Inst.Block`. + /// is inferred based on peer type resolution for a `Zir.Inst.Block`. /// The result instruction from the expression must be ignored. block_ptr: *GenZir, @@ -188,11 +188,11 @@ pub const ResultLoc = union(enum) { } }; -pub fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { +pub fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zir.Inst.Ref { return expr(gz, scope, .{ .ty = .type_type }, type_node); } -fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -386,7 +386,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Ins /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the /// result instruction can be used to inspect whether it is isNoReturn() but that is it, /// it must otherwise not be used. -pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { +pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const mod = gz.astgen.mod; const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); @@ -551,7 +551,7 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn .src_node = gz.astgen.decl.nodeIndexToRelative(node), } }, }); - return zir.Inst.Ref.unreachable_value; + return Zir.Inst.Ref.unreachable_value; }, .@"return" => return ret(gz, scope, node), .field_access => return fieldAccess(gz, scope, rl, node), @@ -570,7 +570,7 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn .slice_open => { const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); const start = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs); - const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{ + const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ .lhs = lhs, .start = start, }); @@ -581,7 +581,7 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice); const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start); const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end); - const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{ + const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ .lhs = lhs, .start = start, .end = end, @@ -594,7 +594,7 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start); const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end); const sentinel = try expr(gz, scope, .{ .ty = .usize_type }, extra.sentinel); - const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{ + const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ .lhs = lhs, .start = start, .end = end, @@ -803,7 +803,7 @@ pub fn structInitExpr( rl: ResultLoc, node: ast.Node.Index, struct_init: ast.full.StructInit, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const astgen = gz.astgen; const mod = astgen.mod; @@ -823,14 +823,14 @@ pub fn structInitExpr( .none, .none_or_ref => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}), .ref => unreachable, // struct literal not valid as l-value .ty => |ty_inst| { - const fields_list = try gpa.alloc(zir.Inst.StructInit.Item, struct_init.ast.fields.len); + const fields_list = try gpa.alloc(Zir.Inst.StructInit.Item, struct_init.ast.fields.len); defer gpa.free(fields_list); for (struct_init.ast.fields) |field_init, i| { const name_token = tree.firstToken(field_init) - 2; const str_index = try gz.identAsString(name_token); - const field_ty_inst = try gz.addPlNode(.field_type, field_init, zir.Inst.FieldType{ + const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ .container_type = ty_inst, .name_start = str_index, }); @@ -839,31 +839,31 @@ pub fn structInitExpr( .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init), }; } - const init_inst = try gz.addPlNode(.struct_init, node, zir.Inst.StructInit{ + const init_inst = try gz.addPlNode(.struct_init, node, Zir.Inst.StructInit{ .fields_len = @intCast(u32, fields_list.len), }); try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - fields_list.len * @typeInfo(zir.Inst.StructInit.Item).Struct.fields.len); + fields_list.len * @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); for (fields_list) |field| { _ = gz.astgen.addExtraAssumeCapacity(field); } return rvalue(gz, scope, rl, init_inst, node); }, .ptr => |ptr_inst| { - const field_ptr_list = try gpa.alloc(zir.Inst.Index, struct_init.ast.fields.len); + const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); defer gpa.free(field_ptr_list); for (struct_init.ast.fields) |field_init, i| { const name_token = tree.firstToken(field_init) - 2; const str_index = try gz.identAsString(name_token); - const field_ptr = try gz.addPlNode(.field_ptr, field_init, zir.Inst.Field{ + const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{ .lhs = ptr_inst, .field_name_start = str_index, }); field_ptr_list[i] = astgen.refToIndex(field_ptr).?; _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init); } - const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, zir.Inst.Block{ + const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{ .body_len = @intCast(u32, field_ptr_list.len), }); try astgen.extra.appendSlice(gpa, field_ptr_list); @@ -883,7 +883,7 @@ pub fn comptimeExpr( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const prev_force_comptime = gz.force_comptime; gz.force_comptime = true; const result = try expr(gz, scope, rl, node); @@ -891,7 +891,7 @@ pub fn comptimeExpr( return result; } -fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; const tree = parent_gz.tree(); const node_datas = tree.nodes.items(.data); @@ -922,7 +922,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) Inn if (rhs == 0) { _ = try parent_gz.addBreak(.@"break", block_inst, .void_value); - return zir.Inst.Ref.unreachable_value; + return Zir.Inst.Ref.unreachable_value; } block_gz.break_count += 1; const prev_rvalue_rl_count = block_gz.rvalue_rl_count; @@ -943,7 +943,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) Inn try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); } } - return zir.Inst.Ref.unreachable_value; + return Zir.Inst.Ref.unreachable_value; }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, @@ -957,7 +957,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) Inn } } -fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; const tree = parent_gz.tree(); const node_datas = tree.nodes.items(.data); @@ -988,7 +988,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) // TODO emit a break_inline if the loop being continued is inline _ = try parent_gz.addBreak(.@"break", continue_block, .void_value); - return zir.Inst.Ref.unreachable_value; + return Zir.Inst.Ref.unreachable_value; }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, @@ -1008,7 +1008,7 @@ pub fn blockExpr( rl: ResultLoc, block_node: ast.Node.Index, statements: []const ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -1075,8 +1075,8 @@ fn labeledBlockExpr( rl: ResultLoc, block_node: ast.Node.Index, statements: []const ast.Node.Index, - zir_tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + zir_tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -1520,8 +1520,8 @@ fn varDecl( }; defer init_scope.instructions.deinit(gpa); - var resolve_inferred_alloc: zir.Inst.Ref = .none; - var opt_type_inst: zir.Inst.Ref = .none; + var resolve_inferred_alloc: Zir.Inst.Ref = .none; + var opt_type_inst: Zir.Inst.Ref = .none; if (var_decl.ast.type_node != 0) { const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node); opt_type_inst = type_inst; @@ -1593,10 +1593,10 @@ fn varDecl( return &sub_scope.base; }, .keyword_var => { - var resolve_inferred_alloc: zir.Inst.Ref = .none; + var resolve_inferred_alloc: Zir.Inst.Ref = .none; const var_data: struct { result_loc: ResultLoc, - alloc: zir.Inst.Ref, + alloc: Zir.Inst.Ref, } = if (var_decl.ast.type_node != 0) a: { const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); @@ -1649,7 +1649,7 @@ fn assignOp( gz: *GenZir, scope: *Scope, infix_node: ast.Node.Index, - op_inst_tag: zir.Inst.Tag, + op_inst_tag: Zir.Inst.Tag, ) InnerError!void { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); @@ -1659,14 +1659,14 @@ fn assignOp( const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); - const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{ + const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs, }); _ = try gz.addBin(.store, lhs_ptr, result); } -fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); @@ -1675,7 +1675,7 @@ fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inne return rvalue(gz, scope, rl, result, node); } -fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); @@ -1689,8 +1689,8 @@ fn negation( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, - tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); @@ -1705,7 +1705,7 @@ fn ptrType( rl: ResultLoc, node: ast.Node.Index, ptr_info: ast.full.PtrType, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); @@ -1727,10 +1727,10 @@ fn ptrType( return rvalue(gz, scope, rl, result, node); } - var sentinel_ref: zir.Inst.Ref = .none; - var align_ref: zir.Inst.Ref = .none; - var bit_start_ref: zir.Inst.Ref = .none; - var bit_end_ref: zir.Inst.Ref = .none; + var sentinel_ref: Zir.Inst.Ref = .none; + var align_ref: Zir.Inst.Ref = .none; + var bit_start_ref: Zir.Inst.Ref = .none; + var bit_end_ref: Zir.Inst.Ref = .none; var trailing_count: u32 = 0; if (ptr_info.ast.sentinel != 0) { @@ -1752,9 +1752,9 @@ fn ptrType( try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count); + @typeInfo(Zir.Inst.PtrType).Struct.fields.len + trailing_count); - const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type }); + const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ .elem_type = elem_type }); if (sentinel_ref != .none) { gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref)); } @@ -1766,7 +1766,7 @@ fn ptrType( gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); } - const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len); + const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); const result = gz.astgen.indexToRef(new_index); gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ .ptr_type = .{ @@ -1787,7 +1787,7 @@ fn ptrType( return rvalue(gz, scope, rl, result, node); } -fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { +fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); @@ -1799,7 +1799,7 @@ fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !z return rvalue(gz, scope, rl, result, node); } -fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { +fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); @@ -1818,10 +1818,10 @@ pub fn structDeclInner( scope: *Scope, node: ast.Node.Index, container_decl: ast.full.ContainerDecl, - tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { if (container_decl.ast.members.len == 0) { - return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); + return gz.addPlNode(tag, node, Zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); } const astgen = gz.astgen; @@ -1891,7 +1891,7 @@ pub fn structDeclInner( field_index += 1; } if (field_index == 0) { - return gz.addPlNode(tag, node, zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); + return gz.addPlNode(tag, node, Zir.Inst.StructDecl{ .fields_len = 0, .body_len = 0 }); } const empty_slot_count = 16 - (field_index % 16); cur_bit_bag >>= @intCast(u5, empty_slot_count * 2); @@ -1901,11 +1901,11 @@ pub fn structDeclInner( _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - @typeInfo(zir.Inst.StructDecl).Struct.fields.len + + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len + bit_bag.items.len + 1 + fields_data.items.len + block_scope.instructions.items.len); const zir_datas = astgen.instructions.items(.data); - zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.StructDecl{ + zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ .body_len = @intCast(u32, block_scope.instructions.items.len), .fields_len = @intCast(u32, field_index), }); @@ -1922,7 +1922,7 @@ fn containerDecl( rl: ResultLoc, node: ast.Node.Index, container_decl: ast.full.ContainerDecl, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const mod = astgen.mod; const gpa = mod.gpa; @@ -1933,7 +1933,7 @@ fn containerDecl( // We must not create any types until Sema. Here the goal is only to generate // ZIR for all the field types, alignments, and default value expressions. - const arg_inst: zir.Inst.Ref = if (container_decl.ast.arg != 0) + const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0) try comptimeExpr(gz, scope, .{ .ty = .type_type }, container_decl.ast.arg) else .none; @@ -1941,10 +1941,10 @@ fn containerDecl( switch (token_tags[container_decl.ast.main_token]) { .keyword_struct => { const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) { - .keyword_packed => zir.Inst.Tag.struct_decl_packed, - .keyword_extern => zir.Inst.Tag.struct_decl_extern, + .keyword_packed => Zir.Inst.Tag.struct_decl_packed, + .keyword_extern => Zir.Inst.Tag.struct_decl_extern, else => unreachable, - } else zir.Inst.Tag.struct_decl; + } else Zir.Inst.Tag.struct_decl; assert(arg_inst == .none); @@ -2123,12 +2123,12 @@ fn containerDecl( // In this case we must generate ZIR code for the tag values, similar to // how structs are handled above. The new anonymous Decl will be created in // Sema, not AstGen. - const tag: zir.Inst.Tag = if (counts.nonexhaustive_node == 0) + const tag: Zir.Inst.Tag = if (counts.nonexhaustive_node == 0) .enum_decl else .enum_decl_nonexhaustive; if (counts.total_fields == 0) { - return gz.addPlNode(tag, node, zir.Inst.EnumDecl{ + return gz.addPlNode(tag, node, Zir.Inst.EnumDecl{ .tag_type = arg_inst, .fields_len = 0, .body_len = 0, @@ -2194,11 +2194,11 @@ fn containerDecl( _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - @typeInfo(zir.Inst.EnumDecl).Struct.fields.len + + @typeInfo(Zir.Inst.EnumDecl).Struct.fields.len + bit_bag.items.len + 1 + fields_data.items.len + block_scope.instructions.items.len); const zir_datas = astgen.instructions.items(.data); - zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.EnumDecl{ + zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ .tag_type = arg_inst, .body_len = @intCast(u32, block_scope.instructions.items.len), .fields_len = @intCast(u32, field_index), @@ -2222,7 +2222,7 @@ fn errorSetDecl( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const mod = astgen.mod; const tree = gz.tree(); @@ -2289,12 +2289,12 @@ fn orelseCatchExpr( rl: ResultLoc, node: ast.Node.Index, lhs: ast.Node.Index, - cond_op: zir.Inst.Tag, - unwrap_op: zir.Inst.Tag, - unwrap_code_op: zir.Inst.Tag, + cond_op: Zir.Inst.Tag, + unwrap_op: Zir.Inst.Tag, + unwrap_code_op: Zir.Inst.Tag, rhs: ast.Node.Index, payload_token: ?ast.TokenIndex, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; const tree = parent_gz.tree(); @@ -2408,16 +2408,16 @@ fn finishThenElseBlock( block_scope: *GenZir, then_scope: *GenZir, else_scope: *GenZir, - condbr: zir.Inst.Index, - cond: zir.Inst.Ref, + condbr: Zir.Inst.Index, + cond: Zir.Inst.Ref, then_src: ast.Node.Index, else_src: ast.Node.Index, - then_result: zir.Inst.Ref, - else_result: zir.Inst.Ref, - main_block: zir.Inst.Index, - then_break_block: zir.Inst.Index, - break_tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + then_result: Zir.Inst.Ref, + else_result: Zir.Inst.Ref, + main_block: Zir.Inst.Index, + then_break_block: Zir.Inst.Index, + break_tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { // We now have enough information to decide whether the result instruction should // be communicated via result location pointer or break instructions. const strat = rl.strategy(block_scope); @@ -2475,7 +2475,7 @@ pub fn fieldAccess( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const mod = astgen.mod; const tree = gz.tree(); @@ -2487,11 +2487,11 @@ pub fn fieldAccess( const field_ident = dot_token + 1; const str_index = try gz.identAsString(field_ident); switch (rl) { - .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{ + .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{ .lhs = try expr(gz, scope, .ref, object_node), .field_name_start = str_index, }), - else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{ + else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, Zir.Inst.Field{ .lhs = try expr(gz, scope, .none_or_ref, object_node), .field_name_start = str_index, }), node), @@ -2503,7 +2503,7 @@ fn arrayAccess( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); const node_datas = tree.nodes.items(.data); @@ -2526,12 +2526,12 @@ fn simpleBinOp( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, - op_inst_tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + op_inst_tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); - const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{ + const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = try expr(gz, scope, .none, node_datas[node].lhs), .rhs = try expr(gz, scope, .none, node_datas[node].rhs), }); @@ -2544,8 +2544,8 @@ fn simpleStrTok( rl: ResultLoc, ident_token: ast.TokenIndex, node: ast.Node.Index, - op_inst_tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + op_inst_tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { const str_index = try gz.identAsString(ident_token); const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); return rvalue(gz, scope, rl, result, node); @@ -2556,8 +2556,8 @@ fn boolBinOp( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, - zir_tag: zir.Inst.Tag, -) InnerError!zir.Inst.Ref { + zir_tag: Zir.Inst.Tag, +) InnerError!Zir.Inst.Ref { const node_datas = gz.tree().nodes.items(.data); const lhs = try expr(gz, scope, .{ .ty = .bool_type }, node_datas[node].lhs); @@ -2583,7 +2583,7 @@ fn ifExpr( rl: ResultLoc, node: ast.Node.Index, if_full: ast.full.If, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; var block_scope: GenZir = .{ @@ -2640,7 +2640,7 @@ fn ifExpr( const else_node = if_full.ast.else_expr; const else_info: struct { src: ast.Node.Index, - result: zir.Inst.Ref, + result: Zir.Inst.Ref, } = if (else_node != 0) blk: { block_scope.break_count += 1; const sub_scope = &else_scope.base; @@ -2674,19 +2674,19 @@ fn ifExpr( } fn setCondBrPayload( - condbr: zir.Inst.Index, - cond: zir.Inst.Ref, + condbr: Zir.Inst.Index, + cond: Zir.Inst.Ref, then_scope: *GenZir, else_scope: *GenZir, ) !void { const astgen = then_scope.astgen; try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + - @typeInfo(zir.Inst.CondBr).Struct.fields.len + + @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_scope.instructions.items.len + else_scope.instructions.items.len); const zir_datas = astgen.instructions.items(.data); - zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ + zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ .condition = cond, .then_body_len = @intCast(u32, then_scope.instructions.items.len), .else_body_len = @intCast(u32, else_scope.instructions.items.len), @@ -2697,19 +2697,19 @@ fn setCondBrPayload( /// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction. fn setCondBrPayloadElideBlockStorePtr( - condbr: zir.Inst.Index, - cond: zir.Inst.Ref, + condbr: Zir.Inst.Index, + cond: Zir.Inst.Ref, then_scope: *GenZir, else_scope: *GenZir, ) !void { const astgen = then_scope.astgen; try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + - @typeInfo(zir.Inst.CondBr).Struct.fields.len + + @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_scope.instructions.items.len + else_scope.instructions.items.len - 2); const zir_datas = astgen.instructions.items(.data); - zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{ + zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ .condition = cond, .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1), .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1), @@ -2731,14 +2731,14 @@ fn whileExpr( rl: ResultLoc, node: ast.Node.Index, while_full: ast.full.While, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; if (while_full.label_token) |label_token| { try checkLabelRedefinition(mod, scope, label_token); } const is_inline = parent_gz.force_comptime or while_full.inline_token != null; - const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; + const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; const loop_block = try parent_gz.addBlock(loop_tag, node); try parent_gz.instructions.append(mod.gpa, loop_block); @@ -2771,9 +2771,9 @@ fn whileExpr( } }; - const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; + const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; const condbr = try continue_scope.addCondBr(condbr_tag, node); - const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; + const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; const cond_block = try loop_scope.addBlock(block_tag, node); try loop_scope.instructions.append(mod.gpa, cond_block); try continue_scope.setBlockBody(cond_block); @@ -2784,7 +2784,7 @@ fn whileExpr( if (while_full.ast.cont_expr != 0) { _ = try expr(&loop_scope, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr); } - const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; + const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; _ = try loop_scope.addNode(repeat_tag, node); try loop_scope.setBlockBody(loop_block); @@ -2821,7 +2821,7 @@ fn whileExpr( const else_node = while_full.ast.else_expr; const else_info: struct { src: ast.Node.Index, - result: zir.Inst.Ref, + result: Zir.Inst.Ref, } = if (else_node != 0) blk: { loop_scope.break_count += 1; const sub_scope = &else_scope.base; @@ -2839,7 +2839,7 @@ fn whileExpr( return mod.failTok(scope, some.token, "unused while loop label", .{}); } } - const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; + const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; return finishThenElseBlock( parent_gz, scope, @@ -2866,7 +2866,7 @@ fn forExpr( rl: ResultLoc, node: ast.Node.Index, for_full: ast.full.While, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = parent_gz.astgen.mod; if (for_full.label_token) |label_token| { try checkLabelRedefinition(mod, scope, label_token); @@ -2886,7 +2886,7 @@ fn forExpr( break :blk index_ptr; }; - const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop; + const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; const loop_block = try parent_gz.addBlock(loop_tag, node); try parent_gz.instructions.append(mod.gpa, loop_block); @@ -2909,26 +2909,26 @@ fn forExpr( // check condition i < array_expr.len const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); - const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{ + const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, Zir.Inst.Bin{ .lhs = index, .rhs = len, }); - const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; + const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; const condbr = try cond_scope.addCondBr(condbr_tag, node); - const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block; + const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; const cond_block = try loop_scope.addBlock(block_tag, node); try loop_scope.instructions.append(mod.gpa, cond_block); try cond_scope.setBlockBody(cond_block); // Increment the index variable. const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr); - const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{ + const index_plus_one = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{ .lhs = index_2, .rhs = .one_usize, }); _ = try loop_scope.addBin(.store, index_ptr, index_plus_one); - const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; + const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; _ = try loop_scope.addNode(repeat_tag, node); try loop_scope.setBlockBody(loop_block); @@ -2996,7 +2996,7 @@ fn forExpr( const else_node = for_full.ast.else_expr; const else_info: struct { src: ast.Node.Index, - result: zir.Inst.Ref, + result: Zir.Inst.Ref, } = if (else_node != 0) blk: { loop_scope.break_count += 1; const sub_scope = &else_scope.base; @@ -3014,7 +3014,7 @@ fn forExpr( return mod.failTok(scope, some.token, "unused for loop label", .{}); } } - const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; + const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; return finishThenElseBlock( parent_gz, scope, @@ -3145,7 +3145,7 @@ fn switchExpr( scope: *Scope, rl: ResultLoc, switch_node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const astgen = parent_gz.astgen; const mod = astgen.mod; const gpa = mod.gpa; @@ -3164,7 +3164,7 @@ fn switchExpr( var any_payload_is_ref = false; var scalar_cases_len: u32 = 0; var multi_cases_len: u32 = 0; - var special_prong: zir.SpecialProng = .none; + var special_prong: Zir.SpecialProng = .none; var special_node: ast.Node.Index = 0; var else_src: ?LazySrcLoc = null; var underscore_src: ?LazySrcLoc = null; @@ -3265,7 +3265,7 @@ fn switchExpr( const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none; const operand = try expr(parent_gz, scope, operand_rl, operand_node); // We need the type of the operand to use as the result location for all the prong items. - const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof; + const typeof_tag: Zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof; const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node); const item_rl: ResultLoc = .{ .ty = operand_ty_inst }; @@ -3321,7 +3321,7 @@ fn switchExpr( } break :blk &case_scope.base; } - const capture_tag: zir.Inst.Tag = if (is_ptr) + const capture_tag: Zir.Inst.Tag = if (is_ptr) .switch_capture_else_ref else .switch_capture_else; @@ -3347,7 +3347,7 @@ fn switchExpr( block_scope.break_count += 1; _ = try case_scope.addBreak(.@"break", switch_block, case_result); } - // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`. + // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + 3 + // operand, scalar_cases_len, else body len @boolToInt(multi_cases_len != 0) + @@ -3360,7 +3360,7 @@ fn switchExpr( scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len)); scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); } else { - // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`. + // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + 2 + // operand, scalar_cases_len @boolToInt(multi_cases_len != 0)); @@ -3404,7 +3404,7 @@ fn switchExpr( } const is_multi_case_bits: u2 = @boolToInt(is_multi_case); const is_ptr_bits: u2 = @boolToInt(is_ptr); - const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) { + const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) { 0b00 => .switch_capture, 0b01 => .switch_capture_ref, 0b10 => .switch_capture_multi, @@ -3495,9 +3495,9 @@ fn switchExpr( const multi_bit: u4 = @boolToInt(multi_cases_len != 0); const special_prong_bits: u4 = @enumToInt(special_prong); comptime { - assert(@enumToInt(zir.SpecialProng.none) == 0b00); - assert(@enumToInt(zir.SpecialProng.@"else") == 0b01); - assert(@enumToInt(zir.SpecialProng.under) == 0b10); + assert(@enumToInt(Zir.SpecialProng.none) == 0b00); + assert(@enumToInt(Zir.SpecialProng.@"else") == 0b01); + assert(@enumToInt(Zir.SpecialProng.under) == 0b10); } const zir_tags = astgen.instructions.items(.tag); zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) { @@ -3732,13 +3732,13 @@ fn switchExpr( } } -fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref { +fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const operand_node = node_datas[node].lhs; - const operand: zir.Inst.Ref = if (operand_node != 0) operand: { + const operand: Zir.Inst.Ref = if (operand_node != 0) operand: { const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ .ptr = try gz.addNode(.ret_ptr, node), } else .{ @@ -3747,7 +3747,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref break :operand try expr(gz, scope, rl, operand_node); } else .void_value; _ = try gz.addUnNode(.ret_node, operand, node); - return zir.Inst.Ref.unreachable_value; + return Zir.Inst.Ref.unreachable_value; } fn identifier( @@ -3755,7 +3755,7 @@ fn identifier( scope: *Scope, rl: ResultLoc, ident: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -3849,7 +3849,7 @@ fn stringLiteral( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); const string_bytes = &gz.astgen.string_bytes; @@ -3873,7 +3873,7 @@ fn multilineStringLiteral( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); @@ -3911,7 +3911,7 @@ fn multilineStringLiteral( return rvalue(gz, scope, rl, result, node); } -fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref { +fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !Zir.Inst.Ref { const mod = gz.astgen.mod; const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); @@ -3936,13 +3936,13 @@ fn integerLiteral( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); const int_token = main_tokens[node]; const prefixed_bytes = tree.tokenSlice(int_token); if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| { - const result: zir.Inst.Ref = switch (small_int) { + const result: Zir.Inst.Ref = switch (small_int) { 0 => .zero, 1 => .one, else => try gz.addInt(small_int), @@ -3958,7 +3958,7 @@ fn floatLiteral( scope: *Scope, rl: ResultLoc, node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const arena = gz.astgen.arena; const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); @@ -3983,7 +3983,7 @@ fn floatLiteral( // We need to use 128 bits. Break the float into 4 u32 values so we can // put it into the `extra` array. const int_bits = @bitCast(u128, float_number); - const result = try gz.addPlNode(.float128, node, zir.Inst.Float128{ + const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{ .piece0 = @truncate(u32, int_bits), .piece1 = @truncate(u32, int_bits >> 32), .piece2 = @truncate(u32, int_bits >> 64), @@ -3998,7 +3998,7 @@ fn asmExpr( rl: ResultLoc, node: ast.Node.Index, full: ast.full.Asm, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = gz.astgen.mod; const arena = gz.astgen.arena; const tree = gz.tree(); @@ -4014,7 +4014,7 @@ fn asmExpr( } const constraints = try arena.alloc(u32, full.inputs.len); - const args = try arena.alloc(zir.Inst.Ref, full.inputs.len); + const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len); for (full.inputs) |input, i| { const constraint_token = main_tokens[input] + 2; @@ -4027,8 +4027,8 @@ fn asmExpr( args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs); } - const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; - const result = try gz.addPlNode(tag, node, zir.Inst.Asm{ + const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; + const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{ .asm_source = asm_source, .return_type = .void_type, .output = .none, @@ -4051,7 +4051,7 @@ fn as( node: ast.Node.Index, lhs: ast.Node.Index, rhs: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const dest_type = try typeExpr(gz, scope, lhs); switch (rl) { .none, .none_or_ref, .discard, .ref, .ty => { @@ -4077,10 +4077,10 @@ fn asRlPtr( parent_gz: *GenZir, scope: *Scope, rl: ResultLoc, - result_ptr: zir.Inst.Ref, + result_ptr: Zir.Inst.Ref, operand_node: ast.Node.Index, - dest_type: zir.Inst.Ref, -) InnerError!zir.Inst.Ref { + dest_type: Zir.Inst.Ref, +) InnerError!Zir.Inst.Ref { // Detect whether this expr() call goes into rvalue() to store the result into the // result location. If it does, elide the coerce_result_ptr instruction // as well as the store instruction, instead passing the result as an rvalue. @@ -4126,13 +4126,13 @@ fn bitCast( node: ast.Node.Index, lhs: ast.Node.Index, rhs: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = gz.astgen.mod; const dest_type = try typeExpr(gz, scope, lhs); switch (rl) { .none, .discard, .ty => { const operand = try expr(gz, scope, .none, rhs); - const result = try gz.addPlNode(.bitcast, node, zir.Inst.Bin{ + const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = operand, }); @@ -4159,7 +4159,7 @@ fn typeOf( rl: ResultLoc, node: ast.Node.Index, params: []const ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { if (params.len < 1) { return gz.astgen.mod.failNode(scope, node, "expected at least 1 argument, found 0", .{}); } @@ -4168,12 +4168,12 @@ fn typeOf( return rvalue(gz, scope, rl, result, node); } const arena = gz.astgen.arena; - var items = try arena.alloc(zir.Inst.Ref, params.len); + var items = try arena.alloc(Zir.Inst.Ref, params.len); for (params) |param, param_i| { items[param_i] = try expr(gz, scope, .none, param); } - const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{ + const result = try gz.addPlNode(.typeof_peer, node, Zir.Inst.MultiOp{ .operands_len = @intCast(u32, params.len), }); try gz.astgen.appendRefs(items); @@ -4187,7 +4187,7 @@ fn builtinCall( rl: ResultLoc, node: ast.Node.Index, params: []const ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = gz.astgen.mod; const tree = gz.tree(); const main_tokens = tree.nodes.items(.main_token); @@ -4223,7 +4223,7 @@ fn builtinCall( .float_cast => { const dest_type = try typeExpr(gz, scope, params[0]); const rhs = try expr(gz, scope, .none, params[1]); - const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{ + const result = try gz.addPlNode(.floatcast, node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = rhs, }); @@ -4232,7 +4232,7 @@ fn builtinCall( .int_cast => { const dest_type = try typeExpr(gz, scope, params[0]); const rhs = try expr(gz, scope, .none, params[1]); - const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{ + const result = try gz.addPlNode(.intcast, node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = rhs, }); @@ -4271,12 +4271,12 @@ fn builtinCall( return rvalue(gz, scope, rl, result, node); }, .compile_log => { - const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len); + const arg_refs = try mod.gpa.alloc(Zir.Inst.Ref, params.len); defer mod.gpa.free(arg_refs); for (params) |param, i| arg_refs[i] = try expr(gz, scope, .none, param); - const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{ + const result = try gz.addPlNode(.compile_log, node, Zir.Inst.MultiOp{ .operands_len = @intCast(u32, params.len), }); try gz.astgen.appendRefs(arg_refs); @@ -4285,12 +4285,12 @@ fn builtinCall( .field => { const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); if (rl == .ref) { - return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{ + return try gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ .lhs = try expr(gz, scope, .ref, params[0]), .field_name = field_name, }); } - const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{ + const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ .lhs = try expr(gz, scope, .none, params[0]), .field_name = field_name, }); @@ -4301,7 +4301,7 @@ fn builtinCall( .TypeOf => return typeOf(gz, scope, rl, node, params), .int_to_enum => { - const result = try gz.addPlNode(.int_to_enum, node, zir.Inst.Bin{ + const result = try gz.addPlNode(.int_to_enum, node, Zir.Inst.Bin{ .lhs = try typeExpr(gz, scope, params[0]), .rhs = try expr(gz, scope, .none, params[1]), }); @@ -4321,7 +4321,7 @@ fn builtinCall( // TODO: the second parameter here is supposed to be // `std.builtin.ExportOptions`, not a string. const export_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); - _ = try gz.addPlNode(.@"export", node, zir.Inst.Bin{ + _ = try gz.addPlNode(.@"export", node, Zir.Inst.Bin{ .lhs = fn_to_export, .rhs = export_name, }); @@ -4331,7 +4331,7 @@ fn builtinCall( .has_decl => { const container_type = try typeExpr(gz, scope, params[0]); const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); - const result = try gz.addPlNode(.has_decl, node, zir.Inst.Bin{ + const result = try gz.addPlNode(.has_decl, node, Zir.Inst.Bin{ .lhs = container_type, .rhs = name, }); @@ -4451,14 +4451,14 @@ fn callExpr( rl: ResultLoc, node: ast.Node.Index, call: ast.full.Call, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { const mod = gz.astgen.mod; if (call.async_token) |async_token| { return mod.failTok(scope, async_token, "async and related features are not yet supported", .{}); } const lhs = try expr(gz, scope, .none, call.ast.fn_expr); - const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len); + const args = try mod.gpa.alloc(Zir.Inst.Ref, call.ast.params.len); defer mod.gpa.free(args); for (call.ast.params) |param_node, i| { @@ -4476,8 +4476,8 @@ fn callExpr( true => .async_kw, false => .auto, }; - const result: zir.Inst.Ref = res: { - const tag: zir.Inst.Tag = switch (modifier) { + const result: Zir.Inst.Ref = res: { + const tag: Zir.Inst.Tag = switch (modifier) { .auto => switch (args.len == 0) { true => break :res try gz.addUnNode(.call_none, lhs, node), false => .call, @@ -4495,7 +4495,7 @@ fn callExpr( return rvalue(gz, scope, rl, result, node); // TODO function call with result location } -pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{ +pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{ .{ "u8", .u8_type }, .{ "i8", .i8_type }, .{ "u16", .u16_type }, @@ -4756,9 +4756,9 @@ fn rvalue( gz: *GenZir, scope: *Scope, rl: ResultLoc, - result: zir.Inst.Ref, + result: Zir.Inst.Ref, src_node: ast.Node.Index, -) InnerError!zir.Inst.Ref { +) InnerError!Zir.Inst.Ref { switch (rl) { .none, .none_or_ref => return result, .discard => { @@ -4774,70 +4774,70 @@ fn rvalue( }, .ty => |ty_inst| { // Quickly eliminate some common, unnecessary type coercion. - const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32; - const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32; - const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32; - const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32; - const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32; + const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32; + const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32; + const as_bool = @as(u64, @enumToInt(Zir.Inst.Ref.bool_type)) << 32; + const as_usize = @as(u64, @enumToInt(Zir.Inst.Ref.usize_type)) << 32; + const as_void = @as(u64, @enumToInt(Zir.Inst.Ref.void_type)) << 32; switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) { - as_ty | @enumToInt(zir.Inst.Ref.u8_type), - as_ty | @enumToInt(zir.Inst.Ref.i8_type), - as_ty | @enumToInt(zir.Inst.Ref.u16_type), - as_ty | @enumToInt(zir.Inst.Ref.i16_type), - as_ty | @enumToInt(zir.Inst.Ref.u32_type), - as_ty | @enumToInt(zir.Inst.Ref.i32_type), - as_ty | @enumToInt(zir.Inst.Ref.u64_type), - as_ty | @enumToInt(zir.Inst.Ref.i64_type), - as_ty | @enumToInt(zir.Inst.Ref.usize_type), - as_ty | @enumToInt(zir.Inst.Ref.isize_type), - as_ty | @enumToInt(zir.Inst.Ref.c_short_type), - as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type), - as_ty | @enumToInt(zir.Inst.Ref.c_int_type), - as_ty | @enumToInt(zir.Inst.Ref.c_uint_type), - as_ty | @enumToInt(zir.Inst.Ref.c_long_type), - as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type), - as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type), - as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type), - as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type), - as_ty | @enumToInt(zir.Inst.Ref.f16_type), - as_ty | @enumToInt(zir.Inst.Ref.f32_type), - as_ty | @enumToInt(zir.Inst.Ref.f64_type), - as_ty | @enumToInt(zir.Inst.Ref.f128_type), - as_ty | @enumToInt(zir.Inst.Ref.c_void_type), - as_ty | @enumToInt(zir.Inst.Ref.bool_type), - as_ty | @enumToInt(zir.Inst.Ref.void_type), - as_ty | @enumToInt(zir.Inst.Ref.type_type), - as_ty | @enumToInt(zir.Inst.Ref.anyerror_type), - as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type), - as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type), - as_ty | @enumToInt(zir.Inst.Ref.noreturn_type), - as_ty | @enumToInt(zir.Inst.Ref.null_type), - as_ty | @enumToInt(zir.Inst.Ref.undefined_type), - as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type), - as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type), - as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type), - as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type), - as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type), - as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type), - as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type), - as_comptime_int | @enumToInt(zir.Inst.Ref.zero), - as_comptime_int | @enumToInt(zir.Inst.Ref.one), - as_bool | @enumToInt(zir.Inst.Ref.bool_true), - as_bool | @enumToInt(zir.Inst.Ref.bool_false), - as_usize | @enumToInt(zir.Inst.Ref.zero_usize), - as_usize | @enumToInt(zir.Inst.Ref.one_usize), - as_void | @enumToInt(zir.Inst.Ref.void_value), + as_ty | @enumToInt(Zir.Inst.Ref.u8_type), + as_ty | @enumToInt(Zir.Inst.Ref.i8_type), + as_ty | @enumToInt(Zir.Inst.Ref.u16_type), + as_ty | @enumToInt(Zir.Inst.Ref.i16_type), + as_ty | @enumToInt(Zir.Inst.Ref.u32_type), + as_ty | @enumToInt(Zir.Inst.Ref.i32_type), + as_ty | @enumToInt(Zir.Inst.Ref.u64_type), + as_ty | @enumToInt(Zir.Inst.Ref.i64_type), + as_ty | @enumToInt(Zir.Inst.Ref.usize_type), + as_ty | @enumToInt(Zir.Inst.Ref.isize_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_short_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_ushort_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_int_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_uint_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_long_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_ulong_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_longlong_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_ulonglong_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_longdouble_type), + as_ty | @enumToInt(Zir.Inst.Ref.f16_type), + as_ty | @enumToInt(Zir.Inst.Ref.f32_type), + as_ty | @enumToInt(Zir.Inst.Ref.f64_type), + as_ty | @enumToInt(Zir.Inst.Ref.f128_type), + as_ty | @enumToInt(Zir.Inst.Ref.c_void_type), + as_ty | @enumToInt(Zir.Inst.Ref.bool_type), + as_ty | @enumToInt(Zir.Inst.Ref.void_type), + as_ty | @enumToInt(Zir.Inst.Ref.type_type), + as_ty | @enumToInt(Zir.Inst.Ref.anyerror_type), + as_ty | @enumToInt(Zir.Inst.Ref.comptime_int_type), + as_ty | @enumToInt(Zir.Inst.Ref.comptime_float_type), + as_ty | @enumToInt(Zir.Inst.Ref.noreturn_type), + as_ty | @enumToInt(Zir.Inst.Ref.null_type), + as_ty | @enumToInt(Zir.Inst.Ref.undefined_type), + as_ty | @enumToInt(Zir.Inst.Ref.fn_noreturn_no_args_type), + as_ty | @enumToInt(Zir.Inst.Ref.fn_void_no_args_type), + as_ty | @enumToInt(Zir.Inst.Ref.fn_naked_noreturn_no_args_type), + as_ty | @enumToInt(Zir.Inst.Ref.fn_ccc_void_no_args_type), + as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type), + as_ty | @enumToInt(Zir.Inst.Ref.const_slice_u8_type), + as_ty | @enumToInt(Zir.Inst.Ref.enum_literal_type), + as_comptime_int | @enumToInt(Zir.Inst.Ref.zero), + as_comptime_int | @enumToInt(Zir.Inst.Ref.one), + as_bool | @enumToInt(Zir.Inst.Ref.bool_true), + as_bool | @enumToInt(Zir.Inst.Ref.bool_false), + as_usize | @enumToInt(Zir.Inst.Ref.zero_usize), + as_usize | @enumToInt(Zir.Inst.Ref.one_usize), + as_void | @enumToInt(Zir.Inst.Ref.void_value), => return result, // type of result is already correct // Need an explicit type coercion instruction. - else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{ + else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{ .dest_type = ty_inst, .operand = result, }), } }, .ptr => |ptr_inst| { - _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{ + _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{ .lhs = ptr_inst, .rhs = result, }); diff --git a/src/Module.zig b/src/Module.zig index c57566ddb0ede7e2526acf1ec1bd1d87e64f6bba..3f9a78f8d143fae9fc89435b63ba8e0057a0223d 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -21,7 +21,7 @@ const TypedValue = @import("TypedValue.zig"); const Package = @import("Package.zig"); const link = @import("link.zig"); const ir = @import("ir.zig"); -const Zir = @import("zir.zig"); // TODO rename this to Zir +const Zir = @import("Zir.zig"); const trace = @import("tracy.zig").trace; const AstGen = @import("AstGen.zig"); const Sema = @import("Sema.zig"); diff --git a/src/Sema.zig b/src/Sema.zig index 8a6c64046dc7ff650a59a8a1a4ce9fe41db50ee1..bc761b802197d189044f40aa607f481a81a4d439 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -52,7 +52,7 @@ const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); const ir = @import("ir.zig"); -const Zir = @import("zir.zig"); // TODO rename to Zir.zig +const Zir = @import("Zir.zig"); const Module = @import("Module.zig"); const Inst = ir.Inst; const Body = ir.Body; diff --git a/src/Zir.zig b/src/Zir.zig new file mode 100644 index 0000000000000000000000000000000000000000..bb1ac5fbc27c98e1599f941d92a24ade25056782 --- /dev/null +++ b/src/Zir.zig @@ -0,0 +1,2548 @@ +//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these +//! untyped IR instructions. Next, Sema.zig processes these into TZIR. +//! The minimum amount of information needed to represent a list of ZIR instructions. +//! Once this structure is completed, it can be used to generate TZIR, followed by +//! machine code, without any memory access into the AST tree token list, node list, +//! or source bytes. Exceptions include: +//! * Compile errors, which may need to reach into these data structures to +//! create a useful report. +//! * In the future, possibly inline assembly, which needs to get parsed and +//! handled by the codegen backend, and errors reported there. However for now, +//! inline assembly is not an exception. + +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const BigIntConst = std.math.big.int.Const; +const BigIntMutable = std.math.big.int.Mutable; +const ast = std.zig.ast; + +const Zir = @This(); +const Type = @import("type.zig").Type; +const Value = @import("value.zig").Value; +const TypedValue = @import("TypedValue.zig"); +const ir = @import("ir.zig"); +const Module = @import("Module.zig"); +const LazySrcLoc = Module.LazySrcLoc; + +/// There is always implicitly a `block` instruction at index 0. +/// This is so that `break_inline` can break from the root block. +instructions: std.MultiArrayList(Inst).Slice, +/// In order to store references to strings in fewer bytes, we copy all +/// string bytes into here. String bytes can be null. It is up to whomever +/// is referencing the data here whether they want to store both index and length, +/// thus allowing null bytes, or store only index, and use null-termination. The +/// `string_bytes` array is agnostic to either usage. +string_bytes: []u8, +/// The meaning of this data is determined by `Inst.Tag` value. +extra: []u32, + +/// Returns the requested data, as well as the new index which is at the start of the +/// trailers for the object. +pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } { + const fields = std.meta.fields(T); + var i: usize = index; + var result: T = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.field_type) { + u32 => code.extra[i], + Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]), + else => unreachable, + }; + i += 1; + } + return .{ + .data = result, + .end = i, + }; +} + +/// Given an index into `string_bytes` returns the null-terminated string found there. +pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 { + var end: usize = index; + while (code.string_bytes[end] != 0) { + end += 1; + } + return code.string_bytes[index..end :0]; +} + +pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref { + const raw_slice = code.extra[start..][0..len]; + return @bitCast([]Inst.Ref, raw_slice); +} + +pub fn deinit(code: *Zir, gpa: *Allocator) void { + code.instructions.deinit(gpa); + gpa.free(code.string_bytes); + gpa.free(code.extra); + code.* = undefined; +} + +/// For debugging purposes, like dumpFn but for unanalyzed zir blocks +pub fn dump( + code: Zir, + gpa: *Allocator, + kind: []const u8, + scope: *Module.Scope, + param_count: usize, +) !void { + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + + var writer: Writer = .{ + .gpa = gpa, + .arena = &arena.allocator, + .scope = scope, + .code = code, + .indent = 0, + .param_count = param_count, + }; + + const decl_name = scope.srcDecl().?.name; + const stderr = std.io.getStdErr().writer(); + try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name }); + try writer.writeInstToStream(stderr, 0); + try stderr.print(" // end ZIR {s} {s}\n\n", .{ kind, decl_name }); +} + +/// These are untyped instructions generated from an Abstract Syntax Tree. +/// The data here is immutable because it is possible to have multiple +/// analyses on the same ZIR happening at the same time. +pub const Inst = struct { + tag: Tag, + data: Data, + + /// These names are used directly as the instruction names in the text format. + pub const Tag = enum(u8) { + /// Arithmetic addition, asserts no integer overflow. + /// Uses the `pl_node` union field. Payload is `Bin`. + add, + /// Twos complement wrapping integer addition. + /// Uses the `pl_node` union field. Payload is `Bin`. + addwrap, + /// Allocates stack local memory. + /// Uses the `un_node` union field. The operand is the type of the allocated object. + /// The node source location points to a var decl node. + /// Indicates the beginning of a new statement in debug info. + alloc, + /// Same as `alloc` except mutable. + alloc_mut, + /// Same as `alloc` except the type is inferred. + /// Uses the `node` union field. + alloc_inferred, + /// Same as `alloc_inferred` except mutable. + alloc_inferred_mut, + /// Array concatenation. `a ++ b` + /// Uses the `pl_node` union field. Payload is `Bin`. + array_cat, + /// Array multiplication `a ** b` + /// Uses the `pl_node` union field. Payload is `Bin`. + array_mul, + /// `[N]T` syntax. No source location provided. + /// Uses the `bin` union field. lhs is length, rhs is element type. + array_type, + /// `[N:S]T` syntax. No source location provided. + /// Uses the `array_type_sentinel` field. + array_type_sentinel, + /// Given a pointer to an indexable object, returns the len property. This is + /// used by for loops. This instruction also emits a for-loop specific compile + /// error if the indexable object is not indexable. + /// Uses the `un_node` field. The AST node is the for loop node. + indexable_ptr_len, + /// Type coercion. No source location attached. + /// Uses the `bin` field. + as, + /// Type coercion to the function's return type. + /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. + as_node, + /// Inline assembly. Non-volatile. + /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node. + @"asm", + /// Inline assembly with the volatile attribute. + /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node. + asm_volatile, + /// Bitwise AND. `&` + bit_and, + /// Bitcast a value to a different type. + /// Uses the pl_node field with payload `Bin`. + bitcast, + /// A typed result location pointer is bitcasted to a new result location pointer. + /// The new result location pointer has an inferred type. + /// Uses the un_node field. + bitcast_result_ptr, + /// Bitwise NOT. `~` + /// Uses `un_node`. + bit_not, + /// Bitwise OR. `|` + bit_or, + /// A labeled block of code, which can return a value. + /// Uses the `pl_node` union field. Payload is `Block`. + block, + /// A list of instructions which are analyzed in the parent context, without + /// generating a runtime block. Must terminate with an "inline" variant of + /// a noreturn instruction. + /// Uses the `pl_node` union field. Payload is `Block`. + block_inline, + /// Boolean AND. See also `bit_and`. + /// Uses the `pl_node` union field. Payload is `Bin`. + bool_and, + /// Boolean NOT. See also `bit_not`. + /// Uses the `un_node` field. + bool_not, + /// Boolean OR. See also `bit_or`. + /// Uses the `pl_node` union field. Payload is `Bin`. + bool_or, + /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand + /// is a block, which is evaluated if `lhs` is `true`. + /// Uses the `bool_br` union field. + bool_br_and, + /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand + /// is a block, which is evaluated if `lhs` is `false`. + /// Uses the `bool_br` union field. + bool_br_or, + /// Return a value from a block. + /// Uses the `break` union field. + /// Uses the source information from previous instruction. + @"break", + /// Return a value from a block. This instruction is used as the terminator + /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`. + /// This instruction may also be used when it is known that there is only one + /// break instruction in a block, and the target block is the parent. + /// Uses the `break` union field. + break_inline, + /// Uses the `node` union field. + breakpoint, + /// Function call with modifier `.auto`. + /// Uses `pl_node`. AST node is the function call. Payload is `Call`. + call, + /// Same as `call` but it also does `ensure_result_used` on the return value. + call_chkused, + /// Same as `call` but with modifier `.compile_time`. + call_compile_time, + /// Function call with modifier `.auto`, empty parameter list. + /// Uses the `un_node` field. Operand is callee. AST node is the function call. + call_none, + /// Same as `call_none` but it also does `ensure_result_used` on the return value. + call_none_chkused, + /// `<` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_lt, + /// `<=` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_lte, + /// `==` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_eq, + /// `>=` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_gte, + /// `>` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_gt, + /// `!=` + /// Uses the `pl_node` union field. Payload is `Bin`. + cmp_neq, + /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- + /// as type coercion from the new element type to the old element type. + /// Uses the `bin` union field. + /// LHS is destination element type, RHS is result pointer. + coerce_result_ptr, + /// Emit an error message and fail compilation. + /// Uses the `un_node` field. + compile_error, + /// Log compile time variables and emit an error message. + /// Uses the `pl_node` union field. The AST node is the compile log builtin call. + /// The payload is `MultiOp`. + compile_log, + /// Conditional branch. Splits control flow based on a boolean condition value. + /// Uses the `pl_node` union field. AST node is an if, while, for, etc. + /// Payload is `CondBr`. + condbr, + /// Same as `condbr`, except the condition is coerced to a comptime value, and + /// only the taken branch is analyzed. The then block and else block must + /// terminate with an "inline" variant of a noreturn instruction. + condbr_inline, + /// A struct type definition. Contains references to ZIR instructions for + /// the field types, defaults, and alignments. + /// Uses the `pl_node` union field. Payload is `StructDecl`. + struct_decl, + /// Same as `struct_decl`, except has the `packed` layout. + struct_decl_packed, + /// Same as `struct_decl`, except has the `extern` layout. + struct_decl_extern, + /// A union type definition. Contains references to ZIR instructions for + /// the field types and optional type tag expression. + /// Uses the `pl_node` union field. Payload is `UnionDecl`. + union_decl, + /// An enum type definition. Contains references to ZIR instructions for + /// the field value expressions and optional type tag expression. + /// Uses the `pl_node` union field. Payload is `EnumDecl`. + enum_decl, + /// Same as `enum_decl`, except the enum is non-exhaustive. + enum_decl_nonexhaustive, + /// An opaque type definition. Provides an AST node only. + /// Uses the `node` union field. + opaque_decl, + /// Declares the beginning of a statement. Used for debug info. + /// Uses the `node` union field. + dbg_stmt_node, + /// Represents a pointer to a global decl. + /// Uses the `pl_node` union field. `payload_index` is into `decls`. + decl_ref, + /// Equivalent to a decl_ref followed by load. + /// Uses the `pl_node` union field. `payload_index` is into `decls`. + decl_val, + /// Same as `decl_ref` except instead of indexing into decls, uses + /// a name to identify the Decl. Uses the `str_tok` union field. + decl_ref_named, + /// Same as `decl_val` except instead of indexing into decls, uses + /// a name to identify the Decl. Uses the `str_tok` union field. + decl_val_named, + /// Load the value from a pointer. Assumes `x.*` syntax. + /// Uses `un_node` field. AST node is the `x.*` syntax. + load, + /// Arithmetic division. Asserts no integer overflow. + /// Uses the `pl_node` union field. Payload is `Bin`. + div, + /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at + /// the provided index. Uses the `bin` union field. Source location is implied + /// to be the same as the previous instruction. + elem_ptr, + /// Same as `elem_ptr` except also stores a source location node. + /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. + elem_ptr_node, + /// Given an array, slice, or pointer, returns the element at the provided index. + /// Uses the `bin` union field. Source location is implied to be the same + /// as the previous instruction. + elem_val, + /// Same as `elem_val` except also stores a source location node. + /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. + elem_val_node, + /// This instruction has been deleted late in the astgen phase. It must + /// be ignored, and the corresponding `Data` is undefined. + elided, + /// Emits a compile error if the operand is not `void`. + /// Uses the `un_node` field. + ensure_result_used, + /// Emits a compile error if an error is ignored. + /// Uses the `un_node` field. + ensure_result_non_error, + /// Create a `E!T` type. + /// Uses the `pl_node` field with `Bin` payload. + error_union_type, + /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. + error_value, + /// Implements the `@export` builtin function. + /// Uses the `pl_node` union field. Payload is `Bin`. + @"export", + /// Given a pointer to a struct or object that contains virtual fields, returns a pointer + /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. + /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. + field_ptr, + /// Given a struct or object that contains virtual fields, returns the named field. + /// The field name is stored in string_bytes. Used by a.b syntax. + /// This instruction also accepts a pointer. + /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. + field_val, + /// Given a pointer to a struct or object that contains virtual fields, returns a pointer + /// to the named field. The field name is a comptime instruction. Used by @field. + /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. + field_ptr_named, + /// Given a struct or object that contains virtual fields, returns the named field. + /// The field name is a comptime instruction. Used by @field. + /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. + field_val_named, + /// Convert a larger float type to any other float type, possibly causing + /// a loss of precision. + /// Uses the `pl_node` field. AST is the `@floatCast` syntax. + /// Payload is `Bin` with lhs as the dest type, rhs the operand. + floatcast, + /// Returns a function type, assuming unspecified calling convention. + /// Uses the `pl_node` union field. `payload_index` points to a `FnType`. + fn_type, + /// Same as `fn_type` but the function is variadic. + fn_type_var_args, + /// Returns a function type, with a calling convention instruction operand. + /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`. + fn_type_cc, + /// Same as `fn_type_cc` but the function is variadic. + fn_type_cc_var_args, + /// Implements the `@hasDecl` builtin. + /// Uses the `pl_node` union field. Payload is `Bin`. + has_decl, + /// `@import(operand)`. + /// Uses the `un_node` field. + import, + /// Integer literal that fits in a u64. Uses the int union value. + int, + /// A float literal that fits in a f32. Uses the float union value. + float, + /// A float literal that fits in a f128. Uses the `pl_node` union value. + /// Payload is `Float128`. + float128, + /// Convert an integer value to another integer type, asserting that the destination type + /// can hold the same mathematical value. + /// Uses the `pl_node` field. AST is the `@intCast` syntax. + /// Payload is `Bin` with lhs as the dest type, rhs the operand. + intcast, + /// Make an integer type out of signedness and bit count. + /// Payload is `int_type` + int_type, + /// Convert an error type to `u16` + error_to_int, + /// Convert a `u16` to `anyerror` + int_to_error, + /// Return a boolean false if an optional is null. `x != null` + /// Uses the `un_node` field. + is_non_null, + /// Return a boolean true if an optional is null. `x == null` + /// Uses the `un_node` field. + is_null, + /// Return a boolean false if an optional is null. `x.* != null` + /// Uses the `un_node` field. + is_non_null_ptr, + /// Return a boolean true if an optional is null. `x.* == null` + /// Uses the `un_node` field. + is_null_ptr, + /// Return a boolean true if value is an error + /// Uses the `un_node` field. + is_err, + /// Return a boolean true if dereferenced pointer is an error + /// Uses the `un_node` field. + is_err_ptr, + /// A labeled block of code that loops forever. At the end of the body will have either + /// a `repeat` instruction or a `repeat_inline` instruction. + /// Uses the `pl_node` field. The AST node is either a for loop or while loop. + /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema + /// needs to emit more than 1 TZIR block for this instruction. + /// The payload is `Block`. + loop, + /// Sends runtime control flow back to the beginning of the current block. + /// Uses the `node` field. + repeat, + /// Sends comptime control flow back to the beginning of the current block. + /// Uses the `node` field. + repeat_inline, + /// Merge two error sets into one, `E1 || E2`. + /// Uses the `pl_node` field with payload `Bin`. + merge_error_sets, + /// Ambiguously remainder division or modulus. If the computation would possibly have + /// a different value depending on whether the operation is remainder division or modulus, + /// a compile error is emitted. Otherwise the computation is performed. + /// Uses the `pl_node` union field. Payload is `Bin`. + mod_rem, + /// Arithmetic multiplication. Asserts no integer overflow. + /// Uses the `pl_node` union field. Payload is `Bin`. + mul, + /// Twos complement wrapping integer multiplication. + /// Uses the `pl_node` union field. Payload is `Bin`. + mulwrap, + /// Given a reference to a function and a parameter index, returns the + /// type of the parameter. The only usage of this instruction is for the + /// result location of parameters of function calls. In the case of a function's + /// parameter type being `anytype`, it is the type coercion's job to detect this + /// scenario and skip the coercion, so that semantic analysis of this instruction + /// is not in a position where it must create an invalid type. + /// Uses the `param_type` union field. + param_type, + /// Convert a pointer to a `usize` integer. + /// Uses the `un_node` field. The AST node is the builtin fn call node. + ptrtoint, + /// Turns an R-Value into a const L-Value. In other words, it takes a value, + /// stores it in a memory location, and returns a const pointer to it. If the value + /// is `comptime`, the memory location is global static constant data. Otherwise, + /// the memory location is in the stack frame, local to the scope containing the + /// instruction. + /// Uses the `un_tok` union field. + ref, + /// Obtains a pointer to the return value. + /// Uses the `node` union field. + ret_ptr, + /// Obtains the return type of the in-scope function. + /// Uses the `node` union field. + ret_type, + /// Sends control flow back to the function's callee. + /// Includes an operand as the return value. + /// Includes an AST node source location. + /// Uses the `un_node` union field. + ret_node, + /// Sends control flow back to the function's callee. + /// Includes an operand as the return value. + /// Includes a token source location. + /// Uses the `un_tok` union field. + ret_tok, + /// Same as `ret_tok` except the operand needs to get coerced to the function's + /// return type. + ret_coerce, + /// Changes the maximum number of backwards branches that compile-time + /// code execution can use before giving up and making a compile error. + /// Uses the `un_node` union field. + set_eval_branch_quota, + /// Integer shift-left. Zeroes are shifted in from the right hand side. + /// Uses the `pl_node` union field. Payload is `Bin`. + shl, + /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. + /// Uses the `pl_node` union field. Payload is `Bin`. + shr, + /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. + /// Uses the `ptr_type_simple` union field. + ptr_type_simple, + /// Create a pointer type which can have a sentinel, alignment, and/or bit range. + /// Uses the `ptr_type` union field. + ptr_type, + /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, + /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. + /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which + /// is the allocation that needs to have its type inferred. + /// Uses the `un_node` field. The AST node is the var decl. + resolve_inferred_alloc, + /// Slice operation `lhs[rhs..]`. No sentinel and no end offset. + /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`. + slice_start, + /// Slice operation `array_ptr[start..end]`. No sentinel. + /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`. + slice_end, + /// Slice operation `array_ptr[start..end:sentinel]`. + /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. + slice_sentinel, + /// Write a value to a pointer. For loading, see `load`. + /// Source location is assumed to be same as previous instruction. + /// Uses the `bin` union field. + store, + /// Same as `store` except provides a source location. + /// Uses the `pl_node` union field. Payload is `Bin`. + store_node, + /// Same as `store` but the type of the value being stored will be used to infer + /// the block type. The LHS is the pointer to store to. + /// Uses the `bin` union field. + store_to_block_ptr, + /// Same as `store` but the type of the value being stored will be used to infer + /// the pointer type. + /// Uses the `bin` union field - Astgen.zig depends on the ability to change + /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` + /// without changing the data. + store_to_inferred_ptr, + /// String Literal. Makes an anonymous Decl and then takes a pointer to it. + /// Uses the `str` union field. + str, + /// Arithmetic subtraction. Asserts no integer overflow. + /// Uses the `pl_node` union field. Payload is `Bin`. + sub, + /// Twos complement wrapping integer subtraction. + /// Uses the `pl_node` union field. Payload is `Bin`. + subwrap, + /// Arithmetic negation. Asserts no integer overflow. + /// Same as sub with a lhs of 0, split into a separate instruction to save memory. + /// Uses `un_node`. + negate, + /// Twos complement wrapping integer negation. + /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory. + /// Uses `un_node`. + negate_wrap, + /// Returns the type of a value. + /// Uses the `un_tok` field. + typeof, + /// Given a value which is a pointer, returns the element type. + /// Uses the `un_node` field. + typeof_elem, + /// The builtin `@TypeOf` which returns the type after Peer Type Resolution + /// of one or more params. + /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`. + typeof_peer, + /// Asserts control-flow will not reach this instruction (`unreachable`). + /// Uses the `unreachable` union field. + @"unreachable", + /// Bitwise XOR. `^` + /// Uses the `pl_node` union field. Payload is `Bin`. + xor, + /// Create an optional type '?T' + /// Uses the `un_node` field. + optional_type, + /// Create an optional type '?T'. The operand is a pointer value. The optional type will + /// be the type of the pointer element, wrapped in an optional. + /// Uses the `un_node` field. + optional_type_from_ptr_elem, + /// ?T => T with safety. + /// Given an optional value, returns the payload value, with a safety check that + /// the value is non-null. Used for `orelse`, `if` and `while`. + /// Uses the `un_node` field. + optional_payload_safe, + /// ?T => T without safety. + /// Given an optional value, returns the payload value. No safety checks. + /// Uses the `un_node` field. + optional_payload_unsafe, + /// *?T => *T with safety. + /// Given a pointer to an optional value, returns a pointer to the payload value, + /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. + /// Uses the `un_node` field. + optional_payload_safe_ptr, + /// *?T => *T without safety. + /// Given a pointer to an optional value, returns a pointer to the payload value. + /// No safety checks. + /// Uses the `un_node` field. + optional_payload_unsafe_ptr, + /// E!T => T with safety. + /// Given an error union value, returns the payload value, with a safety check + /// that the value is not an error. Used for catch, if, and while. + /// Uses the `un_node` field. + err_union_payload_safe, + /// E!T => T without safety. + /// Given an error union value, returns the payload value. No safety checks. + /// Uses the `un_node` field. + err_union_payload_unsafe, + /// *E!T => *T with safety. + /// Given a pointer to an error union value, returns a pointer to the payload value, + /// with a safety check that the value is not an error. Used for catch, if, and while. + /// Uses the `un_node` field. + err_union_payload_safe_ptr, + /// *E!T => *T without safety. + /// Given a pointer to a error union value, returns a pointer to the payload value. + /// No safety checks. + /// Uses the `un_node` field. + err_union_payload_unsafe_ptr, + /// E!T => E without safety. + /// Given an error union value, returns the error code. No safety checks. + /// Uses the `un_node` field. + err_union_code, + /// *E!T => E without safety. + /// Given a pointer to an error union value, returns the error code. No safety checks. + /// Uses the `un_node` field. + err_union_code_ptr, + /// Takes a *E!T and raises a compiler error if T != void + /// Uses the `un_tok` field. + ensure_err_payload_void, + /// An enum literal. Uses the `str_tok` union field. + enum_literal, + /// An enum literal 8 or fewer bytes. No source location. + /// Uses the `small_str` field. + enum_literal_small, + /// A switch expression. Uses the `pl_node` union field. + /// AST node is the switch, payload is `SwitchBlock`. + /// All prongs of target handled. + switch_block, + /// Same as switch_block, except one or more prongs have multiple items. + switch_block_multi, + /// Same as switch_block, except has an else prong. + switch_block_else, + /// Same as switch_block_else, except one or more prongs have multiple items. + switch_block_else_multi, + /// Same as switch_block, except has an underscore prong. + switch_block_under, + /// Same as switch_block, except one or more prongs have multiple items. + switch_block_under_multi, + /// Same as `switch_block` but the target is a pointer to the value being switched on. + switch_block_ref, + /// Same as `switch_block_multi` but the target is a pointer to the value being switched on. + switch_block_ref_multi, + /// Same as `switch_block_else` but the target is a pointer to the value being switched on. + switch_block_ref_else, + /// Same as `switch_block_else_multi` but the target is a pointer to the + /// value being switched on. + switch_block_ref_else_multi, + /// Same as `switch_block_under` but the target is a pointer to the value + /// being switched on. + switch_block_ref_under, + /// Same as `switch_block_under_multi` but the target is a pointer to + /// the value being switched on. + switch_block_ref_under_multi, + /// Produces the capture value for a switch prong. + /// Uses the `switch_capture` field. + switch_capture, + /// Produces the capture value for a switch prong. + /// Result is a pointer to the value. + /// Uses the `switch_capture` field. + switch_capture_ref, + /// Produces the capture value for a switch prong. + /// The prong is one of the multi cases. + /// Uses the `switch_capture` field. + switch_capture_multi, + /// Produces the capture value for a switch prong. + /// The prong is one of the multi cases. + /// Result is a pointer to the value. + /// Uses the `switch_capture` field. + switch_capture_multi_ref, + /// Produces the capture value for the else/'_' switch prong. + /// Uses the `switch_capture` field. + switch_capture_else, + /// Produces the capture value for the else/'_' switch prong. + /// Result is a pointer to the value. + /// Uses the `switch_capture` field. + switch_capture_else_ref, + /// Given a set of `field_ptr` instructions, assumes they are all part of a struct + /// initialization expression, and emits compile errors for duplicate fields + /// as well as missing fields, if applicable. + /// This instruction asserts that there is at least one field_ptr instruction, + /// because it must use one of them to find out the struct type. + /// Uses the `pl_node` field. Payload is `Block`. + validate_struct_init_ptr, + /// A struct literal with a specified type, with no fields. + /// Uses the `un_node` field. + struct_init_empty, + /// Given a struct, union, enum, or opaque and a field name, returns the field type. + /// Uses the `pl_node` field. Payload is `FieldType`. + field_type, + /// Finalizes a typed struct initialization, performs validation, and returns the + /// struct value. + /// Uses the `pl_node` field. Payload is `StructInit`. + struct_init, + /// Converts an integer into an enum value. + /// Uses `pl_node` with payload `Bin`. `lhs` is enum type, `rhs` is operand. + int_to_enum, + /// Converts an enum value into an integer. Resulting type will be the tag type + /// of the enum. Uses `un_node`. + enum_to_int, + /// Implements the `@typeInfo` builtin. Uses `un_node`. + type_info, + /// Implements the `@sizeOf` builtin. Uses `un_node`. + size_of, + /// Implements the `@bitSizeOf` builtin. Uses `un_node`. + bit_size_of, + + /// Returns whether the instruction is one of the control flow "noreturn" types. + /// Function calls do not count. + pub fn isNoReturn(tag: Tag) bool { + return switch (tag) { + .add, + .addwrap, + .alloc, + .alloc_mut, + .alloc_inferred, + .alloc_inferred_mut, + .array_cat, + .array_mul, + .array_type, + .array_type_sentinel, + .indexable_ptr_len, + .as, + .as_node, + .@"asm", + .asm_volatile, + .bit_and, + .bitcast, + .bitcast_result_ptr, + .bit_or, + .block, + .block_inline, + .loop, + .bool_br_and, + .bool_br_or, + .bool_not, + .bool_and, + .bool_or, + .breakpoint, + .call, + .call_chkused, + .call_compile_time, + .call_none, + .call_none_chkused, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .coerce_result_ptr, + .struct_decl, + .struct_decl_packed, + .struct_decl_extern, + .union_decl, + .enum_decl, + .enum_decl_nonexhaustive, + .opaque_decl, + .dbg_stmt_node, + .decl_ref, + .decl_val, + .decl_ref_named, + .decl_val_named, + .load, + .div, + .elem_ptr, + .elem_val, + .elem_ptr_node, + .elem_val_node, + .ensure_result_used, + .ensure_result_non_error, + .@"export", + .floatcast, + .field_ptr, + .field_val, + .field_ptr_named, + .field_val_named, + .fn_type, + .fn_type_var_args, + .fn_type_cc, + .fn_type_cc_var_args, + .has_decl, + .int, + .float, + .float128, + .intcast, + .int_type, + .is_non_null, + .is_null, + .is_non_null_ptr, + .is_null_ptr, + .is_err, + .is_err_ptr, + .mod_rem, + .mul, + .mulwrap, + .param_type, + .ptrtoint, + .ref, + .ret_ptr, + .ret_type, + .shl, + .shr, + .store, + .store_node, + .store_to_block_ptr, + .store_to_inferred_ptr, + .str, + .sub, + .subwrap, + .negate, + .negate_wrap, + .typeof, + .typeof_elem, + .xor, + .optional_type, + .optional_type_from_ptr_elem, + .optional_payload_safe, + .optional_payload_unsafe, + .optional_payload_safe_ptr, + .optional_payload_unsafe_ptr, + .err_union_payload_safe, + .err_union_payload_unsafe, + .err_union_payload_safe_ptr, + .err_union_payload_unsafe_ptr, + .err_union_code, + .err_union_code_ptr, + .error_to_int, + .int_to_error, + .ptr_type, + .ptr_type_simple, + .ensure_err_payload_void, + .enum_literal, + .enum_literal_small, + .merge_error_sets, + .error_union_type, + .bit_not, + .error_value, + .slice_start, + .slice_end, + .slice_sentinel, + .import, + .typeof_peer, + .resolve_inferred_alloc, + .set_eval_branch_quota, + .compile_log, + .elided, + .switch_capture, + .switch_capture_ref, + .switch_capture_multi, + .switch_capture_multi_ref, + .switch_capture_else, + .switch_capture_else_ref, + .switch_block, + .switch_block_multi, + .switch_block_else, + .switch_block_else_multi, + .switch_block_under, + .switch_block_under_multi, + .switch_block_ref, + .switch_block_ref_multi, + .switch_block_ref_else, + .switch_block_ref_else_multi, + .switch_block_ref_under, + .switch_block_ref_under_multi, + .validate_struct_init_ptr, + .struct_init_empty, + .struct_init, + .field_type, + .int_to_enum, + .enum_to_int, + .type_info, + .size_of, + .bit_size_of, + => false, + + .@"break", + .break_inline, + .condbr, + .condbr_inline, + .compile_error, + .ret_node, + .ret_tok, + .ret_coerce, + .@"unreachable", + .repeat, + .repeat_inline, + => true, + }; + } + }; + + /// The position of a ZIR instruction within the `Zir` instructions array. + pub const Index = u32; + + /// A reference to a TypedValue, parameter of the current function, + /// or ZIR instruction. + /// + /// If the Ref has a tag in this enum, it refers to a TypedValue which may be + /// retrieved with Ref.toTypedValue(). + /// + /// If the value of a Ref does not have a tag, it referes to either a parameter + /// of the current function or a ZIR instruction. + /// + /// The first values after the the last tag refer to parameters which may be + /// derived by subtracting typed_value_map.len. + /// + /// All further values refer to ZIR instructions which may be derived by + /// subtracting typed_value_map.len and the number of parameters. + /// + /// When adding a tag to this enum, consider adding a corresponding entry to + /// `simple_types` in astgen. + /// + /// The tag type is specified so that it is safe to bitcast between `[]u32` + /// and `[]Ref`. + pub const Ref = enum(u32) { + /// This Ref does not correspond to any ZIR instruction or constant + /// value and may instead be used as a sentinel to indicate null. + none, + + u8_type, + i8_type, + u16_type, + i16_type, + u32_type, + i32_type, + u64_type, + i64_type, + usize_type, + isize_type, + c_short_type, + c_ushort_type, + c_int_type, + c_uint_type, + c_long_type, + c_ulong_type, + c_longlong_type, + c_ulonglong_type, + c_longdouble_type, + f16_type, + f32_type, + f64_type, + f128_type, + c_void_type, + bool_type, + void_type, + type_type, + anyerror_type, + comptime_int_type, + comptime_float_type, + noreturn_type, + null_type, + undefined_type, + fn_noreturn_no_args_type, + fn_void_no_args_type, + fn_naked_noreturn_no_args_type, + fn_ccc_void_no_args_type, + single_const_pointer_to_comptime_int_type, + const_slice_u8_type, + enum_literal_type, + + /// `undefined` (untyped) + undef, + /// `0` (comptime_int) + zero, + /// `1` (comptime_int) + one, + /// `{}` + void_value, + /// `unreachable` (noreturn type) + unreachable_value, + /// `null` (untyped) + null_value, + /// `true` + bool_true, + /// `false` + bool_false, + /// `.{}` (untyped) + empty_struct, + /// `0` (usize) + zero_usize, + /// `1` (usize) + one_usize, + + _, + + pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{ + .none = undefined, + + .u8_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u8_type), + }, + .i8_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.i8_type), + }, + .u16_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u16_type), + }, + .i16_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.i16_type), + }, + .u32_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u32_type), + }, + .i32_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.i32_type), + }, + .u64_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u64_type), + }, + .i64_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.i64_type), + }, + .usize_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.usize_type), + }, + .isize_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.isize_type), + }, + .c_short_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_short_type), + }, + .c_ushort_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_ushort_type), + }, + .c_int_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_int_type), + }, + .c_uint_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_uint_type), + }, + .c_long_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_long_type), + }, + .c_ulong_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_ulong_type), + }, + .c_longlong_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_longlong_type), + }, + .c_ulonglong_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_ulonglong_type), + }, + .c_longdouble_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_longdouble_type), + }, + .f16_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.f16_type), + }, + .f32_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.f32_type), + }, + .f64_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.f64_type), + }, + .f128_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.f128_type), + }, + .c_void_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.c_void_type), + }, + .bool_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.bool_type), + }, + .void_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.void_type), + }, + .type_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }, + .anyerror_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.anyerror_type), + }, + .comptime_int_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.comptime_int_type), + }, + .comptime_float_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.comptime_float_type), + }, + .noreturn_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.noreturn_type), + }, + .null_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.null_type), + }, + .undefined_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.undefined_type), + }, + .fn_noreturn_no_args_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.fn_noreturn_no_args_type), + }, + .fn_void_no_args_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.fn_void_no_args_type), + }, + .fn_naked_noreturn_no_args_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.fn_naked_noreturn_no_args_type), + }, + .fn_ccc_void_no_args_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.fn_ccc_void_no_args_type), + }, + .single_const_pointer_to_comptime_int_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.single_const_pointer_to_comptime_int_type), + }, + .const_slice_u8_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.const_slice_u8_type), + }, + .enum_literal_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.enum_literal_type), + }, + + .undef = .{ + .ty = Type.initTag(.@"undefined"), + .val = Value.initTag(.undef), + }, + .zero = .{ + .ty = Type.initTag(.comptime_int), + .val = Value.initTag(.zero), + }, + .zero_usize = .{ + .ty = Type.initTag(.usize), + .val = Value.initTag(.zero), + }, + .one = .{ + .ty = Type.initTag(.comptime_int), + .val = Value.initTag(.one), + }, + .one_usize = .{ + .ty = Type.initTag(.usize), + .val = Value.initTag(.one), + }, + .void_value = .{ + .ty = Type.initTag(.void), + .val = Value.initTag(.void_value), + }, + .unreachable_value = .{ + .ty = Type.initTag(.noreturn), + .val = Value.initTag(.unreachable_value), + }, + .null_value = .{ + .ty = Type.initTag(.@"null"), + .val = Value.initTag(.null_value), + }, + .bool_true = .{ + .ty = Type.initTag(.bool), + .val = Value.initTag(.bool_true), + }, + .bool_false = .{ + .ty = Type.initTag(.bool), + .val = Value.initTag(.bool_false), + }, + .empty_struct = .{ + .ty = Type.initTag(.empty_struct_literal), + .val = Value.initTag(.empty_struct_value), + }, + }); + }; + + /// All instructions have an 8-byte payload, which is contained within + /// this union. `Tag` determines which union field is active, as well as + /// how to interpret the data within. + pub const Data = union { + /// Used for unary operators, with an AST node source location. + un_node: struct { + /// Offset from Decl AST node index. + src_node: i32, + /// The meaning of this operand depends on the corresponding `Tag`. + operand: Ref, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .node_offset = self.src_node }; + } + }, + /// Used for unary operators, with a token source location. + un_tok: struct { + /// Offset from Decl AST token index. + src_tok: ast.TokenIndex, + /// The meaning of this operand depends on the corresponding `Tag`. + operand: Ref, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .token_offset = self.src_tok }; + } + }, + pl_node: struct { + /// Offset from Decl AST node index. + /// `Tag` determines which kind of AST node this points to. + src_node: i32, + /// index into extra. + /// `Tag` determines what lives there. + payload_index: u32, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .node_offset = self.src_node }; + } + }, + bin: Bin, + /// For strings which may contain null bytes. + str: struct { + /// Offset into `string_bytes`. + start: u32, + /// Number of bytes in the string. + len: u32, + + pub fn get(self: @This(), code: Zir) []const u8 { + return code.string_bytes[self.start..][0..self.len]; + } + }, + /// Strings 8 or fewer bytes which may not contain null bytes. + small_str: struct { + bytes: [8]u8, + + pub fn get(self: @This()) []const u8 { + const end = for (self.bytes) |byte, i| { + if (byte == 0) break i; + } else self.bytes.len; + return self.bytes[0..end]; + } + }, + str_tok: struct { + /// Offset into `string_bytes`. Null-terminated. + start: u32, + /// Offset from Decl AST token index. + src_tok: u32, + + pub fn get(self: @This(), code: Zir) [:0]const u8 { + return code.nullTerminatedString(self.start); + } + + pub fn src(self: @This()) LazySrcLoc { + return .{ .token_offset = self.src_tok }; + } + }, + /// Offset from Decl AST token index. + tok: ast.TokenIndex, + /// Offset from Decl AST node index. + node: i32, + int: u64, + float: struct { + /// Offset from Decl AST node index. + /// `Tag` determines which kind of AST node this points to. + src_node: i32, + number: f32, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .node_offset = self.src_node }; + } + }, + array_type_sentinel: struct { + len: Ref, + /// index into extra, points to an `ArrayTypeSentinel` + payload_index: u32, + }, + ptr_type_simple: struct { + is_allowzero: bool, + is_mutable: bool, + is_volatile: bool, + size: std.builtin.TypeInfo.Pointer.Size, + elem_type: Ref, + }, + ptr_type: struct { + flags: packed struct { + is_allowzero: bool, + is_mutable: bool, + is_volatile: bool, + has_sentinel: bool, + has_align: bool, + has_bit_range: bool, + _: u2 = undefined, + }, + size: std.builtin.TypeInfo.Pointer.Size, + /// Index into extra. See `PtrType`. + payload_index: u32, + }, + int_type: struct { + /// Offset from Decl AST node index. + /// `Tag` determines which kind of AST node this points to. + src_node: i32, + signedness: std.builtin.Signedness, + bit_count: u16, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .node_offset = self.src_node }; + } + }, + bool_br: struct { + lhs: Ref, + /// Points to a `Block`. + payload_index: u32, + }, + param_type: struct { + callee: Ref, + param_index: u32, + }, + @"unreachable": struct { + /// Offset from Decl AST node index. + /// `Tag` determines which kind of AST node this points to. + src_node: i32, + /// `false`: Not safety checked - the compiler will assume the + /// correctness of this instruction. + /// `true`: In safety-checked modes, this will generate a call + /// to the panic function unless it can be proven unreachable by the compiler. + safety: bool, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .node_offset = self.src_node }; + } + }, + @"break": struct { + block_inst: Index, + operand: Ref, + }, + switch_capture: struct { + switch_inst: Index, + prong_index: u32, + }, + + // Make sure we don't accidentally add a field to make this union + // bigger than expected. Note that in Debug builds, Zig is allowed + // to insert a secret field for safety checks. + comptime { + if (std.builtin.mode != .Debug) { + assert(@sizeOf(Data) == 8); + } + } + }; + + /// Stored in extra. Trailing is: + /// * output_name: u32 // index into string_bytes (null terminated) if output is present + /// * arg: Ref // for every args_len. + /// * constraint: u32 // index into string_bytes (null terminated) for every args_len. + /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len. + pub const Asm = struct { + asm_source: Ref, + return_type: Ref, + /// May be omitted. + output: Ref, + args_len: u32, + clobbers_len: u32, + }; + + /// This data is stored inside extra, with trailing parameter type indexes + /// according to `param_types_len`. + /// Each param type is a `Ref`. + pub const FnTypeCc = struct { + return_type: Ref, + cc: Ref, + param_types_len: u32, + }; + + /// This data is stored inside extra, with trailing parameter type indexes + /// according to `param_types_len`. + /// Each param type is a `Ref`. + pub const FnType = struct { + return_type: Ref, + param_types_len: u32, + }; + + /// This data is stored inside extra, with trailing operands according to `operands_len`. + /// Each operand is a `Ref`. + pub const MultiOp = struct { + operands_len: u32, + }; + + /// This data is stored inside extra, with trailing operands according to `body_len`. + /// Each operand is an `Index`. + pub const Block = struct { + body_len: u32, + }; + + /// Stored inside extra, with trailing arguments according to `args_len`. + /// Each argument is a `Ref`. + pub const Call = struct { + callee: Ref, + args_len: u32, + }; + + /// This data is stored inside extra, with two sets of trailing `Ref`: + /// * 0. the then body, according to `then_body_len`. + /// * 1. the else body, according to `else_body_len`. + pub const CondBr = struct { + condition: Ref, + then_body_len: u32, + else_body_len: u32, + }; + + /// Stored in extra. Depending on the flags in Data, there will be up to 4 + /// trailing Ref fields: + /// 0. sentinel: Ref // if `has_sentinel` flag is set + /// 1. align: Ref // if `has_align` flag is set + /// 2. bit_start: Ref // if `has_bit_range` flag is set + /// 3. bit_end: Ref // if `has_bit_range` flag is set + pub const PtrType = struct { + elem_type: Ref, + }; + + pub const ArrayTypeSentinel = struct { + sentinel: Ref, + elem_type: Ref, + }; + + pub const SliceStart = struct { + lhs: Ref, + start: Ref, + }; + + pub const SliceEnd = struct { + lhs: Ref, + start: Ref, + end: Ref, + }; + + pub const SliceSentinel = struct { + lhs: Ref, + start: Ref, + end: Ref, + sentinel: Ref, + }; + + /// The meaning of these operands depends on the corresponding `Tag`. + pub const Bin = struct { + lhs: Ref, + rhs: Ref, + }; + + /// This form is supported when there are no ranges, and exactly 1 item per block. + /// Depending on zir tag and len fields, extra fields trail + /// this one in the extra array. + /// 0. else_body { // If the tag has "_else" or "_under" in it. + /// body_len: u32, + /// body member Index for every body_len + /// } + /// 1. cases: { + /// item: Ref, + /// body_len: u32, + /// body member Index for every body_len + /// } for every cases_len + pub const SwitchBlock = struct { + operand: Ref, + cases_len: u32, + }; + + /// This form is required when there exists a block which has more than one item, + /// or a range. + /// Depending on zir tag and len fields, extra fields trail + /// this one in the extra array. + /// 0. else_body { // If the tag has "_else" or "_under" in it. + /// body_len: u32, + /// body member Index for every body_len + /// } + /// 1. scalar_cases: { // for every scalar_cases_len + /// item: Ref, + /// body_len: u32, + /// body member Index for every body_len + /// } + /// 2. multi_cases: { // for every multi_cases_len + /// items_len: u32, + /// ranges_len: u32, + /// body_len: u32, + /// item: Ref // for every items_len + /// ranges: { // for every ranges_len + /// item_first: Ref, + /// item_last: Ref, + /// } + /// body member Index for every body_len + /// } + pub const SwitchBlockMulti = struct { + operand: Ref, + scalar_cases_len: u32, + multi_cases_len: u32, + }; + + pub const Field = struct { + lhs: Ref, + /// Offset into `string_bytes`. + field_name_start: u32, + }; + + pub const FieldNamed = struct { + lhs: Ref, + field_name: Ref, + }; + + pub const As = struct { + dest_type: Ref, + operand: Ref, + }; + + /// Trailing: + /// 0. inst: Index // for every body_len + /// 1. has_bits: u32 // for every 16 fields + /// - sets of 2 bits: + /// 0b0X: whether corresponding field has an align expression + /// 0bX0: whether corresponding field has a default expression + /// 2. fields: { // for every fields_len + /// field_name: u32, + /// field_type: Ref, + /// align: Ref, // if corresponding bit is set + /// default_value: Ref, // if corresponding bit is set + /// } + pub const StructDecl = struct { + body_len: u32, + fields_len: u32, + }; + + /// Trailing: + /// 0. inst: Index // for every body_len + /// 1. has_bits: u32 // for every 32 fields + /// - the bit is whether corresponding field has an value expression + /// 2. fields: { // for every fields_len + /// field_name: u32, + /// value: Ref, // if corresponding bit is set + /// } + pub const EnumDecl = struct { + /// Can be `Ref.none`. + tag_type: Ref, + body_len: u32, + fields_len: u32, + }; + + /// Trailing: + /// 0. has_bits: u32 // for every 10 fields (+1) + /// - first bit is special: set if and only if auto enum tag is enabled. + /// - sets of 3 bits: + /// 0b00X: whether corresponding field has a type expression + /// 0b0X0: whether corresponding field has a align expression + /// 0bX00: whether corresponding field has a tag value expression + /// 1. field_name: u32 // for every field: null terminated string index + /// 2. opt_exprs // Ref for every field for which corresponding bit is set + /// - interleaved. type if present, align if present, tag value if present. + pub const UnionDecl = struct { + /// Can be `Ref.none`. + tag_type: Ref, + fields_len: u32, + }; + + /// A f128 value, broken up into 4 u32 parts. + pub const Float128 = struct { + piece0: u32, + piece1: u32, + piece2: u32, + piece3: u32, + + pub fn get(self: Float128) f128 { + const int_bits = @as(u128, self.piece0) | + (@as(u128, self.piece1) << 32) | + (@as(u128, self.piece2) << 64) | + (@as(u128, self.piece3) << 96); + return @bitCast(f128, int_bits); + } + }; + + /// Trailing is an item per field. + pub const StructInit = struct { + fields_len: u32, + + pub const Item = struct { + /// The `field_type` ZIR instruction for this field init. + field_type: Index, + /// The field init expression to be used as the field value. + init: Ref, + }; + }; + + pub const FieldType = struct { + container_type: Ref, + /// Offset into `string_bytes`, null terminated. + name_start: u32, + }; +}; + +pub const SpecialProng = enum { none, @"else", under }; + +const Writer = struct { + gpa: *Allocator, + arena: *Allocator, + scope: *Module.Scope, + code: Zir, + indent: usize, + param_count: usize, + + fn writeInstToStream( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const tags = self.code.instructions.items(.tag); + const tag = tags[inst]; + try stream.print("= {s}(", .{@tagName(tags[inst])}); + switch (tag) { + .array_type, + .as, + .coerce_result_ptr, + .elem_ptr, + .elem_val, + .intcast, + .store, + .store_to_block_ptr, + .store_to_inferred_ptr, + => try self.writeBin(stream, inst), + + .alloc, + .alloc_mut, + .indexable_ptr_len, + .bit_not, + .bool_not, + .negate, + .negate_wrap, + .call_none, + .call_none_chkused, + .compile_error, + .load, + .ensure_result_used, + .ensure_result_non_error, + .import, + .ptrtoint, + .ret_node, + .set_eval_branch_quota, + .resolve_inferred_alloc, + .optional_type, + .optional_type_from_ptr_elem, + .optional_payload_safe, + .optional_payload_unsafe, + .optional_payload_safe_ptr, + .optional_payload_unsafe_ptr, + .err_union_payload_safe, + .err_union_payload_unsafe, + .err_union_payload_safe_ptr, + .err_union_payload_unsafe_ptr, + .err_union_code, + .err_union_code_ptr, + .int_to_error, + .error_to_int, + .is_non_null, + .is_null, + .is_non_null_ptr, + .is_null_ptr, + .is_err, + .is_err_ptr, + .typeof, + .typeof_elem, + .struct_init_empty, + .enum_to_int, + .type_info, + .size_of, + .bit_size_of, + => try self.writeUnNode(stream, inst), + + .ref, + .ret_tok, + .ret_coerce, + .ensure_err_payload_void, + => try self.writeUnTok(stream, inst), + + .bool_br_and, + .bool_br_or, + => try self.writeBoolBr(stream, inst), + + .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), + .param_type => try self.writeParamType(stream, inst), + .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), + .ptr_type => try self.writePtrType(stream, inst), + .int => try self.writeInt(stream, inst), + .float => try self.writeFloat(stream, inst), + .float128 => try self.writeFloat128(stream, inst), + .str => try self.writeStr(stream, inst), + .elided => try stream.writeAll(")"), + .int_type => try self.writeIntType(stream, inst), + + .@"break", + .break_inline, + => try self.writeBreak(stream, inst), + + .@"asm", + .asm_volatile, + .elem_ptr_node, + .elem_val_node, + .field_ptr_named, + .field_val_named, + .floatcast, + .slice_start, + .slice_end, + .slice_sentinel, + .union_decl, + .struct_init, + .field_type, + => try self.writePlNode(stream, inst), + + .add, + .addwrap, + .array_cat, + .array_mul, + .mul, + .mulwrap, + .sub, + .subwrap, + .bool_and, + .bool_or, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .div, + .has_decl, + .mod_rem, + .shl, + .shr, + .xor, + .store_node, + .error_union_type, + .@"export", + .merge_error_sets, + .bit_and, + .bit_or, + .int_to_enum, + => try self.writePlNodeBin(stream, inst), + + .call, + .call_chkused, + .call_compile_time, + => try self.writePlNodeCall(stream, inst), + + .block, + .block_inline, + .loop, + .validate_struct_init_ptr, + => try self.writePlNodeBlock(stream, inst), + + .condbr, + .condbr_inline, + => try self.writePlNodeCondBr(stream, inst), + + .struct_decl, + .struct_decl_packed, + .struct_decl_extern, + => try self.writeStructDecl(stream, inst), + + .enum_decl, + .enum_decl_nonexhaustive, + => try self.writeEnumDecl(stream, inst), + + .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), + .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), + .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under), + .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none), + .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), + .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under), + + .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), + .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), + .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), + .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), + .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), + .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), + + .compile_log, + .typeof_peer, + => try self.writePlNodeMultiOp(stream, inst), + + .decl_ref, + .decl_val, + => try self.writePlNodeDecl(stream, inst), + + .field_ptr, + .field_val, + => try self.writePlNodeField(stream, inst), + + .as_node => try self.writeAs(stream, inst), + + .breakpoint, + .opaque_decl, + .dbg_stmt_node, + .ret_ptr, + .ret_type, + .repeat, + .repeat_inline, + .alloc_inferred, + .alloc_inferred_mut, + => try self.writeNode(stream, inst), + + .error_value, + .enum_literal, + .decl_ref_named, + .decl_val_named, + => try self.writeStrTok(stream, inst), + + .fn_type => try self.writeFnType(stream, inst, false), + .fn_type_cc => try self.writeFnTypeCc(stream, inst, false), + .fn_type_var_args => try self.writeFnType(stream, inst, true), + .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true), + + .@"unreachable" => try self.writeUnreachable(stream, inst), + + .enum_literal_small => try self.writeSmallStr(stream, inst), + + .switch_capture, + .switch_capture_ref, + .switch_capture_multi, + .switch_capture_multi_ref, + .switch_capture_else, + .switch_capture_else_ref, + => try self.writeSwitchCapture(stream, inst), + + .bitcast, + .bitcast_result_ptr, + => try stream.writeAll("TODO)"), + } + } + + fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].bin; + try self.writeInstRef(stream, inst_data.lhs); + try stream.writeAll(", "); + try self.writeInstRef(stream, inst_data.rhs); + try stream.writeByte(')'); + } + + fn writeUnNode( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].un_node; + try self.writeInstRef(stream, inst_data.operand); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeUnTok( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].un_tok; + try self.writeInstRef(stream, inst_data.operand); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeArrayTypeSentinel( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; + try stream.writeAll("TODO)"); + } + + fn writeParamType( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].param_type; + try self.writeInstRef(stream, inst_data.callee); + try stream.print(", {d})", .{inst_data.param_index}); + } + + fn writePtrTypeSimple( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; + const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; + const str_const = if (!inst_data.is_mutable) "const, " else ""; + const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; + try self.writeInstRef(stream, inst_data.elem_type); + try stream.print(", {s}{s}{s}{s})", .{ + str_allowzero, + str_const, + str_volatile, + @tagName(inst_data.size), + }); + } + + fn writePtrType( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].ptr_type; + try stream.writeAll("TODO)"); + } + + fn writeInt( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].int; + try stream.print("{d})", .{inst_data}); + } + + fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].float; + const src = inst_data.src(); + try stream.print("{d}) ", .{inst_data.number}); + try self.writeSrc(stream, src); + } + + fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Float128, inst_data.payload_index).data; + const src = inst_data.src(); + const number = extra.get(); + // TODO improve std.format to be able to print f128 values + try stream.print("{d}) ", .{@floatCast(f64, number)}); + try self.writeSrc(stream, src); + } + + fn writeStr( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].str; + const str = inst_data.get(self.code); + try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); + } + + fn writePlNode( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + try stream.writeAll("TODO) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; + try self.writeInstRef(stream, extra.lhs); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.rhs); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Call, inst_data.payload_index); + const args = self.code.refSlice(extra.end, extra.data.args_len); + + try self.writeInstRef(stream, extra.data.callee); + try stream.writeAll(", ["); + for (args) |arg, i| { + if (i != 0) try stream.writeAll(", "); + try self.writeInstRef(stream, arg); + } + try stream.writeAll("]) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Block, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + try stream.writeAll("{\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index); + const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + try self.writeInstRef(stream, extra.data.condition); + try stream.writeAll(", {\n"); + self.indent += 2; + try self.writeBody(stream, then_body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}, {\n"); + self.indent += 2; + try self.writeBody(stream, else_body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + const fields_len = extra.data.fields_len; + + if (fields_len == 0) { + assert(body.len == 0); + try stream.writeAll("{}, {}) "); + try self.writeSrc(stream, inst_data.src()); + return; + } + + try stream.writeAll("{\n"); + self.indent += 2; + try self.writeBody(stream, body); + + try stream.writeByteNTimes(' ', self.indent - 2); + try stream.writeAll("}, {\n"); + + const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; + const body_end = extra.end + body.len; + var extra_index: usize = body_end + bit_bags_count; + var bit_bag_index: usize = body_end; + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % 16 == 0) { + cur_bit_bag = self.code.extra[bit_bag_index]; + bit_bag_index += 1; + } + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_default = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); + extra_index += 1; + const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeByteNTimes(' ', self.indent); + try stream.print("{}: ", .{std.zig.fmtId(field_name)}); + try self.writeInstRef(stream, field_type); + + if (has_align) { + const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeAll(" align("); + try self.writeInstRef(stream, align_ref); + try stream.writeAll(")"); + } + if (has_default) { + const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeAll(" = "); + try self.writeInstRef(stream, default_ref); + } + try stream.writeAll(",\n"); + } + + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeEnumDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.EnumDecl, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + const fields_len = extra.data.fields_len; + const tag_ty_ref = extra.data.tag_type; + + if (tag_ty_ref != .none) { + try self.writeInstRef(stream, tag_ty_ref); + try stream.writeAll(", "); + } + + if (fields_len == 0) { + assert(body.len == 0); + try stream.writeAll("{}, {}) "); + try self.writeSrc(stream, inst_data.src()); + return; + } + + try stream.writeAll("{\n"); + self.indent += 2; + try self.writeBody(stream, body); + + try stream.writeByteNTimes(' ', self.indent - 2); + try stream.writeAll("}, {\n"); + + const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; + const body_end = extra.end + body.len; + var extra_index: usize = body_end + bit_bags_count; + var bit_bag_index: usize = body_end; + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % 32 == 0) { + cur_bit_bag = self.code.extra[bit_bag_index]; + bit_bag_index += 1; + } + const has_tag_value = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeByteNTimes(' ', self.indent); + try stream.print("{}", .{std.zig.fmtId(field_name)}); + + if (has_tag_value) { + const tag_value_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeAll(" = "); + try self.writeInstRef(stream, tag_value_ref); + } + try stream.writeAll(",\n"); + } + + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}) "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeSwitchBr( + self: *Writer, + stream: anytype, + inst: Inst.Index, + special_prong: SpecialProng, + ) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index); + const special: struct { + body: []const Inst.Index, + end: usize, + } = switch (special_prong) { + .none => .{ .body = &.{}, .end = extra.end }, + .under, .@"else" => blk: { + const body_len = self.code.extra[extra.end]; + const extra_body_start = extra.end + 1; + break :blk .{ + .body = self.code.extra[extra_body_start..][0..body_len], + .end = extra_body_start + body_len, + }; + }, + }; + + try self.writeInstRef(stream, extra.data.operand); + + if (special.body.len != 0) { + const prong_name = switch (special_prong) { + .@"else" => "else", + .under => "_", + else => unreachable, + }; + try stream.print(", {s} => {{\n", .{prong_name}); + self.indent += 2; + try self.writeBody(stream, special.body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}"); + } + + var extra_index: usize = special.end; + { + var scalar_i: usize = 0; + while (scalar_i < extra.data.cases_len) : (scalar_i += 1) { + const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + const body_len = self.code.extra[extra_index]; + extra_index += 1; + const body = self.code.extra[extra_index..][0..body_len]; + extra_index += body_len; + + try stream.writeAll(", "); + try self.writeInstRef(stream, item_ref); + try stream.writeAll(" => {\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}"); + } + } + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeSwitchBlockMulti( + self: *Writer, + stream: anytype, + inst: Inst.Index, + special_prong: SpecialProng, + ) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index); + const special: struct { + body: []const Inst.Index, + end: usize, + } = switch (special_prong) { + .none => .{ .body = &.{}, .end = extra.end }, + .under, .@"else" => blk: { + const body_len = self.code.extra[extra.end]; + const extra_body_start = extra.end + 1; + break :blk .{ + .body = self.code.extra[extra_body_start..][0..body_len], + .end = extra_body_start + body_len, + }; + }, + }; + + try self.writeInstRef(stream, extra.data.operand); + + if (special.body.len != 0) { + const prong_name = switch (special_prong) { + .@"else" => "else", + .under => "_", + else => unreachable, + }; + try stream.print(", {s} => {{\n", .{prong_name}); + self.indent += 2; + try self.writeBody(stream, special.body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}"); + } + + var extra_index: usize = special.end; + { + var scalar_i: usize = 0; + while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) { + const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + const body_len = self.code.extra[extra_index]; + extra_index += 1; + const body = self.code.extra[extra_index..][0..body_len]; + extra_index += body_len; + + try stream.writeAll(", "); + try self.writeInstRef(stream, item_ref); + try stream.writeAll(" => {\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}"); + } + } + { + var multi_i: usize = 0; + while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) { + const items_len = self.code.extra[extra_index]; + extra_index += 1; + const ranges_len = self.code.extra[extra_index]; + extra_index += 1; + const body_len = self.code.extra[extra_index]; + extra_index += 1; + const items = self.code.refSlice(extra_index, items_len); + extra_index += items_len; + + for (items) |item_ref| { + try stream.writeAll(", "); + try self.writeInstRef(stream, item_ref); + } + + var range_i: usize = 0; + while (range_i < ranges_len) : (range_i += 1) { + const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + + try stream.writeAll(", "); + try self.writeInstRef(stream, item_first); + try stream.writeAll("..."); + try self.writeInstRef(stream, item_last); + } + + const body = self.code.extra[extra_index..][0..body_len]; + extra_index += body_len; + try stream.writeAll(" => {\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}"); + } + } + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index); + const operands = self.code.refSlice(extra.end, extra.data.operands_len); + + for (operands) |operand, i| { + if (i != 0) try stream.writeAll(", "); + try self.writeInstRef(stream, operand); + } + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const owner_decl = self.scope.ownerDecl().?; + const decl = owner_decl.dependencies.entries.items[inst_data.payload_index].key; + try stream.print("{s}) ", .{decl.name}); + try self.writeSrc(stream, inst_data.src()); + } + + fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data; + const name = self.code.nullTerminatedString(extra.field_name_start); + try self.writeInstRef(stream, extra.lhs); + try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)}); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; + try self.writeInstRef(stream, extra.dest_type); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.operand); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeNode( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const src_node = self.code.instructions.items(.data)[inst].node; + const src: LazySrcLoc = .{ .node_offset = src_node }; + try stream.writeAll(") "); + try self.writeSrc(stream, src); + } + + fn writeStrTok( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].str_tok; + const str = inst_data.get(self.code); + try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)}); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeFnType( + self: *Writer, + stream: anytype, + inst: Inst.Index, + var_args: bool, + ) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = self.code.extraData(Inst.FnType, inst_data.payload_index); + const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); + return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src); + } + + fn writeFnTypeCc( + self: *Writer, + stream: anytype, + inst: Inst.Index, + var_args: bool, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index); + const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); + const cc = extra.data.cc; + return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src); + } + + fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].bool_br; + const extra = self.code.extraData(Inst.Block, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + try self.writeInstRef(stream, inst_data.lhs); + try stream.writeAll(", {\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("})"); + } + + fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const int_type = self.code.instructions.items(.data)[inst].int_type; + const prefix: u8 = switch (int_type.signedness) { + .signed => 'i', + .unsigned => 'u', + }; + try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count }); + try self.writeSrc(stream, int_type.src()); + } + + fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].@"break"; + + try self.writeInstIndex(stream, inst_data.block_inst); + try stream.writeAll(", "); + try self.writeInstRef(stream, inst_data.operand); + try stream.writeAll(")"); + } + + fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; + const safety_str = if (inst_data.safety) "safe" else "unsafe"; + try stream.print("{s}) ", .{safety_str}); + try self.writeSrc(stream, inst_data.src()); + } + + fn writeFnTypeCommon( + self: *Writer, + stream: anytype, + param_types: []const Inst.Ref, + ret_ty: Inst.Ref, + var_args: bool, + cc: Inst.Ref, + src: LazySrcLoc, + ) !void { + try stream.writeAll("["); + for (param_types) |param_type, i| { + if (i != 0) try stream.writeAll(", "); + try self.writeInstRef(stream, param_type); + } + try stream.writeAll("], "); + try self.writeInstRef(stream, ret_ty); + try self.writeOptionalInstRef(stream, ", cc=", cc); + try self.writeFlag(stream, ", var_args", var_args); + try stream.writeAll(") "); + try self.writeSrc(stream, src); + } + + fn writeSmallStr( + self: *Writer, + stream: anytype, + inst: Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const str = self.code.instructions.items(.data)[inst].small_str.get(); + try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); + } + + fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].switch_capture; + try self.writeInstIndex(stream, inst_data.switch_inst); + try stream.print(", {d})", .{inst_data.prong_index}); + } + + fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void { + var i: usize = @enumToInt(ref); + + if (i < Inst.Ref.typed_value_map.len) { + return stream.print("@{}", .{ref}); + } + i -= Inst.Ref.typed_value_map.len; + + if (i < self.param_count) { + return stream.print("${d}", .{i}); + } + i -= self.param_count; + + return self.writeInstIndex(stream, @intCast(Inst.Index, i)); + } + + fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void { + return stream.print("%{d}", .{inst}); + } + + fn writeOptionalInstRef( + self: *Writer, + stream: anytype, + prefix: []const u8, + inst: Inst.Ref, + ) !void { + if (inst == .none) return; + try stream.writeAll(prefix); + try self.writeInstRef(stream, inst); + } + + fn writeFlag( + self: *Writer, + stream: anytype, + name: []const u8, + flag: bool, + ) !void { + if (!flag) return; + try stream.writeAll(name); + } + + fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { + const tree = self.scope.tree(); + const src_loc = src.toSrcLoc(self.scope); + const abs_byte_off = try src_loc.byteOffset(); + const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); + try stream.print("{s}:{d}:{d}", .{ + @tagName(src), delta_line.line + 1, delta_line.column + 1, + }); + } + + fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void { + for (body) |inst| { + try stream.writeByteNTimes(' ', self.indent); + try stream.print("%{d} ", .{inst}); + try self.writeInstToStream(stream, inst); + try stream.writeByte('\n'); + } + } +}; diff --git a/src/main.zig b/src/main.zig index cf0d08c5ed132b44a0310117f13c1d2cabd0784e..044c6f0fec95161a059c6d578a177c7c3cd9b91a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,7 +12,6 @@ const warn = std.log.warn; const Compilation = @import("Compilation.zig"); const link = @import("link.zig"); const Package = @import("Package.zig"); -const zir = @import("zir.zig"); const build_options = @import("build_options"); const introspect = @import("introspect.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; diff --git a/src/test.zig b/src/test.zig index ca3f073e144674d62fa43bda7296087b7c9c0952..e08f9da37db982e23da85a2329c280d4e0603c0c 100644 --- a/src/test.zig +++ b/src/test.zig @@ -2,7 +2,6 @@ const std = @import("std"); const link = @import("link.zig"); const Compilation = @import("Compilation.zig"); const Allocator = std.mem.Allocator; -const zir = @import("zir.zig"); const Package = @import("Package.zig"); const introspect = @import("introspect.zig"); const build_options = @import("build_options"); diff --git a/src/zir.zig b/src/zir.zig deleted file mode 100644 index bb1ac5fbc27c98e1599f941d92a24ade25056782..0000000000000000000000000000000000000000 --- a/src/zir.zig +++ /dev/null @@ -1,2548 +0,0 @@ -//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these -//! untyped IR instructions. Next, Sema.zig processes these into TZIR. -//! The minimum amount of information needed to represent a list of ZIR instructions. -//! Once this structure is completed, it can be used to generate TZIR, followed by -//! machine code, without any memory access into the AST tree token list, node list, -//! or source bytes. Exceptions include: -//! * Compile errors, which may need to reach into these data structures to -//! create a useful report. -//! * In the future, possibly inline assembly, which needs to get parsed and -//! handled by the codegen backend, and errors reported there. However for now, -//! inline assembly is not an exception. - -const std = @import("std"); -const mem = std.mem; -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const BigIntConst = std.math.big.int.Const; -const BigIntMutable = std.math.big.int.Mutable; -const ast = std.zig.ast; - -const Zir = @This(); -const Type = @import("type.zig").Type; -const Value = @import("value.zig").Value; -const TypedValue = @import("TypedValue.zig"); -const ir = @import("ir.zig"); -const Module = @import("Module.zig"); -const LazySrcLoc = Module.LazySrcLoc; - -/// There is always implicitly a `block` instruction at index 0. -/// This is so that `break_inline` can break from the root block. -instructions: std.MultiArrayList(Inst).Slice, -/// In order to store references to strings in fewer bytes, we copy all -/// string bytes into here. String bytes can be null. It is up to whomever -/// is referencing the data here whether they want to store both index and length, -/// thus allowing null bytes, or store only index, and use null-termination. The -/// `string_bytes` array is agnostic to either usage. -string_bytes: []u8, -/// The meaning of this data is determined by `Inst.Tag` value. -extra: []u32, - -/// Returns the requested data, as well as the new index which is at the start of the -/// trailers for the object. -pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } { - const fields = std.meta.fields(T); - var i: usize = index; - var result: T = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.field_type) { - u32 => code.extra[i], - Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]), - else => unreachable, - }; - i += 1; - } - return .{ - .data = result, - .end = i, - }; -} - -/// Given an index into `string_bytes` returns the null-terminated string found there. -pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 { - var end: usize = index; - while (code.string_bytes[end] != 0) { - end += 1; - } - return code.string_bytes[index..end :0]; -} - -pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref { - const raw_slice = code.extra[start..][0..len]; - return @bitCast([]Inst.Ref, raw_slice); -} - -pub fn deinit(code: *Zir, gpa: *Allocator) void { - code.instructions.deinit(gpa); - gpa.free(code.string_bytes); - gpa.free(code.extra); - code.* = undefined; -} - -/// For debugging purposes, like dumpFn but for unanalyzed zir blocks -pub fn dump( - code: Zir, - gpa: *Allocator, - kind: []const u8, - scope: *Module.Scope, - param_count: usize, -) !void { - var arena = std.heap.ArenaAllocator.init(gpa); - defer arena.deinit(); - - var writer: Writer = .{ - .gpa = gpa, - .arena = &arena.allocator, - .scope = scope, - .code = code, - .indent = 0, - .param_count = param_count, - }; - - const decl_name = scope.srcDecl().?.name; - const stderr = std.io.getStdErr().writer(); - try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name }); - try writer.writeInstToStream(stderr, 0); - try stderr.print(" // end ZIR {s} {s}\n\n", .{ kind, decl_name }); -} - -/// These are untyped instructions generated from an Abstract Syntax Tree. -/// The data here is immutable because it is possible to have multiple -/// analyses on the same ZIR happening at the same time. -pub const Inst = struct { - tag: Tag, - data: Data, - - /// These names are used directly as the instruction names in the text format. - pub const Tag = enum(u8) { - /// Arithmetic addition, asserts no integer overflow. - /// Uses the `pl_node` union field. Payload is `Bin`. - add, - /// Twos complement wrapping integer addition. - /// Uses the `pl_node` union field. Payload is `Bin`. - addwrap, - /// Allocates stack local memory. - /// Uses the `un_node` union field. The operand is the type of the allocated object. - /// The node source location points to a var decl node. - /// Indicates the beginning of a new statement in debug info. - alloc, - /// Same as `alloc` except mutable. - alloc_mut, - /// Same as `alloc` except the type is inferred. - /// Uses the `node` union field. - alloc_inferred, - /// Same as `alloc_inferred` except mutable. - alloc_inferred_mut, - /// Array concatenation. `a ++ b` - /// Uses the `pl_node` union field. Payload is `Bin`. - array_cat, - /// Array multiplication `a ** b` - /// Uses the `pl_node` union field. Payload is `Bin`. - array_mul, - /// `[N]T` syntax. No source location provided. - /// Uses the `bin` union field. lhs is length, rhs is element type. - array_type, - /// `[N:S]T` syntax. No source location provided. - /// Uses the `array_type_sentinel` field. - array_type_sentinel, - /// Given a pointer to an indexable object, returns the len property. This is - /// used by for loops. This instruction also emits a for-loop specific compile - /// error if the indexable object is not indexable. - /// Uses the `un_node` field. The AST node is the for loop node. - indexable_ptr_len, - /// Type coercion. No source location attached. - /// Uses the `bin` field. - as, - /// Type coercion to the function's return type. - /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. - as_node, - /// Inline assembly. Non-volatile. - /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node. - @"asm", - /// Inline assembly with the volatile attribute. - /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node. - asm_volatile, - /// Bitwise AND. `&` - bit_and, - /// Bitcast a value to a different type. - /// Uses the pl_node field with payload `Bin`. - bitcast, - /// A typed result location pointer is bitcasted to a new result location pointer. - /// The new result location pointer has an inferred type. - /// Uses the un_node field. - bitcast_result_ptr, - /// Bitwise NOT. `~` - /// Uses `un_node`. - bit_not, - /// Bitwise OR. `|` - bit_or, - /// A labeled block of code, which can return a value. - /// Uses the `pl_node` union field. Payload is `Block`. - block, - /// A list of instructions which are analyzed in the parent context, without - /// generating a runtime block. Must terminate with an "inline" variant of - /// a noreturn instruction. - /// Uses the `pl_node` union field. Payload is `Block`. - block_inline, - /// Boolean AND. See also `bit_and`. - /// Uses the `pl_node` union field. Payload is `Bin`. - bool_and, - /// Boolean NOT. See also `bit_not`. - /// Uses the `un_node` field. - bool_not, - /// Boolean OR. See also `bit_or`. - /// Uses the `pl_node` union field. Payload is `Bin`. - bool_or, - /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand - /// is a block, which is evaluated if `lhs` is `true`. - /// Uses the `bool_br` union field. - bool_br_and, - /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand - /// is a block, which is evaluated if `lhs` is `false`. - /// Uses the `bool_br` union field. - bool_br_or, - /// Return a value from a block. - /// Uses the `break` union field. - /// Uses the source information from previous instruction. - @"break", - /// Return a value from a block. This instruction is used as the terminator - /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`. - /// This instruction may also be used when it is known that there is only one - /// break instruction in a block, and the target block is the parent. - /// Uses the `break` union field. - break_inline, - /// Uses the `node` union field. - breakpoint, - /// Function call with modifier `.auto`. - /// Uses `pl_node`. AST node is the function call. Payload is `Call`. - call, - /// Same as `call` but it also does `ensure_result_used` on the return value. - call_chkused, - /// Same as `call` but with modifier `.compile_time`. - call_compile_time, - /// Function call with modifier `.auto`, empty parameter list. - /// Uses the `un_node` field. Operand is callee. AST node is the function call. - call_none, - /// Same as `call_none` but it also does `ensure_result_used` on the return value. - call_none_chkused, - /// `<` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_lt, - /// `<=` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_lte, - /// `==` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_eq, - /// `>=` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_gte, - /// `>` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_gt, - /// `!=` - /// Uses the `pl_node` union field. Payload is `Bin`. - cmp_neq, - /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- - /// as type coercion from the new element type to the old element type. - /// Uses the `bin` union field. - /// LHS is destination element type, RHS is result pointer. - coerce_result_ptr, - /// Emit an error message and fail compilation. - /// Uses the `un_node` field. - compile_error, - /// Log compile time variables and emit an error message. - /// Uses the `pl_node` union field. The AST node is the compile log builtin call. - /// The payload is `MultiOp`. - compile_log, - /// Conditional branch. Splits control flow based on a boolean condition value. - /// Uses the `pl_node` union field. AST node is an if, while, for, etc. - /// Payload is `CondBr`. - condbr, - /// Same as `condbr`, except the condition is coerced to a comptime value, and - /// only the taken branch is analyzed. The then block and else block must - /// terminate with an "inline" variant of a noreturn instruction. - condbr_inline, - /// A struct type definition. Contains references to ZIR instructions for - /// the field types, defaults, and alignments. - /// Uses the `pl_node` union field. Payload is `StructDecl`. - struct_decl, - /// Same as `struct_decl`, except has the `packed` layout. - struct_decl_packed, - /// Same as `struct_decl`, except has the `extern` layout. - struct_decl_extern, - /// A union type definition. Contains references to ZIR instructions for - /// the field types and optional type tag expression. - /// Uses the `pl_node` union field. Payload is `UnionDecl`. - union_decl, - /// An enum type definition. Contains references to ZIR instructions for - /// the field value expressions and optional type tag expression. - /// Uses the `pl_node` union field. Payload is `EnumDecl`. - enum_decl, - /// Same as `enum_decl`, except the enum is non-exhaustive. - enum_decl_nonexhaustive, - /// An opaque type definition. Provides an AST node only. - /// Uses the `node` union field. - opaque_decl, - /// Declares the beginning of a statement. Used for debug info. - /// Uses the `node` union field. - dbg_stmt_node, - /// Represents a pointer to a global decl. - /// Uses the `pl_node` union field. `payload_index` is into `decls`. - decl_ref, - /// Equivalent to a decl_ref followed by load. - /// Uses the `pl_node` union field. `payload_index` is into `decls`. - decl_val, - /// Same as `decl_ref` except instead of indexing into decls, uses - /// a name to identify the Decl. Uses the `str_tok` union field. - decl_ref_named, - /// Same as `decl_val` except instead of indexing into decls, uses - /// a name to identify the Decl. Uses the `str_tok` union field. - decl_val_named, - /// Load the value from a pointer. Assumes `x.*` syntax. - /// Uses `un_node` field. AST node is the `x.*` syntax. - load, - /// Arithmetic division. Asserts no integer overflow. - /// Uses the `pl_node` union field. Payload is `Bin`. - div, - /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at - /// the provided index. Uses the `bin` union field. Source location is implied - /// to be the same as the previous instruction. - elem_ptr, - /// Same as `elem_ptr` except also stores a source location node. - /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. - elem_ptr_node, - /// Given an array, slice, or pointer, returns the element at the provided index. - /// Uses the `bin` union field. Source location is implied to be the same - /// as the previous instruction. - elem_val, - /// Same as `elem_val` except also stores a source location node. - /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. - elem_val_node, - /// This instruction has been deleted late in the astgen phase. It must - /// be ignored, and the corresponding `Data` is undefined. - elided, - /// Emits a compile error if the operand is not `void`. - /// Uses the `un_node` field. - ensure_result_used, - /// Emits a compile error if an error is ignored. - /// Uses the `un_node` field. - ensure_result_non_error, - /// Create a `E!T` type. - /// Uses the `pl_node` field with `Bin` payload. - error_union_type, - /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. - error_value, - /// Implements the `@export` builtin function. - /// Uses the `pl_node` union field. Payload is `Bin`. - @"export", - /// Given a pointer to a struct or object that contains virtual fields, returns a pointer - /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. - /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. - field_ptr, - /// Given a struct or object that contains virtual fields, returns the named field. - /// The field name is stored in string_bytes. Used by a.b syntax. - /// This instruction also accepts a pointer. - /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. - field_val, - /// Given a pointer to a struct or object that contains virtual fields, returns a pointer - /// to the named field. The field name is a comptime instruction. Used by @field. - /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. - field_ptr_named, - /// Given a struct or object that contains virtual fields, returns the named field. - /// The field name is a comptime instruction. Used by @field. - /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. - field_val_named, - /// Convert a larger float type to any other float type, possibly causing - /// a loss of precision. - /// Uses the `pl_node` field. AST is the `@floatCast` syntax. - /// Payload is `Bin` with lhs as the dest type, rhs the operand. - floatcast, - /// Returns a function type, assuming unspecified calling convention. - /// Uses the `pl_node` union field. `payload_index` points to a `FnType`. - fn_type, - /// Same as `fn_type` but the function is variadic. - fn_type_var_args, - /// Returns a function type, with a calling convention instruction operand. - /// Uses the `pl_node` union field. `payload_index` points to a `FnTypeCc`. - fn_type_cc, - /// Same as `fn_type_cc` but the function is variadic. - fn_type_cc_var_args, - /// Implements the `@hasDecl` builtin. - /// Uses the `pl_node` union field. Payload is `Bin`. - has_decl, - /// `@import(operand)`. - /// Uses the `un_node` field. - import, - /// Integer literal that fits in a u64. Uses the int union value. - int, - /// A float literal that fits in a f32. Uses the float union value. - float, - /// A float literal that fits in a f128. Uses the `pl_node` union value. - /// Payload is `Float128`. - float128, - /// Convert an integer value to another integer type, asserting that the destination type - /// can hold the same mathematical value. - /// Uses the `pl_node` field. AST is the `@intCast` syntax. - /// Payload is `Bin` with lhs as the dest type, rhs the operand. - intcast, - /// Make an integer type out of signedness and bit count. - /// Payload is `int_type` - int_type, - /// Convert an error type to `u16` - error_to_int, - /// Convert a `u16` to `anyerror` - int_to_error, - /// Return a boolean false if an optional is null. `x != null` - /// Uses the `un_node` field. - is_non_null, - /// Return a boolean true if an optional is null. `x == null` - /// Uses the `un_node` field. - is_null, - /// Return a boolean false if an optional is null. `x.* != null` - /// Uses the `un_node` field. - is_non_null_ptr, - /// Return a boolean true if an optional is null. `x.* == null` - /// Uses the `un_node` field. - is_null_ptr, - /// Return a boolean true if value is an error - /// Uses the `un_node` field. - is_err, - /// Return a boolean true if dereferenced pointer is an error - /// Uses the `un_node` field. - is_err_ptr, - /// A labeled block of code that loops forever. At the end of the body will have either - /// a `repeat` instruction or a `repeat_inline` instruction. - /// Uses the `pl_node` field. The AST node is either a for loop or while loop. - /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema - /// needs to emit more than 1 TZIR block for this instruction. - /// The payload is `Block`. - loop, - /// Sends runtime control flow back to the beginning of the current block. - /// Uses the `node` field. - repeat, - /// Sends comptime control flow back to the beginning of the current block. - /// Uses the `node` field. - repeat_inline, - /// Merge two error sets into one, `E1 || E2`. - /// Uses the `pl_node` field with payload `Bin`. - merge_error_sets, - /// Ambiguously remainder division or modulus. If the computation would possibly have - /// a different value depending on whether the operation is remainder division or modulus, - /// a compile error is emitted. Otherwise the computation is performed. - /// Uses the `pl_node` union field. Payload is `Bin`. - mod_rem, - /// Arithmetic multiplication. Asserts no integer overflow. - /// Uses the `pl_node` union field. Payload is `Bin`. - mul, - /// Twos complement wrapping integer multiplication. - /// Uses the `pl_node` union field. Payload is `Bin`. - mulwrap, - /// Given a reference to a function and a parameter index, returns the - /// type of the parameter. The only usage of this instruction is for the - /// result location of parameters of function calls. In the case of a function's - /// parameter type being `anytype`, it is the type coercion's job to detect this - /// scenario and skip the coercion, so that semantic analysis of this instruction - /// is not in a position where it must create an invalid type. - /// Uses the `param_type` union field. - param_type, - /// Convert a pointer to a `usize` integer. - /// Uses the `un_node` field. The AST node is the builtin fn call node. - ptrtoint, - /// Turns an R-Value into a const L-Value. In other words, it takes a value, - /// stores it in a memory location, and returns a const pointer to it. If the value - /// is `comptime`, the memory location is global static constant data. Otherwise, - /// the memory location is in the stack frame, local to the scope containing the - /// instruction. - /// Uses the `un_tok` union field. - ref, - /// Obtains a pointer to the return value. - /// Uses the `node` union field. - ret_ptr, - /// Obtains the return type of the in-scope function. - /// Uses the `node` union field. - ret_type, - /// Sends control flow back to the function's callee. - /// Includes an operand as the return value. - /// Includes an AST node source location. - /// Uses the `un_node` union field. - ret_node, - /// Sends control flow back to the function's callee. - /// Includes an operand as the return value. - /// Includes a token source location. - /// Uses the `un_tok` union field. - ret_tok, - /// Same as `ret_tok` except the operand needs to get coerced to the function's - /// return type. - ret_coerce, - /// Changes the maximum number of backwards branches that compile-time - /// code execution can use before giving up and making a compile error. - /// Uses the `un_node` union field. - set_eval_branch_quota, - /// Integer shift-left. Zeroes are shifted in from the right hand side. - /// Uses the `pl_node` union field. Payload is `Bin`. - shl, - /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. - /// Uses the `pl_node` union field. Payload is `Bin`. - shr, - /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. - /// Uses the `ptr_type_simple` union field. - ptr_type_simple, - /// Create a pointer type which can have a sentinel, alignment, and/or bit range. - /// Uses the `ptr_type` union field. - ptr_type, - /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, - /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. - /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which - /// is the allocation that needs to have its type inferred. - /// Uses the `un_node` field. The AST node is the var decl. - resolve_inferred_alloc, - /// Slice operation `lhs[rhs..]`. No sentinel and no end offset. - /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`. - slice_start, - /// Slice operation `array_ptr[start..end]`. No sentinel. - /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`. - slice_end, - /// Slice operation `array_ptr[start..end:sentinel]`. - /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. - slice_sentinel, - /// Write a value to a pointer. For loading, see `load`. - /// Source location is assumed to be same as previous instruction. - /// Uses the `bin` union field. - store, - /// Same as `store` except provides a source location. - /// Uses the `pl_node` union field. Payload is `Bin`. - store_node, - /// Same as `store` but the type of the value being stored will be used to infer - /// the block type. The LHS is the pointer to store to. - /// Uses the `bin` union field. - store_to_block_ptr, - /// Same as `store` but the type of the value being stored will be used to infer - /// the pointer type. - /// Uses the `bin` union field - Astgen.zig depends on the ability to change - /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr` - /// without changing the data. - store_to_inferred_ptr, - /// String Literal. Makes an anonymous Decl and then takes a pointer to it. - /// Uses the `str` union field. - str, - /// Arithmetic subtraction. Asserts no integer overflow. - /// Uses the `pl_node` union field. Payload is `Bin`. - sub, - /// Twos complement wrapping integer subtraction. - /// Uses the `pl_node` union field. Payload is `Bin`. - subwrap, - /// Arithmetic negation. Asserts no integer overflow. - /// Same as sub with a lhs of 0, split into a separate instruction to save memory. - /// Uses `un_node`. - negate, - /// Twos complement wrapping integer negation. - /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory. - /// Uses `un_node`. - negate_wrap, - /// Returns the type of a value. - /// Uses the `un_tok` field. - typeof, - /// Given a value which is a pointer, returns the element type. - /// Uses the `un_node` field. - typeof_elem, - /// The builtin `@TypeOf` which returns the type after Peer Type Resolution - /// of one or more params. - /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`. - typeof_peer, - /// Asserts control-flow will not reach this instruction (`unreachable`). - /// Uses the `unreachable` union field. - @"unreachable", - /// Bitwise XOR. `^` - /// Uses the `pl_node` union field. Payload is `Bin`. - xor, - /// Create an optional type '?T' - /// Uses the `un_node` field. - optional_type, - /// Create an optional type '?T'. The operand is a pointer value. The optional type will - /// be the type of the pointer element, wrapped in an optional. - /// Uses the `un_node` field. - optional_type_from_ptr_elem, - /// ?T => T with safety. - /// Given an optional value, returns the payload value, with a safety check that - /// the value is non-null. Used for `orelse`, `if` and `while`. - /// Uses the `un_node` field. - optional_payload_safe, - /// ?T => T without safety. - /// Given an optional value, returns the payload value. No safety checks. - /// Uses the `un_node` field. - optional_payload_unsafe, - /// *?T => *T with safety. - /// Given a pointer to an optional value, returns a pointer to the payload value, - /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. - /// Uses the `un_node` field. - optional_payload_safe_ptr, - /// *?T => *T without safety. - /// Given a pointer to an optional value, returns a pointer to the payload value. - /// No safety checks. - /// Uses the `un_node` field. - optional_payload_unsafe_ptr, - /// E!T => T with safety. - /// Given an error union value, returns the payload value, with a safety check - /// that the value is not an error. Used for catch, if, and while. - /// Uses the `un_node` field. - err_union_payload_safe, - /// E!T => T without safety. - /// Given an error union value, returns the payload value. No safety checks. - /// Uses the `un_node` field. - err_union_payload_unsafe, - /// *E!T => *T with safety. - /// Given a pointer to an error union value, returns a pointer to the payload value, - /// with a safety check that the value is not an error. Used for catch, if, and while. - /// Uses the `un_node` field. - err_union_payload_safe_ptr, - /// *E!T => *T without safety. - /// Given a pointer to a error union value, returns a pointer to the payload value. - /// No safety checks. - /// Uses the `un_node` field. - err_union_payload_unsafe_ptr, - /// E!T => E without safety. - /// Given an error union value, returns the error code. No safety checks. - /// Uses the `un_node` field. - err_union_code, - /// *E!T => E without safety. - /// Given a pointer to an error union value, returns the error code. No safety checks. - /// Uses the `un_node` field. - err_union_code_ptr, - /// Takes a *E!T and raises a compiler error if T != void - /// Uses the `un_tok` field. - ensure_err_payload_void, - /// An enum literal. Uses the `str_tok` union field. - enum_literal, - /// An enum literal 8 or fewer bytes. No source location. - /// Uses the `small_str` field. - enum_literal_small, - /// A switch expression. Uses the `pl_node` union field. - /// AST node is the switch, payload is `SwitchBlock`. - /// All prongs of target handled. - switch_block, - /// Same as switch_block, except one or more prongs have multiple items. - switch_block_multi, - /// Same as switch_block, except has an else prong. - switch_block_else, - /// Same as switch_block_else, except one or more prongs have multiple items. - switch_block_else_multi, - /// Same as switch_block, except has an underscore prong. - switch_block_under, - /// Same as switch_block, except one or more prongs have multiple items. - switch_block_under_multi, - /// Same as `switch_block` but the target is a pointer to the value being switched on. - switch_block_ref, - /// Same as `switch_block_multi` but the target is a pointer to the value being switched on. - switch_block_ref_multi, - /// Same as `switch_block_else` but the target is a pointer to the value being switched on. - switch_block_ref_else, - /// Same as `switch_block_else_multi` but the target is a pointer to the - /// value being switched on. - switch_block_ref_else_multi, - /// Same as `switch_block_under` but the target is a pointer to the value - /// being switched on. - switch_block_ref_under, - /// Same as `switch_block_under_multi` but the target is a pointer to - /// the value being switched on. - switch_block_ref_under_multi, - /// Produces the capture value for a switch prong. - /// Uses the `switch_capture` field. - switch_capture, - /// Produces the capture value for a switch prong. - /// Result is a pointer to the value. - /// Uses the `switch_capture` field. - switch_capture_ref, - /// Produces the capture value for a switch prong. - /// The prong is one of the multi cases. - /// Uses the `switch_capture` field. - switch_capture_multi, - /// Produces the capture value for a switch prong. - /// The prong is one of the multi cases. - /// Result is a pointer to the value. - /// Uses the `switch_capture` field. - switch_capture_multi_ref, - /// Produces the capture value for the else/'_' switch prong. - /// Uses the `switch_capture` field. - switch_capture_else, - /// Produces the capture value for the else/'_' switch prong. - /// Result is a pointer to the value. - /// Uses the `switch_capture` field. - switch_capture_else_ref, - /// Given a set of `field_ptr` instructions, assumes they are all part of a struct - /// initialization expression, and emits compile errors for duplicate fields - /// as well as missing fields, if applicable. - /// This instruction asserts that there is at least one field_ptr instruction, - /// because it must use one of them to find out the struct type. - /// Uses the `pl_node` field. Payload is `Block`. - validate_struct_init_ptr, - /// A struct literal with a specified type, with no fields. - /// Uses the `un_node` field. - struct_init_empty, - /// Given a struct, union, enum, or opaque and a field name, returns the field type. - /// Uses the `pl_node` field. Payload is `FieldType`. - field_type, - /// Finalizes a typed struct initialization, performs validation, and returns the - /// struct value. - /// Uses the `pl_node` field. Payload is `StructInit`. - struct_init, - /// Converts an integer into an enum value. - /// Uses `pl_node` with payload `Bin`. `lhs` is enum type, `rhs` is operand. - int_to_enum, - /// Converts an enum value into an integer. Resulting type will be the tag type - /// of the enum. Uses `un_node`. - enum_to_int, - /// Implements the `@typeInfo` builtin. Uses `un_node`. - type_info, - /// Implements the `@sizeOf` builtin. Uses `un_node`. - size_of, - /// Implements the `@bitSizeOf` builtin. Uses `un_node`. - bit_size_of, - - /// Returns whether the instruction is one of the control flow "noreturn" types. - /// Function calls do not count. - pub fn isNoReturn(tag: Tag) bool { - return switch (tag) { - .add, - .addwrap, - .alloc, - .alloc_mut, - .alloc_inferred, - .alloc_inferred_mut, - .array_cat, - .array_mul, - .array_type, - .array_type_sentinel, - .indexable_ptr_len, - .as, - .as_node, - .@"asm", - .asm_volatile, - .bit_and, - .bitcast, - .bitcast_result_ptr, - .bit_or, - .block, - .block_inline, - .loop, - .bool_br_and, - .bool_br_or, - .bool_not, - .bool_and, - .bool_or, - .breakpoint, - .call, - .call_chkused, - .call_compile_time, - .call_none, - .call_none_chkused, - .cmp_lt, - .cmp_lte, - .cmp_eq, - .cmp_gte, - .cmp_gt, - .cmp_neq, - .coerce_result_ptr, - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - .union_decl, - .enum_decl, - .enum_decl_nonexhaustive, - .opaque_decl, - .dbg_stmt_node, - .decl_ref, - .decl_val, - .decl_ref_named, - .decl_val_named, - .load, - .div, - .elem_ptr, - .elem_val, - .elem_ptr_node, - .elem_val_node, - .ensure_result_used, - .ensure_result_non_error, - .@"export", - .floatcast, - .field_ptr, - .field_val, - .field_ptr_named, - .field_val_named, - .fn_type, - .fn_type_var_args, - .fn_type_cc, - .fn_type_cc_var_args, - .has_decl, - .int, - .float, - .float128, - .intcast, - .int_type, - .is_non_null, - .is_null, - .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, - .mod_rem, - .mul, - .mulwrap, - .param_type, - .ptrtoint, - .ref, - .ret_ptr, - .ret_type, - .shl, - .shr, - .store, - .store_node, - .store_to_block_ptr, - .store_to_inferred_ptr, - .str, - .sub, - .subwrap, - .negate, - .negate_wrap, - .typeof, - .typeof_elem, - .xor, - .optional_type, - .optional_type_from_ptr_elem, - .optional_payload_safe, - .optional_payload_unsafe, - .optional_payload_safe_ptr, - .optional_payload_unsafe_ptr, - .err_union_payload_safe, - .err_union_payload_unsafe, - .err_union_payload_safe_ptr, - .err_union_payload_unsafe_ptr, - .err_union_code, - .err_union_code_ptr, - .error_to_int, - .int_to_error, - .ptr_type, - .ptr_type_simple, - .ensure_err_payload_void, - .enum_literal, - .enum_literal_small, - .merge_error_sets, - .error_union_type, - .bit_not, - .error_value, - .slice_start, - .slice_end, - .slice_sentinel, - .import, - .typeof_peer, - .resolve_inferred_alloc, - .set_eval_branch_quota, - .compile_log, - .elided, - .switch_capture, - .switch_capture_ref, - .switch_capture_multi, - .switch_capture_multi_ref, - .switch_capture_else, - .switch_capture_else_ref, - .switch_block, - .switch_block_multi, - .switch_block_else, - .switch_block_else_multi, - .switch_block_under, - .switch_block_under_multi, - .switch_block_ref, - .switch_block_ref_multi, - .switch_block_ref_else, - .switch_block_ref_else_multi, - .switch_block_ref_under, - .switch_block_ref_under_multi, - .validate_struct_init_ptr, - .struct_init_empty, - .struct_init, - .field_type, - .int_to_enum, - .enum_to_int, - .type_info, - .size_of, - .bit_size_of, - => false, - - .@"break", - .break_inline, - .condbr, - .condbr_inline, - .compile_error, - .ret_node, - .ret_tok, - .ret_coerce, - .@"unreachable", - .repeat, - .repeat_inline, - => true, - }; - } - }; - - /// The position of a ZIR instruction within the `Zir` instructions array. - pub const Index = u32; - - /// A reference to a TypedValue, parameter of the current function, - /// or ZIR instruction. - /// - /// If the Ref has a tag in this enum, it refers to a TypedValue which may be - /// retrieved with Ref.toTypedValue(). - /// - /// If the value of a Ref does not have a tag, it referes to either a parameter - /// of the current function or a ZIR instruction. - /// - /// The first values after the the last tag refer to parameters which may be - /// derived by subtracting typed_value_map.len. - /// - /// All further values refer to ZIR instructions which may be derived by - /// subtracting typed_value_map.len and the number of parameters. - /// - /// When adding a tag to this enum, consider adding a corresponding entry to - /// `simple_types` in astgen. - /// - /// The tag type is specified so that it is safe to bitcast between `[]u32` - /// and `[]Ref`. - pub const Ref = enum(u32) { - /// This Ref does not correspond to any ZIR instruction or constant - /// value and may instead be used as a sentinel to indicate null. - none, - - u8_type, - i8_type, - u16_type, - i16_type, - u32_type, - i32_type, - u64_type, - i64_type, - usize_type, - isize_type, - c_short_type, - c_ushort_type, - c_int_type, - c_uint_type, - c_long_type, - c_ulong_type, - c_longlong_type, - c_ulonglong_type, - c_longdouble_type, - f16_type, - f32_type, - f64_type, - f128_type, - c_void_type, - bool_type, - void_type, - type_type, - anyerror_type, - comptime_int_type, - comptime_float_type, - noreturn_type, - null_type, - undefined_type, - fn_noreturn_no_args_type, - fn_void_no_args_type, - fn_naked_noreturn_no_args_type, - fn_ccc_void_no_args_type, - single_const_pointer_to_comptime_int_type, - const_slice_u8_type, - enum_literal_type, - - /// `undefined` (untyped) - undef, - /// `0` (comptime_int) - zero, - /// `1` (comptime_int) - one, - /// `{}` - void_value, - /// `unreachable` (noreturn type) - unreachable_value, - /// `null` (untyped) - null_value, - /// `true` - bool_true, - /// `false` - bool_false, - /// `.{}` (untyped) - empty_struct, - /// `0` (usize) - zero_usize, - /// `1` (usize) - one_usize, - - _, - - pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{ - .none = undefined, - - .u8_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.u8_type), - }, - .i8_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.i8_type), - }, - .u16_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.u16_type), - }, - .i16_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.i16_type), - }, - .u32_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.u32_type), - }, - .i32_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.i32_type), - }, - .u64_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.u64_type), - }, - .i64_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.i64_type), - }, - .usize_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.usize_type), - }, - .isize_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.isize_type), - }, - .c_short_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_short_type), - }, - .c_ushort_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_ushort_type), - }, - .c_int_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_int_type), - }, - .c_uint_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_uint_type), - }, - .c_long_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_long_type), - }, - .c_ulong_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_ulong_type), - }, - .c_longlong_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_longlong_type), - }, - .c_ulonglong_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_ulonglong_type), - }, - .c_longdouble_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_longdouble_type), - }, - .f16_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.f16_type), - }, - .f32_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.f32_type), - }, - .f64_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.f64_type), - }, - .f128_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.f128_type), - }, - .c_void_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.c_void_type), - }, - .bool_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.bool_type), - }, - .void_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.void_type), - }, - .type_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }, - .anyerror_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.anyerror_type), - }, - .comptime_int_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.comptime_int_type), - }, - .comptime_float_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.comptime_float_type), - }, - .noreturn_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.noreturn_type), - }, - .null_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.null_type), - }, - .undefined_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.undefined_type), - }, - .fn_noreturn_no_args_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.fn_noreturn_no_args_type), - }, - .fn_void_no_args_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.fn_void_no_args_type), - }, - .fn_naked_noreturn_no_args_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.fn_naked_noreturn_no_args_type), - }, - .fn_ccc_void_no_args_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.fn_ccc_void_no_args_type), - }, - .single_const_pointer_to_comptime_int_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.single_const_pointer_to_comptime_int_type), - }, - .const_slice_u8_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.const_slice_u8_type), - }, - .enum_literal_type = .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.enum_literal_type), - }, - - .undef = .{ - .ty = Type.initTag(.@"undefined"), - .val = Value.initTag(.undef), - }, - .zero = .{ - .ty = Type.initTag(.comptime_int), - .val = Value.initTag(.zero), - }, - .zero_usize = .{ - .ty = Type.initTag(.usize), - .val = Value.initTag(.zero), - }, - .one = .{ - .ty = Type.initTag(.comptime_int), - .val = Value.initTag(.one), - }, - .one_usize = .{ - .ty = Type.initTag(.usize), - .val = Value.initTag(.one), - }, - .void_value = .{ - .ty = Type.initTag(.void), - .val = Value.initTag(.void_value), - }, - .unreachable_value = .{ - .ty = Type.initTag(.noreturn), - .val = Value.initTag(.unreachable_value), - }, - .null_value = .{ - .ty = Type.initTag(.@"null"), - .val = Value.initTag(.null_value), - }, - .bool_true = .{ - .ty = Type.initTag(.bool), - .val = Value.initTag(.bool_true), - }, - .bool_false = .{ - .ty = Type.initTag(.bool), - .val = Value.initTag(.bool_false), - }, - .empty_struct = .{ - .ty = Type.initTag(.empty_struct_literal), - .val = Value.initTag(.empty_struct_value), - }, - }); - }; - - /// All instructions have an 8-byte payload, which is contained within - /// this union. `Tag` determines which union field is active, as well as - /// how to interpret the data within. - pub const Data = union { - /// Used for unary operators, with an AST node source location. - un_node: struct { - /// Offset from Decl AST node index. - src_node: i32, - /// The meaning of this operand depends on the corresponding `Tag`. - operand: Ref, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; - } - }, - /// Used for unary operators, with a token source location. - un_tok: struct { - /// Offset from Decl AST token index. - src_tok: ast.TokenIndex, - /// The meaning of this operand depends on the corresponding `Tag`. - operand: Ref, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .token_offset = self.src_tok }; - } - }, - pl_node: struct { - /// Offset from Decl AST node index. - /// `Tag` determines which kind of AST node this points to. - src_node: i32, - /// index into extra. - /// `Tag` determines what lives there. - payload_index: u32, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; - } - }, - bin: Bin, - /// For strings which may contain null bytes. - str: struct { - /// Offset into `string_bytes`. - start: u32, - /// Number of bytes in the string. - len: u32, - - pub fn get(self: @This(), code: Zir) []const u8 { - return code.string_bytes[self.start..][0..self.len]; - } - }, - /// Strings 8 or fewer bytes which may not contain null bytes. - small_str: struct { - bytes: [8]u8, - - pub fn get(self: @This()) []const u8 { - const end = for (self.bytes) |byte, i| { - if (byte == 0) break i; - } else self.bytes.len; - return self.bytes[0..end]; - } - }, - str_tok: struct { - /// Offset into `string_bytes`. Null-terminated. - start: u32, - /// Offset from Decl AST token index. - src_tok: u32, - - pub fn get(self: @This(), code: Zir) [:0]const u8 { - return code.nullTerminatedString(self.start); - } - - pub fn src(self: @This()) LazySrcLoc { - return .{ .token_offset = self.src_tok }; - } - }, - /// Offset from Decl AST token index. - tok: ast.TokenIndex, - /// Offset from Decl AST node index. - node: i32, - int: u64, - float: struct { - /// Offset from Decl AST node index. - /// `Tag` determines which kind of AST node this points to. - src_node: i32, - number: f32, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; - } - }, - array_type_sentinel: struct { - len: Ref, - /// index into extra, points to an `ArrayTypeSentinel` - payload_index: u32, - }, - ptr_type_simple: struct { - is_allowzero: bool, - is_mutable: bool, - is_volatile: bool, - size: std.builtin.TypeInfo.Pointer.Size, - elem_type: Ref, - }, - ptr_type: struct { - flags: packed struct { - is_allowzero: bool, - is_mutable: bool, - is_volatile: bool, - has_sentinel: bool, - has_align: bool, - has_bit_range: bool, - _: u2 = undefined, - }, - size: std.builtin.TypeInfo.Pointer.Size, - /// Index into extra. See `PtrType`. - payload_index: u32, - }, - int_type: struct { - /// Offset from Decl AST node index. - /// `Tag` determines which kind of AST node this points to. - src_node: i32, - signedness: std.builtin.Signedness, - bit_count: u16, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; - } - }, - bool_br: struct { - lhs: Ref, - /// Points to a `Block`. - payload_index: u32, - }, - param_type: struct { - callee: Ref, - param_index: u32, - }, - @"unreachable": struct { - /// Offset from Decl AST node index. - /// `Tag` determines which kind of AST node this points to. - src_node: i32, - /// `false`: Not safety checked - the compiler will assume the - /// correctness of this instruction. - /// `true`: In safety-checked modes, this will generate a call - /// to the panic function unless it can be proven unreachable by the compiler. - safety: bool, - - pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; - } - }, - @"break": struct { - block_inst: Index, - operand: Ref, - }, - switch_capture: struct { - switch_inst: Index, - prong_index: u32, - }, - - // Make sure we don't accidentally add a field to make this union - // bigger than expected. Note that in Debug builds, Zig is allowed - // to insert a secret field for safety checks. - comptime { - if (std.builtin.mode != .Debug) { - assert(@sizeOf(Data) == 8); - } - } - }; - - /// Stored in extra. Trailing is: - /// * output_name: u32 // index into string_bytes (null terminated) if output is present - /// * arg: Ref // for every args_len. - /// * constraint: u32 // index into string_bytes (null terminated) for every args_len. - /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len. - pub const Asm = struct { - asm_source: Ref, - return_type: Ref, - /// May be omitted. - output: Ref, - args_len: u32, - clobbers_len: u32, - }; - - /// This data is stored inside extra, with trailing parameter type indexes - /// according to `param_types_len`. - /// Each param type is a `Ref`. - pub const FnTypeCc = struct { - return_type: Ref, - cc: Ref, - param_types_len: u32, - }; - - /// This data is stored inside extra, with trailing parameter type indexes - /// according to `param_types_len`. - /// Each param type is a `Ref`. - pub const FnType = struct { - return_type: Ref, - param_types_len: u32, - }; - - /// This data is stored inside extra, with trailing operands according to `operands_len`. - /// Each operand is a `Ref`. - pub const MultiOp = struct { - operands_len: u32, - }; - - /// This data is stored inside extra, with trailing operands according to `body_len`. - /// Each operand is an `Index`. - pub const Block = struct { - body_len: u32, - }; - - /// Stored inside extra, with trailing arguments according to `args_len`. - /// Each argument is a `Ref`. - pub const Call = struct { - callee: Ref, - args_len: u32, - }; - - /// This data is stored inside extra, with two sets of trailing `Ref`: - /// * 0. the then body, according to `then_body_len`. - /// * 1. the else body, according to `else_body_len`. - pub const CondBr = struct { - condition: Ref, - then_body_len: u32, - else_body_len: u32, - }; - - /// Stored in extra. Depending on the flags in Data, there will be up to 4 - /// trailing Ref fields: - /// 0. sentinel: Ref // if `has_sentinel` flag is set - /// 1. align: Ref // if `has_align` flag is set - /// 2. bit_start: Ref // if `has_bit_range` flag is set - /// 3. bit_end: Ref // if `has_bit_range` flag is set - pub const PtrType = struct { - elem_type: Ref, - }; - - pub const ArrayTypeSentinel = struct { - sentinel: Ref, - elem_type: Ref, - }; - - pub const SliceStart = struct { - lhs: Ref, - start: Ref, - }; - - pub const SliceEnd = struct { - lhs: Ref, - start: Ref, - end: Ref, - }; - - pub const SliceSentinel = struct { - lhs: Ref, - start: Ref, - end: Ref, - sentinel: Ref, - }; - - /// The meaning of these operands depends on the corresponding `Tag`. - pub const Bin = struct { - lhs: Ref, - rhs: Ref, - }; - - /// This form is supported when there are no ranges, and exactly 1 item per block. - /// Depending on zir tag and len fields, extra fields trail - /// this one in the extra array. - /// 0. else_body { // If the tag has "_else" or "_under" in it. - /// body_len: u32, - /// body member Index for every body_len - /// } - /// 1. cases: { - /// item: Ref, - /// body_len: u32, - /// body member Index for every body_len - /// } for every cases_len - pub const SwitchBlock = struct { - operand: Ref, - cases_len: u32, - }; - - /// This form is required when there exists a block which has more than one item, - /// or a range. - /// Depending on zir tag and len fields, extra fields trail - /// this one in the extra array. - /// 0. else_body { // If the tag has "_else" or "_under" in it. - /// body_len: u32, - /// body member Index for every body_len - /// } - /// 1. scalar_cases: { // for every scalar_cases_len - /// item: Ref, - /// body_len: u32, - /// body member Index for every body_len - /// } - /// 2. multi_cases: { // for every multi_cases_len - /// items_len: u32, - /// ranges_len: u32, - /// body_len: u32, - /// item: Ref // for every items_len - /// ranges: { // for every ranges_len - /// item_first: Ref, - /// item_last: Ref, - /// } - /// body member Index for every body_len - /// } - pub const SwitchBlockMulti = struct { - operand: Ref, - scalar_cases_len: u32, - multi_cases_len: u32, - }; - - pub const Field = struct { - lhs: Ref, - /// Offset into `string_bytes`. - field_name_start: u32, - }; - - pub const FieldNamed = struct { - lhs: Ref, - field_name: Ref, - }; - - pub const As = struct { - dest_type: Ref, - operand: Ref, - }; - - /// Trailing: - /// 0. inst: Index // for every body_len - /// 1. has_bits: u32 // for every 16 fields - /// - sets of 2 bits: - /// 0b0X: whether corresponding field has an align expression - /// 0bX0: whether corresponding field has a default expression - /// 2. fields: { // for every fields_len - /// field_name: u32, - /// field_type: Ref, - /// align: Ref, // if corresponding bit is set - /// default_value: Ref, // if corresponding bit is set - /// } - pub const StructDecl = struct { - body_len: u32, - fields_len: u32, - }; - - /// Trailing: - /// 0. inst: Index // for every body_len - /// 1. has_bits: u32 // for every 32 fields - /// - the bit is whether corresponding field has an value expression - /// 2. fields: { // for every fields_len - /// field_name: u32, - /// value: Ref, // if corresponding bit is set - /// } - pub const EnumDecl = struct { - /// Can be `Ref.none`. - tag_type: Ref, - body_len: u32, - fields_len: u32, - }; - - /// Trailing: - /// 0. has_bits: u32 // for every 10 fields (+1) - /// - first bit is special: set if and only if auto enum tag is enabled. - /// - sets of 3 bits: - /// 0b00X: whether corresponding field has a type expression - /// 0b0X0: whether corresponding field has a align expression - /// 0bX00: whether corresponding field has a tag value expression - /// 1. field_name: u32 // for every field: null terminated string index - /// 2. opt_exprs // Ref for every field for which corresponding bit is set - /// - interleaved. type if present, align if present, tag value if present. - pub const UnionDecl = struct { - /// Can be `Ref.none`. - tag_type: Ref, - fields_len: u32, - }; - - /// A f128 value, broken up into 4 u32 parts. - pub const Float128 = struct { - piece0: u32, - piece1: u32, - piece2: u32, - piece3: u32, - - pub fn get(self: Float128) f128 { - const int_bits = @as(u128, self.piece0) | - (@as(u128, self.piece1) << 32) | - (@as(u128, self.piece2) << 64) | - (@as(u128, self.piece3) << 96); - return @bitCast(f128, int_bits); - } - }; - - /// Trailing is an item per field. - pub const StructInit = struct { - fields_len: u32, - - pub const Item = struct { - /// The `field_type` ZIR instruction for this field init. - field_type: Index, - /// The field init expression to be used as the field value. - init: Ref, - }; - }; - - pub const FieldType = struct { - container_type: Ref, - /// Offset into `string_bytes`, null terminated. - name_start: u32, - }; -}; - -pub const SpecialProng = enum { none, @"else", under }; - -const Writer = struct { - gpa: *Allocator, - arena: *Allocator, - scope: *Module.Scope, - code: Zir, - indent: usize, - param_count: usize, - - fn writeInstToStream( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const tags = self.code.instructions.items(.tag); - const tag = tags[inst]; - try stream.print("= {s}(", .{@tagName(tags[inst])}); - switch (tag) { - .array_type, - .as, - .coerce_result_ptr, - .elem_ptr, - .elem_val, - .intcast, - .store, - .store_to_block_ptr, - .store_to_inferred_ptr, - => try self.writeBin(stream, inst), - - .alloc, - .alloc_mut, - .indexable_ptr_len, - .bit_not, - .bool_not, - .negate, - .negate_wrap, - .call_none, - .call_none_chkused, - .compile_error, - .load, - .ensure_result_used, - .ensure_result_non_error, - .import, - .ptrtoint, - .ret_node, - .set_eval_branch_quota, - .resolve_inferred_alloc, - .optional_type, - .optional_type_from_ptr_elem, - .optional_payload_safe, - .optional_payload_unsafe, - .optional_payload_safe_ptr, - .optional_payload_unsafe_ptr, - .err_union_payload_safe, - .err_union_payload_unsafe, - .err_union_payload_safe_ptr, - .err_union_payload_unsafe_ptr, - .err_union_code, - .err_union_code_ptr, - .int_to_error, - .error_to_int, - .is_non_null, - .is_null, - .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, - .typeof, - .typeof_elem, - .struct_init_empty, - .enum_to_int, - .type_info, - .size_of, - .bit_size_of, - => try self.writeUnNode(stream, inst), - - .ref, - .ret_tok, - .ret_coerce, - .ensure_err_payload_void, - => try self.writeUnTok(stream, inst), - - .bool_br_and, - .bool_br_or, - => try self.writeBoolBr(stream, inst), - - .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), - .param_type => try self.writeParamType(stream, inst), - .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), - .ptr_type => try self.writePtrType(stream, inst), - .int => try self.writeInt(stream, inst), - .float => try self.writeFloat(stream, inst), - .float128 => try self.writeFloat128(stream, inst), - .str => try self.writeStr(stream, inst), - .elided => try stream.writeAll(")"), - .int_type => try self.writeIntType(stream, inst), - - .@"break", - .break_inline, - => try self.writeBreak(stream, inst), - - .@"asm", - .asm_volatile, - .elem_ptr_node, - .elem_val_node, - .field_ptr_named, - .field_val_named, - .floatcast, - .slice_start, - .slice_end, - .slice_sentinel, - .union_decl, - .struct_init, - .field_type, - => try self.writePlNode(stream, inst), - - .add, - .addwrap, - .array_cat, - .array_mul, - .mul, - .mulwrap, - .sub, - .subwrap, - .bool_and, - .bool_or, - .cmp_lt, - .cmp_lte, - .cmp_eq, - .cmp_gte, - .cmp_gt, - .cmp_neq, - .div, - .has_decl, - .mod_rem, - .shl, - .shr, - .xor, - .store_node, - .error_union_type, - .@"export", - .merge_error_sets, - .bit_and, - .bit_or, - .int_to_enum, - => try self.writePlNodeBin(stream, inst), - - .call, - .call_chkused, - .call_compile_time, - => try self.writePlNodeCall(stream, inst), - - .block, - .block_inline, - .loop, - .validate_struct_init_ptr, - => try self.writePlNodeBlock(stream, inst), - - .condbr, - .condbr_inline, - => try self.writePlNodeCondBr(stream, inst), - - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - => try self.writeStructDecl(stream, inst), - - .enum_decl, - .enum_decl_nonexhaustive, - => try self.writeEnumDecl(stream, inst), - - .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), - .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), - .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under), - .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none), - .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), - .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under), - - .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), - .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), - .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), - .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), - .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), - .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), - - .compile_log, - .typeof_peer, - => try self.writePlNodeMultiOp(stream, inst), - - .decl_ref, - .decl_val, - => try self.writePlNodeDecl(stream, inst), - - .field_ptr, - .field_val, - => try self.writePlNodeField(stream, inst), - - .as_node => try self.writeAs(stream, inst), - - .breakpoint, - .opaque_decl, - .dbg_stmt_node, - .ret_ptr, - .ret_type, - .repeat, - .repeat_inline, - .alloc_inferred, - .alloc_inferred_mut, - => try self.writeNode(stream, inst), - - .error_value, - .enum_literal, - .decl_ref_named, - .decl_val_named, - => try self.writeStrTok(stream, inst), - - .fn_type => try self.writeFnType(stream, inst, false), - .fn_type_cc => try self.writeFnTypeCc(stream, inst, false), - .fn_type_var_args => try self.writeFnType(stream, inst, true), - .fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true), - - .@"unreachable" => try self.writeUnreachable(stream, inst), - - .enum_literal_small => try self.writeSmallStr(stream, inst), - - .switch_capture, - .switch_capture_ref, - .switch_capture_multi, - .switch_capture_multi_ref, - .switch_capture_else, - .switch_capture_else_ref, - => try self.writeSwitchCapture(stream, inst), - - .bitcast, - .bitcast_result_ptr, - => try stream.writeAll("TODO)"), - } - } - - fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].bin; - try self.writeInstRef(stream, inst_data.lhs); - try stream.writeAll(", "); - try self.writeInstRef(stream, inst_data.rhs); - try stream.writeByte(')'); - } - - fn writeUnNode( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].un_node; - try self.writeInstRef(stream, inst_data.operand); - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeUnTok( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].un_tok; - try self.writeInstRef(stream, inst_data.operand); - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeArrayTypeSentinel( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; - try stream.writeAll("TODO)"); - } - - fn writeParamType( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].param_type; - try self.writeInstRef(stream, inst_data.callee); - try stream.print(", {d})", .{inst_data.param_index}); - } - - fn writePtrTypeSimple( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; - const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; - const str_const = if (!inst_data.is_mutable) "const, " else ""; - const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; - try self.writeInstRef(stream, inst_data.elem_type); - try stream.print(", {s}{s}{s}{s})", .{ - str_allowzero, - str_const, - str_volatile, - @tagName(inst_data.size), - }); - } - - fn writePtrType( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].ptr_type; - try stream.writeAll("TODO)"); - } - - fn writeInt( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].int; - try stream.print("{d})", .{inst_data}); - } - - fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].float; - const src = inst_data.src(); - try stream.print("{d}) ", .{inst_data.number}); - try self.writeSrc(stream, src); - } - - fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.Float128, inst_data.payload_index).data; - const src = inst_data.src(); - const number = extra.get(); - // TODO improve std.format to be able to print f128 values - try stream.print("{d}) ", .{@floatCast(f64, number)}); - try self.writeSrc(stream, src); - } - - fn writeStr( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].str; - const str = inst_data.get(self.code); - try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); - } - - fn writePlNode( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - try stream.writeAll("TODO) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; - try self.writeInstRef(stream, extra.lhs); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.rhs); - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.Call, inst_data.payload_index); - const args = self.code.refSlice(extra.end, extra.data.args_len); - - try self.writeInstRef(stream, extra.data.callee); - try stream.writeAll(", ["); - for (args) |arg, i| { - if (i != 0) try stream.writeAll(", "); - try self.writeInstRef(stream, arg); - } - try stream.writeAll("]) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.Block, inst_data.payload_index); - const body = self.code.extra[extra.end..][0..extra.data.body_len]; - try stream.writeAll("{\n"); - self.indent += 2; - try self.writeBody(stream, body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index); - const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; - const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - try self.writeInstRef(stream, extra.data.condition); - try stream.writeAll(", {\n"); - self.indent += 2; - try self.writeBody(stream, then_body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}, {\n"); - self.indent += 2; - try self.writeBody(stream, else_body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index); - const body = self.code.extra[extra.end..][0..extra.data.body_len]; - const fields_len = extra.data.fields_len; - - if (fields_len == 0) { - assert(body.len == 0); - try stream.writeAll("{}, {}) "); - try self.writeSrc(stream, inst_data.src()); - return; - } - - try stream.writeAll("{\n"); - self.indent += 2; - try self.writeBody(stream, body); - - try stream.writeByteNTimes(' ', self.indent - 2); - try stream.writeAll("}, {\n"); - - const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable; - const body_end = extra.end + body.len; - var extra_index: usize = body_end + bit_bags_count; - var bit_bag_index: usize = body_end; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % 16 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_align = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - const has_default = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - - const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); - extra_index += 1; - const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeByteNTimes(' ', self.indent); - try stream.print("{}: ", .{std.zig.fmtId(field_name)}); - try self.writeInstRef(stream, field_type); - - if (has_align) { - const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeAll(" align("); - try self.writeInstRef(stream, align_ref); - try stream.writeAll(")"); - } - if (has_default) { - const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeAll(" = "); - try self.writeInstRef(stream, default_ref); - } - try stream.writeAll(",\n"); - } - - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeEnumDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.EnumDecl, inst_data.payload_index); - const body = self.code.extra[extra.end..][0..extra.data.body_len]; - const fields_len = extra.data.fields_len; - const tag_ty_ref = extra.data.tag_type; - - if (tag_ty_ref != .none) { - try self.writeInstRef(stream, tag_ty_ref); - try stream.writeAll(", "); - } - - if (fields_len == 0) { - assert(body.len == 0); - try stream.writeAll("{}, {}) "); - try self.writeSrc(stream, inst_data.src()); - return; - } - - try stream.writeAll("{\n"); - self.indent += 2; - try self.writeBody(stream, body); - - try stream.writeByteNTimes(' ', self.indent - 2); - try stream.writeAll("}, {\n"); - - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - const body_end = extra.end + body.len; - var extra_index: usize = body_end + bit_bags_count; - var bit_bag_index: usize = body_end; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % 32 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_tag_value = @truncate(u1, cur_bit_bag) != 0; - cur_bit_bag >>= 1; - - const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeByteNTimes(' ', self.indent); - try stream.print("{}", .{std.zig.fmtId(field_name)}); - - if (has_tag_value) { - const tag_value_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeAll(" = "); - try self.writeInstRef(stream, tag_value_ref); - } - try stream.writeAll(",\n"); - } - - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeSwitchBr( - self: *Writer, - stream: anytype, - inst: Inst.Index, - special_prong: SpecialProng, - ) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index); - const special: struct { - body: []const Inst.Index, - end: usize, - } = switch (special_prong) { - .none => .{ .body = &.{}, .end = extra.end }, - .under, .@"else" => blk: { - const body_len = self.code.extra[extra.end]; - const extra_body_start = extra.end + 1; - break :blk .{ - .body = self.code.extra[extra_body_start..][0..body_len], - .end = extra_body_start + body_len, - }; - }, - }; - - try self.writeInstRef(stream, extra.data.operand); - - if (special.body.len != 0) { - const prong_name = switch (special_prong) { - .@"else" => "else", - .under => "_", - else => unreachable, - }; - try stream.print(", {s} => {{\n", .{prong_name}); - self.indent += 2; - try self.writeBody(stream, special.body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - } - - var extra_index: usize = special.end; - { - var scalar_i: usize = 0; - while (scalar_i < extra.data.cases_len) : (scalar_i += 1) { - const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - const body_len = self.code.extra[extra_index]; - extra_index += 1; - const body = self.code.extra[extra_index..][0..body_len]; - extra_index += body_len; - - try stream.writeAll(", "); - try self.writeInstRef(stream, item_ref); - try stream.writeAll(" => {\n"); - self.indent += 2; - try self.writeBody(stream, body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - } - } - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeSwitchBlockMulti( - self: *Writer, - stream: anytype, - inst: Inst.Index, - special_prong: SpecialProng, - ) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index); - const special: struct { - body: []const Inst.Index, - end: usize, - } = switch (special_prong) { - .none => .{ .body = &.{}, .end = extra.end }, - .under, .@"else" => blk: { - const body_len = self.code.extra[extra.end]; - const extra_body_start = extra.end + 1; - break :blk .{ - .body = self.code.extra[extra_body_start..][0..body_len], - .end = extra_body_start + body_len, - }; - }, - }; - - try self.writeInstRef(stream, extra.data.operand); - - if (special.body.len != 0) { - const prong_name = switch (special_prong) { - .@"else" => "else", - .under => "_", - else => unreachable, - }; - try stream.print(", {s} => {{\n", .{prong_name}); - self.indent += 2; - try self.writeBody(stream, special.body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - } - - var extra_index: usize = special.end; - { - var scalar_i: usize = 0; - while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) { - const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - const body_len = self.code.extra[extra_index]; - extra_index += 1; - const body = self.code.extra[extra_index..][0..body_len]; - extra_index += body_len; - - try stream.writeAll(", "); - try self.writeInstRef(stream, item_ref); - try stream.writeAll(" => {\n"); - self.indent += 2; - try self.writeBody(stream, body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - } - } - { - var multi_i: usize = 0; - while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) { - const items_len = self.code.extra[extra_index]; - extra_index += 1; - const ranges_len = self.code.extra[extra_index]; - extra_index += 1; - const body_len = self.code.extra[extra_index]; - extra_index += 1; - const items = self.code.refSlice(extra_index, items_len); - extra_index += items_len; - - for (items) |item_ref| { - try stream.writeAll(", "); - try self.writeInstRef(stream, item_ref); - } - - var range_i: usize = 0; - while (range_i < ranges_len) : (range_i += 1) { - const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - - try stream.writeAll(", "); - try self.writeInstRef(stream, item_first); - try stream.writeAll("..."); - try self.writeInstRef(stream, item_last); - } - - const body = self.code.extra[extra_index..][0..body_len]; - extra_index += body_len; - try stream.writeAll(" => {\n"); - self.indent += 2; - try self.writeBody(stream, body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - } - } - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index); - const operands = self.code.refSlice(extra.end, extra.data.operands_len); - - for (operands) |operand, i| { - if (i != 0) try stream.writeAll(", "); - try self.writeInstRef(stream, operand); - } - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const owner_decl = self.scope.ownerDecl().?; - const decl = owner_decl.dependencies.entries.items[inst_data.payload_index].key; - try stream.print("{s}) ", .{decl.name}); - try self.writeSrc(stream, inst_data.src()); - } - - fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data; - const name = self.code.nullTerminatedString(extra.field_name_start); - try self.writeInstRef(stream, extra.lhs); - try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)}); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; - try self.writeInstRef(stream, extra.dest_type); - try stream.writeAll(", "); - try self.writeInstRef(stream, extra.operand); - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeNode( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const src_node = self.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; - try stream.writeAll(") "); - try self.writeSrc(stream, src); - } - - fn writeStrTok( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].str_tok; - const str = inst_data.get(self.code); - try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)}); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeFnType( - self: *Writer, - stream: anytype, - inst: Inst.Index, - var_args: bool, - ) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = self.code.extraData(Inst.FnType, inst_data.payload_index); - const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); - return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, .none, src); - } - - fn writeFnTypeCc( - self: *Writer, - stream: anytype, - inst: Inst.Index, - var_args: bool, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index); - const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); - const cc = extra.data.cc; - return self.writeFnTypeCommon(stream, param_types, extra.data.return_type, var_args, cc, src); - } - - fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].bool_br; - const extra = self.code.extraData(Inst.Block, inst_data.payload_index); - const body = self.code.extra[extra.end..][0..extra.data.body_len]; - try self.writeInstRef(stream, inst_data.lhs); - try stream.writeAll(", {\n"); - self.indent += 2; - try self.writeBody(stream, body); - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("})"); - } - - fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const int_type = self.code.instructions.items(.data)[inst].int_type; - const prefix: u8 = switch (int_type.signedness) { - .signed => 'i', - .unsigned => 'u', - }; - try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count }); - try self.writeSrc(stream, int_type.src()); - } - - fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].@"break"; - - try self.writeInstIndex(stream, inst_data.block_inst); - try stream.writeAll(", "); - try self.writeInstRef(stream, inst_data.operand); - try stream.writeAll(")"); - } - - fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; - const safety_str = if (inst_data.safety) "safe" else "unsafe"; - try stream.print("{s}) ", .{safety_str}); - try self.writeSrc(stream, inst_data.src()); - } - - fn writeFnTypeCommon( - self: *Writer, - stream: anytype, - param_types: []const Inst.Ref, - ret_ty: Inst.Ref, - var_args: bool, - cc: Inst.Ref, - src: LazySrcLoc, - ) !void { - try stream.writeAll("["); - for (param_types) |param_type, i| { - if (i != 0) try stream.writeAll(", "); - try self.writeInstRef(stream, param_type); - } - try stream.writeAll("], "); - try self.writeInstRef(stream, ret_ty); - try self.writeOptionalInstRef(stream, ", cc=", cc); - try self.writeFlag(stream, ", var_args", var_args); - try stream.writeAll(") "); - try self.writeSrc(stream, src); - } - - fn writeSmallStr( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const str = self.code.instructions.items(.data)[inst].small_str.get(); - try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); - } - - fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].switch_capture; - try self.writeInstIndex(stream, inst_data.switch_inst); - try stream.print(", {d})", .{inst_data.prong_index}); - } - - fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void { - var i: usize = @enumToInt(ref); - - if (i < Inst.Ref.typed_value_map.len) { - return stream.print("@{}", .{ref}); - } - i -= Inst.Ref.typed_value_map.len; - - if (i < self.param_count) { - return stream.print("${d}", .{i}); - } - i -= self.param_count; - - return self.writeInstIndex(stream, @intCast(Inst.Index, i)); - } - - fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void { - return stream.print("%{d}", .{inst}); - } - - fn writeOptionalInstRef( - self: *Writer, - stream: anytype, - prefix: []const u8, - inst: Inst.Ref, - ) !void { - if (inst == .none) return; - try stream.writeAll(prefix); - try self.writeInstRef(stream, inst); - } - - fn writeFlag( - self: *Writer, - stream: anytype, - name: []const u8, - flag: bool, - ) !void { - if (!flag) return; - try stream.writeAll(name); - } - - fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { - const tree = self.scope.tree(); - const src_loc = src.toSrcLoc(self.scope); - const abs_byte_off = try src_loc.byteOffset(); - const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); - try stream.print("{s}:{d}:{d}", .{ - @tagName(src), delta_line.line + 1, delta_line.column + 1, - }); - } - - fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void { - for (body) |inst| { - try stream.writeByteNTimes(' ', self.indent); - try stream.print("%{d} ", .{inst}); - try self.writeInstToStream(stream, inst); - try stream.writeByte('\n'); - } - } -};