diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 125 | ||||
| -rw-r--r-- | src/Sema.zig | 601 | ||||
| -rw-r--r-- | src/astgen.zig | 126 | ||||
| -rw-r--r-- | src/zir.zig | 75 |
4 files changed, 512 insertions, 415 deletions
diff --git a/src/Module.zig b/src/Module.zig index 55e301c21c..d535a6d580 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -477,7 +477,7 @@ pub const Scope = struct { switch (scope.tag) { .file => return &scope.cast(File).?.tree, .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree, - .gen_zir => return &scope.cast(GenZir).?.zir_code.decl.container.file_scope.tree, + .gen_zir => return scope.cast(GenZir).?.tree(), .local_val => return &scope.cast(LocalVal).?.gen_zir.zir_code.decl.container.file_scope.tree, .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container.file_scope.tree, .container => return &scope.cast(Container).?.file_scope.tree, @@ -983,6 +983,30 @@ pub const Scope = struct { return gz.zir_code.decl.nodeSrcLoc(node_index); } + pub fn tree(gz: *const GenZir) *const ast.Tree { + return &gz.zir_code.decl.container.file_scope.tree; + } + + pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void { + try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len + + @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); + const zir_datas = gz.zir_code.instructions.items(.data); + zir_datas[inst].bool_br.payload_index = gz.zir_code.addExtraAssumeCapacity( + zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, + ); + gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items); + } + + pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void { + try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len + + @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); + const zir_datas = gz.zir_code.instructions.items(.data); + zir_datas[inst].pl_node.payload_index = gz.zir_code.addExtraAssumeCapacity( + zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, + ); + gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items); + } + pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct { param_types: []const zir.Inst.Ref, ret_ty: zir.Inst.Ref, @@ -1044,73 +1068,62 @@ pub const Scope = struct { return new_index + gz.zir_code.ref_start_index; } - pub fn addCondBr( + pub fn addCall( gz: *GenZir, - condition: zir.Inst.Ref, - then_body: []const zir.Inst.Ref, - else_body: []const zir.Inst.Ref, + tag: zir.Inst.Tag, + callee: zir.Inst.Ref, + args: []const zir.Inst.Ref, /// Absolute node index. This function does the conversion to offset from Decl. abs_node_index: ast.Node.Index, - ) !zir.Inst.Ref { + ) !zir.Inst.Index { + assert(callee != 0); + assert(abs_node_index != 0); const gpa = gz.zir_code.gpa; try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + - @typeInfo(zir.Inst.CondBr).Struct.fields.len + then_body.len + else_body.len); + @typeInfo(zir.Inst.Call).Struct.fields.len + args.len); - const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.CondBr{ - .condition = condition, - .then_body_len = @intCast(u32, then_body.len), - .else_body_len = @intCast(u32, else_body.len), + const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{ + .callee = callee, + .args_len = @intCast(u32, args.len), }); - gz.zir_code.extra.appendSliceAssumeCapacity(then_body); - gz.zir_code.extra.appendSliceAssumeCapacity(else_body); + gz.zir_code.extra.appendSliceAssumeCapacity(args); const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(.{ - .tag = .condbr, + .tag = tag, .data = .{ .pl_node = .{ .src_node = gz.zir_code.decl.nodeIndexToRelative(abs_node_index), .payload_index = payload_index, } }, }); gz.instructions.appendAssumeCapacity(new_index); - return new_index + gz.zir_code.ref_start_index; } - pub fn addCall( + /// Note that this returns a `zir.Inst.Index` not a ref. + /// Leaves the `payload_index` field undefined. + pub fn addBoolBr( gz: *GenZir, tag: zir.Inst.Tag, - callee: zir.Inst.Ref, - args: []const zir.Inst.Ref, - /// Absolute node index. This function does the conversion to offset from Decl. - abs_node_index: ast.Node.Index, + lhs: zir.Inst.Ref, ) !zir.Inst.Index { - assert(callee != 0); - assert(abs_node_index != 0); + assert(lhs != 0); const gpa = gz.zir_code.gpa; try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1); - try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len + - @typeInfo(zir.Inst.Call).Struct.fields.len + args.len); - - const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{ - .callee = callee, - .args_len = @intCast(u32, args.len), - }); - gz.zir_code.extra.appendSliceAssumeCapacity(args); const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(.{ .tag = tag, - .data = .{ .pl_node = .{ - .src_node = gz.zir_code.decl.nodeIndexToRelative(abs_node_index), - .payload_index = payload_index, + .data = .{ .bool_br = .{ + .lhs = lhs, + .payload_index = undefined, } }, }); gz.instructions.appendAssumeCapacity(new_index); - return new_index + gz.zir_code.ref_start_index; + return new_index; } pub fn addInt(gz: *GenZir, integer: u64) !zir.Inst.Ref { @@ -1291,6 +1304,20 @@ pub const Scope = struct { return new_index; } + /// Note that this returns a `zir.Inst.Index` not a ref. + /// Leaves the `payload_index` field undefined. + pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index { + const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); + try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ + .tag = .condbr, + .data = .{ .pl_node = .{ + .src_node = gz.zir_code.decl.nodeIndexToRelative(node), + .payload_index = undefined, + } }, + }); + return new_index; + } + pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref { const gpa = gz.zir_code.gpa; try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); @@ -1409,9 +1436,9 @@ pub const WipZirCode = struct { .bitcast_result_ptr, .bit_or, .block, - .block_flat, .block_comptime, - .block_comptime_flat, + .bool_br_and, + .bool_br_or, .bool_not, .bool_and, .bool_or, @@ -1461,9 +1488,6 @@ pub const WipZirCode = struct { .ret_type, .shl, .shr, - .store, - .store_to_block_ptr, - .store_to_inferred_ptr, .str, .sub, .subwrap, @@ -1497,7 +1521,6 @@ pub const WipZirCode = struct { .slice_sentinel, .import, .typeof_peer, - .resolve_inferred_alloc, => return false, .breakpoint, @@ -1509,6 +1532,7 @@ pub const WipZirCode = struct { .ensure_err_payload_void, .@"break", .break_void_tok, + .break_flat, .condbr, .compile_error, .ret_node, @@ -1517,6 +1541,10 @@ pub const WipZirCode = struct { .@"unreachable", .loop, .elided, + .store, + .store_to_block_ptr, + .store_to_inferred_ptr, + .resolve_inferred_alloc, => return true, } } @@ -2150,7 +2178,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { }; defer block_scope.instructions.deinit(mod.gpa); - try sema.root(&block_scope); + _ = try sema.root(&block_scope); decl.analysis = .complete; decl.generation = mod.generation; @@ -2338,6 +2366,7 @@ fn astgenAndSemaFn( const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type; break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types); }; + _ = try fn_type_scope.addUnNode(.break_flat, fn_type_inst, 0); // We need the memory for the Type to go into the arena for the Decl var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); @@ -2370,7 +2399,7 @@ fn astgenAndSemaFn( }; defer block_scope.instructions.deinit(mod.gpa); - const fn_type = try fn_type_sema.rootAsType(&block_scope, fn_type_inst); + const fn_type = try fn_type_sema.rootAsType(&block_scope); if (body_node == 0) { if (!is_extern) { return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); @@ -2650,6 +2679,7 @@ fn astgenAndSemaVarDecl( init_result_loc, var_decl.ast.init_node, ); + _ = try gen_scope.addUnNode(.break_flat, init_inst, var_decl.ast.init_node); var code = try gen_scope.finish(); defer code.deinit(mod.gpa); if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { @@ -2676,10 +2706,9 @@ fn astgenAndSemaVarDecl( }; defer block_scope.instructions.deinit(mod.gpa); - try sema.root(&block_scope); - + const init_inst_zir_ref = try sema.root(&block_scope); // The result location guarantees the type coercion. - const analyzed_init_inst = try sema.resolveInst(init_inst); + const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref); // The is_comptime in the Scope.Block guarantees the result is comptime-known. const val = analyzed_init_inst.value().?; @@ -2713,6 +2742,8 @@ fn astgenAndSemaVarDecl( defer type_scope.instructions.deinit(mod.gpa); const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node); + _ = try type_scope.addUnNode(.break_flat, var_type, 0); + var code = try type_scope.finish(); defer code.deinit(mod.gpa); if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { @@ -2739,7 +2770,7 @@ fn astgenAndSemaVarDecl( }; defer block_scope.instructions.deinit(mod.gpa); - const ty = try sema.rootAsType(&block_scope, var_type); + const ty = try sema.rootAsType(&block_scope); break :vi .{ .ty = try ty.copy(&decl_arena.allocator), @@ -3328,7 +3359,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { func.state = .in_progress; log.debug("set {s} to in_progress", .{decl.name}); - try sema.root(&inner_block); + _ = try sema.root(&inner_block); const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items); func.state = .success; diff --git a/src/Sema.zig b/src/Sema.zig index 3b257b666e..7ad18ac66e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -53,172 +53,230 @@ const InnerError = Module.InnerError; const Decl = Module.Decl; const LazySrcLoc = Module.LazySrcLoc; -pub fn root(sema: *Sema, root_block: *Scope.Block) !void { +pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref { const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; return sema.analyzeBody(root_block, root_body); } -pub fn rootAsType(sema: *Sema, root_block: *Scope.Block, result_inst: zir.Inst.Ref) !Type { - const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; - try sema.analyzeBody(root_block, root_body); - +/// Assumes that `root_block` ends with `break_flat`. +pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type { + const zir_inst_ref = try sema.root(root_block); // Source location is unneeded because resolveConstValue must have already // been successfully called when coercing the value to a type, from the // result location. - return sema.resolveType(root_block, .unneeded, result_inst); -} - -pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !void { - const tracy = trace(@src()); - defer tracy.end(); + return sema.resolveType(root_block, .unneeded, zir_inst_ref); +} + +/// ZIR instructions which are always `noreturn` return this. This matches the +/// return type of `analyzeBody` so that we can tail call them. +/// Only appropriate to return when the instruction is known to be NoReturn +/// solely based on the ZIR tag. +const always_noreturn: InnerError!zir.Inst.Ref = @as(zir.Inst.Index, 0); + +/// This function is the main loop of `Sema` and it can be used in two different ways: +/// * The traditional way where there are N breaks out of the block and peer type +/// resolution is done on the break operands. In this case, the `zir.Inst.Index` +/// part of the return value will be `undefined`, and callsites should ignore it, +/// finding the block result value via the block scope. +/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_flat` +/// instruction. In this case, the `zir.Inst.Index` part of the return value will be +/// the block result value. No block scope needs to be created for this strategy. +pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !zir.Inst.Index { + // No tracy calls here, to avoid interfering with the tail call mechanism. const map = block.sema.inst_map; const tags = block.sema.code.instructions.items(.tag); - // TODO: As an optimization, look into making these switch prongs directly jump - // to the next one, rather than detouring through the loop condition. - // Also, look into leaving only the "noreturn" loop break condition, and removing - // the iteration based one. Better yet, have an extra entry in the tags array as a - // sentinel, so that exiting the loop is just another jump table prong. - // Related: https://github.com/ziglang/zig/issues/8220 - for (body) |zir_inst| { - map[zir_inst] = switch (tags[zir_inst]) { - .alloc => try sema.zirAlloc(block, zir_inst), - .alloc_mut => try sema.zirAllocMut(block, zir_inst), - .alloc_inferred => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_const)), - .alloc_inferred_mut => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_mut)), - .bitcast_ref => try sema.zirBitcastRef(block, zir_inst), - .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, zir_inst), - .block => try sema.zirBlock(block, zir_inst, false), - .block_comptime => try sema.zirBlock(block, zir_inst, true), - .block_flat => try sema.zirBlockFlat(block, zir_inst, false), - .block_comptime_flat => try sema.zirBlockFlat(block, zir_inst, true), - .@"break" => try sema.zirBreak(block, zir_inst), - .break_void_tok => try sema.zirBreakVoidTok(block, zir_inst), - .breakpoint => try sema.zirBreakpoint(block, zir_inst), - .call => try sema.zirCall(block, zir_inst, .auto), - .call_compile_time => try sema.zirCall(block, zir_inst, .compile_time), - .call_none => try sema.zirCallNone(block, zir_inst), - .coerce_result_ptr => try sema.zirCoerceResultPtr(block, zir_inst), - .compile_error => try sema.zirCompileError(block, zir_inst), - .compile_log => try sema.zirCompileLog(block, zir_inst), - .@"const" => try sema.zirConst(block, zir_inst), - .dbg_stmt_node => try sema.zirDbgStmtNode(block, zir_inst), - .decl_ref => try sema.zirDeclRef(block, zir_inst), - .decl_val => try sema.zirDeclVal(block, zir_inst), + // We use a while(true) loop here to avoid a redundant way of breaking out of + // the loop. The only way to break out of the loop is with a `noreturn` + // instruction. + // TODO: As an optimization, make sure the codegen for these switch prongs + // directly jump to the next one, rather than detouring through the loop + // continue expression. Related: https://github.com/ziglang/zig/issues/8220 + var i: usize = 0; + while (true) : (i += 1) { + const inst = body[i]; + map[inst] = switch (tags[inst]) { .elided => continue, - .ensure_result_used => try sema.zirEnsureResultUsed(block, zir_inst), - .ensure_result_non_error => try sema.zirEnsureResultNonError(block, zir_inst), - .indexable_ptr_len => try sema.zirIndexablePtrLen(block, zir_inst), - .ref => try sema.zirRef(block, zir_inst), - .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, zir_inst), - .ret_ptr => try sema.zirRetPtr(block, zir_inst), - .ret_type => try sema.zirRetType(block, zir_inst), - .store_to_block_ptr => try sema.zirStoreToBlockPtr(block, zir_inst), - .store_to_inferred_ptr => try sema.zirStoreToInferredPtr(block, zir_inst), - .ptr_type_simple => try sema.zirPtrTypeSimple(block, zir_inst), - .ptr_type => try sema.zirPtrType(block, zir_inst), - .store => try sema.zirStore(block, zir_inst), - .set_eval_branch_quota => try sema.zirSetEvalBranchQuota(block, zir_inst), - .str => try sema.zirStr(block, zir_inst), - .int => try sema.zirInt(block, zir_inst), - .int_type => try sema.zirIntType(block, zir_inst), - .loop => try sema.zirLoop(block, zir_inst), - .param_type => try sema.zirParamType(block, zir_inst), - .ptrtoint => try sema.zirPtrtoint(block, zir_inst), - .field_ptr => try sema.zirFieldPtr(block, zir_inst), - .field_val => try sema.zirFieldVal(block, zir_inst), - .field_ptr_named => try sema.zirFieldPtrNamed(block, zir_inst), - .field_val_named => try sema.zirFieldValNamed(block, zir_inst), - .deref_node => try sema.zirDerefNode(block, zir_inst), - .as => try sema.zirAs(block, zir_inst), - .as_node => try sema.zirAsNode(block, zir_inst), - .@"asm" => try sema.zirAsm(block, zir_inst, false), - .asm_volatile => try sema.zirAsm(block, zir_inst, true), - .@"unreachable" => try sema.zirUnreachable(block, zir_inst), - .ret_coerce => try sema.zirRetTok(block, zir_inst, true), - .ret_tok => try sema.zirRetTok(block, zir_inst, false), - .ret_node => try sema.zirRetNode(block, zir_inst), - .fn_type => try sema.zirFnType(block, zir_inst, false), - .fn_type_cc => try sema.zirFnTypeCc(block, zir_inst, false), - .fn_type_var_args => try sema.zirFnType(block, zir_inst, true), - .fn_type_cc_var_args => try sema.zirFnTypeCc(block, zir_inst, true), - .intcast => try sema.zirIntcast(block, zir_inst), - .bitcast => try sema.zirBitcast(block, zir_inst), - .floatcast => try sema.zirFloatcast(block, zir_inst), - .elem_ptr => try sema.zirElemPtr(block, zir_inst), - .elem_ptr_node => try sema.zirElemPtrNode(block, zir_inst), - .elem_val => try sema.zirElemVal(block, zir_inst), - .elem_val_node => try sema.zirElemValNode(block, zir_inst), - .add => try sema.zirArithmetic(block, zir_inst), - .addwrap => try sema.zirArithmetic(block, zir_inst), - .sub => try sema.zirArithmetic(block, zir_inst), - .subwrap => try sema.zirArithmetic(block, zir_inst), + + .add => try sema.zirArithmetic(block, inst), + .addwrap => try sema.zirArithmetic(block, inst), + .alloc => try sema.zirAlloc(block, inst), + .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), + .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), + .alloc_mut => try sema.zirAllocMut(block, inst), + .array_cat => try sema.zirArrayCat(block, inst), + .array_mul => try sema.zirArrayMul(block, inst), + .array_type => try sema.zirArrayType(block, inst), + .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), + .as => try sema.zirAs(block, inst), + .as_node => try sema.zirAsNode(block, inst), + .@"asm" => try sema.zirAsm(block, inst, false), + .asm_volatile => try sema.zirAsm(block, inst, true), + .bit_and => try sema.zirBitwise(block, inst), + .bit_not => try sema.zirBitNot(block, inst), + .bit_or => try sema.zirBitwise(block, inst), + .bitcast => try sema.zirBitcast(block, inst), + .bitcast_ref => try sema.zirBitcastRef(block, inst), + .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst), + .block => try sema.zirBlock(block, inst, false), + .block_comptime => try sema.zirBlock(block, inst, true), + .bool_not => try sema.zirBoolNot(block, inst), + .bool_and => try sema.zirBoolOp(block, inst, false), + .bool_or => try sema.zirBoolOp(block, inst, true), + .bool_br_and => try sema.zirBoolBr(block, inst, false), + .bool_br_or => try sema.zirBoolBr(block, inst, true), + .call => try sema.zirCall(block, inst, .auto), + .call_compile_time => try sema.zirCall(block, inst, .compile_time), + .call_none => try sema.zirCallNone(block, inst), + .cmp_eq => try sema.zirCmp(block, inst, .eq), + .cmp_gt => try sema.zirCmp(block, inst, .gt), + .cmp_gte => try sema.zirCmp(block, inst, .gte), + .cmp_lt => try sema.zirCmp(block, inst, .lt), + .cmp_lte => try sema.zirCmp(block, inst, .lte), + .cmp_neq => try sema.zirCmp(block, inst, .neq), + .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), + .@"const" => try sema.zirConst(block, inst), + .decl_ref => try sema.zirDeclRef(block, inst), + .decl_val => try sema.zirDeclVal(block, inst), + .deref_node => try sema.zirDerefNode(block, inst), + .div => try sema.zirArithmetic(block, inst), + .elem_ptr => try sema.zirElemPtr(block, inst), + .elem_ptr_node => try sema.zirElemPtrNode(block, inst), + .elem_val => try sema.zirElemVal(block, inst), + .elem_val_node => try sema.zirElemValNode(block, inst), + .enum_literal => try sema.zirEnumLiteral(block, inst), + .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst), + .err_union_code => try sema.zirErrUnionCode(block, inst), + .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), + .err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true), + .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, true), + .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst, false), + .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false), + .error_set => try sema.zirErrorSet(block, inst), + .error_union_type => try sema.zirErrorUnionType(block, inst), + .error_value => try sema.zirErrorValue(block, inst), + .field_ptr => try sema.zirFieldPtr(block, inst), + .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), + .field_val => try sema.zirFieldVal(block, inst), + .field_val_named => try sema.zirFieldValNamed(block, inst), + .floatcast => try sema.zirFloatcast(block, inst), + .fn_type => try sema.zirFnType(block, inst, false), + .fn_type_cc => try sema.zirFnTypeCc(block, inst, false), + .fn_type_cc_var_args => try sema.zirFnTypeCc(block, inst, true), + .fn_type_var_args => try sema.zirFnType(block, inst, true), + .import => try sema.zirImport(block, inst), + .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), + .int => try sema.zirInt(block, inst), + .int_type => try sema.zirIntType(block, inst), + .intcast => try sema.zirIntcast(block, inst), + .is_err => try sema.zirIsErr(block, inst), + .is_err_ptr => try sema.zirIsErrPtr(block, inst), + .is_non_null => try sema.zirIsNull(block, inst, true), + .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), + .is_null => try sema.zirIsNull(block, inst, false), + .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), + .merge_error_sets => try sema.zirMergeErrorSets(block, inst), + .mod_rem => try sema.zirArithmetic(block, inst), + .mul => try sema.zirArithmetic(block, inst), + .mulwrap => try sema.zirArithmetic(block, inst), .negate => @panic("TODO"), .negate_wrap => @panic("TODO"), - .mul => try sema.zirArithmetic(block, zir_inst), - .mulwrap => try sema.zirArithmetic(block, zir_inst), - .div => try sema.zirArithmetic(block, zir_inst), - .mod_rem => try sema.zirArithmetic(block, zir_inst), - .array_cat => try sema.zirArrayCat(block, zir_inst), - .array_mul => try sema.zirArrayMul(block, zir_inst), - .bit_and => try sema.zirBitwise(block, zir_inst), - .bit_not => try sema.zirBitNot(block, zir_inst), - .bit_or => try sema.zirBitwise(block, zir_inst), - .xor => try sema.zirBitwise(block, zir_inst), - .shl => try sema.zirShl(block, zir_inst), - .shr => try sema.zirShr(block, zir_inst), - .cmp_lt => try sema.zirCmp(block, zir_inst, .lt), - .cmp_lte => try sema.zirCmp(block, zir_inst, .lte), - .cmp_eq => try sema.zirCmp(block, zir_inst, .eq), - .cmp_gte => try sema.zirCmp(block, zir_inst, .gte), - .cmp_gt => try sema.zirCmp(block, zir_inst, .gt), - .cmp_neq => try sema.zirCmp(block, zir_inst, .neq), - .condbr => try sema.zirCondbr(block, zir_inst), - .is_null => try sema.zirIsNull(block, zir_inst, false), - .is_non_null => try sema.zirIsNull(block, zir_inst, true), - .is_null_ptr => try sema.zirIsNullPtr(block, zir_inst, false), - .is_non_null_ptr => try sema.zirIsNullPtr(block, zir_inst, true), - .is_err => try sema.zirIsErr(block, zir_inst), - .is_err_ptr => try sema.zirIsErrPtr(block, zir_inst), - .bool_not => try sema.zirBoolNot(block, zir_inst), - .typeof => try sema.zirTypeof(block, zir_inst), - .typeof_peer => try sema.zirTypeofPeer(block, zir_inst), - .optional_type => try sema.zirOptionalType(block, zir_inst), - .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, zir_inst), - .optional_payload_safe => try sema.zirOptionalPayload(block, zir_inst, true), - .optional_payload_unsafe => try sema.zirOptionalPayload(block, zir_inst, false), - .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, true), - .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, false), - .err_union_payload_safe => try sema.zirErrUnionPayload(block, zir_inst, true), - .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, zir_inst, false), - .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, true), - .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, false), - .err_union_code => try sema.zirErrUnionCode(block, zir_inst), - .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, zir_inst), - .ensure_err_payload_void => try sema.zirEnsureErrPayloadVoid(block, zir_inst), - .array_type => try sema.zirArrayType(block, zir_inst), - .array_type_sentinel => try sema.zirArrayTypeSentinel(block, zir_inst), - .enum_literal => try sema.zirEnumLiteral(block, zir_inst), - .enum_literal_small => try sema.zirEnumLiteralSmall(block, zir_inst), - .merge_error_sets => try sema.zirMergeErrorSets(block, zir_inst), - .error_union_type => try sema.zirErrorUnionType(block, zir_inst), - .error_set => try sema.zirErrorSet(block, zir_inst), - .error_value => try sema.zirErrorValue(block, zir_inst), - .slice_start => try sema.zirSliceStart(block, zir_inst), - .slice_end => try sema.zirSliceEnd(block, zir_inst), - .slice_sentinel => try sema.zirSliceSentinel(block, zir_inst), - .import => try sema.zirImport(block, zir_inst), - .bool_and => try sema.zirBoolOp(block, zir_inst, false), - .bool_or => try sema.zirBoolOp(block, zir_inst, true), + .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), + .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), + .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), + .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), + .optional_type => try sema.zirOptionalType(block, inst), + .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, inst), + .param_type => try sema.zirParamType(block, inst), + .ptr_type => try sema.zirPtrType(block, inst), + .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), + .ptrtoint => try sema.zirPtrtoint(block, inst), + .ref => try sema.zirRef(block, inst), + .ret_ptr => try sema.zirRetPtr(block, inst), + .ret_type => try sema.zirRetType(block, inst), + .shl => try sema.zirShl(block, inst), + .shr => try sema.zirShr(block, inst), + .slice_end => try sema.zirSliceEnd(block, inst), + .slice_sentinel => try sema.zirSliceSentinel(block, inst), + .slice_start => try sema.zirSliceStart(block, inst), + .str => try sema.zirStr(block, inst), + .sub => try sema.zirArithmetic(block, inst), + .subwrap => try sema.zirArithmetic(block, inst), + .typeof => try sema.zirTypeof(block, inst), + .typeof_peer => try sema.zirTypeofPeer(block, inst), + .xor => try sema.zirBitwise(block, inst), // TODO - //.switchbr => try sema.zirSwitchBr(block, zir_inst, false), - //.switchbr_ref => try sema.zirSwitchBr(block, zir_inst, true), - //.switch_range => try sema.zirSwitchRange(block, zir_inst), + //.switchbr => try sema.zirSwitchBr(block, inst, false), + //.switchbr_ref => try sema.zirSwitchBr(block, inst, true), + //.switch_range => try sema.zirSwitchRange(block, inst), + + // Instructions that we know to *always* be noreturn based solely on their tag. + // These functions match the return type of analyzeBody so that we can + // tail call them here. + .condbr => return sema.zirCondbr(block, inst), + .@"break" => return sema.zirBreak(block, inst), + .break_void_tok => return sema.zirBreakVoidTok(block, inst), + .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand, + .compile_error => return sema.zirCompileError(block, inst), + .ret_coerce => return sema.zirRetTok(block, inst, true), + .ret_node => return sema.zirRetNode(block, inst), + .ret_tok => return sema.zirRetTok(block, inst, false), + .@"unreachable" => return sema.zirUnreachable(block, inst), + .loop => return sema.zirLoop(block, inst), + + // Instructions that we know can *never* be noreturn based solely on + // their tag. We avoid needlessly checking if they are noreturn and + // continue the loop. + // We also know that they cannot be referenced later, so we avoid + // putting them into the map. + .breakpoint => { + try sema.zirBreakpoint(block, inst); + continue; + }, + .dbg_stmt_node => { + try sema.zirDbgStmtNode(block, inst); + continue; + }, + .ensure_err_payload_void => { + try sema.zirEnsureErrPayloadVoid(block, inst); + continue; + }, + .ensure_result_non_error => { + try sema.zirEnsureResultNonError(block, inst); + continue; + }, + .ensure_result_used => { + try sema.zirEnsureResultUsed(block, inst); + continue; + }, + .compile_log => { + try sema.zirCompileLog(block, inst); + continue; + }, + .set_eval_branch_quota => { + try sema.zirSetEvalBranchQuota(block, inst); + continue; + }, + .store => { + try sema.zirStore(block, inst); + continue; + }, + .store_to_block_ptr => { + try sema.zirStoreToBlockPtr(block, inst); + continue; + }, + .store_to_inferred_ptr => { + try sema.zirStoreToInferredPtr(block, inst); + continue; + }, + .resolve_inferred_alloc => { + try sema.zirResolveInferredAlloc(block, inst); + continue; + }, }; - if (map[zir_inst].ty.isNoReturn()) { - break; - } + if (map[inst].ty.isNoReturn()) + return always_noreturn; } } @@ -392,7 +450,7 @@ fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError return sema.mod.constType(sema.arena, src, ret_type); } -fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -400,12 +458,12 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) I const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); switch (operand.ty.zigTypeTag()) { - .Void, .NoReturn => return sema.mod.constVoid(sema.arena, .unneeded), + .Void, .NoReturn => return, else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}), } } -fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -414,7 +472,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Inde const src = inst_data.src(); switch (operand.ty.zigTypeTag()) { .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}), - else => return sema.mod.constVoid(sema.arena, .unneeded), + else => return, } } @@ -508,11 +566,7 @@ fn zirAllocInferred( return result; } -fn zirResolveInferredAlloc( - sema: *Sema, - block: *Scope.Block, - inst: zir.Inst.Index, -) InnerError!*Inst { +fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -536,15 +590,9 @@ fn zirResolveInferredAlloc( // Change it to a normal alloc. ptr.ty = final_ptr_ty; ptr.tag = .alloc; - - return sema.mod.constVoid(sema.arena, .unneeded); } -fn zirStoreToBlockPtr( - sema: *Sema, - block: *Scope.Block, - inst: zir.Inst.Index, -) InnerError!*Inst { +fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -560,11 +608,7 @@ fn zirStoreToBlockPtr( return sema.storePtr(block, src, bitcasted_ptr, value); } -fn zirStoreToInferredPtr( - sema: *Sema, - block: *Scope.Block, - inst: zir.Inst.Index, -) InnerError!*Inst { +fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -583,21 +627,16 @@ fn zirStoreToInferredPtr( return sema.storePtr(block, src, bitcasted_ptr, value); } -fn zirSetEvalBranchQuota( - sema: *Sema, - block: *Scope.Block, - inst: zir.Inst.Index, -) InnerError!*Inst { +fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); try sema.requireFunctionBlock(block, src); const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32); if (sema.branch_quota < quota) sema.branch_quota = quota; - return sema.mod.constVoid(sema.arena, .unneeded); } -fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -677,7 +716,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int); } -fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { const tracy = trace(@src()); defer tracy.end(); @@ -688,7 +727,7 @@ fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inner return sema.mod.fail(&block.base, src, "{s}", .{msg}); } -fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { var managed = sema.mod.compile_log_text.toManaged(sema.gpa); defer sema.mod.compile_log_text = managed.moveToUnmanaged(); const writer = managed.writer(); @@ -711,10 +750,9 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr if (!gop.found_existing) { gop.entry.value = inst_data.src().toSrcLoc(&block.base); } - return sema.mod.constVoid(sema.arena, .unneeded); } -fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -746,41 +784,13 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE }; defer child_block.instructions.deinit(sema.gpa); - try sema.analyzeBody(&child_block, body); + _ = try sema.analyzeBody(&child_block, body); // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. try parent_block.instructions.append(sema.gpa, &loop_inst.base); loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items) }; - return &loop_inst.base; -} - -fn zirBlockFlat( - sema: *Sema, - parent_block: *Scope.Block, - inst: zir.Inst.Index, - is_comptime: bool, -) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); - const body = sema.code.extra[extra.end..][0..extra.data.operands_len]; - - var child_block = parent_block.makeSubBlock(); - defer child_block.instructions.deinit(sema.gpa); - child_block.is_comptime = child_block.is_comptime or is_comptime; - - try sema.analyzeBody(&child_block, body); - - // Move the analyzed instructions into the parent block arena. - const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items); - try parent_block.instructions.appendSlice(sema.gpa, copied_instructions); - - // The result of a flat block is the last instruction. - return sema.inst_map[body[body.len - 1]]; + return always_noreturn; } fn zirBlock( @@ -794,8 +804,8 @@ fn zirBlock( const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); - const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); - const body = sema.code.extra[extra.end..][0..extra.data.operands_len]; + const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; // Reserve space for a Block instruction so that generated Break instructions can // point to it, even if it doesn't end up getting used because the code ends up being @@ -833,7 +843,7 @@ fn zirBlock( defer merges.results.deinit(sema.gpa); defer merges.br_list.deinit(sema.gpa); - try sema.analyzeBody(&child_block, body); + _ = try sema.analyzeBody(&child_block, body); return sema.analyzeBlockBody(parent_block, &child_block, merges); } @@ -919,17 +929,17 @@ fn analyzeBlockBody( return &merges.block_inst.base; } -fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); const src_node = sema.code.instructions.items(.data)[inst].node; const src: LazySrcLoc = .{ .node_offset = src_node }; try sema.requireRuntimeBlock(block, src); - return block.addNoOp(src, Type.initTag(.void), .breakpoint); + _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); } -fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { const tracy = trace(@src()); defer tracy.end(); @@ -939,7 +949,7 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!* return sema.analyzeBreak(block, sema.src, zir_block, operand); } -fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { const tracy = trace(@src()); defer tracy.end(); @@ -955,7 +965,7 @@ fn analyzeBreak( src: LazySrcLoc, zir_block: zir.Inst.Index, operand: *Inst, -) InnerError!*Inst { +) InnerError!zir.Inst.Ref { var block = start_block; while (true) { if (block.label) |*label| { @@ -981,26 +991,24 @@ fn analyzeBreak( try block.instructions.append(sema.gpa, &br.base); try label.merges.results.append(sema.gpa, operand); try label.merges.br_list.append(sema.gpa, br); - return &br.base; + return always_noreturn; } } block = block.parent.?; } } -fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); - if (block.is_comptime) { - return sema.mod.constVoid(sema.arena, .unneeded); - } + if (block.is_comptime) return; const src_node = sema.code.instructions.items(.data)[inst].node; const src: LazySrcLoc = .{ .node_offset = src_node }; const src_loc = src.toSrcLoc(&block.base); const abs_byte_off = try src_loc.byteOffset(); - return block.addDbgStmt(src, abs_byte_off); + _ = try block.addDbgStmt(src, abs_byte_off); } fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { @@ -1185,7 +1193,7 @@ fn analyzeCall( // This will have return instructions analyzed as break instructions to // the block_inst above. - try sema.root(&child_block); + _ = try sema.root(&child_block); return sema.analyzeBlockBody(block, &child_block, merges); } @@ -1638,7 +1646,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) In return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); } -fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -1650,7 +1658,6 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Inde if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) { return sema.mod.fail(&block.base, src, "expression value is ignored", .{}); } - return sema.mod.constVoid(sema.arena, .unneeded); } fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst { @@ -2067,7 +2074,7 @@ fn zirSwitchBr( parent_block: *Scope.Block, inst: zir.Inst.Index, ref: bool, -) InnerError!*Inst { +) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2087,18 +2094,18 @@ fn zirSwitchBr( const item = try sema.resolveConstValue(parent_block, case_src, casted); if (target_val.eql(item)) { - try sema.analyzeBody(parent_block, case.body); - return sema.mod.constNoReturn(sema.arena, inst.base.src); + _ = try sema.analyzeBody(parent_block, case.body); + return always_noreturn; } } - try sema.analyzeBody(parent_block, inst.positionals.else_body); - return sema.mod.constNoReturn(sema.arena, inst.base.src); + _ = try sema.analyzeBody(parent_block, inst.positionals.else_body); + return always_noreturn; } if (inst.positionals.cases.len == 0) { // no cases just analyze else_branch - try sema.analyzeBody(parent_block, inst.positionals.else_body); - return sema.mod.constNoReturn(sema.arena, inst.base.src); + _ = try sema.analyzeBody(parent_block, inst.positionals.else_body); + return always_noreturn; } try sema.requireRuntimeBlock(parent_block, inst.base.src); @@ -2122,7 +2129,7 @@ fn zirSwitchBr( const casted = try sema.coerce(block, target.ty, resolved, resolved_src); const item = try sema.resolveConstValue(parent_block, case_src, casted); - try sema.analyzeBody(&case_block, case.body); + _ = try sema.analyzeBody(&case_block, case.body); cases[i] = .{ .item = item, @@ -2131,7 +2138,7 @@ fn zirSwitchBr( } case_block.instructions.items.len = 0; - try sema.analyzeBody(&case_block, inst.positionals.else_body); + _ = try sema.analyzeBody(&case_block, inst.positionals.else_body); const else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), @@ -2719,6 +2726,75 @@ fn zirBoolOp( return block.addBinOp(src, bool_type, tag, lhs, rhs); } +fn zirBoolBr( + sema: *Sema, + parent_block: *Scope.Block, + inst: zir.Inst.Index, + is_bool_or: bool, +) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + const inst_data = sema.code.instructions.items(.data)[inst].bool_br; + const src: LazySrcLoc = .unneeded; + const lhs = try sema.resolveInst(inst_data.lhs); + const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + + if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| { + if (lhs_val.toBool() == is_bool_or) { + return sema.mod.constBool(sema.arena, src, is_bool_or); + } + // comptime-known left-hand side. No need for a block here; the result + // is simply the rhs expression. Here we rely on there only being 1 + // break instruction (`break_flat`). + const zir_inst_ref = try sema.analyzeBody(parent_block, body); + return sema.resolveInst(zir_inst_ref); + } + + const block_inst = try sema.arena.create(Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = Inst.Block.base_tag, + .ty = Type.initTag(.bool), + .src = src, + }, + .body = undefined, + }; + + var child_block = parent_block.makeSubBlock(); + defer child_block.instructions.deinit(sema.gpa); + + var then_block = child_block.makeSubBlock(); + defer then_block.instructions.deinit(sema.gpa); + + var else_block = child_block.makeSubBlock(); + defer else_block.instructions.deinit(sema.gpa); + + const lhs_block = if (is_bool_or) &then_block else &else_block; + const rhs_block = if (is_bool_or) &else_block else &then_block; + + const lhs_result = try sema.mod.constInst(sema.arena, src, .{ + .ty = Type.initTag(.bool), + .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false), + }); + _ = try lhs_block.addBr(src, block_inst, lhs_result); + + const rhs_result_zir_ref = try sema.analyzeBody(rhs_block, body); + const rhs_result = try sema.resolveInst(rhs_result_zir_ref); + _ = try rhs_block.addBr(src, block_inst, rhs_result); + + const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) }; + const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) }; + _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body); + + block_inst.body = .{ + .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items), + }; + try parent_block.instructions.append(sema.gpa, &block_inst.base); + return &block_inst.base; +} + fn zirIsNull( sema: *Sema, block: *Scope.Block, @@ -2770,7 +2846,11 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro return sema.analyzeIsErr(block, src, loaded); } -fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirCondbr( + sema: *Sema, + parent_block: *Scope.Block, + inst: zir.Inst.Index, +) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2787,8 +2867,8 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| { const body = if (cond_val.toBool()) then_body else else_body; - try sema.analyzeBody(parent_block, body); - return sema.mod.constNoReturn(sema.arena, src); + _ = try sema.analyzeBody(parent_block, body); + return always_noreturn; } var true_block: Scope.Block = .{ @@ -2800,7 +2880,7 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne .is_comptime = parent_block.is_comptime, }; defer true_block.instructions.deinit(sema.gpa); - try sema.analyzeBody(&true_block, then_body); + _ = try sema.analyzeBody(&true_block, then_body); var false_block: Scope.Block = .{ .parent = parent_block, @@ -2811,14 +2891,15 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne .is_comptime = parent_block.is_comptime, }; defer false_block.instructions.deinit(sema.gpa); - try sema.analyzeBody(&false_block, else_body); + _ = try sema.analyzeBody(&false_block, else_body); const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, true_block.instructions.items) }; const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, false_block.instructions.items) }; - return parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body); + _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body); + return always_noreturn; } -fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2830,7 +2911,8 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE if (safety_check and block.wantSafety()) { return sema.safetyPanic(block, src, .unreach); } else { - return block.addNoOp(src, Type.initTag(.noreturn), .unreach); + _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); + return always_noreturn; } } @@ -2839,7 +2921,7 @@ fn zirRetTok( block: *Scope.Block, inst: zir.Inst.Index, need_coercion: bool, -) InnerError!*Inst { +) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2850,7 +2932,7 @@ fn zirRetTok( return sema.analyzeRet(block, operand, src, need_coercion); } -fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { +fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2867,22 +2949,24 @@ fn analyzeRet( operand: *Inst, src: LazySrcLoc, need_coercion: bool, -) InnerError!*Inst { +) InnerError!zir.Inst.Ref { if (block.inlining) |inlining| { // We are inlining a function call; rewrite the `ret` as a `break`. try inlining.merges.results.append(sema.gpa, operand); - const br = try block.addBr(src, inlining.merges.block_inst, operand); - return &br.base; + _ = try block.addBr(src, inlining.merges.block_inst, operand); + return always_noreturn; } if (need_coercion) { if (sema.func) |func| { const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty; const casted_operand = try sema.coerce(block, fn_ty.fnReturnType(), operand, src); - return block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand); + _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand); + return always_noreturn; } } - return block.addUnOp(src, Type.initTag(.noreturn), .ret, operand); + _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand); + return always_noreturn; } fn floatOpAllowed(tag: zir.Inst.Tag) bool { @@ -3051,10 +3135,11 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: try parent_block.instructions.append(sema.gpa, &block_inst.base); } -fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !*Inst { +fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Ref { // TODO Once we have a panic function to call, call it here instead of breakpoint. _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); - return block.addNoOp(src, Type.initTag(.noreturn), .unreach); + _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); + return always_noreturn; } fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { @@ -3405,20 +3490,20 @@ fn storePtr( src: LazySrcLoc, ptr: *Inst, uncasted_value: *Inst, -) !*Inst { +) !void { if (ptr.ty.isConstPtr()) return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); const elem_ty = ptr.ty.elemType(); const value = try sema.coerce(block, elem_ty, uncasted_value, uncasted_value.src); if (elem_ty.onePossibleValue() != null) - return sema.mod.constVoid(sema.arena, .unneeded); + return; // TODO handle comptime pointer writes // TODO handle if the element type requires comptime try sema.requireRuntimeBlock(block, src); - return block.addBinOp(src, Type.initTag(.void), .store, ptr, value); + _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value); } fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { diff --git a/src/astgen.zig b/src/astgen.zig index 81382c73cc..bc1349048f 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -370,8 +370,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat), .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul), - .bool_and => return boolBinOp(mod, scope, rl, node, .bool_and), - .bool_or => return boolBinOp(mod, scope, rl, node, .bool_or), + .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and), + .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or), .bool_not => return boolNot(mod, scope, rl, node), .bit_not => return bitNot(mod, scope, rl, node), @@ -425,8 +425,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .field_access => return fieldAccess(mod, scope, rl, node), .float_literal => return floatLiteral(mod, scope, rl, node), - .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)), - .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)), + .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)), + .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)), .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)), .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)), @@ -923,6 +923,7 @@ fn labeledBlockExpr( // so that break statements can reference it. const gz = parent_scope.getGenZir(); const block_inst = try gz.addBlock(zir_tag, block_node); + try gz.instructions.append(mod.gpa, block_inst); var block_scope: Scope.GenZir = .{ .parent = parent_scope, @@ -946,8 +947,6 @@ fn labeledBlockExpr( return mod.failTok(parent_scope, label_token, "unused block label", .{}); } - try gz.instructions.append(mod.gpa, block_inst); - const zir_tags = gz.zir_code.instructions.items(.tag); const zir_datas = gz.zir_code.instructions.items(.data); @@ -961,7 +960,7 @@ fn labeledBlockExpr( } // TODO technically not needed since we changed the tag to break_void but // would be better still to elide the ones that are in this list. - try copyBodyNoEliding(block_inst, block_scope); + try block_scope.setBlockBody(block_inst); return gz.zir_code.ref_start_index + block_inst; }, @@ -975,7 +974,7 @@ fn labeledBlockExpr( // TODO technically not needed since we changed the tag to elided but // would be better still to elide the ones that are in this list. } - try copyBodyNoEliding(block_inst, block_scope); + try block_scope.setBlockBody(block_inst); const block_ref = gz.zir_code.ref_start_index + block_inst; switch (rl) { .ref => return block_ref, @@ -1635,8 +1634,8 @@ fn finishThenElseBlock( }); } assert(!strat.elide_store_to_block_ptr_instructions); - try copyBodyNoEliding(then_body, then_scope.*); - try copyBodyNoEliding(else_body, else_scope.*); + try then_scope.setBlockBody(then_body); + try else_scope.setBlockBody(else_body); return &main_block.base; }, .break_operand => { @@ -1662,8 +1661,8 @@ fn finishThenElseBlock( try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*); try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*); } else { - try copyBodyNoEliding(then_body, then_scope.*); - try copyBodyNoEliding(else_body, else_scope.*); + try then_scope.setBlockBody(then_body); + try else_scope.setBlockBody(else_body); } switch (rl) { .ref => return &main_block.base, @@ -1801,95 +1800,49 @@ fn boolBinOp( mod: *Module, scope: *Scope, rl: ResultLoc, - infix_node: ast.Node.Index, - kind: enum { bool_and, bool_or }, + node: ast.Node.Index, + zir_tag: zir.Inst.Tag, ) InnerError!zir.Inst.Ref { - const tree = scope.tree(); - const node_datas = tree.nodes.items(.data); - const bool_type = @enumToInt(zir.Const.bool_type); const gz = scope.getGenZir(); + const node_datas = gz.tree().nodes.items(.data); + const bool_type = @enumToInt(zir.Const.bool_type); - const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[infix_node].lhs); - - const block_inst = try gz.addBlock(.block, infix_node); - const block_ref = gz.zir_code.ref_start_index + block_inst; - var block_scope: Scope.GenZir = .{ - .parent = scope, - .zir_code = gz.zir_code, - .force_comptime = gz.force_comptime, - }; - defer block_scope.instructions.deinit(mod.gpa); + const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[node].lhs); + const bool_br = try gz.addBoolBr(zir_tag, lhs); var rhs_scope: Scope.GenZir = .{ - .parent = &block_scope.base, + .parent = scope, .zir_code = gz.zir_code, .force_comptime = gz.force_comptime, }; defer rhs_scope.instructions.deinit(mod.gpa); - const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[infix_node].rhs); - _ = try rhs_scope.addBin(.@"break", block_inst, rhs); - - // TODO: should we have zir.Const instructions for `break true` and `break false`? - const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); - const break_true_false_ref = new_index + gz.zir_code.ref_start_index; - try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ .tag = .@"break", .data = .{ .bin = .{ - .lhs = block_inst, - .rhs = switch (kind) { - .bool_and => @enumToInt(zir.Const.bool_false), - .bool_or => @enumToInt(zir.Const.bool_true), - }, - } } }); - - switch (kind) { - // if lhs // AND - // break rhs - // else - // break false - .bool_and => _ = try block_scope.addCondBr( - lhs, - rhs_scope.instructions.items, - &[_]zir.Inst.Ref{break_true_false_ref}, - infix_node, - ), - // if lhs // OR - // break true - // else - // break rhs - .bool_or => _ = try block_scope.addCondBr( - lhs, - &[_]zir.Inst.Ref{break_true_false_ref}, - rhs_scope.instructions.items, - infix_node, - ), - } + const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[node].rhs); + _ = try rhs_scope.addUnNode(.break_flat, rhs, node); + try rhs_scope.setBoolBrBody(bool_br); - try gz.instructions.append(mod.gpa, block_inst); - try copyBodyNoEliding(block_inst, block_scope); - - return rvalue(mod, scope, rl, block_ref, infix_node); + const block_ref = gz.zir_code.ref_start_index + bool_br; + return rvalue(mod, scope, rl, block_ref, node); } fn ifExpr( mod: *Module, scope: *Scope, rl: ResultLoc, + node: ast.Node.Index, if_full: ast.full.If, ) InnerError!zir.Inst.Ref { if (true) @panic("TODO update for zir-memory-layout"); + const parent_gz = scope.getGenZir(); var block_scope: Scope.GenZir = .{ .parent = scope, - .decl = scope.ownerDecl().?, - .arena = scope.arena(), + .zir_code = parent_gz.zir_code, .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&block_scope, rl); defer block_scope.instructions.deinit(mod.gpa); - const tree = scope.tree(); - const main_tokens = tree.nodes.items(.main_token); - - const if_src = token_starts[if_full.ast.if_token]; + const tree = parent_gz.tree(); const cond = c: { // TODO https://github.com/ziglang/zig/issues/7929 @@ -1898,23 +1851,16 @@ fn ifExpr( } else if (if_full.payload_token) |payload_token| { return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); } else { - const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.bool_type), - }); - break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr); + const bool_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.bool_type) }; + break :c try expr(mod, &block_scope.base, bool_rl, if_full.ast.cond_expr); } }; - const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{ - .condition = cond, - .then_body = undefined, // populated below - .else_body = undefined, // populated below - }, .{}); + const condbr = try block_scope.addCondBr(node); - const block = try addZIRInstBlock(mod, scope, if_src, .block, .{ - .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items), - }); + const block = try parent_gz.addBlock(.block, node); + try parent_gz.instructions.append(mod.gpa, block); + try block_scope.setBlockBody(block); const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)]; var then_scope: Scope.GenZir = .{ @@ -1990,12 +1936,6 @@ fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZir) assert(dst_index == body.instructions.len); } -fn copyBodyNoEliding(block_inst: zir.Inst.Index, gz: Module.Scope.GenZir) !void { - const zir_datas = gz.zir_code.instructions.items(.data); - zir_datas[block_inst].pl_node.payload_index = @intCast(u32, gz.zir_code.extra.items.len); - try gz.zir_code.extra.appendSlice(gz.zir_code.gpa, gz.instructions.items); -} - fn whileExpr( mod: *Module, scope: *Scope, diff --git a/src/zir.zig b/src/zir.zig index 12ad5fabb0..a057e0df2e 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -99,11 +99,7 @@ pub const Code = struct { try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name }); const root_body = code.extra[code.root_start..][0..code.root_len]; - for (root_body) |inst| { - try stderr.print(" %{d} ", .{inst}); - try writer.writeInstToStream(stderr, inst); - try stderr.writeByte('\n'); - } + try writer.writeBody(stderr, root_body); try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name }); } @@ -451,16 +447,10 @@ pub const Inst = struct { /// Bitwise OR. `|` bit_or, /// A labeled block of code, which can return a value. - /// Uses the `pl_node` union field. Payload is `MultiOp`. + /// Uses the `pl_node` union field. Payload is `Block`. block, - /// A block of code, which can return a value. There are no instructions that break out of - /// this block; it is implied that the final instruction is the result. - /// Uses the `pl_node` union field. Payload is `MultiOp`. - block_flat, /// Same as `block` but additionally makes the inner instructions execute at comptime. block_comptime, - /// Same as `block_flat` but additionally makes the inner instructions execute at comptime. - block_comptime_flat, /// Boolean AND. See also `bit_and`. /// Uses the `pl_node` union field. Payload is `Bin`. bool_and, @@ -470,6 +460,14 @@ pub const Inst = struct { /// 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 `bin` union field: `lhs` is `Index` to the block (*not* `Ref`!), /// `rhs` is operand. @@ -480,6 +478,12 @@ pub const Inst = struct { /// Uses the `un_tok` union field. /// Note that the block operand is a `Index`, not `Ref`. break_void_tok, + /// Return a value from a block. This is a special form that is only valid + /// when there is exactly 1 break from a block (this one). This instruction + /// allows using the return value from `Sema.analyzeBody`. The block is + /// assumed to be the direct parent of this instruction. + /// Uses the `un_node` union field. The AST node is unused. + break_flat, /// Uses the `node` union field. breakpoint, /// Function call with modifier `.auto`. @@ -637,7 +641,7 @@ pub const Inst = struct { /// A labeled block of code that loops forever. At the end of the body it is implied /// to repeat; no explicit "repeat" instruction terminates loop bodies. /// Uses the `pl_node` field. The AST node is either a for loop or while loop. - /// The payload is `MultiOp`. + /// The payload is `Block`. loop, /// Merge two error sets into one, `E1 || E2`. merge_error_sets, @@ -886,9 +890,9 @@ pub const Inst = struct { .bitcast_result_ptr, .bit_or, .block, - .block_flat, .block_comptime, - .block_comptime_flat, + .bool_br_and, + .bool_br_or, .bool_not, .bool_and, .bool_or, @@ -988,6 +992,7 @@ pub const Inst = struct { .@"break", .break_void_tok, + .break_flat, .condbr, .compile_error, .ret_node, @@ -1127,6 +1132,11 @@ pub const Inst = struct { /// For `fn_type_cc` this points to `FnTypeCc` in `extra`. payload_index: u32, }, + bool_br: struct { + lhs: Ref, + /// Points to a `Block`. + payload_index: u32, + }, param_type: struct { callee: Ref, param_index: u32, @@ -1191,6 +1201,12 @@ pub const Inst = 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 { @@ -1342,6 +1358,7 @@ const Writer = struct { .err_union_payload_unsafe_ptr, .err_union_code, .err_union_code_ptr, + .break_flat, => try self.writeUnNode(stream, inst), .break_void_tok, @@ -1358,6 +1375,10 @@ const Writer = struct { .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), .@"const" => try self.writeConst(stream, inst), .param_type => try self.writeParamType(stream, inst), @@ -1370,9 +1391,7 @@ const Writer = struct { .@"asm", .asm_volatile, .block, - .block_flat, .block_comptime, - .block_comptime_flat, .call, .call_compile_time, .compile_log, @@ -1618,6 +1637,19 @@ const Writer = struct { return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc); } + 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 writeFnTypeCc( self: *Writer, stream: anytype, @@ -1713,4 +1745,13 @@ const Writer = struct { @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'); + } + } }; |
