transformation_move_instruction_down.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. // Copyright (c) 2020 Vasyl Teliman
  2. //
  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
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  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.
  14. #include "source/fuzz/transformation_move_instruction_down.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/instruction_descriptor.h"
  17. #include "spirv/unified1/GLSL.std.450.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. namespace {
  21. const char* const kExtensionSetName = "GLSL.std.450";
  22. std::string GetExtensionSet(opt::IRContext* ir_context,
  23. const opt::Instruction& op_ext_inst) {
  24. assert(op_ext_inst.opcode() == spv::Op::OpExtInst && "Wrong opcode");
  25. const auto* ext_inst_import = ir_context->get_def_use_mgr()->GetDef(
  26. op_ext_inst.GetSingleWordInOperand(0));
  27. assert(ext_inst_import && "Extension set is not imported");
  28. return ext_inst_import->GetInOperand(0).AsString();
  29. }
  30. } // namespace
  31. TransformationMoveInstructionDown::TransformationMoveInstructionDown(
  32. protobufs::TransformationMoveInstructionDown message)
  33. : message_(std::move(message)) {}
  34. TransformationMoveInstructionDown::TransformationMoveInstructionDown(
  35. const protobufs::InstructionDescriptor& instruction) {
  36. *message_.mutable_instruction() = instruction;
  37. }
  38. bool TransformationMoveInstructionDown::IsApplicable(
  39. opt::IRContext* ir_context,
  40. const TransformationContext& transformation_context) const {
  41. // |instruction| must be valid.
  42. auto* inst = FindInstruction(message_.instruction(), ir_context);
  43. if (!inst) {
  44. return false;
  45. }
  46. // Instruction's opcode must be supported by this transformation.
  47. if (!IsInstructionSupported(ir_context, *inst)) {
  48. return false;
  49. }
  50. auto* inst_block = ir_context->get_instr_block(inst);
  51. assert(inst_block &&
  52. "Global instructions and function parameters are not supported");
  53. auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst);
  54. assert(inst_it != inst_block->end() &&
  55. "Can't get an iterator for the instruction");
  56. // |instruction| can't be the last instruction in the block.
  57. auto successor_it = ++inst_it;
  58. if (successor_it == inst_block->end()) {
  59. return false;
  60. }
  61. // We don't risk swapping a memory instruction with an unsupported one.
  62. if (!IsSimpleInstruction(ir_context, *inst) &&
  63. !IsInstructionSupported(ir_context, *successor_it)) {
  64. return false;
  65. }
  66. // It must be safe to swap the instructions without changing the semantics of
  67. // the module.
  68. if (IsInstructionSupported(ir_context, *successor_it) &&
  69. !CanSafelySwapInstructions(ir_context, *inst, *successor_it,
  70. *transformation_context.GetFactManager())) {
  71. return false;
  72. }
  73. // Check that we can insert |instruction| after |inst_it|.
  74. auto successors_successor_it = ++inst_it;
  75. if (successors_successor_it == inst_block->end() ||
  76. !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(),
  77. successors_successor_it)) {
  78. return false;
  79. }
  80. // Check that |instruction|'s successor doesn't depend on the |instruction|.
  81. if (inst->result_id()) {
  82. for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) {
  83. const auto& operand = successor_it->GetInOperand(i);
  84. if (spvIsInIdType(operand.type) &&
  85. operand.words[0] == inst->result_id()) {
  86. return false;
  87. }
  88. }
  89. }
  90. return true;
  91. }
  92. void TransformationMoveInstructionDown::Apply(
  93. opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
  94. auto* inst = FindInstruction(message_.instruction(), ir_context);
  95. assert(inst &&
  96. "The instruction should've been validated in the IsApplicable");
  97. auto inst_it = fuzzerutil::GetIteratorForInstruction(
  98. ir_context->get_instr_block(inst), inst);
  99. // Move the instruction down in the block.
  100. inst->InsertAfter(&*++inst_it);
  101. ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone);
  102. }
  103. protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const {
  104. protobufs::Transformation result;
  105. *result.mutable_move_instruction_down() = message_;
  106. return result;
  107. }
  108. bool TransformationMoveInstructionDown::IsInstructionSupported(
  109. opt::IRContext* ir_context, const opt::Instruction& inst) {
  110. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
  111. // Add support for more instructions here.
  112. return IsSimpleInstruction(ir_context, inst) ||
  113. IsMemoryInstruction(ir_context, inst) || IsBarrierInstruction(inst);
  114. }
  115. bool TransformationMoveInstructionDown::IsSimpleInstruction(
  116. opt::IRContext* ir_context, const opt::Instruction& inst) {
  117. switch (inst.opcode()) {
  118. case spv::Op::OpNop:
  119. case spv::Op::OpUndef:
  120. case spv::Op::OpAccessChain:
  121. case spv::Op::OpInBoundsAccessChain:
  122. // OpAccessChain and OpInBoundsAccessChain are considered simple
  123. // instructions since they result in a pointer to the object in memory,
  124. // not the object itself.
  125. case spv::Op::OpVectorExtractDynamic:
  126. case spv::Op::OpVectorInsertDynamic:
  127. case spv::Op::OpVectorShuffle:
  128. case spv::Op::OpCompositeConstruct:
  129. case spv::Op::OpCompositeExtract:
  130. case spv::Op::OpCompositeInsert:
  131. case spv::Op::OpCopyObject:
  132. case spv::Op::OpTranspose:
  133. case spv::Op::OpConvertFToU:
  134. case spv::Op::OpConvertFToS:
  135. case spv::Op::OpConvertSToF:
  136. case spv::Op::OpConvertUToF:
  137. case spv::Op::OpUConvert:
  138. case spv::Op::OpSConvert:
  139. case spv::Op::OpFConvert:
  140. case spv::Op::OpQuantizeToF16:
  141. case spv::Op::OpSatConvertSToU:
  142. case spv::Op::OpSatConvertUToS:
  143. case spv::Op::OpBitcast:
  144. case spv::Op::OpSNegate:
  145. case spv::Op::OpFNegate:
  146. case spv::Op::OpIAdd:
  147. case spv::Op::OpFAdd:
  148. case spv::Op::OpISub:
  149. case spv::Op::OpFSub:
  150. case spv::Op::OpIMul:
  151. case spv::Op::OpFMul:
  152. case spv::Op::OpUDiv:
  153. case spv::Op::OpSDiv:
  154. case spv::Op::OpFDiv:
  155. case spv::Op::OpUMod:
  156. case spv::Op::OpSRem:
  157. case spv::Op::OpSMod:
  158. case spv::Op::OpFRem:
  159. case spv::Op::OpFMod:
  160. case spv::Op::OpVectorTimesScalar:
  161. case spv::Op::OpMatrixTimesScalar:
  162. case spv::Op::OpVectorTimesMatrix:
  163. case spv::Op::OpMatrixTimesVector:
  164. case spv::Op::OpMatrixTimesMatrix:
  165. case spv::Op::OpOuterProduct:
  166. case spv::Op::OpDot:
  167. case spv::Op::OpIAddCarry:
  168. case spv::Op::OpISubBorrow:
  169. case spv::Op::OpUMulExtended:
  170. case spv::Op::OpSMulExtended:
  171. case spv::Op::OpAny:
  172. case spv::Op::OpAll:
  173. case spv::Op::OpIsNan:
  174. case spv::Op::OpIsInf:
  175. case spv::Op::OpIsFinite:
  176. case spv::Op::OpIsNormal:
  177. case spv::Op::OpSignBitSet:
  178. case spv::Op::OpLessOrGreater:
  179. case spv::Op::OpOrdered:
  180. case spv::Op::OpUnordered:
  181. case spv::Op::OpLogicalEqual:
  182. case spv::Op::OpLogicalNotEqual:
  183. case spv::Op::OpLogicalOr:
  184. case spv::Op::OpLogicalAnd:
  185. case spv::Op::OpLogicalNot:
  186. case spv::Op::OpSelect:
  187. case spv::Op::OpIEqual:
  188. case spv::Op::OpINotEqual:
  189. case spv::Op::OpUGreaterThan:
  190. case spv::Op::OpSGreaterThan:
  191. case spv::Op::OpUGreaterThanEqual:
  192. case spv::Op::OpSGreaterThanEqual:
  193. case spv::Op::OpULessThan:
  194. case spv::Op::OpSLessThan:
  195. case spv::Op::OpULessThanEqual:
  196. case spv::Op::OpSLessThanEqual:
  197. case spv::Op::OpFOrdEqual:
  198. case spv::Op::OpFUnordEqual:
  199. case spv::Op::OpFOrdNotEqual:
  200. case spv::Op::OpFUnordNotEqual:
  201. case spv::Op::OpFOrdLessThan:
  202. case spv::Op::OpFUnordLessThan:
  203. case spv::Op::OpFOrdGreaterThan:
  204. case spv::Op::OpFUnordGreaterThan:
  205. case spv::Op::OpFOrdLessThanEqual:
  206. case spv::Op::OpFUnordLessThanEqual:
  207. case spv::Op::OpFOrdGreaterThanEqual:
  208. case spv::Op::OpFUnordGreaterThanEqual:
  209. case spv::Op::OpShiftRightLogical:
  210. case spv::Op::OpShiftRightArithmetic:
  211. case spv::Op::OpShiftLeftLogical:
  212. case spv::Op::OpBitwiseOr:
  213. case spv::Op::OpBitwiseXor:
  214. case spv::Op::OpBitwiseAnd:
  215. case spv::Op::OpNot:
  216. case spv::Op::OpBitFieldInsert:
  217. case spv::Op::OpBitFieldSExtract:
  218. case spv::Op::OpBitFieldUExtract:
  219. case spv::Op::OpBitReverse:
  220. case spv::Op::OpBitCount:
  221. case spv::Op::OpCopyLogical:
  222. return true;
  223. case spv::Op::OpExtInst: {
  224. const auto* ext_inst_import =
  225. ir_context->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
  226. if (ext_inst_import->GetInOperand(0).AsString() != kExtensionSetName) {
  227. return false;
  228. }
  229. switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
  230. case GLSLstd450Round:
  231. case GLSLstd450RoundEven:
  232. case GLSLstd450Trunc:
  233. case GLSLstd450FAbs:
  234. case GLSLstd450SAbs:
  235. case GLSLstd450FSign:
  236. case GLSLstd450SSign:
  237. case GLSLstd450Floor:
  238. case GLSLstd450Ceil:
  239. case GLSLstd450Fract:
  240. case GLSLstd450Radians:
  241. case GLSLstd450Degrees:
  242. case GLSLstd450Sin:
  243. case GLSLstd450Cos:
  244. case GLSLstd450Tan:
  245. case GLSLstd450Asin:
  246. case GLSLstd450Acos:
  247. case GLSLstd450Atan:
  248. case GLSLstd450Sinh:
  249. case GLSLstd450Cosh:
  250. case GLSLstd450Tanh:
  251. case GLSLstd450Asinh:
  252. case GLSLstd450Acosh:
  253. case GLSLstd450Atanh:
  254. case GLSLstd450Atan2:
  255. case GLSLstd450Pow:
  256. case GLSLstd450Exp:
  257. case GLSLstd450Log:
  258. case GLSLstd450Exp2:
  259. case GLSLstd450Log2:
  260. case GLSLstd450Sqrt:
  261. case GLSLstd450InverseSqrt:
  262. case GLSLstd450Determinant:
  263. case GLSLstd450MatrixInverse:
  264. case GLSLstd450ModfStruct:
  265. case GLSLstd450FMin:
  266. case GLSLstd450UMin:
  267. case GLSLstd450SMin:
  268. case GLSLstd450FMax:
  269. case GLSLstd450UMax:
  270. case GLSLstd450SMax:
  271. case GLSLstd450FClamp:
  272. case GLSLstd450UClamp:
  273. case GLSLstd450SClamp:
  274. case GLSLstd450FMix:
  275. case GLSLstd450IMix:
  276. case GLSLstd450Step:
  277. case GLSLstd450SmoothStep:
  278. case GLSLstd450Fma:
  279. case GLSLstd450FrexpStruct:
  280. case GLSLstd450Ldexp:
  281. case GLSLstd450PackSnorm4x8:
  282. case GLSLstd450PackUnorm4x8:
  283. case GLSLstd450PackSnorm2x16:
  284. case GLSLstd450PackUnorm2x16:
  285. case GLSLstd450PackHalf2x16:
  286. case GLSLstd450PackDouble2x32:
  287. case GLSLstd450UnpackSnorm2x16:
  288. case GLSLstd450UnpackUnorm2x16:
  289. case GLSLstd450UnpackHalf2x16:
  290. case GLSLstd450UnpackSnorm4x8:
  291. case GLSLstd450UnpackUnorm4x8:
  292. case GLSLstd450UnpackDouble2x32:
  293. case GLSLstd450Length:
  294. case GLSLstd450Distance:
  295. case GLSLstd450Cross:
  296. case GLSLstd450Normalize:
  297. case GLSLstd450FaceForward:
  298. case GLSLstd450Reflect:
  299. case GLSLstd450Refract:
  300. case GLSLstd450FindILsb:
  301. case GLSLstd450FindSMsb:
  302. case GLSLstd450FindUMsb:
  303. case GLSLstd450NMin:
  304. case GLSLstd450NMax:
  305. case GLSLstd450NClamp:
  306. return true;
  307. default:
  308. return false;
  309. }
  310. }
  311. default:
  312. return false;
  313. }
  314. }
  315. bool TransformationMoveInstructionDown::IsMemoryReadInstruction(
  316. opt::IRContext* ir_context, const opt::Instruction& inst) {
  317. switch (inst.opcode()) {
  318. // Some simple instructions.
  319. case spv::Op::OpLoad:
  320. case spv::Op::OpCopyMemory:
  321. // Image instructions.
  322. case spv::Op::OpImageSampleImplicitLod:
  323. case spv::Op::OpImageSampleExplicitLod:
  324. case spv::Op::OpImageSampleDrefImplicitLod:
  325. case spv::Op::OpImageSampleDrefExplicitLod:
  326. case spv::Op::OpImageSampleProjImplicitLod:
  327. case spv::Op::OpImageSampleProjExplicitLod:
  328. case spv::Op::OpImageSampleProjDrefImplicitLod:
  329. case spv::Op::OpImageSampleProjDrefExplicitLod:
  330. case spv::Op::OpImageFetch:
  331. case spv::Op::OpImageGather:
  332. case spv::Op::OpImageDrefGather:
  333. case spv::Op::OpImageRead:
  334. case spv::Op::OpImageSparseSampleImplicitLod:
  335. case spv::Op::OpImageSparseSampleExplicitLod:
  336. case spv::Op::OpImageSparseSampleDrefImplicitLod:
  337. case spv::Op::OpImageSparseSampleDrefExplicitLod:
  338. case spv::Op::OpImageSparseSampleProjImplicitLod:
  339. case spv::Op::OpImageSparseSampleProjExplicitLod:
  340. case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
  341. case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
  342. case spv::Op::OpImageSparseFetch:
  343. case spv::Op::OpImageSparseGather:
  344. case spv::Op::OpImageSparseDrefGather:
  345. case spv::Op::OpImageSparseRead:
  346. // Atomic instructions.
  347. case spv::Op::OpAtomicLoad:
  348. case spv::Op::OpAtomicExchange:
  349. case spv::Op::OpAtomicCompareExchange:
  350. case spv::Op::OpAtomicCompareExchangeWeak:
  351. case spv::Op::OpAtomicIIncrement:
  352. case spv::Op::OpAtomicIDecrement:
  353. case spv::Op::OpAtomicIAdd:
  354. case spv::Op::OpAtomicISub:
  355. case spv::Op::OpAtomicSMin:
  356. case spv::Op::OpAtomicUMin:
  357. case spv::Op::OpAtomicSMax:
  358. case spv::Op::OpAtomicUMax:
  359. case spv::Op::OpAtomicAnd:
  360. case spv::Op::OpAtomicOr:
  361. case spv::Op::OpAtomicXor:
  362. case spv::Op::OpAtomicFlagTestAndSet:
  363. return true;
  364. // Extensions.
  365. case spv::Op::OpExtInst: {
  366. if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
  367. return false;
  368. }
  369. switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
  370. case GLSLstd450InterpolateAtCentroid:
  371. case GLSLstd450InterpolateAtOffset:
  372. case GLSLstd450InterpolateAtSample:
  373. return true;
  374. default:
  375. return false;
  376. }
  377. }
  378. default:
  379. return false;
  380. }
  381. }
  382. uint32_t TransformationMoveInstructionDown::GetMemoryReadTarget(
  383. opt::IRContext* ir_context, const opt::Instruction& inst) {
  384. (void)ir_context; // |ir_context| is only used in assertions.
  385. assert(IsMemoryReadInstruction(ir_context, inst) &&
  386. "|inst| is not a memory read instruction");
  387. switch (inst.opcode()) {
  388. // Simple instructions.
  389. case spv::Op::OpLoad:
  390. // Image instructions.
  391. case spv::Op::OpImageSampleImplicitLod:
  392. case spv::Op::OpImageSampleExplicitLod:
  393. case spv::Op::OpImageSampleDrefImplicitLod:
  394. case spv::Op::OpImageSampleDrefExplicitLod:
  395. case spv::Op::OpImageSampleProjImplicitLod:
  396. case spv::Op::OpImageSampleProjExplicitLod:
  397. case spv::Op::OpImageSampleProjDrefImplicitLod:
  398. case spv::Op::OpImageSampleProjDrefExplicitLod:
  399. case spv::Op::OpImageFetch:
  400. case spv::Op::OpImageGather:
  401. case spv::Op::OpImageDrefGather:
  402. case spv::Op::OpImageRead:
  403. case spv::Op::OpImageSparseSampleImplicitLod:
  404. case spv::Op::OpImageSparseSampleExplicitLod:
  405. case spv::Op::OpImageSparseSampleDrefImplicitLod:
  406. case spv::Op::OpImageSparseSampleDrefExplicitLod:
  407. case spv::Op::OpImageSparseSampleProjImplicitLod:
  408. case spv::Op::OpImageSparseSampleProjExplicitLod:
  409. case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
  410. case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
  411. case spv::Op::OpImageSparseFetch:
  412. case spv::Op::OpImageSparseGather:
  413. case spv::Op::OpImageSparseDrefGather:
  414. case spv::Op::OpImageSparseRead:
  415. // Atomic instructions.
  416. case spv::Op::OpAtomicLoad:
  417. case spv::Op::OpAtomicExchange:
  418. case spv::Op::OpAtomicCompareExchange:
  419. case spv::Op::OpAtomicCompareExchangeWeak:
  420. case spv::Op::OpAtomicIIncrement:
  421. case spv::Op::OpAtomicIDecrement:
  422. case spv::Op::OpAtomicIAdd:
  423. case spv::Op::OpAtomicISub:
  424. case spv::Op::OpAtomicSMin:
  425. case spv::Op::OpAtomicUMin:
  426. case spv::Op::OpAtomicSMax:
  427. case spv::Op::OpAtomicUMax:
  428. case spv::Op::OpAtomicAnd:
  429. case spv::Op::OpAtomicOr:
  430. case spv::Op::OpAtomicXor:
  431. case spv::Op::OpAtomicFlagTestAndSet:
  432. return inst.GetSingleWordInOperand(0);
  433. case spv::Op::OpCopyMemory:
  434. return inst.GetSingleWordInOperand(1);
  435. case spv::Op::OpExtInst: {
  436. assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
  437. "Extension set is not supported");
  438. switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
  439. case GLSLstd450InterpolateAtCentroid:
  440. case GLSLstd450InterpolateAtOffset:
  441. case GLSLstd450InterpolateAtSample:
  442. return inst.GetSingleWordInOperand(2);
  443. default:
  444. // This assertion will fail if not all memory read extension
  445. // instructions are handled in the switch.
  446. assert(false && "Not all memory opcodes are handled");
  447. return 0;
  448. }
  449. }
  450. default:
  451. // This assertion will fail if not all memory read opcodes are handled in
  452. // the switch.
  453. assert(false && "Not all memory opcodes are handled");
  454. return 0;
  455. }
  456. }
  457. bool TransformationMoveInstructionDown::IsMemoryWriteInstruction(
  458. opt::IRContext* ir_context, const opt::Instruction& inst) {
  459. switch (inst.opcode()) {
  460. // Simple Instructions.
  461. case spv::Op::OpStore:
  462. case spv::Op::OpCopyMemory:
  463. // Image instructions.
  464. case spv::Op::OpImageWrite:
  465. // Atomic instructions.
  466. case spv::Op::OpAtomicStore:
  467. case spv::Op::OpAtomicExchange:
  468. case spv::Op::OpAtomicCompareExchange:
  469. case spv::Op::OpAtomicCompareExchangeWeak:
  470. case spv::Op::OpAtomicIIncrement:
  471. case spv::Op::OpAtomicIDecrement:
  472. case spv::Op::OpAtomicIAdd:
  473. case spv::Op::OpAtomicISub:
  474. case spv::Op::OpAtomicSMin:
  475. case spv::Op::OpAtomicUMin:
  476. case spv::Op::OpAtomicSMax:
  477. case spv::Op::OpAtomicUMax:
  478. case spv::Op::OpAtomicAnd:
  479. case spv::Op::OpAtomicOr:
  480. case spv::Op::OpAtomicXor:
  481. case spv::Op::OpAtomicFlagTestAndSet:
  482. case spv::Op::OpAtomicFlagClear:
  483. return true;
  484. // Extensions.
  485. case spv::Op::OpExtInst: {
  486. if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
  487. return false;
  488. }
  489. auto extension = static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1));
  490. return extension == GLSLstd450Modf || extension == GLSLstd450Frexp;
  491. }
  492. default:
  493. return false;
  494. }
  495. }
  496. uint32_t TransformationMoveInstructionDown::GetMemoryWriteTarget(
  497. opt::IRContext* ir_context, const opt::Instruction& inst) {
  498. (void)ir_context; // |ir_context| is only used in assertions.
  499. assert(IsMemoryWriteInstruction(ir_context, inst) &&
  500. "|inst| is not a memory write instruction");
  501. switch (inst.opcode()) {
  502. case spv::Op::OpStore:
  503. case spv::Op::OpCopyMemory:
  504. case spv::Op::OpImageWrite:
  505. case spv::Op::OpAtomicStore:
  506. case spv::Op::OpAtomicExchange:
  507. case spv::Op::OpAtomicCompareExchange:
  508. case spv::Op::OpAtomicCompareExchangeWeak:
  509. case spv::Op::OpAtomicIIncrement:
  510. case spv::Op::OpAtomicIDecrement:
  511. case spv::Op::OpAtomicIAdd:
  512. case spv::Op::OpAtomicISub:
  513. case spv::Op::OpAtomicSMin:
  514. case spv::Op::OpAtomicUMin:
  515. case spv::Op::OpAtomicSMax:
  516. case spv::Op::OpAtomicUMax:
  517. case spv::Op::OpAtomicAnd:
  518. case spv::Op::OpAtomicOr:
  519. case spv::Op::OpAtomicXor:
  520. case spv::Op::OpAtomicFlagTestAndSet:
  521. case spv::Op::OpAtomicFlagClear:
  522. return inst.GetSingleWordInOperand(0);
  523. case spv::Op::OpExtInst: {
  524. assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
  525. "Extension set is not supported");
  526. switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
  527. case GLSLstd450Modf:
  528. case GLSLstd450Frexp:
  529. return inst.GetSingleWordInOperand(3);
  530. default:
  531. // This assertion will fail if not all memory write extension
  532. // instructions are handled in the switch.
  533. assert(false && "Not all opcodes are handled");
  534. return 0;
  535. }
  536. }
  537. default:
  538. // This assertion will fail if not all memory write opcodes are handled in
  539. // the switch.
  540. assert(false && "Not all opcodes are handled");
  541. return 0;
  542. }
  543. }
  544. bool TransformationMoveInstructionDown::IsMemoryInstruction(
  545. opt::IRContext* ir_context, const opt::Instruction& inst) {
  546. return IsMemoryReadInstruction(ir_context, inst) ||
  547. IsMemoryWriteInstruction(ir_context, inst);
  548. }
  549. bool TransformationMoveInstructionDown::IsBarrierInstruction(
  550. const opt::Instruction& inst) {
  551. switch (inst.opcode()) {
  552. case spv::Op::OpMemoryBarrier:
  553. case spv::Op::OpControlBarrier:
  554. case spv::Op::OpMemoryNamedBarrier:
  555. return true;
  556. default:
  557. return false;
  558. }
  559. }
  560. bool TransformationMoveInstructionDown::CanSafelySwapInstructions(
  561. opt::IRContext* ir_context, const opt::Instruction& a,
  562. const opt::Instruction& b, const FactManager& fact_manager) {
  563. assert(IsInstructionSupported(ir_context, a) &&
  564. IsInstructionSupported(ir_context, b) &&
  565. "Both opcodes must be supported");
  566. // One of opcodes is simple - we can swap them without any side-effects.
  567. if (IsSimpleInstruction(ir_context, a) ||
  568. IsSimpleInstruction(ir_context, b)) {
  569. return true;
  570. }
  571. // Both parameters are either memory instruction or barriers.
  572. // One of the opcodes is a barrier - can't swap them.
  573. if (IsBarrierInstruction(a) || IsBarrierInstruction(b)) {
  574. return false;
  575. }
  576. // Both parameters are memory instructions.
  577. // Both parameters only read from memory - it's OK to swap them.
  578. if (!IsMemoryWriteInstruction(ir_context, a) &&
  579. !IsMemoryWriteInstruction(ir_context, b)) {
  580. return true;
  581. }
  582. // At least one of parameters is a memory read instruction.
  583. // In theory, we can swap two memory instructions, one of which reads
  584. // from the memory, if the read target (the pointer the memory is read from)
  585. // and the write target (the memory is written into):
  586. // - point to different memory regions
  587. // - point to the same region with irrelevant value
  588. // - point to the same region and the region is not used anymore.
  589. //
  590. // However, we can't currently determine if two pointers point to two
  591. // different memory regions. That being said, if two pointers are not
  592. // synonymous, they still might point to the same memory region. For example:
  593. // %1 = OpVariable ...
  594. // %2 = OpAccessChain %1 0
  595. // %3 = OpAccessChain %1 0
  596. // In this pseudo-code, %2 and %3 are not synonymous but point to the same
  597. // memory location. This implies that we can't determine if some memory
  598. // location is not used in the block.
  599. //
  600. // With this in mind, consider two cases (we will build a table for each one):
  601. // - one instruction only reads from memory, the other one only writes to it.
  602. // S - both point to the same memory region.
  603. // D - both point to different memory regions.
  604. // 0, 1, 2 - neither, one of or both of the memory regions are irrelevant.
  605. // |-| - can't swap; |+| - can swap.
  606. // | 0 | 1 | 2 |
  607. // S : - + +
  608. // D : + + +
  609. // - both instructions write to memory. Notation is the same.
  610. // | 0 | 1 | 2 |
  611. // S : * + +
  612. // D : + + +
  613. // * - we can swap two instructions that write into the same non-irrelevant
  614. // memory region if the written value is the same.
  615. //
  616. // Note that we can't always distinguish between S and D. Also note that
  617. // in case of S, if one of the instructions is marked with
  618. // PointeeValueIsIrrelevant, then the pointee of the other one is irrelevant
  619. // as well even if the instruction is not marked with that fact.
  620. //
  621. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3723):
  622. // This procedure can be improved when we can determine if two pointers point
  623. // to different memory regions.
  624. // From now on we will denote an instruction that:
  625. // - only reads from memory - R
  626. // - only writes into memory - W
  627. // - reads and writes - RW
  628. //
  629. // Both |a| and |b| can be either W or RW at this point. Additionally, at most
  630. // one of them can be R. The procedure below checks all possible combinations
  631. // of R, W and RW according to the tables above. We conservatively assume that
  632. // both |a| and |b| point to the same memory region.
  633. auto memory_is_irrelevant = [ir_context, &fact_manager](uint32_t id) {
  634. const auto* inst = ir_context->get_def_use_mgr()->GetDef(id);
  635. if (!inst->type_id()) {
  636. return false;
  637. }
  638. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  639. assert(type && "|id| has invalid type");
  640. if (!type->AsPointer()) {
  641. return false;
  642. }
  643. return fact_manager.PointeeValueIsIrrelevant(id);
  644. };
  645. if (IsMemoryWriteInstruction(ir_context, a) &&
  646. IsMemoryWriteInstruction(ir_context, b) &&
  647. (memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) ||
  648. memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b)))) {
  649. // We ignore the case when the written value is the same. This is because
  650. // the written value might not be equal to any of the instruction's
  651. // operands.
  652. return true;
  653. }
  654. if (IsMemoryReadInstruction(ir_context, a) &&
  655. IsMemoryWriteInstruction(ir_context, b) &&
  656. !memory_is_irrelevant(GetMemoryReadTarget(ir_context, a)) &&
  657. !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b))) {
  658. return false;
  659. }
  660. if (IsMemoryWriteInstruction(ir_context, a) &&
  661. IsMemoryReadInstruction(ir_context, b) &&
  662. !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) &&
  663. !memory_is_irrelevant(GetMemoryReadTarget(ir_context, b))) {
  664. return false;
  665. }
  666. return IsMemoryReadInstruction(ir_context, a) ||
  667. IsMemoryReadInstruction(ir_context, b);
  668. }
  669. std::unordered_set<uint32_t> TransformationMoveInstructionDown::GetFreshIds()
  670. const {
  671. return std::unordered_set<uint32_t>();
  672. }
  673. } // namespace fuzz
  674. } // namespace spvtools