stage2: codegen for labeled blocks
This commit is contained in:
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user