From 1294ebe1f5eaca1f11d68284d1b96419d53253be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 14 Jul 2021 22:44:57 -0700 Subject: [PATCH] Sema: AIR memory layout reworking for noreturn instructions --- src/Module.zig | 2 +- src/Sema.zig | 88 +++++++++++++++++++++++--------------------------- 2 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 94d8b63744..fb514ccbd2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1255,7 +1255,7 @@ pub const Scope = struct { pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { return block.addInst(.{ .tag = tag, - .data = .no_op, + .data = .{ .no_op = {} }, }); } diff --git a/src/Sema.zig b/src/Sema.zig index b4e8cd5af5..d33d5bd49b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -381,20 +381,20 @@ pub fn analyzeBody( .sub => try sema.zirArithmetic(block, inst), .subwrap => try sema.zirArithmetic(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. - //.break_inline => return inst, - //.condbr => return sema.zirCondbr(block, inst), - //.@"break" => return sema.zirBreak(block, inst), - //.compile_error => return sema.zirCompileError(block, inst), - //.ret_coerce => return sema.zirRetCoerce(block, inst, true), - //.ret_node => return sema.zirRetNode(block, inst), - //.ret_err_value => return sema.zirRetErrValue(block, inst), - //.@"unreachable" => return sema.zirUnreachable(block, inst), - //.repeat => return sema.zirRepeat(block, inst), - //.panic => return sema.zirPanic(block, inst), - //// zig fmt: on + // 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. + .break_inline => return inst, + .condbr => return sema.zirCondbr(block, inst), + .@"break" => return sema.zirBreak(block, inst), + .compile_error => return sema.zirCompileError(block, inst), + .ret_coerce => return sema.zirRetCoerce(block, inst, true), + .ret_node => return sema.zirRetNode(block, inst), + .ret_err_value => return sema.zirRetErrValue(block, inst), + .@"unreachable" => return sema.zirUnreachable(block, inst), + .repeat => return sema.zirRepeat(block, inst), + .panic => return sema.zirPanic(block, inst), + // zig fmt: on //// Instructions that we know can *never* be noreturn based solely on //// their tag. We avoid needlessly checking if they are noreturn and @@ -534,7 +534,7 @@ pub fn analyzeBody( return break_inst; } }, - else => @panic("TODO finish updating Sema for AIR memory layout changes and then remove this else prong"), + else => |t| @panic(@tagName(t)), }; if (sema.getTypeOf(air_inst).isNoReturn()) return always_noreturn; @@ -2128,7 +2128,6 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) Compil defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].@"break"; - const src = sema.src; const operand = sema.resolveInst(inst_data.operand); const zir_block = inst_data.block_inst; @@ -2136,26 +2135,9 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) Compil while (true) { if (block.label) |label| { if (label.zir_block == zir_block) { - // Here we add a br instruction, but we over-allocate a little bit - // (if necessary) to make it possible to convert the instruction into - // a br_block_flat instruction later. - const br = @ptrCast(*Inst.Br, try sema.arena.alignedAlloc( - u8, - Inst.convertable_br_align, - Inst.convertable_br_size, - )); - br.* = .{ - .base = .{ - .tag = .br, - .ty = Type.initTag(.noreturn), - .src = src, - }, - .operand = operand, - .block = label.merges.block_inst, - }; - try start_block.instructions.append(sema.gpa, &br.base); + const br_ref = try start_block.addBr(label.merges.block_inst, operand); try label.merges.results.append(sema.gpa, operand); - try label.merges.br_list.append(sema.gpa, br); + try label.merges.br_list.append(sema.gpa, refToIndex(br_ref).?); return inst; } } @@ -5391,25 +5373,35 @@ fn zirCondbr( return always_noreturn; } + const gpa = sema.gpa; + + // We'll re-use the sub block to save on memory bandwidth, and yank out the + // instructions array in between using it for the then block and else block. var sub_block = parent_block.makeSubBlock(); sub_block.runtime_loop = null; - sub_block.runtime_cond = cond.src; + sub_block.runtime_cond = cond_src; sub_block.runtime_index += 1; - defer sub_block.instructions.deinit(sema.gpa); + defer sub_block.instructions.deinit(gpa); _ = try sema.analyzeBody(&sub_block, then_body); - const air_then_body: ir.Body = .{ - .instructions = try sema.arena.dupe(Air.Inst.Index, sub_block.instructions.items), - }; - - sub_block.instructions.shrinkRetainingCapacity(0); + const true_instructions = sub_block.instructions.toOwnedSlice(gpa); + defer gpa.free(true_instructions); _ = try sema.analyzeBody(&sub_block, else_body); - const air_else_body: ir.Body = .{ - .instructions = try sema.arena.dupe(Air.Inst.Index, sub_block.instructions.items), - }; - - _ = try parent_block.addCondBr(src, cond, air_then_body, air_else_body); + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + + true_instructions.len + sub_block.instructions.items.len); + _ = try parent_block.addInst(.{ + .tag = .cond_br, + .data = .{ .pl_op = .{ + .operand = cond, + .payload = sema.addExtraAssumeCapacity(Air.CondBr{ + .then_body_len = @intCast(u32, true_instructions.len), + .else_body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(true_instructions); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); return always_noreturn; } @@ -6443,7 +6435,7 @@ fn panicWithMsg( try mod.optionalType(arena, ptr_stack_trace_ty), Value.initTag(.null_value), ); - const args = try arena.create([2]Air.Inst.Index); + const args = try arena.create([2]Air.Inst.Ref); args.* = .{ msg_inst, null_stack_trace }; _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args); return always_noreturn;