1 // Copyright (c) 2020 Vasyl Teliman
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #include "source/fuzz/transformation_move_instruction_down.h"
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "spirv/unified1/GLSL.std.450.h"
25 const char* const kExtensionSetName
= "GLSL.std.450";
27 std::string
GetExtensionSet(opt::IRContext
* ir_context
,
28 const opt::Instruction
& op_ext_inst
) {
29 assert(op_ext_inst
.opcode() == spv::Op::OpExtInst
&& "Wrong opcode");
31 const auto* ext_inst_import
= ir_context
->get_def_use_mgr()->GetDef(
32 op_ext_inst
.GetSingleWordInOperand(0));
33 assert(ext_inst_import
&& "Extension set is not imported");
35 return ext_inst_import
->GetInOperand(0).AsString();
40 TransformationMoveInstructionDown::TransformationMoveInstructionDown(
41 protobufs::TransformationMoveInstructionDown message
)
42 : message_(std::move(message
)) {}
44 TransformationMoveInstructionDown::TransformationMoveInstructionDown(
45 const protobufs::InstructionDescriptor
& instruction
) {
46 *message_
.mutable_instruction() = instruction
;
49 bool TransformationMoveInstructionDown::IsApplicable(
50 opt::IRContext
* ir_context
,
51 const TransformationContext
& transformation_context
) const {
52 // |instruction| must be valid.
53 auto* inst
= FindInstruction(message_
.instruction(), ir_context
);
58 // Instruction's opcode must be supported by this transformation.
59 if (!IsInstructionSupported(ir_context
, *inst
)) {
63 auto* inst_block
= ir_context
->get_instr_block(inst
);
65 "Global instructions and function parameters are not supported");
67 auto inst_it
= fuzzerutil::GetIteratorForInstruction(inst_block
, inst
);
68 assert(inst_it
!= inst_block
->end() &&
69 "Can't get an iterator for the instruction");
71 // |instruction| can't be the last instruction in the block.
72 auto successor_it
= ++inst_it
;
73 if (successor_it
== inst_block
->end()) {
77 // We don't risk swapping a memory instruction with an unsupported one.
78 if (!IsSimpleInstruction(ir_context
, *inst
) &&
79 !IsInstructionSupported(ir_context
, *successor_it
)) {
83 // It must be safe to swap the instructions without changing the semantics of
85 if (IsInstructionSupported(ir_context
, *successor_it
) &&
86 !CanSafelySwapInstructions(ir_context
, *inst
, *successor_it
,
87 *transformation_context
.GetFactManager())) {
91 // Check that we can insert |instruction| after |inst_it|.
92 auto successors_successor_it
= ++inst_it
;
93 if (successors_successor_it
== inst_block
->end() ||
94 !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst
->opcode(),
95 successors_successor_it
)) {
99 // Check that |instruction|'s successor doesn't depend on the |instruction|.
100 if (inst
->result_id()) {
101 for (uint32_t i
= 0; i
< successor_it
->NumInOperands(); ++i
) {
102 const auto& operand
= successor_it
->GetInOperand(i
);
103 if (spvIsInIdType(operand
.type
) &&
104 operand
.words
[0] == inst
->result_id()) {
113 void TransformationMoveInstructionDown::Apply(
114 opt::IRContext
* ir_context
, TransformationContext
* /*unused*/) const {
115 auto* inst
= FindInstruction(message_
.instruction(), ir_context
);
117 "The instruction should've been validated in the IsApplicable");
119 auto inst_it
= fuzzerutil::GetIteratorForInstruction(
120 ir_context
->get_instr_block(inst
), inst
);
122 // Move the instruction down in the block.
123 inst
->InsertAfter(&*++inst_it
);
125 ir_context
->InvalidateAnalyses(opt::IRContext::kAnalysisNone
);
128 protobufs::Transformation
TransformationMoveInstructionDown::ToMessage() const {
129 protobufs::Transformation result
;
130 *result
.mutable_move_instruction_down() = message_
;
134 bool TransformationMoveInstructionDown::IsInstructionSupported(
135 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
136 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
137 // Add support for more instructions here.
138 return IsSimpleInstruction(ir_context
, inst
) ||
139 IsMemoryInstruction(ir_context
, inst
) || IsBarrierInstruction(inst
);
142 bool TransformationMoveInstructionDown::IsSimpleInstruction(
143 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
144 switch (inst
.opcode()) {
146 case spv::Op::OpUndef
:
147 case spv::Op::OpAccessChain
:
148 case spv::Op::OpInBoundsAccessChain
:
149 // OpAccessChain and OpInBoundsAccessChain are considered simple
150 // instructions since they result in a pointer to the object in memory,
151 // not the object itself.
152 case spv::Op::OpVectorExtractDynamic
:
153 case spv::Op::OpVectorInsertDynamic
:
154 case spv::Op::OpVectorShuffle
:
155 case spv::Op::OpCompositeConstruct
:
156 case spv::Op::OpCompositeExtract
:
157 case spv::Op::OpCompositeInsert
:
158 case spv::Op::OpCopyObject
:
159 case spv::Op::OpTranspose
:
160 case spv::Op::OpConvertFToU
:
161 case spv::Op::OpConvertFToS
:
162 case spv::Op::OpConvertSToF
:
163 case spv::Op::OpConvertUToF
:
164 case spv::Op::OpUConvert
:
165 case spv::Op::OpSConvert
:
166 case spv::Op::OpFConvert
:
167 case spv::Op::OpQuantizeToF16
:
168 case spv::Op::OpSatConvertSToU
:
169 case spv::Op::OpSatConvertUToS
:
170 case spv::Op::OpBitcast
:
171 case spv::Op::OpSNegate
:
172 case spv::Op::OpFNegate
:
173 case spv::Op::OpIAdd
:
174 case spv::Op::OpFAdd
:
175 case spv::Op::OpISub
:
176 case spv::Op::OpFSub
:
177 case spv::Op::OpIMul
:
178 case spv::Op::OpFMul
:
179 case spv::Op::OpUDiv
:
180 case spv::Op::OpSDiv
:
181 case spv::Op::OpFDiv
:
182 case spv::Op::OpUMod
:
183 case spv::Op::OpSRem
:
184 case spv::Op::OpSMod
:
185 case spv::Op::OpFRem
:
186 case spv::Op::OpFMod
:
187 case spv::Op::OpVectorTimesScalar
:
188 case spv::Op::OpMatrixTimesScalar
:
189 case spv::Op::OpVectorTimesMatrix
:
190 case spv::Op::OpMatrixTimesVector
:
191 case spv::Op::OpMatrixTimesMatrix
:
192 case spv::Op::OpOuterProduct
:
194 case spv::Op::OpIAddCarry
:
195 case spv::Op::OpISubBorrow
:
196 case spv::Op::OpUMulExtended
:
197 case spv::Op::OpSMulExtended
:
200 case spv::Op::OpIsNan
:
201 case spv::Op::OpIsInf
:
202 case spv::Op::OpIsFinite
:
203 case spv::Op::OpIsNormal
:
204 case spv::Op::OpSignBitSet
:
205 case spv::Op::OpLessOrGreater
:
206 case spv::Op::OpOrdered
:
207 case spv::Op::OpUnordered
:
208 case spv::Op::OpLogicalEqual
:
209 case spv::Op::OpLogicalNotEqual
:
210 case spv::Op::OpLogicalOr
:
211 case spv::Op::OpLogicalAnd
:
212 case spv::Op::OpLogicalNot
:
213 case spv::Op::OpSelect
:
214 case spv::Op::OpIEqual
:
215 case spv::Op::OpINotEqual
:
216 case spv::Op::OpUGreaterThan
:
217 case spv::Op::OpSGreaterThan
:
218 case spv::Op::OpUGreaterThanEqual
:
219 case spv::Op::OpSGreaterThanEqual
:
220 case spv::Op::OpULessThan
:
221 case spv::Op::OpSLessThan
:
222 case spv::Op::OpULessThanEqual
:
223 case spv::Op::OpSLessThanEqual
:
224 case spv::Op::OpFOrdEqual
:
225 case spv::Op::OpFUnordEqual
:
226 case spv::Op::OpFOrdNotEqual
:
227 case spv::Op::OpFUnordNotEqual
:
228 case spv::Op::OpFOrdLessThan
:
229 case spv::Op::OpFUnordLessThan
:
230 case spv::Op::OpFOrdGreaterThan
:
231 case spv::Op::OpFUnordGreaterThan
:
232 case spv::Op::OpFOrdLessThanEqual
:
233 case spv::Op::OpFUnordLessThanEqual
:
234 case spv::Op::OpFOrdGreaterThanEqual
:
235 case spv::Op::OpFUnordGreaterThanEqual
:
236 case spv::Op::OpShiftRightLogical
:
237 case spv::Op::OpShiftRightArithmetic
:
238 case spv::Op::OpShiftLeftLogical
:
239 case spv::Op::OpBitwiseOr
:
240 case spv::Op::OpBitwiseXor
:
241 case spv::Op::OpBitwiseAnd
:
243 case spv::Op::OpBitFieldInsert
:
244 case spv::Op::OpBitFieldSExtract
:
245 case spv::Op::OpBitFieldUExtract
:
246 case spv::Op::OpBitReverse
:
247 case spv::Op::OpBitCount
:
248 case spv::Op::OpCopyLogical
:
250 case spv::Op::OpExtInst
: {
251 const auto* ext_inst_import
=
252 ir_context
->get_def_use_mgr()->GetDef(inst
.GetSingleWordInOperand(0));
254 if (ext_inst_import
->GetInOperand(0).AsString() != kExtensionSetName
) {
258 switch (static_cast<GLSLstd450
>(inst
.GetSingleWordInOperand(1))) {
259 case GLSLstd450Round
:
260 case GLSLstd450RoundEven
:
261 case GLSLstd450Trunc
:
264 case GLSLstd450FSign
:
265 case GLSLstd450SSign
:
266 case GLSLstd450Floor
:
268 case GLSLstd450Fract
:
269 case GLSLstd450Radians
:
270 case GLSLstd450Degrees
:
280 case GLSLstd450Asinh
:
281 case GLSLstd450Acosh
:
282 case GLSLstd450Atanh
:
283 case GLSLstd450Atan2
:
290 case GLSLstd450InverseSqrt
:
291 case GLSLstd450Determinant
:
292 case GLSLstd450MatrixInverse
:
293 case GLSLstd450ModfStruct
:
300 case GLSLstd450FClamp
:
301 case GLSLstd450UClamp
:
302 case GLSLstd450SClamp
:
306 case GLSLstd450SmoothStep
:
308 case GLSLstd450FrexpStruct
:
309 case GLSLstd450Ldexp
:
310 case GLSLstd450PackSnorm4x8
:
311 case GLSLstd450PackUnorm4x8
:
312 case GLSLstd450PackSnorm2x16
:
313 case GLSLstd450PackUnorm2x16
:
314 case GLSLstd450PackHalf2x16
:
315 case GLSLstd450PackDouble2x32
:
316 case GLSLstd450UnpackSnorm2x16
:
317 case GLSLstd450UnpackUnorm2x16
:
318 case GLSLstd450UnpackHalf2x16
:
319 case GLSLstd450UnpackSnorm4x8
:
320 case GLSLstd450UnpackUnorm4x8
:
321 case GLSLstd450UnpackDouble2x32
:
322 case GLSLstd450Length
:
323 case GLSLstd450Distance
:
324 case GLSLstd450Cross
:
325 case GLSLstd450Normalize
:
326 case GLSLstd450FaceForward
:
327 case GLSLstd450Reflect
:
328 case GLSLstd450Refract
:
329 case GLSLstd450FindILsb
:
330 case GLSLstd450FindSMsb
:
331 case GLSLstd450FindUMsb
:
334 case GLSLstd450NClamp
:
345 bool TransformationMoveInstructionDown::IsMemoryReadInstruction(
346 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
347 switch (inst
.opcode()) {
348 // Some simple instructions.
349 case spv::Op::OpLoad
:
350 case spv::Op::OpCopyMemory
:
351 // Image instructions.
352 case spv::Op::OpImageSampleImplicitLod
:
353 case spv::Op::OpImageSampleExplicitLod
:
354 case spv::Op::OpImageSampleDrefImplicitLod
:
355 case spv::Op::OpImageSampleDrefExplicitLod
:
356 case spv::Op::OpImageSampleProjImplicitLod
:
357 case spv::Op::OpImageSampleProjExplicitLod
:
358 case spv::Op::OpImageSampleProjDrefImplicitLod
:
359 case spv::Op::OpImageSampleProjDrefExplicitLod
:
360 case spv::Op::OpImageFetch
:
361 case spv::Op::OpImageGather
:
362 case spv::Op::OpImageDrefGather
:
363 case spv::Op::OpImageRead
:
364 case spv::Op::OpImageSparseSampleImplicitLod
:
365 case spv::Op::OpImageSparseSampleExplicitLod
:
366 case spv::Op::OpImageSparseSampleDrefImplicitLod
:
367 case spv::Op::OpImageSparseSampleDrefExplicitLod
:
368 case spv::Op::OpImageSparseSampleProjImplicitLod
:
369 case spv::Op::OpImageSparseSampleProjExplicitLod
:
370 case spv::Op::OpImageSparseSampleProjDrefImplicitLod
:
371 case spv::Op::OpImageSparseSampleProjDrefExplicitLod
:
372 case spv::Op::OpImageSparseFetch
:
373 case spv::Op::OpImageSparseGather
:
374 case spv::Op::OpImageSparseDrefGather
:
375 case spv::Op::OpImageSparseRead
:
376 // Atomic instructions.
377 case spv::Op::OpAtomicLoad
:
378 case spv::Op::OpAtomicExchange
:
379 case spv::Op::OpAtomicCompareExchange
:
380 case spv::Op::OpAtomicCompareExchangeWeak
:
381 case spv::Op::OpAtomicIIncrement
:
382 case spv::Op::OpAtomicIDecrement
:
383 case spv::Op::OpAtomicIAdd
:
384 case spv::Op::OpAtomicISub
:
385 case spv::Op::OpAtomicSMin
:
386 case spv::Op::OpAtomicUMin
:
387 case spv::Op::OpAtomicSMax
:
388 case spv::Op::OpAtomicUMax
:
389 case spv::Op::OpAtomicAnd
:
390 case spv::Op::OpAtomicOr
:
391 case spv::Op::OpAtomicXor
:
392 case spv::Op::OpAtomicFlagTestAndSet
:
395 case spv::Op::OpExtInst
: {
396 if (GetExtensionSet(ir_context
, inst
) != kExtensionSetName
) {
400 switch (static_cast<GLSLstd450
>(inst
.GetSingleWordInOperand(1))) {
401 case GLSLstd450InterpolateAtCentroid
:
402 case GLSLstd450InterpolateAtOffset
:
403 case GLSLstd450InterpolateAtSample
:
414 uint32_t TransformationMoveInstructionDown::GetMemoryReadTarget(
415 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
416 (void)ir_context
; // |ir_context| is only used in assertions.
417 assert(IsMemoryReadInstruction(ir_context
, inst
) &&
418 "|inst| is not a memory read instruction");
420 switch (inst
.opcode()) {
421 // Simple instructions.
422 case spv::Op::OpLoad
:
423 // Image instructions.
424 case spv::Op::OpImageSampleImplicitLod
:
425 case spv::Op::OpImageSampleExplicitLod
:
426 case spv::Op::OpImageSampleDrefImplicitLod
:
427 case spv::Op::OpImageSampleDrefExplicitLod
:
428 case spv::Op::OpImageSampleProjImplicitLod
:
429 case spv::Op::OpImageSampleProjExplicitLod
:
430 case spv::Op::OpImageSampleProjDrefImplicitLod
:
431 case spv::Op::OpImageSampleProjDrefExplicitLod
:
432 case spv::Op::OpImageFetch
:
433 case spv::Op::OpImageGather
:
434 case spv::Op::OpImageDrefGather
:
435 case spv::Op::OpImageRead
:
436 case spv::Op::OpImageSparseSampleImplicitLod
:
437 case spv::Op::OpImageSparseSampleExplicitLod
:
438 case spv::Op::OpImageSparseSampleDrefImplicitLod
:
439 case spv::Op::OpImageSparseSampleDrefExplicitLod
:
440 case spv::Op::OpImageSparseSampleProjImplicitLod
:
441 case spv::Op::OpImageSparseSampleProjExplicitLod
:
442 case spv::Op::OpImageSparseSampleProjDrefImplicitLod
:
443 case spv::Op::OpImageSparseSampleProjDrefExplicitLod
:
444 case spv::Op::OpImageSparseFetch
:
445 case spv::Op::OpImageSparseGather
:
446 case spv::Op::OpImageSparseDrefGather
:
447 case spv::Op::OpImageSparseRead
:
448 // Atomic instructions.
449 case spv::Op::OpAtomicLoad
:
450 case spv::Op::OpAtomicExchange
:
451 case spv::Op::OpAtomicCompareExchange
:
452 case spv::Op::OpAtomicCompareExchangeWeak
:
453 case spv::Op::OpAtomicIIncrement
:
454 case spv::Op::OpAtomicIDecrement
:
455 case spv::Op::OpAtomicIAdd
:
456 case spv::Op::OpAtomicISub
:
457 case spv::Op::OpAtomicSMin
:
458 case spv::Op::OpAtomicUMin
:
459 case spv::Op::OpAtomicSMax
:
460 case spv::Op::OpAtomicUMax
:
461 case spv::Op::OpAtomicAnd
:
462 case spv::Op::OpAtomicOr
:
463 case spv::Op::OpAtomicXor
:
464 case spv::Op::OpAtomicFlagTestAndSet
:
465 return inst
.GetSingleWordInOperand(0);
466 case spv::Op::OpCopyMemory
:
467 return inst
.GetSingleWordInOperand(1);
468 case spv::Op::OpExtInst
: {
469 assert(GetExtensionSet(ir_context
, inst
) == kExtensionSetName
&&
470 "Extension set is not supported");
472 switch (static_cast<GLSLstd450
>(inst
.GetSingleWordInOperand(1))) {
473 case GLSLstd450InterpolateAtCentroid
:
474 case GLSLstd450InterpolateAtOffset
:
475 case GLSLstd450InterpolateAtSample
:
476 return inst
.GetSingleWordInOperand(2);
478 // This assertion will fail if not all memory read extension
479 // instructions are handled in the switch.
480 assert(false && "Not all memory opcodes are handled");
485 // This assertion will fail if not all memory read opcodes are handled in
487 assert(false && "Not all memory opcodes are handled");
492 bool TransformationMoveInstructionDown::IsMemoryWriteInstruction(
493 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
494 switch (inst
.opcode()) {
495 // Simple Instructions.
496 case spv::Op::OpStore
:
497 case spv::Op::OpCopyMemory
:
498 // Image instructions.
499 case spv::Op::OpImageWrite
:
500 // Atomic instructions.
501 case spv::Op::OpAtomicStore
:
502 case spv::Op::OpAtomicExchange
:
503 case spv::Op::OpAtomicCompareExchange
:
504 case spv::Op::OpAtomicCompareExchangeWeak
:
505 case spv::Op::OpAtomicIIncrement
:
506 case spv::Op::OpAtomicIDecrement
:
507 case spv::Op::OpAtomicIAdd
:
508 case spv::Op::OpAtomicISub
:
509 case spv::Op::OpAtomicSMin
:
510 case spv::Op::OpAtomicUMin
:
511 case spv::Op::OpAtomicSMax
:
512 case spv::Op::OpAtomicUMax
:
513 case spv::Op::OpAtomicAnd
:
514 case spv::Op::OpAtomicOr
:
515 case spv::Op::OpAtomicXor
:
516 case spv::Op::OpAtomicFlagTestAndSet
:
517 case spv::Op::OpAtomicFlagClear
:
520 case spv::Op::OpExtInst
: {
521 if (GetExtensionSet(ir_context
, inst
) != kExtensionSetName
) {
525 auto extension
= static_cast<GLSLstd450
>(inst
.GetSingleWordInOperand(1));
526 return extension
== GLSLstd450Modf
|| extension
== GLSLstd450Frexp
;
533 uint32_t TransformationMoveInstructionDown::GetMemoryWriteTarget(
534 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
535 (void)ir_context
; // |ir_context| is only used in assertions.
536 assert(IsMemoryWriteInstruction(ir_context
, inst
) &&
537 "|inst| is not a memory write instruction");
539 switch (inst
.opcode()) {
540 case spv::Op::OpStore
:
541 case spv::Op::OpCopyMemory
:
542 case spv::Op::OpImageWrite
:
543 case spv::Op::OpAtomicStore
:
544 case spv::Op::OpAtomicExchange
:
545 case spv::Op::OpAtomicCompareExchange
:
546 case spv::Op::OpAtomicCompareExchangeWeak
:
547 case spv::Op::OpAtomicIIncrement
:
548 case spv::Op::OpAtomicIDecrement
:
549 case spv::Op::OpAtomicIAdd
:
550 case spv::Op::OpAtomicISub
:
551 case spv::Op::OpAtomicSMin
:
552 case spv::Op::OpAtomicUMin
:
553 case spv::Op::OpAtomicSMax
:
554 case spv::Op::OpAtomicUMax
:
555 case spv::Op::OpAtomicAnd
:
556 case spv::Op::OpAtomicOr
:
557 case spv::Op::OpAtomicXor
:
558 case spv::Op::OpAtomicFlagTestAndSet
:
559 case spv::Op::OpAtomicFlagClear
:
560 return inst
.GetSingleWordInOperand(0);
561 case spv::Op::OpExtInst
: {
562 assert(GetExtensionSet(ir_context
, inst
) == kExtensionSetName
&&
563 "Extension set is not supported");
565 switch (static_cast<GLSLstd450
>(inst
.GetSingleWordInOperand(1))) {
567 case GLSLstd450Frexp
:
568 return inst
.GetSingleWordInOperand(3);
570 // This assertion will fail if not all memory write extension
571 // instructions are handled in the switch.
572 assert(false && "Not all opcodes are handled");
577 // This assertion will fail if not all memory write opcodes are handled in
579 assert(false && "Not all opcodes are handled");
584 bool TransformationMoveInstructionDown::IsMemoryInstruction(
585 opt::IRContext
* ir_context
, const opt::Instruction
& inst
) {
586 return IsMemoryReadInstruction(ir_context
, inst
) ||
587 IsMemoryWriteInstruction(ir_context
, inst
);
590 bool TransformationMoveInstructionDown::IsBarrierInstruction(
591 const opt::Instruction
& inst
) {
592 switch (inst
.opcode()) {
593 case spv::Op::OpMemoryBarrier
:
594 case spv::Op::OpControlBarrier
:
595 case spv::Op::OpMemoryNamedBarrier
:
602 bool TransformationMoveInstructionDown::CanSafelySwapInstructions(
603 opt::IRContext
* ir_context
, const opt::Instruction
& a
,
604 const opt::Instruction
& b
, const FactManager
& fact_manager
) {
605 assert(IsInstructionSupported(ir_context
, a
) &&
606 IsInstructionSupported(ir_context
, b
) &&
607 "Both opcodes must be supported");
609 // One of opcodes is simple - we can swap them without any side-effects.
610 if (IsSimpleInstruction(ir_context
, a
) ||
611 IsSimpleInstruction(ir_context
, b
)) {
615 // Both parameters are either memory instruction or barriers.
617 // One of the opcodes is a barrier - can't swap them.
618 if (IsBarrierInstruction(a
) || IsBarrierInstruction(b
)) {
622 // Both parameters are memory instructions.
624 // Both parameters only read from memory - it's OK to swap them.
625 if (!IsMemoryWriteInstruction(ir_context
, a
) &&
626 !IsMemoryWriteInstruction(ir_context
, b
)) {
630 // At least one of parameters is a memory read instruction.
632 // In theory, we can swap two memory instructions, one of which reads
633 // from the memory, if the read target (the pointer the memory is read from)
634 // and the write target (the memory is written into):
635 // - point to different memory regions
636 // - point to the same region with irrelevant value
637 // - point to the same region and the region is not used anymore.
639 // However, we can't currently determine if two pointers point to two
640 // different memory regions. That being said, if two pointers are not
641 // synonymous, they still might point to the same memory region. For example:
642 // %1 = OpVariable ...
643 // %2 = OpAccessChain %1 0
644 // %3 = OpAccessChain %1 0
645 // In this pseudo-code, %2 and %3 are not synonymous but point to the same
646 // memory location. This implies that we can't determine if some memory
647 // location is not used in the block.
649 // With this in mind, consider two cases (we will build a table for each one):
650 // - one instruction only reads from memory, the other one only writes to it.
651 // S - both point to the same memory region.
652 // D - both point to different memory regions.
653 // 0, 1, 2 - neither, one of or both of the memory regions are irrelevant.
654 // |-| - can't swap; |+| - can swap.
658 // - both instructions write to memory. Notation is the same.
662 // * - we can swap two instructions that write into the same non-irrelevant
663 // memory region if the written value is the same.
665 // Note that we can't always distinguish between S and D. Also note that
666 // in case of S, if one of the instructions is marked with
667 // PointeeValueIsIrrelevant, then the pointee of the other one is irrelevant
668 // as well even if the instruction is not marked with that fact.
670 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3723):
671 // This procedure can be improved when we can determine if two pointers point
672 // to different memory regions.
674 // From now on we will denote an instruction that:
675 // - only reads from memory - R
676 // - only writes into memory - W
677 // - reads and writes - RW
679 // Both |a| and |b| can be either W or RW at this point. Additionally, at most
680 // one of them can be R. The procedure below checks all possible combinations
681 // of R, W and RW according to the tables above. We conservatively assume that
682 // both |a| and |b| point to the same memory region.
684 auto memory_is_irrelevant
= [ir_context
, &fact_manager
](uint32_t id
) {
685 const auto* inst
= ir_context
->get_def_use_mgr()->GetDef(id
);
686 if (!inst
->type_id()) {
690 const auto* type
= ir_context
->get_type_mgr()->GetType(inst
->type_id());
691 assert(type
&& "|id| has invalid type");
693 if (!type
->AsPointer()) {
697 return fact_manager
.PointeeValueIsIrrelevant(id
);
700 if (IsMemoryWriteInstruction(ir_context
, a
) &&
701 IsMemoryWriteInstruction(ir_context
, b
) &&
702 (memory_is_irrelevant(GetMemoryWriteTarget(ir_context
, a
)) ||
703 memory_is_irrelevant(GetMemoryWriteTarget(ir_context
, b
)))) {
704 // We ignore the case when the written value is the same. This is because
705 // the written value might not be equal to any of the instruction's
710 if (IsMemoryReadInstruction(ir_context
, a
) &&
711 IsMemoryWriteInstruction(ir_context
, b
) &&
712 !memory_is_irrelevant(GetMemoryReadTarget(ir_context
, a
)) &&
713 !memory_is_irrelevant(GetMemoryWriteTarget(ir_context
, b
))) {
717 if (IsMemoryWriteInstruction(ir_context
, a
) &&
718 IsMemoryReadInstruction(ir_context
, b
) &&
719 !memory_is_irrelevant(GetMemoryWriteTarget(ir_context
, a
)) &&
720 !memory_is_irrelevant(GetMemoryReadTarget(ir_context
, b
))) {
724 return IsMemoryReadInstruction(ir_context
, a
) ||
725 IsMemoryReadInstruction(ir_context
, b
);
728 std::unordered_set
<uint32_t> TransformationMoveInstructionDown::GetFreshIds()
730 return std::unordered_set
<uint32_t>();
734 } // namespace spvtools