authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-15 17:03:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-15 17:03:05-07:00
log66d76cc4f938209470fedd8e52f71267f0e0d0e4
tree7125ad59107e2dd5ba4f66da1e93e319652bd012
parent2cd19c05d0f615071102a5f18bb4cc800e86c07f

stage2: codegen for labeled blocks


3 files changed, 76 insertions(+), 12 deletions(-)

src-self-hosted/astgen.zig+3
...@@ -1165,6 +1165,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built...@@ -1165,6 +1165,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
1165 return simpleCast(mod, scope, rl, call, .intcast);1165 return simpleCast(mod, scope, rl, call, .intcast);
1166 } else if (mem.eql(u8, builtin_name, "@bitCast")) {1166 } else if (mem.eql(u8, builtin_name, "@bitCast")) {
1167 return bitCast(mod, scope, rl, call);1167 return bitCast(mod, scope, rl, call);
1168 } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
1169 const src = tree.token_locs[call.builtin_token].start;
1170 return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
1168 } else {1171 } else {
1169 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});1172 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
1170 }1173 }
src-self-hosted/codegen.zig+36-12
...@@ -20,7 +20,21 @@ const leb128 = std.debug.leb;...@@ -20,7 +20,21 @@ const leb128 = std.debug.leb;
2020
21/// The codegen-related data that is stored in `ir.Inst.Block` instructions.21/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
22pub const BlockData = struct {22pub const BlockData = struct {
23 relocs: std.ArrayListUnmanaged(Reloc) = .{},23 relocs: std.ArrayListUnmanaged(Reloc) = undefined,
24 /// The first break instruction encounters `null` here and chooses a
25 /// machine code value for the block result, populating this field.
26 /// Following break instructions encounter that value and use it for
27 /// the location to store their block results.
28 mcv: AnyMCValue = undefined,
29};
30
31/// Architecture-independent MCValue. Here, we have a type that is the same size as
32/// the architecture-specific MCValue. Next to the declaration of MCValue is a
33/// comptime assert that makes sure we guessed correctly about the size. This only
34/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
35pub const AnyMCValue = extern struct {
36 a: u64,
37 b: u64,
24};38};
2539
26pub const Reloc = union(enum) {40pub const Reloc = union(enum) {
...@@ -1387,16 +1401,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1387,16 +1401,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1387 }1401 }
13881402
1389 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {1403 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
1390 if (inst.base.ty.hasCodeGenBits()) {1404 inst.codegen = .{
1391 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});1405 // A block is a setup to be able to jump to the end.
1392 }1406 .relocs = .{},
1393 // A block is a setup to be able to jump to the end.1407 // It also acts as a receptical for break operands.
1408 // Here we use `MCValue.none` to represent a null value so that the first
1409 // break instruction will choose a MCValue for the block result and overwrite
1410 // this field. Following break instructions will use that MCValue to put their
1411 // block results.
1412 .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
1413 };
1394 defer inst.codegen.relocs.deinit(self.gpa);1414 defer inst.codegen.relocs.deinit(self.gpa);
1415
1395 try self.genBody(inst.body);1416 try self.genBody(inst.body);
13961417
1397 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);1418 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
13981419
1399 return MCValue.none;1420 return @bitCast(MCValue, inst.codegen.mcv);
1400 }1421 }
14011422
1402 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {1423 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
...@@ -1416,13 +1437,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1416,13 +1437,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1416 }1437 }
14171438
1418 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {1439 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
1419 if (!inst.operand.ty.hasCodeGenBits())1440 if (inst.operand.ty.hasCodeGenBits()) {
1420 return self.brVoid(inst.base.src, inst.block);1441 const operand = try self.resolveInst(inst.operand);
14211442 const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
1422 const operand = try self.resolveInst(inst.operand);1443 if (block_mcv == .none) {
1423 switch (arch) {1444 inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
1424 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),1445 } else {
1446 try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
1447 }
1425 }1448 }
1449 return self.brVoid(inst.base.src, inst.block);
1426 }1450 }
14271451
1428 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {1452 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
test/stage2/compare_output.zig+37
...@@ -509,5 +509,42 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -509,5 +509,42 @@ pub fn addCases(ctx: *TestContext) !void {
509 ,509 ,
510 "hello\nhello\nhello\nhello\n",510 "hello\nhello\nhello\nhello\n",
511 );511 );
512
513 // Labeled blocks (no conditional branch)
514 case.addCompareOutput(
515 \\export fn _start() noreturn {
516 \\ assert(add(3, 4) == 20);
517 \\
518 \\ exit();
519 \\}
520 \\
521 \\fn add(a: u32, b: u32) u32 {
522 \\ const x: u32 = blk: {
523 \\ const c = a + b; // 7
524 \\ const d = a + c; // 10
525 \\ const e = d + b; // 14
526 \\ break :blk e;
527 \\ };
528 \\ const y = x + a; // 17
529 \\ const z = y + a; // 20
530 \\ return z;
531 \\}
532 \\
533 \\pub fn assert(ok: bool) void {
534 \\ if (!ok) unreachable; // assertion failure
535 \\}
536 \\
537 \\fn exit() noreturn {
538 \\ asm volatile ("syscall"
539 \\ :
540 \\ : [number] "{rax}" (231),
541 \\ [arg1] "{rdi}" (0)
542 \\ : "rcx", "r11", "memory"
543 \\ );
544 \\ unreachable;
545 \\}
546 ,
547 "",
548 );
512 }549 }
513}550}