authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-18 23:11:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-18 23:11:56-07:00
log083c0f1cebc763e4e43529b50f6df9839c32c1c7
tree6ea2d31ae701bcd0703026fde44f2efaa2981c33
parentdc35b8641badf3169a3124e84b672c3bf4bfd5f8

stage2 codegen: proper abstraction for re-using dying operands

closes #6064

3 files changed, 60 insertions(+), 4 deletions(-)

src-self-hosted/codegen.zig+13-4
...@@ -858,6 +858,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -858,6 +858,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
858 }858 }
859 }859 }
860860
861 fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
862 if (!inst.operandDies(op_index) or !mcv.isMutable())
863 return false;
864
865 // OK we're going to do it, but we need to clear the operand death bit so that
866 // it stays allocated.
867 inst.clearOperandDeath(op_index);
868 return true;
869 }
870
861 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {871 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
862 const elem_ty = inst.base.ty;872 const elem_ty = inst.base.ty;
863 if (!elem_ty.hasCodeGenBits())873 if (!elem_ty.hasCodeGenBits())
...@@ -867,9 +877,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -867,9 +877,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
867 if (inst.base.isUnused() and !is_volatile)877 if (inst.base.isUnused() and !is_volatile)
868 return MCValue.dead;878 return MCValue.dead;
869 const dst_mcv: MCValue = blk: {879 const dst_mcv: MCValue = blk: {
870 if (inst.base.operandDies(0) and ptr.isMutable()) {880 if (reuseOperand(&inst.base, 0, ptr)) {
871 // The MCValue that holds the pointer can be re-used as the value.881 // The MCValue that holds the pointer can be re-used as the value.
872 // TODO track this in the register/stack allocation metadata.
873 break :blk ptr;882 break :blk ptr;
874 } else {883 } else {
875 break :blk try self.allocRegOrMem(&inst.base);884 break :blk try self.allocRegOrMem(&inst.base);
...@@ -966,7 +975,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -966,7 +975,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
966 var dst_mcv: MCValue = undefined;975 var dst_mcv: MCValue = undefined;
967 var src_mcv: MCValue = undefined;976 var src_mcv: MCValue = undefined;
968 var src_inst: *ir.Inst = undefined;977 var src_inst: *ir.Inst = undefined;
969 if (inst.operandDies(0) and lhs.isMutable()) {978 if (reuseOperand(inst, 0, lhs)) {
970 // LHS dies; use it as the destination.979 // LHS dies; use it as the destination.
971 // Both operands cannot be memory.980 // Both operands cannot be memory.
972 src_inst = op_rhs;981 src_inst = op_rhs;
...@@ -977,7 +986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -977,7 +986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
977 dst_mcv = lhs;986 dst_mcv = lhs;
978 src_mcv = rhs;987 src_mcv = rhs;
979 }988 }
980 } else if (inst.operandDies(1) and rhs.isMutable()) {989 } else if (reuseOperand(inst, 1, rhs)) {
981 // RHS dies; use it as the destination.990 // RHS dies; use it as the destination.
982 // Both operands cannot be memory.991 // Both operands cannot be memory.
983 src_inst = op_lhs;992 src_inst = op_lhs;
src-self-hosted/ir.zig+5
...@@ -42,6 +42,11 @@ pub const Inst = struct {...@@ -42,6 +42,11 @@ pub const Inst = struct {
42 return @truncate(u1, self.deaths >> index) != 0;42 return @truncate(u1, self.deaths >> index) != 0;
43 }43 }
4444
45 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
46 assert(index < deaths_bits);
47 self.deaths &= ~(@as(DeathsInt, 1) << index);
48 }
49
45 pub fn specialOperandDeaths(self: Inst) bool {50 pub fn specialOperandDeaths(self: Inst) bool {
46 return (self.deaths & (1 << deaths_bits)) != 0;51 return (self.deaths & (1 << deaths_bits)) != 0;
47 }52 }
test/stage2/compare_output.zig+42
...@@ -544,6 +544,48 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -544,6 +544,48 @@ pub fn addCases(ctx: *TestContext) !void {
544 "",544 "",
545 );545 );
546546
547 // This catches a possible bug in the logic for re-using dying operands.
548 case.addCompareOutput(
549 \\export fn _start() noreturn {
550 \\ assert(add(3, 4) == 116);
551 \\
552 \\ exit();
553 \\}
554 \\
555 \\fn add(a: u32, b: u32) u32 {
556 \\ const x: u32 = blk: {
557 \\ const c = a + b; // 7
558 \\ const d = a + c; // 10
559 \\ const e = d + b; // 14
560 \\ const f = d + e; // 24
561 \\ const g = e + f; // 38
562 \\ const h = f + g; // 62
563 \\ const i = g + h; // 100
564 \\ const j = i + d; // 110
565 \\ break :blk j;
566 \\ };
567 \\ const y = x + a; // 113
568 \\ const z = y + a; // 116
569 \\ return z;
570 \\}
571 \\
572 \\pub fn assert(ok: bool) void {
573 \\ if (!ok) unreachable; // assertion failure
574 \\}
575 \\
576 \\fn exit() noreturn {
577 \\ asm volatile ("syscall"
578 \\ :
579 \\ : [number] "{rax}" (231),
580 \\ [arg1] "{rdi}" (0)
581 \\ : "rcx", "r11", "memory"
582 \\ );
583 \\ unreachable;
584 \\}
585 ,
586 "",
587 );
588
547 case.addCompareOutput(589 case.addCompareOutput(
548 \\export fn _start() noreturn {590 \\export fn _start() noreturn {
549 \\ const ignore = 591 \\ const ignore =