From 23d7921758524f76f2157e6f8a5823da2511396a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Apr 2023 14:04:04 -0400 Subject: [PATCH] Sema: avoid emitting loops that can't loop If a `loop` ends with a `noreturn` instruction, then it cannot loop and will be emitted as a `block` instead. --- src/Air.zig | 9 +++++++-- src/Sema.zig | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index c63e9826f9..b8771b2a61 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -221,11 +221,16 @@ pub const Inst = struct { /// Reinterpret the memory representation of a value as a different type. /// Uses the `ty_op` field. bitcast, - /// Uses the `ty_pl` field with payload `Block`. + /// Uses the `ty_pl` field with payload `Block`. A block runs its body which always ends + /// with a `noreturn` instruction, so the only way to proceed to the code after the `block` + /// is to encounter a `br` that targets this `block`. If the `block` type is `noreturn`, + /// then there do not exist any `br` instructions targetting this `block`. block, /// 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. - /// Result type is always noreturn; no instructions in a block follow this one. + /// Result type is always `noreturn`; no instructions in a block follow this one. + /// The body never ends with a `noreturn` instruction, so the "repeat" operation + /// is always statically reachable. /// Uses the `ty_pl` field. Payload is `Block`. loop, /// Return from a block with a result. diff --git a/src/Sema.zig b/src/Sema.zig index a0827d805f..28a749b602 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5294,14 +5294,20 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError try sema.analyzeBody(&loop_block, body); - try child_block.instructions.append(gpa, loop_inst); + if (sema.typeOf(Air.indexToRef(loop_block.instructions.items[loop_block.instructions.items.len - 1])).isNoReturn()) { + // If the loop ended with a noreturn terminator, then there is no way for it to loop, + // so we can just use the block instead. + try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); + } else { + try child_block.instructions.append(gpa, loop_inst); - try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + - loop_block.instructions.items.len); - sema.air_instructions.items(.data)[loop_inst].ty_pl.payload = sema.addExtraAssumeCapacity( - Air.Block{ .body_len = @intCast(u32, loop_block.instructions.items.len) }, - ); - sema.air_extra.appendSliceAssumeCapacity(loop_block.instructions.items); + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + + loop_block.instructions.items.len); + sema.air_instructions.items(.data)[loop_inst].ty_pl.payload = sema.addExtraAssumeCapacity( + Air.Block{ .body_len = @intCast(u32, loop_block.instructions.items.len) }, + ); + sema.air_extra.appendSliceAssumeCapacity(loop_block.instructions.items); + } return sema.analyzeBlockBody(parent_block, src, &child_block, merges); }