diff options
| author | Robin Voetter <robin@voetter.nl> | 2022-11-26 17:37:13 +0100 |
|---|---|---|
| committer | Robin Voetter <robin@voetter.nl> | 2023-04-09 01:51:49 +0200 |
| commit | 3c5ab4dd3d23b191b184b70dc8255b253a7ddc2c (patch) | |
| tree | acfe8f23d30ca6b08cef7d8615f3bd1b6bd80c5c /src | |
| parent | 39016948f068c52b3cec24cdfdf5882193ea09b9 (diff) | |
| download | zig-3c5ab4dd3d23b191b184b70dc8255b253a7ddc2c.tar.gz zig-3c5ab4dd3d23b191b184b70dc8255b253a7ddc2c.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/spirv.zig | 72 |
1 files changed, 43 insertions, 29 deletions
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 48f893404a..82f156fc99 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -703,7 +703,7 @@ pub const DeclGen = struct { fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void { const air_tags = self.air.instructions.items(.tag); - const result_id = switch (air_tags[inst]) { + const maybe_result_id: ?IdRef = switch (air_tags[inst]) { // zig fmt: off .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd), .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), @@ -717,7 +717,8 @@ pub const DeclGen = struct { .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), - .not => try self.airNot(inst), + .bitcast => try self.airBitcast(inst), + .not => try self.airNot(inst), .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual), .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual), @@ -728,10 +729,9 @@ pub const DeclGen = struct { .arg => self.airArg(), .alloc => try self.airAlloc(inst), - .block => (try self.airBlock(inst)) orelse return, + .block => try self.airBlock(inst), .load => try self.airLoad(inst), - .bitcast => try self.airBitcast(inst), .br => return self.airBr(inst), .breakpoint => return, .cond_br => return self.airCondBr(inst), @@ -741,12 +741,13 @@ pub const DeclGen = struct { .ret => return self.airRet(inst), .store => return self.airStore(inst), .unreach => return self.airUnreach(), - .assembly => (try self.airAssembly(inst)) orelse return, - .call => (try self.airCall(inst, .auto)) orelse return, - .call_always_tail => (try self.airCall(inst, .always_tail)) orelse return, - .call_never_tail => (try self.airCall(inst, .never_tail)) orelse return, - .call_never_inline => (try self.airCall(inst, .never_inline)) orelse return, + .assembly => try self.airAssembly(inst), + + .call => try self.airCall(inst, .auto), + .call_always_tail => try self.airCall(inst, .always_tail), + .call_never_tail => try self.airCall(inst, .never_tail), + .call_never_inline => try self.airCall(inst, .never_inline), .dbg_var_ptr => return, .dbg_var_val => return, @@ -757,10 +758,12 @@ pub const DeclGen = struct { else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}), }; + const result_id = maybe_result_id orelse return; try self.inst_results.putNoClobber(self.gpa, inst, result_id); } - fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !IdRef { + fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { + if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -781,7 +784,8 @@ pub const DeclGen = struct { comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode, - ) !IdRef { + ) !?IdRef { + if (self.liveness.isUnused(inst)) return null; // LHS and RHS are guaranteed to have the same type, and AIR guarantees // the result to be the same as the LHS and RHS, which matches SPIR-V. const ty = self.air.typeOfIndex(inst); @@ -833,7 +837,8 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !IdRef { + fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { + if (self.liveness.isUnused(inst)) return null; const ty = self.air.typeOfIndex(inst); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; @@ -868,7 +873,8 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef { + fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { + if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -913,7 +919,22 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airNot(self: *DeclGen, inst: Air.Inst.Index) !IdRef { + fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { + if (self.liveness.isUnused(inst)) return null; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand_id = try self.resolve(ty_op.operand); + const result_id = self.spv.allocId(); + const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); + try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ + .id_result_type = result_type_id, + .id_result = result_id, + .operand = operand_id, + }); + return result_id.toRef(); + } + + fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { + if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand_id = try self.resolve(ty_op.operand); const result_id = self.spv.allocId(); @@ -926,7 +947,8 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !IdRef { + fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { + if (self.liveness.isUnused(inst)) return null; const ty = self.air.typeOfIndex(inst); const result_type_id = try self.resolveTypeId(ty); const result_id = self.spv.allocId(); @@ -981,7 +1003,7 @@ pub const DeclGen = struct { try self.beginSpvBlock(label_id); // If this block didn't produce a value, simply return here. - if (!ty.hasRuntimeBits()) + if (!ty.hasRuntimeBitsIgnoreComptime()) return null; // Combine the result from the blocks using the Phi instruction. @@ -1002,19 +1024,6 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !IdRef { - const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const operand_id = try self.resolve(ty_op.operand); - const result_id = self.spv.allocId(); - const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); - try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ - .id_result_type = result_type_id, - .id_result = result_id, - .operand = operand_id, - }); - return result_id.toRef(); - } - fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { const br = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(br.block_inst).?; @@ -1104,6 +1113,7 @@ pub const DeclGen = struct { } fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void { + if (self.liveness.isUnused(inst)) return; const operand = self.air.instructions.items(.data)[inst].un_op; const operand_ty = self.air.typeOf(operand); if (operand_ty.hasRuntimeBits()) { @@ -1299,6 +1309,10 @@ pub const DeclGen = struct { try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); } + if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime()) { + return null; + } + return result_id.toRef(); } }; |
