Skip to content

Commit 1c9f3b6

Browse files
spirv-opt: Fix when ConvertToHalfPass relaxes a image operand
1 parent 91ac969 commit 1c9f3b6

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

source/opt/convert_to_half_pass.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include "convert_to_half_pass.h"
1818

19+
#include <cstdint>
20+
1921
#include "source/opt/ir_builder.h"
2022

2123
namespace spvtools {
@@ -195,6 +197,117 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
195197
return true;
196198
}
197199

200+
// Return operand position of Image Operands <ID> or zero if there is none
201+
static constexpr uint32_t GetImageOperandsPosition(spv::Op opcode) {
202+
uint32_t position = 0;
203+
switch (opcode) {
204+
case spv::Op::OpImageWrite:
205+
return 3;
206+
case spv::Op::OpImageSampleImplicitLod:
207+
case spv::Op::OpImageSampleExplicitLod:
208+
case spv::Op::OpImageSampleProjImplicitLod:
209+
case spv::Op::OpImageSampleProjExplicitLod:
210+
case spv::Op::OpImageFetch:
211+
case spv::Op::OpImageRead:
212+
case spv::Op::OpImageSparseSampleImplicitLod:
213+
case spv::Op::OpImageSparseSampleExplicitLod:
214+
case spv::Op::OpImageSparseSampleProjImplicitLod:
215+
case spv::Op::OpImageSparseSampleProjExplicitLod:
216+
case spv::Op::OpImageSparseFetch:
217+
case spv::Op::OpImageSparseRead:
218+
return 4;
219+
case spv::Op::OpImageSampleDrefImplicitLod:
220+
case spv::Op::OpImageSampleDrefExplicitLod:
221+
case spv::Op::OpImageSampleProjDrefImplicitLod:
222+
case spv::Op::OpImageSampleProjDrefExplicitLod:
223+
case spv::Op::OpImageGather:
224+
case spv::Op::OpImageDrefGather:
225+
case spv::Op::OpImageSparseSampleDrefImplicitLod:
226+
case spv::Op::OpImageSparseSampleDrefExplicitLod:
227+
case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
228+
case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
229+
case spv::Op::OpImageSparseGather:
230+
case spv::Op::OpImageSparseDrefGather:
231+
return 5;
232+
case spv::Op::OpImageSampleFootprintNV:
233+
return 6;
234+
default:
235+
break;
236+
}
237+
return position;
238+
}
239+
240+
bool ConvertToHalfPass::ImageOperandCleanup(Instruction* inst) {
241+
if (image_ops_.count(inst->opcode()) == 0) return false;
242+
243+
const uint32_t image_operand_position =
244+
GetImageOperandsPosition(inst->opcode());
245+
// Image operands can be optional, also some things like |SignExtend| don't
246+
// have additonal operands
247+
if (image_operand_position == 0 ||
248+
(image_operand_position + 1) >= inst->NumOperandWords()) {
249+
return false;
250+
}
251+
252+
bool modified = false;
253+
const uint32_t mask = inst->GetSingleWordOperand(image_operand_position);
254+
uint32_t next_operand = image_operand_position + 1;
255+
256+
InstructionBuilder builder(
257+
context(), inst,
258+
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
259+
260+
uint32_t float32_type =
261+
context()->get_type_mgr()->GetTypeInstruction(FloatScalarType(32));
262+
263+
// All mask after don't have a 32-bit restriction
264+
for (uint32_t i = 1; i <= (uint32_t)spv::ImageOperandsMask::MinLod; i <<= 1) {
265+
if ((mask & i) == 0) {
266+
continue;
267+
}
268+
269+
uint32_t update_operand = next_operand++;
270+
const uint32_t ref_id = inst->GetSingleWordOperand(update_operand);
271+
Instruction* ref_inst = get_def_use_mgr()->GetDef(ref_id);
272+
Instruction* type_inst = get_def_use_mgr()->GetDef(ref_inst->type_id());
273+
assert(type_inst->opcode() == spv::Op::OpTypeFloat);
274+
if (type_inst->GetSingleWordInOperand(0) != 32) {
275+
Instruction* cvt_inst =
276+
builder.AddUnaryOp(float32_type, spv::Op::OpFConvert, ref_id);
277+
if (cvt_inst == nullptr) {
278+
status_ = Status::Failure;
279+
return false;
280+
}
281+
inst->SetOperand(update_operand, {cvt_inst->result_id()});
282+
get_def_use_mgr()->AnalyzeInstUse(inst);
283+
modified = true;
284+
}
285+
286+
// All masks have 1 operand, except grad has two
287+
if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
288+
uint32_t dy_update_operand = next_operand++;
289+
const uint32_t dy_ref_id = inst->GetSingleWordOperand(dy_update_operand);
290+
Instruction* dy_ref_inst = get_def_use_mgr()->GetDef(dy_ref_id);
291+
Instruction* dy_type_inst =
292+
get_def_use_mgr()->GetDef(dy_ref_inst->type_id());
293+
assert(dy_type_inst->opcode() == spv::Op::OpTypeFloat);
294+
if (dy_type_inst->GetSingleWordInOperand(0) != 32) {
295+
Instruction* dy_cvt_inst =
296+
builder.AddUnaryOp(float32_type, spv::Op::OpFConvert, ref_id);
297+
if (dy_cvt_inst == nullptr) {
298+
status_ = Status::Failure;
299+
return false;
300+
}
301+
inst->SetOperand(dy_update_operand, {dy_cvt_inst->result_id()});
302+
get_def_use_mgr()->AnalyzeInstUse(inst);
303+
modified = true;
304+
}
305+
}
306+
}
307+
308+
return modified;
309+
}
310+
198311
bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) {
199312
return context()->get_decoration_mgr()->RemoveDecorationsFrom(
200313
id, [](const Instruction& dec) {
@@ -452,6 +565,7 @@ bool ConvertToHalfPass::ProcessFunction(Function* func) {
452565
}
453566
for (auto ii = bb->begin(); ii != bb->end(); ++ii) {
454567
bool Mmodified = MatConvertCleanup(&*ii);
568+
Mmodified |= ImageOperandCleanup(&*ii);
455569
if (status_ == Status::Failure) {
456570
ok = false;
457571
break;

source/opt/convert_to_half_pass.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class ConvertToHalfPass : public Pass {
114114
// invalid so we need to clean them up.
115115
bool MatConvertCleanup(Instruction* inst);
116116

117+
// If |inst| has any image operands, make sure to covert it back to a 32-bit
118+
// float
119+
bool ImageOperandCleanup(Instruction* inst);
120+
117121
// Call GenHalfInst on every instruction in |func|.
118122
// If code is generated for an instruction, replace the instruction
119123
// with the new instructions that are generated.

0 commit comments

Comments
 (0)