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
signaturelock-open 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 {...@@ -703,7 +703,7 @@ pub const DeclGen = struct {
703703
704 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {704 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
705 const air_tags = self.air.instructions.items(.tag);705 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]) {
707 // zig fmt: off707 // zig fmt: off
708 .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),708 .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
709 .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),709 .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
...@@ -717,7 +717,8 @@ pub const DeclGen = struct {...@@ -717,7 +717,8 @@ pub const DeclGen = struct {
717 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),717 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
718 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),718 .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
722 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),723 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),
723 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),724 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),
...@@ -728,10 +729,9 @@ pub const DeclGen = struct {...@@ -728,10 +729,9 @@ pub const DeclGen = struct {
728729
729 .arg => self.airArg(),730 .arg => self.airArg(),
730 .alloc => try self.airAlloc(inst),731 .alloc => try self.airAlloc(inst),
731 .block => (try self.airBlock(inst)) orelse return,732 .block => try self.airBlock(inst),
732 .load => try self.airLoad(inst),733 .load => try self.airLoad(inst),
733734
734 .bitcast => try self.airBitcast(inst),
735 .br => return self.airBr(inst),735 .br => return self.airBr(inst),
736 .breakpoint => return,736 .breakpoint => return,
737 .cond_br => return self.airCondBr(inst),737 .cond_br => return self.airCondBr(inst),
...@@ -741,12 +741,13 @@ pub const DeclGen = struct {...@@ -741,12 +741,13 @@ pub const DeclGen = struct {
741 .ret => return self.airRet(inst),741 .ret => return self.airRet(inst),
742 .store => return self.airStore(inst),742 .store => return self.airStore(inst),
743 .unreach => return self.airUnreach(),743 .unreach => return self.airUnreach(),
744 .assembly => (try self.airAssembly(inst)) orelse return,
745744
746 .call => (try self.airCall(inst, .auto)) orelse return,745 .assembly => try self.airAssembly(inst),
747 .call_always_tail => (try self.airCall(inst, .always_tail)) orelse return,746
748 .call_never_tail => (try self.airCall(inst, .never_tail)) orelse return,747 .call => try self.airCall(inst, .auto),
749 .call_never_inline => (try self.airCall(inst, .never_inline)) orelse return,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
751 .dbg_var_ptr => return,752 .dbg_var_ptr => return,
752 .dbg_var_val => return,753 .dbg_var_val => return,
...@@ -757,10 +758,12 @@ pub const DeclGen = struct {...@@ -757,10 +758,12 @@ pub const DeclGen = struct {
757 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}),758 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}),
758 };759 };
759760
761 const result_id = maybe_result_id orelse return;
760 try self.inst_results.putNoClobber(self.gpa, inst, result_id);762 try self.inst_results.putNoClobber(self.gpa, inst, result_id);
761 }763 }
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;
764 const bin_op = self.air.instructions.items(.data)[inst].bin_op;767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
765 const lhs_id = try self.resolve(bin_op.lhs);768 const lhs_id = try self.resolve(bin_op.lhs);
766 const rhs_id = try self.resolve(bin_op.rhs);769 const rhs_id = try self.resolve(bin_op.rhs);
...@@ -781,7 +784,8 @@ pub const DeclGen = struct {...@@ -781,7 +784,8 @@ pub const DeclGen = struct {
781 comptime fop: Opcode,784 comptime fop: Opcode,
782 comptime sop: Opcode,785 comptime sop: Opcode,
783 comptime uop: Opcode,786 comptime uop: Opcode,
784 ) !IdRef {787 ) !?IdRef {
788 if (self.liveness.isUnused(inst)) return null;
785 // LHS and RHS are guaranteed to have the same type, and AIR guarantees789 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
786 // the result to be the same as the LHS and RHS, which matches SPIR-V.790 // the result to be the same as the LHS and RHS, which matches SPIR-V.
787 const ty = self.air.typeOfIndex(inst);791 const ty = self.air.typeOfIndex(inst);
...@@ -833,7 +837,8 @@ pub const DeclGen = struct {...@@ -833,7 +837,8 @@ pub const DeclGen = struct {
833 return result_id.toRef();837 return result_id.toRef();
834 }838 }
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;
837 const ty = self.air.typeOfIndex(inst);842 const ty = self.air.typeOfIndex(inst);
838 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;843 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
839 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;844 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
...@@ -868,7 +873,8 @@ pub const DeclGen = struct {...@@ -868,7 +873,8 @@ pub const DeclGen = struct {
868 return result_id.toRef();873 return result_id.toRef();
869 }874 }
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;
872 const bin_op = self.air.instructions.items(.data)[inst].bin_op;878 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
873 const lhs_id = try self.resolve(bin_op.lhs);879 const lhs_id = try self.resolve(bin_op.lhs);
874 const rhs_id = try self.resolve(bin_op.rhs);880 const rhs_id = try self.resolve(bin_op.rhs);
...@@ -913,7 +919,22 @@ pub const DeclGen = struct {...@@ -913,7 +919,22 @@ pub const DeclGen = struct {
913 return result_id.toRef();919 return result_id.toRef();
914 }920 }
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;
917 const ty_op = self.air.instructions.items(.data)[inst].ty_op;938 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
918 const operand_id = try self.resolve(ty_op.operand);939 const operand_id = try self.resolve(ty_op.operand);
919 const result_id = self.spv.allocId();940 const result_id = self.spv.allocId();
...@@ -926,7 +947,8 @@ pub const DeclGen = struct {...@@ -926,7 +947,8 @@ pub const DeclGen = struct {
926 return result_id.toRef();947 return result_id.toRef();
927 }948 }
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;
930 const ty = self.air.typeOfIndex(inst);952 const ty = self.air.typeOfIndex(inst);
931 const result_type_id = try self.resolveTypeId(ty);953 const result_type_id = try self.resolveTypeId(ty);
932 const result_id = self.spv.allocId();954 const result_id = self.spv.allocId();
...@@ -981,7 +1003,7 @@ pub const DeclGen = struct {...@@ -981,7 +1003,7 @@ pub const DeclGen = struct {
981 try self.beginSpvBlock(label_id);1003 try self.beginSpvBlock(label_id);
9821004
983 // If this block didn't produce a value, simply return here.1005 // If this block didn't produce a value, simply return here.
984 if (!ty.hasRuntimeBits())1006 if (!ty.hasRuntimeBitsIgnoreComptime())
985 return null;1007 return null;
9861008
987 // Combine the result from the blocks using the Phi instruction.1009 // Combine the result from the blocks using the Phi instruction.
...@@ -1002,19 +1024,6 @@ pub const DeclGen = struct {...@@ -1002,19 +1024,6 @@ pub const DeclGen = struct {
1002 return result_id.toRef();1024 return result_id.toRef();
1003 }1025 }
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
1018 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {1027 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
1019 const br = self.air.instructions.items(.data)[inst].br;1028 const br = self.air.instructions.items(.data)[inst].br;
1020 const block = self.blocks.get(br.block_inst).?;1029 const block = self.blocks.get(br.block_inst).?;
...@@ -1104,6 +1113,7 @@ pub const DeclGen = struct {...@@ -1104,6 +1113,7 @@ pub const DeclGen = struct {
1104 }1113 }
11051114
1106 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {1115 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
1116 if (self.liveness.isUnused(inst)) return;
1107 const operand = self.air.instructions.items(.data)[inst].un_op;1117 const operand = self.air.instructions.items(.data)[inst].un_op;
1108 const operand_ty = self.air.typeOf(operand);1118 const operand_ty = self.air.typeOf(operand);
1109 if (operand_ty.hasRuntimeBits()) {1119 if (operand_ty.hasRuntimeBits()) {
...@@ -1299,6 +1309,10 @@ pub const DeclGen = struct {...@@ -1299,6 +1309,10 @@ pub const DeclGen = struct {
1299 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});1309 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
1300 }1310 }
13011311
1312 if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime()) {
1313 return null;
1314 }
1315
1302 return result_id.toRef();1316 return result_id.toRef();
1303 }1317 }
1304};1318};