authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-11-26 17:37:13+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:49+02:00
log3c5ab4dd3d23b191b184b70dc8255b253a7ddc2c
treeacfe8f23d30ca6b08cef7d8615f3bd1b6bd80c5c
parent39016948f068c52b3cec24cdfdf5882193ea09b9
signature Commit is signed but in an unrecognized format.

spirv: add liveness checks

When a result of a pure instruction is not used, it also does not need to be generated. The other backends already implement these checks, they were ignored in SPIR-V up until now. New instructions added in the future should have these be implemented from the start.

1 files changed, 43 insertions(+), 29 deletions(-)

src/codegen/spirv.zig+43-29
......@@ -703,7 +703,7 @@ pub const DeclGen = struct {
703703
704704 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
705705 const air_tags = self.air.instructions.items(.tag);
706 const result_id = switch (air_tags[inst]) {
706 const maybe_result_id: ?IdRef = switch (air_tags[inst]) {
707707 // zig fmt: off
708708 .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
709709 .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
......@@ -717,7 +717,8 @@ pub const DeclGen = struct {
717717 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
718718 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),
719719
720 .not => try self.airNot(inst),
720 .bitcast => try self.airBitcast(inst),
721 .not => try self.airNot(inst),
721722
722723 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),
723724 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),
......@@ -728,10 +729,9 @@ pub const DeclGen = struct {
728729
729730 .arg => self.airArg(),
730731 .alloc => try self.airAlloc(inst),
731 .block => (try self.airBlock(inst)) orelse return,
732 .block => try self.airBlock(inst),
732733 .load => try self.airLoad(inst),
733734
734 .bitcast => try self.airBitcast(inst),
735735 .br => return self.airBr(inst),
736736 .breakpoint => return,
737737 .cond_br => return self.airCondBr(inst),
......@@ -741,12 +741,13 @@ pub const DeclGen = struct {
741741 .ret => return self.airRet(inst),
742742 .store => return self.airStore(inst),
743743 .unreach => return self.airUnreach(),
744 .assembly => (try self.airAssembly(inst)) orelse return,
745744
746 .call => (try self.airCall(inst, .auto)) orelse return,
747 .call_always_tail => (try self.airCall(inst, .always_tail)) orelse return,
748 .call_never_tail => (try self.airCall(inst, .never_tail)) orelse return,
749 .call_never_inline => (try self.airCall(inst, .never_inline)) orelse return,
745 .assembly => try self.airAssembly(inst),
746
747 .call => try self.airCall(inst, .auto),
748 .call_always_tail => try self.airCall(inst, .always_tail),
749 .call_never_tail => try self.airCall(inst, .never_tail),
750 .call_never_inline => try self.airCall(inst, .never_inline),
750751
751752 .dbg_var_ptr => return,
752753 .dbg_var_val => return,
......@@ -757,10 +758,12 @@ pub const DeclGen = struct {
757758 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}),
758759 };
759760
761 const result_id = maybe_result_id orelse return;
760762 try self.inst_results.putNoClobber(self.gpa, inst, result_id);
761763 }
762764
763 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !IdRef {
765 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
766 if (self.liveness.isUnused(inst)) return null;
764767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
765768 const lhs_id = try self.resolve(bin_op.lhs);
766769 const rhs_id = try self.resolve(bin_op.rhs);
......@@ -781,7 +784,8 @@ pub const DeclGen = struct {
781784 comptime fop: Opcode,
782785 comptime sop: Opcode,
783786 comptime uop: Opcode,
784 ) !IdRef {
787 ) !?IdRef {
788 if (self.liveness.isUnused(inst)) return null;
785789 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
786790 // the result to be the same as the LHS and RHS, which matches SPIR-V.
787791 const ty = self.air.typeOfIndex(inst);
......@@ -833,7 +837,8 @@ pub const DeclGen = struct {
833837 return result_id.toRef();
834838 }
835839
836 fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
840 fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
841 if (self.liveness.isUnused(inst)) return null;
837842 const ty = self.air.typeOfIndex(inst);
838843 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
839844 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
......@@ -868,7 +873,8 @@ pub const DeclGen = struct {
868873 return result_id.toRef();
869874 }
870875
871 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef {
876 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef {
877 if (self.liveness.isUnused(inst)) return null;
872878 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
873879 const lhs_id = try self.resolve(bin_op.lhs);
874880 const rhs_id = try self.resolve(bin_op.rhs);
......@@ -913,7 +919,22 @@ pub const DeclGen = struct {
913919 return result_id.toRef();
914920 }
915921
916 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
922 fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
923 if (self.liveness.isUnused(inst)) return null;
924 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
925 const operand_id = try self.resolve(ty_op.operand);
926 const result_id = self.spv.allocId();
927 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
928 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
929 .id_result_type = result_type_id,
930 .id_result = result_id,
931 .operand = operand_id,
932 });
933 return result_id.toRef();
934 }
935
936 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
937 if (self.liveness.isUnused(inst)) return null;
917938 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
918939 const operand_id = try self.resolve(ty_op.operand);
919940 const result_id = self.spv.allocId();
......@@ -926,7 +947,8 @@ pub const DeclGen = struct {
926947 return result_id.toRef();
927948 }
928949
929 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
950 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
951 if (self.liveness.isUnused(inst)) return null;
930952 const ty = self.air.typeOfIndex(inst);
931953 const result_type_id = try self.resolveTypeId(ty);
932954 const result_id = self.spv.allocId();
......@@ -981,7 +1003,7 @@ pub const DeclGen = struct {
9811003 try self.beginSpvBlock(label_id);
9821004
9831005 // If this block didn't produce a value, simply return here.
984 if (!ty.hasRuntimeBits())
1006 if (!ty.hasRuntimeBitsIgnoreComptime())
9851007 return null;
9861008
9871009 // Combine the result from the blocks using the Phi instruction.
......@@ -1002,19 +1024,6 @@ pub const DeclGen = struct {
10021024 return result_id.toRef();
10031025 }
10041026
1005 fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
1006 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1007 const operand_id = try self.resolve(ty_op.operand);
1008 const result_id = self.spv.allocId();
1009 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
1010 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
1011 .id_result_type = result_type_id,
1012 .id_result = result_id,
1013 .operand = operand_id,
1014 });
1015 return result_id.toRef();
1016 }
1017
10181027 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
10191028 const br = self.air.instructions.items(.data)[inst].br;
10201029 const block = self.blocks.get(br.block_inst).?;
......@@ -1104,6 +1113,7 @@ pub const DeclGen = struct {
11041113 }
11051114
11061115 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
1116 if (self.liveness.isUnused(inst)) return;
11071117 const operand = self.air.instructions.items(.data)[inst].un_op;
11081118 const operand_ty = self.air.typeOf(operand);
11091119 if (operand_ty.hasRuntimeBits()) {
......@@ -1299,6 +1309,10 @@ pub const DeclGen = struct {
12991309 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
13001310 }
13011311
1312 if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime()) {
1313 return null;
1314 }
1315
13021316 return result_id.toRef();
13031317 }
13041318};