Commit 66d76cc4f9

Andrew Kelley <andrew@ziglang.org>
2020-08-16 02:03:05
stage2: codegen for labeled blocks
1 parent 2cd19c0
Changed files (3)
src-self-hosted
test
src-self-hosted/astgen.zig
@@ -1165,6 +1165,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
         return simpleCast(mod, scope, rl, call, .intcast);
     } else if (mem.eql(u8, builtin_name, "@bitCast")) {
         return bitCast(mod, scope, rl, call);
+    } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
+        const src = tree.token_locs[call.builtin_token].start;
+        return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
     } else {
         return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
     }
src-self-hosted/codegen.zig
@@ -20,7 +20,21 @@ const leb128 = std.debug.leb;
 
 /// The codegen-related data that is stored in `ir.Inst.Block` instructions.
 pub const BlockData = struct {
-    relocs: std.ArrayListUnmanaged(Reloc) = .{},
+    relocs: std.ArrayListUnmanaged(Reloc) = undefined,
+    /// The first break instruction encounters `null` here and chooses a
+    /// machine code value for the block result, populating this field.
+    /// Following break instructions encounter that value and use it for
+    /// the location to store their block results.
+    mcv: AnyMCValue = undefined,
+};
+
+/// Architecture-independent MCValue. Here, we have a type that is the same size as
+/// the architecture-specific MCValue. Next to the declaration of MCValue is a
+/// comptime assert that makes sure we guessed correctly about the size. This only
+/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
+pub const AnyMCValue = extern struct {
+    a: u64,
+    b: u64,
 };
 
 pub const Reloc = union(enum) {
@@ -1387,16 +1401,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
         }
 
         fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
-            if (inst.base.ty.hasCodeGenBits()) {
-                return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
-            }
-            // A block is a setup to be able to jump to the end.
+            inst.codegen = .{
+                // A block is a setup to be able to jump to the end.
+                .relocs = .{},
+                // It also acts as a receptical for break operands.
+                // Here we use `MCValue.none` to represent a null value so that the first
+                // break instruction will choose a MCValue for the block result and overwrite
+                // this field. Following break instructions will use that MCValue to put their
+                // block results.
+                .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
+            };
             defer inst.codegen.relocs.deinit(self.gpa);
+
             try self.genBody(inst.body);
 
             for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
 
-            return MCValue.none;
+            return @bitCast(MCValue, inst.codegen.mcv);
         }
 
         fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
@@ -1416,13 +1437,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
         }
 
         fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
-            if (!inst.operand.ty.hasCodeGenBits())
-                return self.brVoid(inst.base.src, inst.block);
-
-            const operand = try self.resolveInst(inst.operand);
-            switch (arch) {
-                else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
+            if (inst.operand.ty.hasCodeGenBits()) {
+                const operand = try self.resolveInst(inst.operand);
+                const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
+                if (block_mcv == .none) {
+                    inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
+                } else {
+                    try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
+                }
             }
+            return self.brVoid(inst.base.src, inst.block);
         }
 
         fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
test/stage2/compare_output.zig
@@ -509,5 +509,42 @@ pub fn addCases(ctx: *TestContext) !void {
         ,
             "hello\nhello\nhello\nhello\n",
         );
+
+        // Labeled blocks (no conditional branch)
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    assert(add(3, 4) == 20);
+            \\
+            \\    exit();
+            \\}
+            \\
+            \\fn add(a: u32, b: u32) u32 {
+            \\    const x: u32 = blk: {
+            \\        const c = a + b; // 7
+            \\        const d = a + c; // 10
+            \\        const e = d + b; // 14
+            \\        break :blk e;
+            \\    };
+            \\    const y = x + a; // 17
+            \\    const z = y + a; // 20
+            \\    return z;
+            \\}
+            \\
+            \\pub fn assert(ok: bool) void {
+            \\    if (!ok) unreachable; // assertion failure
+            \\}
+            \\
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "",
+        );
     }
 }