diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 1747d77df3..b5f439ec27 100644 --- a/src-self-hosted/astgen.zig +++ b/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}); } diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 29bcf1bd01..12d3884308 100644 --- a/src-self-hosted/codegen.zig +++ b/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 { diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 4664d001fd..4979f1b2bc 100644 --- a/test/stage2/compare_output.zig +++ b/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; + \\} + , + "", + ); } }