transformation_propagate_instruction_up.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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_propagate_instruction_up.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/instruction_descriptor.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. namespace {
  20. uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst,
  21. uint32_t label_id) {
  22. assert(phi_inst.opcode() == spv::Op::OpPhi && "|phi_inst| is not an OpPhi");
  23. for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) {
  24. if (phi_inst.GetSingleWordInOperand(i) == label_id) {
  25. return phi_inst.GetSingleWordInOperand(i - 1);
  26. }
  27. }
  28. return 0;
  29. }
  30. bool ContainsPointers(const opt::analysis::Type& type) {
  31. switch (type.kind()) {
  32. case opt::analysis::Type::kPointer:
  33. return true;
  34. case opt::analysis::Type::kStruct:
  35. return std::any_of(type.AsStruct()->element_types().begin(),
  36. type.AsStruct()->element_types().end(),
  37. [](const opt::analysis::Type* element_type) {
  38. return ContainsPointers(*element_type);
  39. });
  40. default:
  41. return false;
  42. }
  43. }
  44. bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) {
  45. const auto* inst_block = ir_context->get_instr_block(inst);
  46. assert(inst_block &&
  47. "This function shouldn't be applied to global instructions or function"
  48. "parameters");
  49. for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
  50. const auto& operand = inst->GetInOperand(i);
  51. if (operand.type != SPV_OPERAND_TYPE_ID) {
  52. // Consider only <id> operands.
  53. continue;
  54. }
  55. auto* dependency = ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
  56. assert(dependency && "Operand has invalid id");
  57. if (ir_context->get_instr_block(dependency) == inst_block &&
  58. dependency->opcode() != spv::Op::OpPhi) {
  59. // |dependency| is "valid" if it's an OpPhi from the same basic block or
  60. // an instruction from a different basic block.
  61. return false;
  62. }
  63. }
  64. return true;
  65. }
  66. } // namespace
  67. TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
  68. protobufs::TransformationPropagateInstructionUp message)
  69. : message_(std::move(message)) {}
  70. TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
  71. uint32_t block_id,
  72. const std::map<uint32_t, uint32_t>& predecessor_id_to_fresh_id) {
  73. message_.set_block_id(block_id);
  74. *message_.mutable_predecessor_id_to_fresh_id() =
  75. fuzzerutil::MapToRepeatedUInt32Pair(predecessor_id_to_fresh_id);
  76. }
  77. bool TransformationPropagateInstructionUp::IsApplicable(
  78. opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
  79. // Check that we can apply this transformation to the |block_id|.
  80. if (!IsApplicableToBlock(ir_context, message_.block_id())) {
  81. return false;
  82. }
  83. const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
  84. message_.predecessor_id_to_fresh_id());
  85. for (auto id : ir_context->cfg()->preds(message_.block_id())) {
  86. // Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id|
  87. // map.
  88. if (!predecessor_id_to_fresh_id.count(id)) {
  89. return false;
  90. }
  91. }
  92. std::vector<uint32_t> maybe_fresh_ids;
  93. maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size());
  94. for (const auto& entry : predecessor_id_to_fresh_id) {
  95. maybe_fresh_ids.push_back(entry.second);
  96. }
  97. // All ids must be unique and fresh.
  98. return !fuzzerutil::HasDuplicates(maybe_fresh_ids) &&
  99. std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(),
  100. [ir_context](uint32_t id) {
  101. return fuzzerutil::IsFreshId(ir_context, id);
  102. });
  103. }
  104. void TransformationPropagateInstructionUp::Apply(
  105. opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
  106. auto* inst = GetInstructionToPropagate(ir_context, message_.block_id());
  107. assert(inst &&
  108. "The block must have at least one supported instruction to propagate");
  109. assert(inst->result_id() && inst->type_id() &&
  110. "|inst| must have a result id and a type id");
  111. opt::Instruction::OperandList op_phi_operands;
  112. const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
  113. message_.predecessor_id_to_fresh_id());
  114. std::unordered_set<uint32_t> visited_predecessors;
  115. for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) {
  116. // A block can have multiple identical predecessors.
  117. if (visited_predecessors.count(predecessor_id)) {
  118. continue;
  119. }
  120. visited_predecessors.insert(predecessor_id);
  121. auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id);
  122. // Compute InOperands for the OpPhi instruction to be inserted later.
  123. op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {new_result_id}});
  124. op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}});
  125. // Create a clone of the |inst| to be inserted into the |predecessor_id|.
  126. std::unique_ptr<opt::Instruction> clone(inst->Clone(ir_context));
  127. clone->SetResultId(new_result_id);
  128. fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id);
  129. // Adjust |clone|'s operands to account for possible dependencies on OpPhi
  130. // instructions from the same basic block.
  131. for (uint32_t i = 0; i < clone->NumInOperands(); ++i) {
  132. auto& operand = clone->GetInOperand(i);
  133. if (operand.type != SPV_OPERAND_TYPE_ID) {
  134. // Consider only ids.
  135. continue;
  136. }
  137. const auto* dependency_inst =
  138. ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
  139. assert(dependency_inst && "|clone| depends on an invalid id");
  140. if (ir_context->get_instr_block(dependency_inst->result_id()) !=
  141. ir_context->cfg()->block(message_.block_id())) {
  142. // We don't need to adjust anything if |dependency_inst| is from a
  143. // different block, a global instruction or a function parameter.
  144. continue;
  145. }
  146. assert(dependency_inst->opcode() == spv::Op::OpPhi &&
  147. "Propagated instruction can depend only on OpPhis from the same "
  148. "basic block or instructions from different basic blocks");
  149. auto new_id = GetResultIdFromLabelId(*dependency_inst, predecessor_id);
  150. assert(new_id && "OpPhi instruction is missing a predecessor");
  151. operand.words[0] = new_id;
  152. }
  153. auto* insert_before_inst = fuzzerutil::GetLastInsertBeforeInstruction(
  154. ir_context, predecessor_id, clone->opcode());
  155. assert(insert_before_inst && "Can't insert |clone| into |predecessor_id");
  156. insert_before_inst->InsertBefore(std::move(clone));
  157. }
  158. // Insert an OpPhi instruction into the basic block of |inst|.
  159. ir_context->get_instr_block(inst)->begin()->InsertBefore(
  160. MakeUnique<opt::Instruction>(ir_context, spv::Op::OpPhi, inst->type_id(),
  161. inst->result_id(),
  162. std::move(op_phi_operands)));
  163. // Remove |inst| from the basic block.
  164. ir_context->KillInst(inst);
  165. // We have changed the module so most analyzes are now invalid.
  166. ir_context->InvalidateAnalysesExceptFor(
  167. opt::IRContext::Analysis::kAnalysisNone);
  168. }
  169. protobufs::Transformation TransformationPropagateInstructionUp::ToMessage()
  170. const {
  171. protobufs::Transformation result;
  172. *result.mutable_propagate_instruction_up() = message_;
  173. return result;
  174. }
  175. bool TransformationPropagateInstructionUp::IsOpcodeSupported(spv::Op opcode) {
  176. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
  177. // We only support "simple" instructions that don't work with memory.
  178. // We should extend this so that we support the ones that modify the memory
  179. // too.
  180. switch (opcode) {
  181. case spv::Op::OpUndef:
  182. case spv::Op::OpAccessChain:
  183. case spv::Op::OpInBoundsAccessChain:
  184. case spv::Op::OpArrayLength:
  185. case spv::Op::OpVectorExtractDynamic:
  186. case spv::Op::OpVectorInsertDynamic:
  187. case spv::Op::OpVectorShuffle:
  188. case spv::Op::OpCompositeConstruct:
  189. case spv::Op::OpCompositeExtract:
  190. case spv::Op::OpCompositeInsert:
  191. case spv::Op::OpCopyObject:
  192. case spv::Op::OpTranspose:
  193. case spv::Op::OpConvertFToU:
  194. case spv::Op::OpConvertFToS:
  195. case spv::Op::OpConvertSToF:
  196. case spv::Op::OpConvertUToF:
  197. case spv::Op::OpUConvert:
  198. case spv::Op::OpSConvert:
  199. case spv::Op::OpFConvert:
  200. case spv::Op::OpQuantizeToF16:
  201. case spv::Op::OpSatConvertSToU:
  202. case spv::Op::OpSatConvertUToS:
  203. case spv::Op::OpBitcast:
  204. case spv::Op::OpSNegate:
  205. case spv::Op::OpFNegate:
  206. case spv::Op::OpIAdd:
  207. case spv::Op::OpFAdd:
  208. case spv::Op::OpISub:
  209. case spv::Op::OpFSub:
  210. case spv::Op::OpIMul:
  211. case spv::Op::OpFMul:
  212. case spv::Op::OpUDiv:
  213. case spv::Op::OpSDiv:
  214. case spv::Op::OpFDiv:
  215. case spv::Op::OpUMod:
  216. case spv::Op::OpSRem:
  217. case spv::Op::OpSMod:
  218. case spv::Op::OpFRem:
  219. case spv::Op::OpFMod:
  220. case spv::Op::OpVectorTimesScalar:
  221. case spv::Op::OpMatrixTimesScalar:
  222. case spv::Op::OpVectorTimesMatrix:
  223. case spv::Op::OpMatrixTimesVector:
  224. case spv::Op::OpMatrixTimesMatrix:
  225. case spv::Op::OpOuterProduct:
  226. case spv::Op::OpDot:
  227. case spv::Op::OpIAddCarry:
  228. case spv::Op::OpISubBorrow:
  229. case spv::Op::OpUMulExtended:
  230. case spv::Op::OpSMulExtended:
  231. case spv::Op::OpAny:
  232. case spv::Op::OpAll:
  233. case spv::Op::OpIsNan:
  234. case spv::Op::OpIsInf:
  235. case spv::Op::OpIsFinite:
  236. case spv::Op::OpIsNormal:
  237. case spv::Op::OpSignBitSet:
  238. case spv::Op::OpLessOrGreater:
  239. case spv::Op::OpOrdered:
  240. case spv::Op::OpUnordered:
  241. case spv::Op::OpLogicalEqual:
  242. case spv::Op::OpLogicalNotEqual:
  243. case spv::Op::OpLogicalOr:
  244. case spv::Op::OpLogicalAnd:
  245. case spv::Op::OpLogicalNot:
  246. case spv::Op::OpSelect:
  247. case spv::Op::OpIEqual:
  248. case spv::Op::OpINotEqual:
  249. case spv::Op::OpUGreaterThan:
  250. case spv::Op::OpSGreaterThan:
  251. case spv::Op::OpUGreaterThanEqual:
  252. case spv::Op::OpSGreaterThanEqual:
  253. case spv::Op::OpULessThan:
  254. case spv::Op::OpSLessThan:
  255. case spv::Op::OpULessThanEqual:
  256. case spv::Op::OpSLessThanEqual:
  257. case spv::Op::OpFOrdEqual:
  258. case spv::Op::OpFUnordEqual:
  259. case spv::Op::OpFOrdNotEqual:
  260. case spv::Op::OpFUnordNotEqual:
  261. case spv::Op::OpFOrdLessThan:
  262. case spv::Op::OpFUnordLessThan:
  263. case spv::Op::OpFOrdGreaterThan:
  264. case spv::Op::OpFUnordGreaterThan:
  265. case spv::Op::OpFOrdLessThanEqual:
  266. case spv::Op::OpFUnordLessThanEqual:
  267. case spv::Op::OpFOrdGreaterThanEqual:
  268. case spv::Op::OpFUnordGreaterThanEqual:
  269. case spv::Op::OpShiftRightLogical:
  270. case spv::Op::OpShiftRightArithmetic:
  271. case spv::Op::OpShiftLeftLogical:
  272. case spv::Op::OpBitwiseOr:
  273. case spv::Op::OpBitwiseXor:
  274. case spv::Op::OpBitwiseAnd:
  275. case spv::Op::OpNot:
  276. case spv::Op::OpBitFieldInsert:
  277. case spv::Op::OpBitFieldSExtract:
  278. case spv::Op::OpBitFieldUExtract:
  279. case spv::Op::OpBitReverse:
  280. case spv::Op::OpBitCount:
  281. case spv::Op::OpCopyLogical:
  282. case spv::Op::OpPtrEqual:
  283. case spv::Op::OpPtrNotEqual:
  284. return true;
  285. default:
  286. return false;
  287. }
  288. }
  289. opt::Instruction*
  290. TransformationPropagateInstructionUp::GetInstructionToPropagate(
  291. opt::IRContext* ir_context, uint32_t block_id) {
  292. auto* block = ir_context->cfg()->block(block_id);
  293. assert(block && "|block_id| is invalid");
  294. for (auto& inst : *block) {
  295. // We look for the first instruction in the block that satisfies the
  296. // following rules:
  297. // - it's not an OpPhi
  298. // - it must be supported by this transformation
  299. // - it may depend only on instructions from different basic blocks or on
  300. // OpPhi instructions from the same basic block.
  301. if (inst.opcode() == spv::Op::OpPhi || !IsOpcodeSupported(inst.opcode()) ||
  302. !inst.type_id() || !inst.result_id()) {
  303. continue;
  304. }
  305. const auto* inst_type = ir_context->get_type_mgr()->GetType(inst.type_id());
  306. assert(inst_type && "|inst| has invalid type");
  307. if (inst_type->AsSampledImage()) {
  308. // OpTypeSampledImage cannot be used as an argument to OpPhi instructions,
  309. // thus we cannot support this type.
  310. continue;
  311. }
  312. if (!ir_context->get_feature_mgr()->HasCapability(
  313. spv::Capability::VariablePointersStorageBuffer) &&
  314. ContainsPointers(*inst_type)) {
  315. // OpPhi supports pointer operands only with VariablePointers or
  316. // VariablePointersStorageBuffer capabilities.
  317. //
  318. // Note that VariablePointers capability implicitly declares
  319. // VariablePointersStorageBuffer capability.
  320. continue;
  321. }
  322. if (!HasValidDependencies(ir_context, &inst)) {
  323. continue;
  324. }
  325. return &inst;
  326. }
  327. return nullptr;
  328. }
  329. bool TransformationPropagateInstructionUp::IsApplicableToBlock(
  330. opt::IRContext* ir_context, uint32_t block_id) {
  331. // Check that |block_id| is valid.
  332. const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
  333. if (!label_inst || label_inst->opcode() != spv::Op::OpLabel) {
  334. return false;
  335. }
  336. // Check that |block| has predecessors.
  337. const auto& predecessors = ir_context->cfg()->preds(block_id);
  338. if (predecessors.empty()) {
  339. return false;
  340. }
  341. // The block must contain an instruction to propagate.
  342. const auto* inst_to_propagate =
  343. GetInstructionToPropagate(ir_context, block_id);
  344. if (!inst_to_propagate) {
  345. return false;
  346. }
  347. // We should be able to insert |inst_to_propagate| into every predecessor of
  348. // |block|.
  349. return std::all_of(predecessors.begin(), predecessors.end(),
  350. [ir_context, inst_to_propagate](uint32_t predecessor_id) {
  351. return fuzzerutil::GetLastInsertBeforeInstruction(
  352. ir_context, predecessor_id,
  353. inst_to_propagate->opcode()) != nullptr;
  354. });
  355. }
  356. std::unordered_set<uint32_t> TransformationPropagateInstructionUp::GetFreshIds()
  357. const {
  358. std::unordered_set<uint32_t> result;
  359. for (auto& pair : message_.predecessor_id_to_fresh_id()) {
  360. result.insert(pair.second());
  361. }
  362. return result;
  363. }
  364. } // namespace fuzz
  365. } // namespace spvtools