const std = @import("std"); const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const Ast = std.zig.Ast; const Zir = @import("Zir.zig"); const Module = @import("Module.zig"); const LazySrcLoc = Module.LazySrcLoc; /// Write human-readable, debug formatted ZIR code to a file. pub fn renderAsTextToFile( gpa: Allocator, scope_file: *Module.File, fs_file: std.fs.File, ) !void { var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); var writer: Writer = .{ .gpa = gpa, .arena = arena.allocator(), .file = scope_file, .code = scope_file.zir, .indent = 0, .parent_decl_node = 0, .recurse_decls = true, .recurse_blocks = true, }; var raw_stream = std.io.bufferedWriter(fs_file.writer()); const stream = raw_stream.writer(); const main_struct_inst = Zir.main_struct_inst; try stream.print("%{d} ", .{main_struct_inst}); try writer.writeInstToStream(stream, main_struct_inst); try stream.writeAll("\n"); const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; if (imports_index != 0) { try stream.writeAll("Imports:\n"); const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); var import_i: u32 = 0; var extra_index = extra.end; while (import_i < extra.data.imports_len) : (import_i += 1) { const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index); extra_index = item.end; const src: LazySrcLoc = .{ .token_abs = item.data.token }; const import_path = scope_file.zir.nullTerminatedString(item.data.name); try stream.print(" @import(\"{}\") ", .{ std.zig.fmtEscapes(import_path), }); try writer.writeSrc(stream, src); try stream.writeAll("\n"); } } try raw_stream.flush(); } pub fn renderInstructionContext( gpa: Allocator, block: []const Zir.Inst.Index, block_index: usize, scope_file: *Module.File, parent_decl_node: Ast.Node.Index, indent: u32, stream: anytype, ) !void { var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); var writer: Writer = .{ .gpa = gpa, .arena = arena.allocator(), .file = scope_file, .code = scope_file.zir, .indent = if (indent < 2) 2 else indent, .parent_decl_node = parent_decl_node, .recurse_decls = false, .recurse_blocks = true, }; try writer.writeBody(stream, block[0..block_index]); try stream.writeByteNTimes(' ', writer.indent - 2); try stream.print("> %{d} ", .{block[block_index]}); try writer.writeInstToStream(stream, block[block_index]); try stream.writeByte('\n'); if (block_index + 1 < block.len) { try writer.writeBody(stream, block[block_index + 1 ..]); } } pub fn renderSingleInstruction( gpa: Allocator, inst: Zir.Inst.Index, scope_file: *Module.File, parent_decl_node: Ast.Node.Index, indent: u32, stream: anytype, ) !void { var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); var writer: Writer = .{ .gpa = gpa, .arena = arena.allocator(), .file = scope_file, .code = scope_file.zir, .indent = indent, .parent_decl_node = parent_decl_node, .recurse_decls = false, .recurse_blocks = false, }; try stream.print("%{d} ", .{inst}); try writer.writeInstToStream(stream, inst); } const Writer = struct { gpa: Allocator, arena: Allocator, file: *Module.File, code: Zir, indent: u32, parent_decl_node: Ast.Node.Index, recurse_decls: bool, recurse_blocks: bool, fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); } fn writeInstToStream( self: *Writer, stream: anytype, inst: Zir.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) { .as, .store, .store_to_block_ptr, .store_to_inferred_ptr, => try self.writeBin(stream, inst), .elem_type_index => try self.writeElemTypeIndex(stream, inst), .alloc, .alloc_mut, .alloc_comptime_mut, .indexable_ptr_len, .anyframe_type, .bit_not, .bool_not, .negate, .negate_wrap, .load, .ensure_result_used, .ensure_result_non_error, .ensure_err_union_payload_void, .ret_node, .ret_load, .resolve_inferred_alloc, .optional_type, .optional_payload_safe, .optional_payload_unsafe, .optional_payload_safe_ptr, .optional_payload_unsafe_ptr, .err_union_payload_unsafe, .err_union_payload_unsafe_ptr, .err_union_code, .err_union_code_ptr, .is_non_null, .is_non_null_ptr, .is_non_err, .is_non_err_ptr, .ret_is_non_err, .typeof, .struct_init_empty, .type_info, .size_of, .bit_size_of, .typeof_log2_int_type, .ptr_to_int, .compile_error, .set_eval_branch_quota, .enum_to_int, .align_of, .bool_to_int, .embed_file, .error_name, .panic, .panic_comptime, .set_cold, .set_runtime_safety, .sqrt, .sin, .cos, .tan, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .trunc, .round, .tag_name, .type_name, .frame_type, .frame_size, .clz, .ctz, .pop_count, .byte_swap, .bit_reverse, .@"resume", .@"await", .switch_cond, .switch_cond_ref, .array_base_ptr, .field_base_ptr, .validate_struct_init_ty, .make_ptr_const, .validate_deref, .check_comptime_control_flow, => try self.writeUnNode(stream, inst), .ref, .ret_implicit, .closure_capture, .switch_capture_tag, => try self.writeUnTok(stream, inst), .bool_br_and, .bool_br_or, => try self.writeBoolBr(stream, inst), .validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst), .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), .ptr_type => try self.writePtrType(stream, inst), .int => try self.writeInt(stream, inst), .int_big => try self.writeIntBig(stream, inst), .float => try self.writeFloat(stream, inst), .float128 => try self.writeFloat128(stream, inst), .str => try self.writeStr(stream, inst), .int_type => try self.writeIntType(stream, inst), .save_err_ret_index => try self.writeSaveErrRetIndex(stream, inst), .restore_err_ret_index => try self.writeRestoreErrRetIndex(stream, inst), .@"break", .break_inline, => try self.writeBreak(stream, inst), .array_init, .array_init_ref, => try self.writeArrayInit(stream, inst), .array_init_anon, .array_init_anon_ref, => try self.writeArrayInitAnon(stream, inst), .slice_start => try self.writeSliceStart(stream, inst), .slice_end => try self.writeSliceEnd(stream, inst), .slice_sentinel => try self.writeSliceSentinel(stream, inst), .union_init => try self.writeUnionInit(stream, inst), .struct_init, .struct_init_ref, => try self.writeStructInit(stream, inst), .atomic_load => try self.writeAtomicLoad(stream, inst), .atomic_store => try self.writeAtomicStore(stream, inst), .atomic_rmw => try self.writeAtomicRmw(stream, inst), .memcpy => try self.writeMemcpy(stream, inst), .memset => try self.writeMemset(stream, inst), .shuffle => try self.writeShuffle(stream, inst), .mul_add => try self.writeMulAdd(stream, inst), .field_parent_ptr => try self.writeFieldParentPtr(stream, inst), .builtin_call => try self.writeBuiltinCall(stream, inst), .struct_init_anon, .struct_init_anon_ref, => try self.writeStructInitAnon(stream, inst), .field_type => try self.writeFieldType(stream, inst), .field_type_ref => try self.writeFieldTypeRef(stream, inst), .add, .addwrap, .add_sat, .add_unsafe, .array_cat, .array_mul, .mul, .mulwrap, .mul_sat, .sub, .subwrap, .sub_sat, .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq, .div, .has_decl, .has_field, .mod_rem, .shl, .shl_exact, .shl_sat, .shr, .shr_exact, .xor, .store_node, .error_union_type, .merge_error_sets, .bit_and, .bit_or, .float_to_int, .int_to_float, .int_to_ptr, .int_to_enum, .float_cast, .int_cast, .ptr_cast, .truncate, .align_cast, .div_exact, .div_floor, .div_trunc, .mod, .rem, .bit_offset_of, .offset_of, .splat, .reduce, .bitcast, .vector_type, .max, .min, .elem_ptr_node, .elem_val_node, .elem_ptr, .elem_val, .coerce_result_ptr, .array_type, => try self.writePlNodeBin(stream, inst), .for_len => try self.writePlNodeMultiOp(stream, inst), .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), .@"export" => try self.writePlNodeExport(stream, inst), .export_value => try self.writePlNodeExportValue(stream, inst), .call => try self.writeCall(stream, inst), .block, .block_inline, .suspend_block, .loop, .validate_struct_init, .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, .c_import, .typeof_builtin, => try self.writeBlock(stream, inst), .condbr, .condbr_inline, => try self.writeCondBr(stream, inst), .@"try", .try_ptr, => try self.writeTry(stream, inst), .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), .switch_block => try self.writeSwitchBlock(stream, inst), .field_ptr, .field_ptr_init, .field_val, .field_call_bind, => try self.writePlNodeField(stream, inst), .field_ptr_named, .field_val_named, => try self.writePlNodeFieldNamed(stream, inst), .as_node, .as_shift_operand => try self.writeAs(stream, inst), .repeat, .repeat_inline, .alloc_inferred, .alloc_inferred_mut, .alloc_inferred_comptime, .alloc_inferred_comptime_mut, .ret_ptr, .ret_type, => try self.writeNode(stream, inst), .error_value, .enum_literal, .decl_ref, .decl_val, .import, .ret_err_value, .ret_err_value_code, .param_anytype, .param_anytype_comptime, => try self.writeStrTok(stream, inst), .dbg_var_ptr, .dbg_var_val, => try self.writeStrOp(stream, inst), .param, .param_comptime => try self.writeParam(stream, inst), .func => try self.writeFunc(stream, inst, false), .func_inferred => try self.writeFunc(stream, inst, true), .func_fancy => try self.writeFuncFancy(stream, inst), .@"unreachable" => try self.writeUnreachable(stream, inst), .switch_capture, .switch_capture_ref, .switch_capture_multi, .switch_capture_multi_ref, => try self.writeSwitchCapture(stream, inst), .dbg_stmt => try self.writeDbgStmt(stream, inst), .dbg_block_begin, .dbg_block_end, => try stream.writeAll(")"), .closure_get => try self.writeInstNode(stream, inst), .@"defer" => try self.writeDefer(stream, inst), .defer_err_code => try self.writeDeferErrCode(stream, inst), .extended => try self.writeExtended(stream, inst), } } fn writeExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const extended = self.code.instructions.items(.data)[inst].extended; try stream.print("{s}(", .{@tagName(extended.opcode)}); switch (extended.opcode) { .this, .ret_addr, .error_return_trace, .frame, .frame_address, .breakpoint, .c_va_start, => try self.writeExtNode(stream, extended), .builtin_src => { try stream.writeAll("))"); const inst_data = self.code.extraData(Zir.Inst.LineColumn, extended.operand).data; try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); }, .@"asm" => try self.writeAsm(stream, extended, false), .asm_expr => try self.writeAsm(stream, extended, true), .variable => try self.writeVarExtended(stream, extended), .alloc => try self.writeAllocExtended(stream, extended), .compile_log => try self.writeNodeMultiOp(stream, extended), .typeof_peer => try self.writeTypeofPeer(stream, extended), .select => try self.writeSelect(stream, extended), .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow, => try self.writeOverflowArithmetic(stream, extended), .struct_decl => try self.writeStructDecl(stream, extended), .union_decl => try self.writeUnionDecl(stream, extended), .enum_decl => try self.writeEnumDecl(stream, extended), .opaque_decl => try self.writeOpaqueDecl(stream, extended), .await_nosuspend, .c_undef, .c_include, .fence, .set_float_mode, .set_align_stack, .wasm_memory_size, .error_to_int, .int_to_error, .reify, .c_va_copy, .c_va_end, .const_cast, .volatile_cast, => { const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(inst_data.node); try self.writeInstRef(stream, inst_data.operand); try stream.writeAll(")) "); try self.writeSrc(stream, src); }, .builtin_extern, .c_define, .err_set_cast, .wasm_memory_grow, .prefetch, .addrspace_cast, .c_va_arg, => { const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(inst_data.node); try self.writeInstRef(stream, inst_data.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, inst_data.rhs); try stream.writeAll(")) "); try self.writeSrc(stream, src); }, .field_call_bind_named => { const extra = self.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); try stream.writeAll(") "); try self.writeSrc(stream, src); }, .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended), .cmpxchg => try self.writeCmpxchg(stream, extended), } } fn writeExtNode(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); try stream.writeAll(")) "); try self.writeSrc(stream, src); } fn writeBin(self: *Writer, stream: anytype, inst: Zir.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 writeElemTypeIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].bin; try self.writeInstRef(stream, inst_data.lhs); try stream.print(", {d})", .{@enumToInt(inst_data.rhs)}); } fn writeUnNode( self: *Writer, stream: anytype, inst: Zir.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: Zir.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 writeValidateArrayInitTy( self: *Writer, stream: anytype, inst: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; try self.writeInstRef(stream, extra.ty); try stream.print(", {d}) ", .{extra.init_count}); try self.writeSrc(stream, inst_data.src()); } fn writeArrayTypeSentinel( self: *Writer, stream: anytype, inst: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; try self.writeInstRef(stream, extra.len); try stream.writeAll(", "); try self.writeInstRef(stream, extra.sentinel); try stream.writeAll(", "); try self.writeInstRef(stream, extra.elem_type); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writePtrType( self: *Writer, stream: anytype, inst: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].ptr_type; const str_allowzero = if (inst_data.flags.is_allowzero) "allowzero, " else ""; const str_const = if (!inst_data.flags.is_mutable) "const, " else ""; const str_volatile = if (inst_data.flags.is_volatile) "volatile, " else ""; const extra = self.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); try self.writeInstRef(stream, extra.data.elem_type); try stream.print(", {s}{s}{s}{s}", .{ str_allowzero, str_const, str_volatile, @tagName(inst_data.size), }); var extra_index = extra.end; if (inst_data.flags.has_sentinel) { try stream.writeAll(", "); try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index])); extra_index += 1; } if (inst_data.flags.has_align) { try stream.writeAll(", align("); try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index])); extra_index += 1; if (inst_data.flags.has_bit_range) { const bit_start = extra_index + @boolToInt(inst_data.flags.has_addrspace); try stream.writeAll(":"); try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[bit_start])); try stream.writeAll(":"); try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[bit_start + 1])); } try stream.writeAll(")"); } if (inst_data.flags.has_addrspace) { try stream.writeAll(", addrspace("); try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index])); try stream.writeAll(")"); } try stream.writeAll(") "); try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.data.src_node)); } fn writeInt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].int; try stream.print("{d})", .{inst_data}); } fn writeIntBig(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].str; const byte_count = inst_data.len * @sizeOf(std.math.big.Limb); const limb_bytes = self.code.string_bytes[inst_data.start..][0..byte_count]; // limb_bytes is not aligned properly; we must allocate and copy the bytes // in order to accomplish this. const limbs = try self.gpa.alloc(std.math.big.Limb, inst_data.len); defer self.gpa.free(limbs); mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes); const big_int: std.math.big.int.Const = .{ .limbs = limbs, .positive = true, }; const as_string = try big_int.toStringAlloc(self.gpa, 10, .lower); defer self.gpa.free(as_string); try stream.print("{s})", .{as_string}); } fn writeFloat(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const number = self.code.instructions.items(.data)[inst].float; try stream.print("{d})", .{number}); } fn writeFloat128(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.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: Zir.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 writeSliceStart(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.start); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeSliceEnd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.start); try stream.writeAll(", "); try self.writeInstRef(stream, extra.end); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeSliceSentinel(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.start); try stream.writeAll(", "); try self.writeInstRef(stream, extra.end); try stream.writeAll(", "); try self.writeInstRef(stream, extra.sentinel); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; try self.writeInstRef(stream, extra.union_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); try stream.writeAll(", "); try self.writeInstRef(stream, extra.init); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeShuffle(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; try self.writeInstRef(stream, extra.elem_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.a); try stream.writeAll(", "); try self.writeInstRef(stream, extra.b); try stream.writeAll(", "); try self.writeInstRef(stream, extra.mask); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeSelect(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.Select, extended.operand).data; try self.writeInstRef(stream, extra.elem_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.pred); try stream.writeAll(", "); try self.writeInstRef(stream, extra.a); try stream.writeAll(", "); try self.writeInstRef(stream, extra.b); try stream.writeAll(") "); try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node)); } fn writeMulAdd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; try self.writeInstRef(stream, extra.mulend1); try stream.writeAll(", "); try self.writeInstRef(stream, extra.mulend2); try stream.writeAll(", "); try self.writeInstRef(stream, extra.addend); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeBuiltinCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; try self.writeFlag(stream, "nodiscard ", extra.flags.ensure_result_used); try self.writeFlag(stream, "nosuspend ", extra.flags.is_nosuspend); try self.writeFlag(stream, "comptime ", extra.flags.is_comptime); try self.writeInstRef(stream, extra.modifier); try stream.writeAll(", "); try self.writeInstRef(stream, extra.callee); try stream.writeAll(", "); try self.writeInstRef(stream, extra.args); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeFieldParentPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data; try self.writeInstRef(stream, extra.parent_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_ptr); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data; try self.writeInstRef(stream, extra.frame_buffer); try stream.writeAll(", "); try self.writeInstRef(stream, extra.result_ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.fn_ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.args); try stream.writeAll(") "); try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node)); } fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_tok; const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index); const body = self.code.extra[extra.end..][0..extra.data.body_len]; try stream.print("\"{}\", ", .{ std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), }); if (extra.data.doc_comment != 0) { try stream.writeAll("\n"); try self.writeDocComment(stream, extra.data.doc_comment); try stream.writeByteNTimes(' ', self.indent); } try self.writeBracedBody(stream, body); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writePlNodeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.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 writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.operands_len); try stream.writeAll("{"); for (args, 0..) |arg, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, arg); } try stream.writeAll("}) "); try self.writeSrc(stream, inst_data.src()); } fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; try self.writeInstRef(stream, extra.ptr); try stream.print(", {d}) ", .{extra.index}); try self.writeSrc(stream, inst_data.src()); } fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; const decl_name = self.code.nullTerminatedString(extra.decl_name); try self.writeInstRef(stream, extra.namespace); try stream.print(", {}, ", .{std.zig.fmtId(decl_name)}); try self.writeInstRef(stream, extra.options); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; try self.writeInstRef(stream, extra.operand); try stream.writeAll(", "); try self.writeInstRef(stream, extra.options); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeStructInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); var field_i: u32 = 0; var extra_index = extra.end; while (field_i < extra.data.fields_len) : (field_i += 1) { const item = self.code.extraData(Zir.Inst.StructInit.Item, extra_index); extra_index = item.end; if (field_i != 0) { try stream.writeAll(", ["); } else { try stream.writeAll("["); } try self.writeInstIndex(stream, item.data.field_type); try stream.writeAll(", "); try self.writeInstRef(stream, item.data.init); try stream.writeAll("]"); } try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeCmpxchg(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); try self.writeInstRef(stream, extra.ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.expected_value); try stream.writeAll(", "); try self.writeInstRef(stream, extra.new_value); try stream.writeAll(", "); try self.writeInstRef(stream, extra.success_order); try stream.writeAll(", "); try self.writeInstRef(stream, extra.failure_order); try stream.writeAll(") "); try self.writeSrc(stream, src); } fn writeAtomicLoad(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; try self.writeInstRef(stream, extra.elem_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.ordering); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; try self.writeInstRef(stream, extra.ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.operand); try stream.writeAll(", "); try self.writeInstRef(stream, extra.ordering); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeAtomicRmw(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; try self.writeInstRef(stream, extra.ptr); try stream.writeAll(", "); try self.writeInstRef(stream, extra.operation); try stream.writeAll(", "); try self.writeInstRef(stream, extra.operand); try stream.writeAll(", "); try self.writeInstRef(stream, extra.ordering); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data; try self.writeInstRef(stream, extra.dest); try stream.writeAll(", "); try self.writeInstRef(stream, extra.source); try stream.writeAll(", "); try self.writeInstRef(stream, extra.byte_count); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data; try self.writeInstRef(stream, extra.dest); try stream.writeAll(", "); try self.writeInstRef(stream, extra.byte); try stream.writeAll(", "); try self.writeInstRef(stream, extra.byte_count); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); var field_i: u32 = 0; var extra_index = extra.end; while (field_i < extra.data.fields_len) : (field_i += 1) { const item = self.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; const field_name = self.code.nullTerminatedString(item.data.field_name); const prefix = if (field_i != 0) ", [" else "["; try stream.print("{s}{s}=", .{ prefix, field_name }); try self.writeInstRef(stream, item.data.init); try stream.writeAll("]"); } try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; try self.writeInstRef(stream, extra.container_type); const field_name = self.code.nullTerminatedString(extra.name_start); try stream.print(", {s}) ", .{field_name}); try self.writeSrc(stream, inst_data.src()); } fn writeFieldTypeRef(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; try self.writeInstRef(stream, extra.container_type); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); const src = LazySrcLoc.nodeOffset(extra.data.src_node); const operands = self.code.refSlice(extra.end, extended.small); for (operands, 0..) |operand, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, operand); } try stream.writeAll(")) "); try self.writeSrc(stream, src); } fn writeInstNode( self: *Writer, stream: anytype, inst: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].inst_node; try self.writeInstIndex(stream, inst_data.inst); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeAsm( self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData, tmpl_is_expr: bool, ) !void { const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); const src = LazySrcLoc.nodeOffset(extra.data.src_node); const outputs_len = @truncate(u5, extended.small); const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); const is_volatile = @truncate(u1, extended.small >> 15) != 0; try self.writeFlag(stream, "volatile, ", is_volatile); if (tmpl_is_expr) { try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, extra.data.asm_source)); try stream.writeAll(", "); } else { const asm_source = self.code.nullTerminatedString(extra.data.asm_source); try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); } try stream.writeAll(", "); var extra_i: usize = extra.end; var output_type_bits = extra.data.output_type_bits; { var i: usize = 0; while (i < outputs_len) : (i += 1) { const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i); extra_i = output.end; const is_type = @truncate(u1, output_type_bits) != 0; output_type_bits >>= 1; const name = self.code.nullTerminatedString(output.data.name); const constraint = self.code.nullTerminatedString(output.data.constraint); try stream.print("output({}, \"{}\", ", .{ std.zig.fmtId(name), std.zig.fmtEscapes(constraint), }); try self.writeFlag(stream, "->", is_type); try self.writeInstRef(stream, output.data.operand); try stream.writeAll(")"); if (i + 1 < outputs_len) { try stream.writeAll("), "); } } } { var i: usize = 0; while (i < inputs_len) : (i += 1) { const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i); extra_i = input.end; const name = self.code.nullTerminatedString(input.data.name); const constraint = self.code.nullTerminatedString(input.data.constraint); try stream.print("input({}, \"{}\", ", .{ std.zig.fmtId(name), std.zig.fmtEscapes(constraint), }); try self.writeInstRef(stream, input.data.operand); try stream.writeAll(")"); if (i + 1 < inputs_len) { try stream.writeAll(", "); } } } { var i: usize = 0; while (i < clobbers_len) : (i += 1) { const str_index = self.code.extra[extra_i]; extra_i += 1; const clobber = self.code.nullTerminatedString(str_index); try stream.print("{}", .{std.zig.fmtId(clobber)}); if (i + 1 < clobbers_len) { try stream.writeAll(", "); } } } try stream.writeAll(")) "); try self.writeSrc(stream, src); } fn writeOverflowArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.rhs); try stream.writeAll(")) "); try self.writeSrc(stream, src); } fn writeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index); const args_len = extra.data.flags.args_len; const body = self.code.extra[extra.end..]; if (extra.data.flags.ensure_result_used) { try stream.writeAll("nodiscard "); } try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallModifier, extra.data.flags.packed_modifier))}); try self.writeInstRef(stream, extra.data.callee); try stream.writeAll(", ["); self.indent += 2; if (args_len != 0) { try stream.writeAll("\n"); } var i: usize = 0; var arg_start: u32 = args_len; while (i < args_len) : (i += 1) { try stream.writeByteNTimes(' ', self.indent); const arg_end = self.code.extra[extra.end + i]; defer arg_start = arg_end; const arg_body = body[arg_start..arg_end]; try self.writeBracedBody(stream, arg_body); try stream.writeAll(",\n"); } self.indent -= 2; if (args_len != 0) { try stream.writeByteNTimes(' ', self.indent); } try stream.writeAll("]) "); try self.writeSrc(stream, inst_data.src()); } fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; try self.writePlNodeBlockWithoutSrc(stream, inst); try self.writeSrc(stream, inst_data.src()); } fn writePlNodeBlockWithoutSrc(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); const body = self.code.extra[extra.end..][0..extra.data.body_len]; try self.writeBracedBody(stream, body); try stream.writeAll(") "); } fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.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(", "); try self.writeBracedBody(stream, then_body); try stream.writeAll(", "); try self.writeBracedBody(stream, else_body); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = self.code.extra[extra.end..][0..extra.data.body_len]; try self.writeInstRef(stream, extra.data.operand); try stream.writeAll(", "); try self.writeBracedBody(stream, body); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); var extra_index: usize = extended.operand; const src_node: ?i32 = if (small.has_src_node) blk: { const src_node = @bitCast(i32, self.code.extra[extra_index]); extra_index += 1; break :blk src_node; } else null; const fields_len = if (small.has_fields_len) blk: { const fields_len = self.code.extra[extra_index]; extra_index += 1; break :blk fields_len; } else 0; const decls_len = if (small.has_decls_len) blk: { const decls_len = self.code.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv); try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only); try self.writeFlag(stream, "tuple, ", small.is_tuple); try stream.print("{s}, ", .{@tagName(small.name_strategy)}); if (small.layout == .Packed and small.has_backing_int) { const backing_int_body_len = self.code.extra[extra_index]; extra_index += 1; try stream.writeAll("Packed("); if (backing_int_body_len == 0) { const backing_int_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; try self.writeInstRef(stream, backing_int_ref); } else { const body = self.code.extra[extra_index..][0..backing_int_body_len]; extra_index += backing_int_body_len; self.indent += 2; try self.writeBracedDecl(stream, body); self.indent -= 2; } try stream.writeAll("), "); } else { try stream.print("{s}, ", .{@tagName(small.layout)}); } if (decls_len == 0) { try stream.writeAll("{}, "); } else { const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); defer self.parent_decl_node = prev_parent_decl_node; try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } if (fields_len == 0) { try stream.writeAll("{}, {})"); } else { const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; const Field = struct { doc_comment_index: u32, type_len: u32 = 0, align_len: u32 = 0, init_len: u32 = 0, type: Zir.Inst.Ref = .none, name: u32, is_comptime: bool, }; const fields = try self.arena.alloc(Field, fields_len); { var bit_bag_index: usize = extra_index; extra_index += bit_bags_count; var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; while (field_i < fields_len) : (field_i += 1) { if (field_i % fields_per_u32 == 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 is_comptime = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const has_type_body = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; var field_name: u32 = 0; if (!small.is_tuple) { field_name = self.code.extra[extra_index]; extra_index += 1; } const doc_comment_index = self.code.extra[extra_index]; extra_index += 1; fields[field_i] = .{ .doc_comment_index = doc_comment_index, .is_comptime = is_comptime, .name = field_name, }; if (has_type_body) { fields[field_i].type_len = self.code.extra[extra_index]; } else { fields[field_i].type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); } extra_index += 1; if (has_align) { fields[field_i].align_len = self.code.extra[extra_index]; extra_index += 1; } if (has_default) { fields[field_i].init_len = self.code.extra[extra_index]; extra_index += 1; } } } const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); try stream.writeAll("{\n"); self.indent += 2; for (fields, 0..) |field, i| { try self.writeDocComment(stream, field.doc_comment_index); try stream.writeByteNTimes(' ', self.indent); try self.writeFlag(stream, "comptime ", field.is_comptime); if (field.name != 0) { const field_name = self.code.nullTerminatedString(field.name); try stream.print("{}: ", .{std.zig.fmtId(field_name)}); } else { try stream.print("@\"{d}\": ", .{i}); } if (field.type != .none) { try self.writeInstRef(stream, field.type); } if (field.type_len > 0) { const body = self.code.extra[extra_index..][0..field.type_len]; extra_index += body.len; self.indent += 2; try self.writeBracedDecl(stream, body); self.indent -= 2; } if (field.align_len > 0) { const body = self.code.extra[extra_index..][0..field.align_len]; extra_index += body.len; self.indent += 2; try stream.writeAll(" align("); try self.writeBracedDecl(stream, body); try stream.writeAll(")"); self.indent -= 2; } if (field.init_len > 0) { const body = self.code.extra[extra_index..][0..field.init_len]; extra_index += body.len; self.indent += 2; try stream.writeAll(" = "); try self.writeBracedDecl(stream, body); self.indent -= 2; } try stream.writeAll(",\n"); } self.parent_decl_node = prev_parent_decl_node; self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("})"); } try self.writeSrcNode(stream, src_node); } fn writeUnionDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; const src_node: ?i32 = if (small.has_src_node) blk: { const src_node = @bitCast(i32, self.code.extra[extra_index]); extra_index += 1; break :blk src_node; } else null; const tag_type_ref = if (small.has_tag_type) blk: { const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk tag_type_ref; } else .none; const body_len = if (small.has_body_len) blk: { const body_len = self.code.extra[extra_index]; extra_index += 1; break :blk body_len; } else 0; const fields_len = if (small.has_fields_len) blk: { const fields_len = self.code.extra[extra_index]; extra_index += 1; break :blk fields_len; } else 0; const decls_len = if (small.has_decls_len) blk: { const decls_len = self.code.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; try stream.print("{s}, {s}, ", .{ @tagName(small.name_strategy), @tagName(small.layout), }); try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); if (decls_len == 0) { try stream.writeAll("{}"); } else { const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); defer self.parent_decl_node = prev_parent_decl_node; try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}"); } if (tag_type_ref != .none) { try stream.writeAll(", "); try self.writeInstRef(stream, tag_type_ref); } if (fields_len == 0) { try stream.writeAll("})"); try self.writeSrcNode(stream, src_node); return; } try stream.writeAll(", "); const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); try self.writeBracedDecl(stream, body); try stream.writeAll(", {\n"); self.indent += 2; const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; const body_end = extra_index; extra_index += 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 % fields_per_u32 == 0) { cur_bit_bag = self.code.extra[bit_bag_index]; bit_bag_index += 1; } const has_type = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const has_align = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const has_value = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const unused = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; _ = unused; const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; const doc_comment_index = self.code.extra[extra_index]; extra_index += 1; try self.writeDocComment(stream, doc_comment_index); try stream.writeByteNTimes(' ', self.indent); try stream.print("{}", .{std.zig.fmtId(field_name)}); if (has_type) { const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; try stream.writeAll(": "); try self.writeInstRef(stream, field_type); } if (has_align) { const align_ref = @intToEnum(Zir.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_value) { const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; try stream.writeAll(" = "); try self.writeInstRef(stream, default_ref); } try stream.writeAll(",\n"); } self.parent_decl_node = prev_parent_decl_node; self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("})"); try self.writeSrcNode(stream, src_node); } fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize { const parent_decl_node = self.parent_decl_node; const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; var extra_index = extra_start + bit_bags_count; var bit_bag_index: usize = extra_start; var cur_bit_bag: u32 = undefined; var decl_i: u32 = 0; while (decl_i < decls_len) : (decl_i += 1) { if (decl_i % 8 == 0) { cur_bit_bag = self.code.extra[bit_bag_index]; bit_bag_index += 1; } const is_pub = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const is_exported = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const has_align = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; const sub_index = extra_index; const hash_u32s = self.code.extra[extra_index..][0..4]; extra_index += 4; const line = self.code.extra[extra_index]; extra_index += 1; const decl_name_index = self.code.extra[extra_index]; extra_index += 1; const decl_index = self.code.extra[extra_index]; extra_index += 1; const doc_comment_index = self.code.extra[extra_index]; extra_index += 1; const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :inst inst; }; const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :inst inst; }; const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :inst inst; }; const pub_str = if (is_pub) "pub " else ""; const hash_bytes = @bitCast([16]u8, hash_u32s.*); if (decl_name_index == 0) { try stream.writeByteNTimes(' ', self.indent); const name = if (is_exported) "usingnamespace" else "comptime"; try stream.writeAll(pub_str); try stream.writeAll(name); } else if (decl_name_index == 1) { try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("test"); } else if (decl_name_index == 2) { try stream.writeByteNTimes(' ', self.indent); try stream.print("[{d}] decltest {s}", .{ sub_index, self.code.nullTerminatedString(doc_comment_index) }); } else { const raw_decl_name = self.code.nullTerminatedString(decl_name_index); const decl_name = if (raw_decl_name.len == 0) self.code.nullTerminatedString(decl_name_index + 1) else raw_decl_name; const test_str = if (raw_decl_name.len == 0) "test \"" else ""; const export_str = if (is_exported) "export " else ""; try self.writeDocComment(stream, doc_comment_index); try stream.writeByteNTimes(' ', self.indent); const endquote_if_test: []const u8 = if (raw_decl_name.len == 0) "\"" else ""; try stream.print("[{d}] {s}{s}{s}{}{s}", .{ sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), endquote_if_test, }); if (align_inst != .none) { try stream.writeAll(" align("); try self.writeInstRef(stream, align_inst); try stream.writeAll(")"); } if (addrspace_inst != .none) { try stream.writeAll(" addrspace("); try self.writeInstRef(stream, addrspace_inst); try stream.writeAll(")"); } if (section_inst != .none) { try stream.writeAll(" linksection("); try self.writeInstRef(stream, section_inst); try stream.writeAll(")"); } } if (self.recurse_decls) { const tag = self.code.instructions.items(.tag)[decl_index]; try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), }); const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; const sub_decl_node_off = decl_block_inst_data.src_node; self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); try self.writePlNodeBlockWithoutSrc(stream, decl_index); self.parent_decl_node = parent_decl_node; try self.writeSrc(stream, decl_block_inst_data.src()); try stream.writeAll("\n"); } else { try stream.print(" line({d}) hash({}): %{d} = ...\n", .{ line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, }); } } return extra_index; } fn writeEnumDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small); var extra_index: usize = extended.operand; const src_node: ?i32 = if (small.has_src_node) blk: { const src_node = @bitCast(i32, self.code.extra[extra_index]); extra_index += 1; break :blk src_node; } else null; const tag_type_ref = if (small.has_tag_type) blk: { const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk tag_type_ref; } else .none; const body_len = if (small.has_body_len) blk: { const body_len = self.code.extra[extra_index]; extra_index += 1; break :blk body_len; } else 0; const fields_len = if (small.has_fields_len) blk: { const fields_len = self.code.extra[extra_index]; extra_index += 1; break :blk fields_len; } else 0; const decls_len = if (small.has_decls_len) blk: { const decls_len = self.code.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; try stream.print("{s}, ", .{@tagName(small.name_strategy)}); try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive); if (decls_len == 0) { try stream.writeAll("{}, "); } else { const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); defer self.parent_decl_node = prev_parent_decl_node; try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } if (tag_type_ref != .none) { try self.writeInstRef(stream, tag_type_ref); try stream.writeAll(", "); } const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); try self.writeBracedDecl(stream, body); if (fields_len == 0) { try stream.writeAll(", {})"); self.parent_decl_node = prev_parent_decl_node; } else { try stream.writeAll(", {\n"); self.indent += 2; const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; const body_end = extra_index; extra_index += 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; const doc_comment_index = self.code.extra[extra_index]; extra_index += 1; try self.writeDocComment(stream, doc_comment_index); try stream.writeByteNTimes(' ', self.indent); try stream.print("{}", .{std.zig.fmtId(field_name)}); if (has_tag_value) { const tag_value_ref = @intToEnum(Zir.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.parent_decl_node = prev_parent_decl_node; self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("})"); } try self.writeSrcNode(stream, src_node); } fn writeOpaqueDecl( self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData, ) !void { const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); var extra_index: usize = extended.operand; const src_node: ?i32 = if (small.has_src_node) blk: { const src_node = @bitCast(i32, self.code.extra[extra_index]); extra_index += 1; break :blk src_node; } else null; const decls_len = if (small.has_decls_len) blk: { const decls_len = self.code.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; try stream.print("{s}, ", .{@tagName(small.name_strategy)}); if (decls_len == 0) { try stream.writeAll("{})"); } else { const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); defer self.parent_decl_node = prev_parent_decl_node; try stream.writeAll("{\n"); self.indent += 2; _ = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("})"); } try self.writeSrcNode(stream, src_node); } fn writeErrorSetDecl( self: *Writer, stream: anytype, inst: Zir.Inst.Index, name_strategy: Zir.Inst.NameStrategy, ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); try stream.print("{s}, ", .{@tagName(name_strategy)}); try stream.writeAll("{\n"); self.indent += 2; var extra_index = @intCast(u32, extra.end); const extra_index_end = extra_index + (extra.data.fields_len * 2); while (extra_index < extra_index_end) : (extra_index += 2) { const str_index = self.code.extra[extra_index]; const name = self.code.nullTerminatedString(str_index); const doc_comment_index = self.code.extra[extra_index + 1]; try self.writeDocComment(stream, doc_comment_index); try stream.writeByteNTimes(' ', self.indent); try stream.print("{},\n", .{std.zig.fmtId(name)}); } self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}) "); try self.writeSrc(stream, inst_data.src()); } fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); var extra_index: usize = extra.end; const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { const multi_cases_len = self.code.extra[extra_index]; extra_index += 1; break :blk multi_cases_len; } else 0; try self.writeInstRef(stream, extra.data.operand); self.indent += 2; else_prong: { const special_prong = extra.data.bits.specialProng(); const prong_name = switch (special_prong) { .@"else" => "else", .under => "_", else => break :else_prong, }; const body_len = @truncate(u31, self.code.extra[extra_index]); const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else ""; extra_index += 1; const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; try stream.writeAll(",\n"); try stream.writeByteNTimes(' ', self.indent); try stream.print("{s}{s} => ", .{ inline_text, prong_name }); try self.writeBracedBody(stream, body); } { const scalar_cases_len = extra.data.bits.scalar_cases_len; var scalar_i: usize = 0; while (scalar_i < scalar_cases_len) : (scalar_i += 1) { const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; const body_len = @truncate(u31, self.code.extra[extra_index]); const is_inline = self.code.extra[extra_index] >> 31 != 0; extra_index += 1; const body = self.code.extra[extra_index..][0..body_len]; extra_index += body_len; try stream.writeAll(",\n"); try stream.writeByteNTimes(' ', self.indent); if (is_inline) try stream.writeAll("inline "); try self.writeInstRef(stream, item_ref); try stream.writeAll(" => "); try self.writeBracedBody(stream, body); } } { var multi_i: usize = 0; while (multi_i < 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 = @truncate(u31, self.code.extra[extra_index]); const is_inline = self.code.extra[extra_index] >> 31 != 0; extra_index += 1; const items = self.code.refSlice(extra_index, items_len); extra_index += items_len; try stream.writeAll(",\n"); try stream.writeByteNTimes(' ', self.indent); if (is_inline) try stream.writeAll("inline "); for (items, 0..) |item_ref, item_i| { if (item_i != 0) 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(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; if (range_i != 0 or items.len != 0) { 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(" => "); try self.writeBracedBody(stream, body); } } self.indent -= 2; try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writePlNodeField(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.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 writePlNodeFieldNamed(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.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: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const src_node = self.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(src_node); try stream.writeAll(") "); try self.writeSrc(stream, src); } fn writeStrTok( self: *Writer, stream: anytype, inst: Zir.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 writeStrOp(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].str_op; const str = inst_data.getStr(self.code); try self.writeInstRef(stream, inst_data.operand); try stream.print(", \"{}\")", .{std.zig.fmtEscapes(str)}); } fn writeFunc( self: *Writer, stream: anytype, inst: Zir.Inst.Index, inferred_error_set: bool, ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = self.code.extraData(Zir.Inst.Func, inst_data.payload_index); var extra_index = extra.end; var ret_ty_ref: Zir.Inst.Ref = .none; var ret_ty_body: []const Zir.Inst.Index = &.{}; switch (extra.data.ret_body_len) { 0 => { ret_ty_ref = .void_type; }, 1 => { ret_ty_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; }, else => { ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; extra_index += ret_ty_body.len; }, } const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; var src_locs: Zir.Inst.Func.SrcLocs = undefined; if (body.len != 0) { src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } return self.writeFuncCommon( stream, inferred_error_set, false, false, false, .none, &.{}, .none, &.{}, .none, &.{}, .none, &.{}, ret_ty_ref, ret_ty_body, body, src, src_locs, 0, ); } fn writeFuncFancy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); const src = inst_data.src(); var extra_index: usize = extra.end; var align_ref: Zir.Inst.Ref = .none; var align_body: []const Zir.Inst.Index = &.{}; var addrspace_ref: Zir.Inst.Ref = .none; var addrspace_body: []const Zir.Inst.Index = &.{}; var section_ref: Zir.Inst.Ref = .none; var section_body: []const Zir.Inst.Index = &.{}; var cc_ref: Zir.Inst.Ref = .none; var cc_body: []const Zir.Inst.Index = &.{}; var ret_ty_ref: Zir.Inst.Ref = .none; var ret_ty_body: []const Zir.Inst.Index = &.{}; if (extra.data.bits.has_lib_name) { const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); } try self.writeFlag(stream, "test, ", extra.data.bits.is_test); if (extra.data.bits.has_align_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; align_body = self.code.extra[extra_index..][0..body_len]; extra_index += align_body.len; } else if (extra.data.bits.has_align_ref) { align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; } if (extra.data.bits.has_addrspace_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; addrspace_body = self.code.extra[extra_index..][0..body_len]; extra_index += addrspace_body.len; } else if (extra.data.bits.has_addrspace_ref) { addrspace_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; } if (extra.data.bits.has_section_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; section_body = self.code.extra[extra_index..][0..body_len]; extra_index += section_body.len; } else if (extra.data.bits.has_section_ref) { section_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; } if (extra.data.bits.has_cc_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; cc_body = self.code.extra[extra_index..][0..body_len]; extra_index += cc_body.len; } else if (extra.data.bits.has_cc_ref) { cc_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; } if (extra.data.bits.has_ret_ty_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; ret_ty_body = self.code.extra[extra_index..][0..body_len]; extra_index += ret_ty_body.len; } else if (extra.data.bits.has_ret_ty_ref) { ret_ty_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; } const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { const x = self.code.extra[extra_index]; extra_index += 1; break :blk x; } else 0; const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; var src_locs: Zir.Inst.Func.SrcLocs = undefined; if (body.len != 0) { src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } return self.writeFuncCommon( stream, extra.data.bits.is_inferred_error, extra.data.bits.is_var_args, extra.data.bits.is_extern, extra.data.bits.is_noinline, align_ref, align_body, addrspace_ref, addrspace_body, section_ref, section_body, cc_ref, cc_body, ret_ty_ref, ret_ty_body, body, src, src_locs, noalias_bits, ); } fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand); const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small); try self.writeInstRef(stream, extra.data.var_type); var extra_index: usize = extra.end; if (small.has_lib_name) { const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)}); } const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk align_inst; }; const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: { const init_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk init_inst; }; try self.writeFlag(stream, ", is_extern", small.is_extern); try self.writeFlag(stream, ", is_threadlocal", small.is_threadlocal); try self.writeOptionalInstRef(stream, ", align=", align_inst); try self.writeOptionalInstRef(stream, ", init=", init_inst); try stream.writeAll("))"); } fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand); const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small); const src = LazySrcLoc.nodeOffset(extra.data.src_node); var extra_index: usize = extra.end; const type_inst: Zir.Inst.Ref = if (!small.has_type) .none else blk: { const type_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk type_inst; }; const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk align_inst; }; try self.writeFlag(stream, ",is_const", small.is_const); try self.writeFlag(stream, ",is_comptime", small.is_comptime); try self.writeOptionalInstRef(stream, ",ty=", type_inst); try self.writeOptionalInstRef(stream, ",align=", align_inst); try stream.writeAll(")) "); try self.writeSrc(stream, src); } fn writeTypeofPeer(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); const body = self.code.extra[extra.data.body_index..][0..extra.data.body_len]; try self.writeBracedBody(stream, body); try stream.writeAll(",["); const args = self.code.refSlice(extra.end, extended.small); for (args, 0..) |arg, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, arg); } try stream.writeAll("])"); } fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].bool_br; const extra = self.code.extraData(Zir.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(", "); try self.writeBracedBody(stream, body); } fn writeIntType(self: *Writer, stream: anytype, inst: Zir.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 writeSaveErrRetIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].save_err_ret_index; try self.writeInstRef(stream, inst_data.operand); try stream.writeAll(")"); } fn writeRestoreErrRetIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].restore_err_ret_index; try self.writeInstRef(stream, inst_data.block); try stream.writeAll(", "); try self.writeInstRef(stream, inst_data.operand); try stream.writeAll(")"); } fn writeBreak(self: *Writer, stream: anytype, inst: Zir.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 writeArrayInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.operands_len); try self.writeInstRef(stream, args[0]); try stream.writeAll("{"); for (args[1..], 0..) |arg, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, arg); } try stream.writeAll("}) "); try self.writeSrc(stream, inst_data.src()); } fn writeArrayInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.operands_len); try stream.writeAll("{"); for (args, 0..) |arg, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, arg); } try stream.writeAll("}) "); try self.writeSrc(stream, inst_data.src()); } fn writeArrayInitSent(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.operands_len); const sent = args[args.len - 1]; const elems = args[0 .. args.len - 1]; try self.writeInstRef(stream, sent); try stream.writeAll(", "); try stream.writeAll(".{"); for (elems, 0..) |elem, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, elem); } try stream.writeAll("}) "); try self.writeSrc(stream, inst_data.src()); } fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } fn writeFuncCommon( self: *Writer, stream: anytype, inferred_error_set: bool, var_args: bool, is_extern: bool, is_noinline: bool, align_ref: Zir.Inst.Ref, align_body: []const Zir.Inst.Index, addrspace_ref: Zir.Inst.Ref, addrspace_body: []const Zir.Inst.Index, section_ref: Zir.Inst.Ref, section_body: []const Zir.Inst.Index, cc_ref: Zir.Inst.Ref, cc_body: []const Zir.Inst.Index, ret_ty_ref: Zir.Inst.Ref, ret_ty_body: []const Zir.Inst.Index, body: []const Zir.Inst.Index, src: LazySrcLoc, src_locs: Zir.Inst.Func.SrcLocs, noalias_bits: u32, ) !void { try self.writeOptionalInstRefOrBody(stream, "align=", align_ref, align_body); try self.writeOptionalInstRefOrBody(stream, "addrspace=", addrspace_ref, addrspace_body); try self.writeOptionalInstRefOrBody(stream, "section=", section_ref, section_body); try self.writeOptionalInstRefOrBody(stream, "cc=", cc_ref, cc_body); try self.writeOptionalInstRefOrBody(stream, "ret_ty=", ret_ty_ref, ret_ty_body); try self.writeFlag(stream, "vargs, ", var_args); try self.writeFlag(stream, "extern, ", is_extern); try self.writeFlag(stream, "inferror, ", inferred_error_set); try self.writeFlag(stream, "noinline, ", is_noinline); if (noalias_bits != 0) { try stream.print("noalias=0b{b}, ", .{noalias_bits}); } try stream.writeAll("body="); try self.writeBracedBody(stream, body); try stream.writeAll(") "); if (body.len != 0) { try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ src_locs.lbrace_line + 1, @truncate(u16, src_locs.columns) + 1, src_locs.rbrace_line + 1, @truncate(u16, src_locs.columns >> 16) + 1, }); } try self.writeSrc(stream, src); } fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Zir.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 writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt; try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 }); } fn writeDefer(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].@"defer"; const body = self.code.extra[inst_data.index..][0..inst_data.len]; try self.writeBracedBody(stream, body); try stream.writeByte(')'); } fn writeDeferErrCode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].defer_err_code; const extra = self.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; try self.writeInstRef(stream, Zir.indexToRef(extra.remapped_err_code)); try stream.writeAll(" = "); try self.writeInstRef(stream, inst_data.err_code); try stream.writeAll(", "); const body = self.code.extra[extra.index..][0..extra.len]; try self.writeBracedBody(stream, body); try stream.writeByte(')'); } fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void { var i: usize = @enumToInt(ref); if (i < Zir.Inst.Ref.typed_value_map.len) { return stream.print("@{}", .{ref}); } i -= Zir.Inst.Ref.typed_value_map.len; return self.writeInstIndex(stream, @intCast(Zir.Inst.Index, i)); } fn writeInstIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { _ = self; return stream.print("%{d}", .{inst}); } fn writeOptionalInstRef( self: *Writer, stream: anytype, prefix: []const u8, inst: Zir.Inst.Ref, ) !void { if (inst == .none) return; try stream.writeAll(prefix); try self.writeInstRef(stream, inst); } fn writeOptionalInstRefOrBody( self: *Writer, stream: anytype, prefix: []const u8, ref: Zir.Inst.Ref, body: []const Zir.Inst.Index, ) !void { if (body.len != 0) { try stream.writeAll(prefix); try self.writeBracedBody(stream, body); try stream.writeAll(", "); } else if (ref != .none) { try stream.writeAll(prefix); try self.writeInstRef(stream, ref); try stream.writeAll(", "); } } fn writeFlag( self: *Writer, stream: anytype, name: []const u8, flag: bool, ) !void { _ = self; if (!flag) return; try stream.writeAll(name); } fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { if (self.file.tree_loaded) { const tree = self.file.tree; const src_loc: Module.SrcLoc = .{ .file_scope = self.file, .parent_decl_node = self.parent_decl_node, .lazy = src, }; const src_span = src_loc.span(self.gpa) catch unreachable; const start = std.zig.findLineColumn(tree.source, src_span.start); const end = std.zig.findLineColumn(tree.source, src_span.end); try stream.print("{s}:{d}:{d} to :{d}:{d}", .{ @tagName(src), start.line + 1, start.column + 1, end.line + 1, end.column + 1, }); } } fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { const node_offset = src_node orelse return; const src = LazySrcLoc.nodeOffset(node_offset); try stream.writeAll(" "); return self.writeSrc(stream, src); } fn writeBracedDecl(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { try self.writeBracedBodyConditional(stream, body, self.recurse_decls); } fn writeBracedBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { try self.writeBracedBodyConditional(stream, body, self.recurse_blocks); } fn writeBracedBodyConditional(self: *Writer, stream: anytype, body: []const Zir.Inst.Index, enabled: bool) !void { if (body.len == 0) { try stream.writeAll("{}"); } else if (enabled) { try stream.writeAll("{\n"); self.indent += 2; try self.writeBody(stream, body); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}"); } else if (body.len == 1) { try stream.writeByte('{'); try self.writeInstIndex(stream, body[0]); try stream.writeByte('}'); } else if (body.len == 2) { try stream.writeByte('{'); try self.writeInstIndex(stream, body[0]); try stream.writeAll(", "); try self.writeInstIndex(stream, body[1]); try stream.writeByte('}'); } else { try stream.writeByte('{'); try self.writeInstIndex(stream, body[0]); try stream.writeAll(".."); try self.writeInstIndex(stream, body[body.len - 1]); try stream.writeByte('}'); } } fn writeDocComment(self: *Writer, stream: anytype, doc_comment_index: u32) !void { if (doc_comment_index != 0) { const doc_comment = self.code.nullTerminatedString(doc_comment_index); var it = std.mem.tokenize(u8, doc_comment, "\n"); while (it.next()) |doc_line| { try stream.writeByteNTimes(' ', self.indent); try stream.print("///{s}\n", .{doc_line}); } } } fn writeBody(self: *Writer, stream: anytype, body: []const Zir.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'); } } };