From fc402bdbbbb5be2a538f0469f3f213264e7fcc59 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 13 Aug 2020 11:03:13 -0700 Subject: [PATCH] stage2: zir_sema for loops Also remove the "repeat" instruction and make it implied to be at the end of a Loop body. --- src-self-hosted/astgen.zig | 11 +++++------ src-self-hosted/zir.zig | 17 ++--------------- src-self-hosted/zir_sema.zig | 37 ++++++++++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 95c4e63c92..d34c2dc458 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -531,13 +531,12 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .{ .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items), }); + // TODO avoid emitting the continue expr when there + // are no jumps to it. This happens when the last statement of a while body is noreturn + // and there are no `continue` statements. + // The "repeat" at the end of a loop body is implied. if (while_node.continue_expr) |cont_expr| { - const cont_expr_result = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr); - if (!cont_expr_result.tag.isNoReturn()) { - _ = try addZIRNoOp(mod, &loop_scope.base, while_src, .repeat); - } - } else { - _ = try addZIRNoOp(mod, &loop_scope.base, while_src, .repeat); + _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr); } const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{ .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items), diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 2e638b0755..974e6f9ef6 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -151,7 +151,8 @@ pub const Inst = struct { isnonnull, /// Return a boolean true if an optional is null. `x == null` isnull, - /// A labeled block of code that loops forever. + /// 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. loop, /// Ambiguously remainder division or modulus. If the computation would possibly have /// a different value depending on whether the operation is remainder division or modulus, @@ -175,8 +176,6 @@ pub const Inst = struct { /// the memory location is in the stack frame, local to the scope containing the /// instruction. ref, - /// Sends control flow back to the loop block operand. - repeat, /// Obtains a pointer to the return value. ret_ptr, /// Obtains the return type of the in-scope function. @@ -294,7 +293,6 @@ pub const Inst = struct { .compileerror => CompileError, .loop => Loop, .@"const" => Const, - .repeat => Repeat, .str => Str, .int => Int, .inttype => IntType, @@ -390,7 +388,6 @@ pub const Inst = struct { .breakvoid, .condbr, .compileerror, - .repeat, .@"return", .returnvoid, .unreach_nocheck, @@ -587,16 +584,6 @@ pub const Inst = struct { kw_args: struct {}, }; - pub const Repeat = struct { - pub const base_tag = Tag.repeat; - base: Inst, - - positionals: struct { - loop: *Loop, - }, - kw_args: struct {}, - }; - pub const Str = struct { pub const base_tag = Tag.str; base: Inst, diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 74e57b7735..97c47db9b6 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -61,7 +61,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! }, .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?), .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?), - .repeat => return analyzeInstRepeat(mod, scope, old_inst.castTag(.repeat).?), .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?), .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?), .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?), @@ -440,12 +439,38 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!* return mod.addArg(b, inst.base.src, param_type, name); } -fn analyzeInstRepeat(mod: *Module, scope: *Scope, inst: *zir.Inst.Repeat) InnerError!*Inst { - return mod.fail(scope, inst.base.src, "TODO analyze .repeat ZIR", .{}); -} - fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst { - return mod.fail(scope, inst.base.src, "TODO analyze .loop ZIR", .{}); + const parent_block = scope.cast(Scope.Block).?; + + // Reserve space for a Loop 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 + // comptime evaluated. + const loop_inst = try parent_block.arena.create(Inst.Loop); + loop_inst.* = .{ + .base = .{ + .tag = Inst.Loop.base_tag, + .ty = Type.initTag(.noreturn), + .src = inst.base.src, + }, + .body = undefined, + }; + + var child_block: Scope.Block = .{ + .parent = parent_block, + .func = parent_block.func, + .decl = parent_block.decl, + .instructions = .{}, + .arena = parent_block.arena, + }; + defer child_block.instructions.deinit(mod.gpa); + + try analyzeBody(mod, &child_block.base, inst.positionals.body); + + // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. + + try parent_block.instructions.append(mod.gpa, &loop_inst.base); + loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; + return &loop_inst.base; } fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {