transformation_add_bit_instruction_synonym.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright (c) 2020 André Perez Maselco
  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_add_bit_instruction_synonym.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/instruction_descriptor.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
  20. protobufs::TransformationAddBitInstructionSynonym message)
  21. : message_(std::move(message)) {}
  22. TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
  23. const uint32_t instruction_result_id,
  24. const std::vector<uint32_t>& fresh_ids) {
  25. message_.set_instruction_result_id(instruction_result_id);
  26. *message_.mutable_fresh_ids() =
  27. google::protobuf::RepeatedField<google::protobuf::uint32>(
  28. fresh_ids.begin(), fresh_ids.end());
  29. }
  30. bool TransformationAddBitInstructionSynonym::IsApplicable(
  31. opt::IRContext* ir_context,
  32. const TransformationContext& transformation_context) const {
  33. auto instruction =
  34. ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
  35. // Checks on: only integer operands are supported, instructions are bitwise
  36. // operations only. Signedness of the operands must be the same.
  37. if (!IsInstructionSupported(ir_context, instruction)) {
  38. return false;
  39. }
  40. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3791):
  41. // This condition could be relaxed if the index exists as another integer
  42. // type.
  43. // All bit indexes must be defined as 32-bit unsigned integers.
  44. uint32_t width = ir_context->get_type_mgr()
  45. ->GetType(instruction->type_id())
  46. ->AsInteger()
  47. ->width();
  48. for (uint32_t i = 0; i < width; i++) {
  49. if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context,
  50. {i}, 32, false, false)) {
  51. return false;
  52. }
  53. }
  54. // |message_.fresh_ids.size| must have the exact number of fresh ids required
  55. // to apply the transformation.
  56. if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
  57. GetRequiredFreshIdCount(ir_context, instruction)) {
  58. return false;
  59. }
  60. // All ids in |message_.fresh_ids| must be fresh.
  61. for (uint32_t fresh_id : message_.fresh_ids()) {
  62. if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
  63. return false;
  64. }
  65. }
  66. return true;
  67. }
  68. void TransformationAddBitInstructionSynonym::Apply(
  69. opt::IRContext* ir_context,
  70. TransformationContext* transformation_context) const {
  71. auto bit_instruction =
  72. ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
  73. // Use an appropriate helper function to add the new instruction and new
  74. // synonym fact. The helper function should take care of invalidating
  75. // analyses before adding facts.
  76. switch (bit_instruction->opcode()) {
  77. case spv::Op::OpBitwiseOr:
  78. case spv::Op::OpBitwiseXor:
  79. case spv::Op::OpBitwiseAnd:
  80. case spv::Op::OpNot:
  81. AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context,
  82. bit_instruction);
  83. break;
  84. default:
  85. assert(false && "Should be unreachable.");
  86. break;
  87. }
  88. }
  89. bool TransformationAddBitInstructionSynonym::IsInstructionSupported(
  90. opt::IRContext* ir_context, opt::Instruction* instruction) {
  91. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
  92. // Right now we only support certain operations. When this issue is addressed
  93. // the following conditional can use the function |spvOpcodeIsBit|.
  94. // |instruction| must be defined and must be a supported bit instruction.
  95. if (!instruction || (instruction->opcode() != spv::Op::OpBitwiseOr &&
  96. instruction->opcode() != spv::Op::OpBitwiseXor &&
  97. instruction->opcode() != spv::Op::OpBitwiseAnd &&
  98. instruction->opcode() != spv::Op::OpNot)) {
  99. return false;
  100. }
  101. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
  102. // Right now, only integer operands are supported.
  103. if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
  104. return false;
  105. }
  106. if (instruction->opcode() == spv::Op::OpNot) {
  107. auto operand = instruction->GetInOperand(0).words[0];
  108. auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand);
  109. auto operand_type =
  110. ir_context->get_type_mgr()->GetType(operand_inst->type_id());
  111. auto operand_sign = operand_type->AsInteger()->IsSigned();
  112. auto type_id_sign = ir_context->get_type_mgr()
  113. ->GetType(instruction->type_id())
  114. ->AsInteger()
  115. ->IsSigned();
  116. return operand_sign == type_id_sign;
  117. } else {
  118. // Other BitWise operations that takes two operands.
  119. auto first_operand = instruction->GetInOperand(0).words[0];
  120. auto first_operand_inst =
  121. ir_context->get_def_use_mgr()->GetDef(first_operand);
  122. auto first_operand_type =
  123. ir_context->get_type_mgr()->GetType(first_operand_inst->type_id());
  124. auto first_operand_sign = first_operand_type->AsInteger()->IsSigned();
  125. auto second_operand = instruction->GetInOperand(1).words[0];
  126. auto second_operand_inst =
  127. ir_context->get_def_use_mgr()->GetDef(second_operand);
  128. auto second_operand_type =
  129. ir_context->get_type_mgr()->GetType(second_operand_inst->type_id());
  130. auto second_operand_sign = second_operand_type->AsInteger()->IsSigned();
  131. auto type_id_sign = ir_context->get_type_mgr()
  132. ->GetType(instruction->type_id())
  133. ->AsInteger()
  134. ->IsSigned();
  135. return first_operand_sign == second_operand_sign &&
  136. first_operand_sign == type_id_sign;
  137. }
  138. }
  139. protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
  140. const {
  141. protobufs::Transformation result;
  142. *result.mutable_add_bit_instruction_synonym() = message_;
  143. return result;
  144. }
  145. uint32_t TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount(
  146. opt::IRContext* ir_context, opt::Instruction* bit_instruction) {
  147. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
  148. // Right now, only certain operations are supported.
  149. switch (bit_instruction->opcode()) {
  150. case spv::Op::OpBitwiseOr:
  151. case spv::Op::OpBitwiseXor:
  152. case spv::Op::OpBitwiseAnd:
  153. case spv::Op::OpNot:
  154. return (2 + bit_instruction->NumInOperands()) *
  155. ir_context->get_type_mgr()
  156. ->GetType(bit_instruction->type_id())
  157. ->AsInteger()
  158. ->width() -
  159. 1;
  160. default:
  161. assert(false && "Unsupported bit instruction.");
  162. return 0;
  163. }
  164. }
  165. void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym(
  166. opt::IRContext* ir_context, TransformationContext* transformation_context,
  167. opt::Instruction* bit_instruction) const {
  168. // Fresh id iterator.
  169. auto fresh_id = message_.fresh_ids().begin();
  170. // |width| is the bit width of operands (8, 16, 32 or 64).
  171. const uint32_t width = ir_context->get_type_mgr()
  172. ->GetType(bit_instruction->type_id())
  173. ->AsInteger()
  174. ->width();
  175. // |count| is the number of bits to be extracted and inserted at a time.
  176. const uint32_t count = fuzzerutil::MaybeGetIntegerConstant(
  177. ir_context, *transformation_context, {1}, 32, false, false);
  178. // |extracted_bit_instructions| is the collection of OpBiwise* or OpNot
  179. // instructions that evaluate the extracted bits. Those ids will be used to
  180. // insert the result bits.
  181. std::vector<uint32_t> extracted_bit_instructions(width);
  182. for (uint32_t i = 0; i < width; i++) {
  183. // |offset| is the current bit index.
  184. uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
  185. ir_context, *transformation_context, {i}, 32, false, false);
  186. // |bit_extract_ids| are the two extracted bits from the operands.
  187. opt::Instruction::OperandList bit_extract_ids;
  188. // Extracts the i-th bit from operands.
  189. for (auto operand = bit_instruction->begin() + 2;
  190. operand != bit_instruction->end(); operand++) {
  191. auto bit_extract =
  192. opt::Instruction(ir_context, spv::Op::OpBitFieldUExtract,
  193. bit_instruction->type_id(), *fresh_id++,
  194. {{SPV_OPERAND_TYPE_ID, operand->words},
  195. {SPV_OPERAND_TYPE_ID, {offset}},
  196. {SPV_OPERAND_TYPE_ID, {count}}});
  197. bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_extract));
  198. fuzzerutil::UpdateModuleIdBound(ir_context, bit_extract.result_id());
  199. bit_extract_ids.push_back(
  200. {SPV_OPERAND_TYPE_ID, {bit_extract.result_id()}});
  201. }
  202. // Applies |bit_instruction| to the extracted bits.
  203. auto extracted_bit_instruction = opt::Instruction(
  204. ir_context, bit_instruction->opcode(), bit_instruction->type_id(),
  205. *fresh_id++, bit_extract_ids);
  206. bit_instruction->InsertBefore(
  207. MakeUnique<opt::Instruction>(extracted_bit_instruction));
  208. fuzzerutil::UpdateModuleIdBound(ir_context,
  209. extracted_bit_instruction.result_id());
  210. extracted_bit_instructions[i] = extracted_bit_instruction.result_id();
  211. }
  212. // The first two ids in |extracted_bit_instructions| are used to insert the
  213. // first two bits of the result.
  214. uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
  215. ir_context, *transformation_context, {1}, 32, false, false);
  216. auto bit_insert =
  217. opt::Instruction(ir_context, spv::Op::OpBitFieldInsert,
  218. bit_instruction->type_id(), *fresh_id++,
  219. {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}},
  220. {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}},
  221. {SPV_OPERAND_TYPE_ID, {offset}},
  222. {SPV_OPERAND_TYPE_ID, {count}}});
  223. bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
  224. fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
  225. // Inserts the remaining bits.
  226. for (uint32_t i = 2; i < width; i++) {
  227. offset = fuzzerutil::MaybeGetIntegerConstant(
  228. ir_context, *transformation_context, {i}, 32, false, false);
  229. bit_insert = opt::Instruction(
  230. ir_context, spv::Op::OpBitFieldInsert, bit_instruction->type_id(),
  231. *fresh_id++,
  232. {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}},
  233. {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}},
  234. {SPV_OPERAND_TYPE_ID, {offset}},
  235. {SPV_OPERAND_TYPE_ID, {count}}});
  236. bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
  237. fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
  238. }
  239. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  240. // We only add a synonym fact if the bit instruction is not irrelevant, and if
  241. // the new result id we would make it synonymous with is not irrelevant. (It
  242. // could be irrelevant if we are in a dead block.)
  243. if (!transformation_context->GetFactManager()->IdIsIrrelevant(
  244. bit_instruction->result_id()) &&
  245. !transformation_context->GetFactManager()->IdIsIrrelevant(
  246. bit_insert.result_id())) {
  247. // Adds the fact that the last |bit_insert| instruction is synonymous of
  248. // |bit_instruction|.
  249. transformation_context->GetFactManager()->AddFactDataSynonym(
  250. MakeDataDescriptor(bit_insert.result_id(), {}),
  251. MakeDataDescriptor(bit_instruction->result_id(), {}));
  252. }
  253. }
  254. std::unordered_set<uint32_t>
  255. TransformationAddBitInstructionSynonym::GetFreshIds() const {
  256. std::unordered_set<uint32_t> result;
  257. for (auto id : message_.fresh_ids()) {
  258. result.insert(id);
  259. }
  260. return result;
  261. }
  262. } // namespace fuzz
  263. } // namespace spvtools