transformation_set_loop_control.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright (c) 2019 Google LLC
  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_set_loop_control.h"
  15. namespace spvtools {
  16. namespace fuzz {
  17. TransformationSetLoopControl::TransformationSetLoopControl(
  18. const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
  19. : message_(message) {}
  20. TransformationSetLoopControl::TransformationSetLoopControl(
  21. uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
  22. uint32_t partial_count) {
  23. message_.set_block_id(block_id);
  24. message_.set_loop_control(loop_control);
  25. message_.set_peel_count(peel_count);
  26. message_.set_partial_count(partial_count);
  27. }
  28. bool TransformationSetLoopControl::IsApplicable(
  29. opt::IRContext* context, const FactManager& /*unused*/) const {
  30. // |message_.block_id| must identify a block that ends with OpLoopMerge.
  31. auto block = context->get_instr_block(message_.block_id());
  32. if (!block) {
  33. return false;
  34. }
  35. auto merge_inst = block->GetMergeInst();
  36. if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) {
  37. return false;
  38. }
  39. // We sanity-check that the transformation does not try to set any meaningless
  40. // bits of the loop control mask.
  41. uint32_t all_loop_control_mask_bits_set =
  42. SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
  43. SpvLoopControlDependencyInfiniteMask |
  44. SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask |
  45. SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask |
  46. SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask;
  47. // The variable is only used in an assertion; the following keeps release-mode
  48. // compilers happy.
  49. (void)(all_loop_control_mask_bits_set);
  50. // No additional bits should be set.
  51. assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
  52. // Grab the loop control mask currently associated with the OpLoopMerge
  53. // instruction.
  54. auto existing_loop_control_mask =
  55. merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
  56. // Check that there is no attempt to set one of the loop controls that
  57. // requires guarantees to hold.
  58. for (SpvLoopControlMask mask :
  59. {SpvLoopControlDependencyInfiniteMask,
  60. SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
  61. SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
  62. // We have a problem if this loop control bit was not set in the original
  63. // loop control mask but is set by the transformation.
  64. if (LoopControlBitIsAddedByTransformation(mask,
  65. existing_loop_control_mask)) {
  66. return false;
  67. }
  68. }
  69. if ((message_.loop_control() &
  70. (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
  71. !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) {
  72. // At least one of PeelCount or PartialCount is used, but the SPIR-V version
  73. // in question does not support these loop controls.
  74. return false;
  75. }
  76. if (message_.peel_count() > 0 &&
  77. !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
  78. // Peel count provided, but peel count mask bit not set.
  79. return false;
  80. }
  81. if (message_.partial_count() > 0 &&
  82. !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
  83. // Partial count provided, but partial count mask bit not set.
  84. return false;
  85. }
  86. // We must not set both 'don't unroll' and one of 'peel count' or 'partial
  87. // count'.
  88. return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
  89. (message_.loop_control() &
  90. (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
  91. }
  92. void TransformationSetLoopControl::Apply(opt::IRContext* context,
  93. FactManager* /*unused*/) const {
  94. // Grab the loop merge instruction and its associated loop control mask.
  95. auto merge_inst =
  96. context->get_instr_block(message_.block_id())->GetMergeInst();
  97. auto existing_loop_control_mask =
  98. merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
  99. // We are going to replace the OpLoopMerge's operands with this list.
  100. opt::Instruction::OperandList new_operands;
  101. // We add the existing merge block and continue target ids.
  102. new_operands.push_back(merge_inst->GetInOperand(0));
  103. new_operands.push_back(merge_inst->GetInOperand(1));
  104. // We use the loop control mask from the transformation.
  105. new_operands.push_back(
  106. {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
  107. // It remains to determine what literals to provide, in association with
  108. // the new loop control mask.
  109. //
  110. // For the loop controls that require guarantees to hold about the number
  111. // of loop iterations, we need to keep, from the original OpLoopMerge, any
  112. // literals associated with loop control bits that are still set.
  113. uint32_t literal_index = 0; // Indexes into the literals from the original
  114. // instruction.
  115. for (SpvLoopControlMask mask :
  116. {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
  117. SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
  118. // Check whether the bit was set in the original loop control mask.
  119. if (existing_loop_control_mask & mask) {
  120. // Check whether the bit is set in the new loop control mask.
  121. if (message_.loop_control() & mask) {
  122. // Add the associated literal to our sequence of replacement operands.
  123. new_operands.push_back(
  124. {SPV_OPERAND_TYPE_LITERAL_INTEGER,
  125. {merge_inst->GetSingleWordInOperand(
  126. kLoopControlFirstLiteralInOperandIndex + literal_index)}});
  127. }
  128. // Increment our index into the original loop control mask's literals,
  129. // whether or not the bit was set in the new mask.
  130. literal_index++;
  131. }
  132. }
  133. // If PeelCount is set in the new mask, |message_.peel_count| provides the
  134. // associated peel count.
  135. if (message_.loop_control() & SpvLoopControlPeelCountMask) {
  136. new_operands.push_back(
  137. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
  138. }
  139. // Similar, but for PartialCount.
  140. if (message_.loop_control() & SpvLoopControlPartialCountMask) {
  141. new_operands.push_back(
  142. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
  143. }
  144. // Replace the input operands of the OpLoopMerge with the new operands we have
  145. // accumulated.
  146. merge_inst->SetInOperands(std::move(new_operands));
  147. }
  148. protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
  149. protobufs::Transformation result;
  150. *result.mutable_set_loop_control() = message_;
  151. return result;
  152. }
  153. bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
  154. SpvLoopControlMask loop_control_single_bit_mask,
  155. uint32_t existing_loop_control_mask) const {
  156. return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
  157. (loop_control_single_bit_mask & message_.loop_control());
  158. }
  159. bool TransformationSetLoopControl::PartialCountIsSupported(
  160. opt::IRContext* context) {
  161. // TODO(afd): We capture the universal environments for which this loop
  162. // control is definitely not supported. The check should be refined on
  163. // demand for other target environments.
  164. switch (context->grammar().target_env()) {
  165. case SPV_ENV_UNIVERSAL_1_0:
  166. case SPV_ENV_UNIVERSAL_1_1:
  167. case SPV_ENV_UNIVERSAL_1_2:
  168. case SPV_ENV_UNIVERSAL_1_3:
  169. return false;
  170. default:
  171. return true;
  172. }
  173. }
  174. bool TransformationSetLoopControl::PeelCountIsSupported(
  175. opt::IRContext* context) {
  176. // TODO(afd): We capture the universal environments for which this loop
  177. // control is definitely not supported. The check should be refined on
  178. // demand for other target environments.
  179. switch (context->grammar().target_env()) {
  180. case SPV_ENV_UNIVERSAL_1_0:
  181. case SPV_ENV_UNIVERSAL_1_1:
  182. case SPV_ENV_UNIVERSAL_1_2:
  183. case SPV_ENV_UNIVERSAL_1_3:
  184. return false;
  185. default:
  186. return true;
  187. }
  188. }
  189. } // namespace fuzz
  190. } // namespace spvtools