transformation_invert_comparison_operator.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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_invert_comparison_operator.h"
  15. #include <utility>
  16. #include "source/fuzz/fuzzer_util.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
  20. protobufs::TransformationInvertComparisonOperator message)
  21. : message_(std::move(message)) {}
  22. TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
  23. uint32_t operator_id, uint32_t fresh_id) {
  24. message_.set_operator_id(operator_id);
  25. message_.set_fresh_id(fresh_id);
  26. }
  27. bool TransformationInvertComparisonOperator::IsApplicable(
  28. opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
  29. // |message_.operator_id| must be valid and inversion must be supported for
  30. // it.
  31. auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
  32. if (!inst || !IsInversionSupported(inst->opcode())) {
  33. return false;
  34. }
  35. // Check that we can insert negation instruction.
  36. auto* block = ir_context->get_instr_block(inst);
  37. assert(block && "Instruction must have a basic block");
  38. auto iter = fuzzerutil::GetIteratorForInstruction(block, inst);
  39. ++iter;
  40. assert(iter != block->end() && "Instruction can't be the last in the block");
  41. assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
  42. iter) &&
  43. "Can't insert negation after comparison operator");
  44. // |message_.fresh_id| must be fresh.
  45. return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
  46. }
  47. void TransformationInvertComparisonOperator::Apply(
  48. opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
  49. auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
  50. assert(inst && "Result id of an operator is invalid");
  51. // Insert negation after |inst|.
  52. auto iter = fuzzerutil::GetIteratorForInstruction(
  53. ir_context->get_instr_block(inst), inst);
  54. ++iter;
  55. iter.InsertBefore(MakeUnique<opt::Instruction>(
  56. ir_context, spv::Op::OpLogicalNot, inst->type_id(), inst->result_id(),
  57. opt::Instruction::OperandList{
  58. {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
  59. // Change the result id of the original operator to |fresh_id|.
  60. inst->SetResultId(message_.fresh_id());
  61. // Invert the operator.
  62. inst->SetOpcode(InvertOpcode(inst->opcode()));
  63. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
  64. ir_context->InvalidateAnalysesExceptFor(
  65. opt::IRContext::Analysis::kAnalysisNone);
  66. }
  67. bool TransformationInvertComparisonOperator::IsInversionSupported(
  68. spv::Op opcode) {
  69. switch (opcode) {
  70. case spv::Op::OpSGreaterThan:
  71. case spv::Op::OpSGreaterThanEqual:
  72. case spv::Op::OpSLessThan:
  73. case spv::Op::OpSLessThanEqual:
  74. case spv::Op::OpUGreaterThan:
  75. case spv::Op::OpUGreaterThanEqual:
  76. case spv::Op::OpULessThan:
  77. case spv::Op::OpULessThanEqual:
  78. case spv::Op::OpIEqual:
  79. case spv::Op::OpINotEqual:
  80. case spv::Op::OpFOrdEqual:
  81. case spv::Op::OpFUnordEqual:
  82. case spv::Op::OpFOrdNotEqual:
  83. case spv::Op::OpFUnordNotEqual:
  84. case spv::Op::OpFOrdLessThan:
  85. case spv::Op::OpFUnordLessThan:
  86. case spv::Op::OpFOrdLessThanEqual:
  87. case spv::Op::OpFUnordLessThanEqual:
  88. case spv::Op::OpFOrdGreaterThan:
  89. case spv::Op::OpFUnordGreaterThan:
  90. case spv::Op::OpFOrdGreaterThanEqual:
  91. case spv::Op::OpFUnordGreaterThanEqual:
  92. return true;
  93. default:
  94. return false;
  95. }
  96. }
  97. spv::Op TransformationInvertComparisonOperator::InvertOpcode(spv::Op opcode) {
  98. assert(IsInversionSupported(opcode) && "Inversion must be supported");
  99. switch (opcode) {
  100. case spv::Op::OpSGreaterThan:
  101. return spv::Op::OpSLessThanEqual;
  102. case spv::Op::OpSGreaterThanEqual:
  103. return spv::Op::OpSLessThan;
  104. case spv::Op::OpSLessThan:
  105. return spv::Op::OpSGreaterThanEqual;
  106. case spv::Op::OpSLessThanEqual:
  107. return spv::Op::OpSGreaterThan;
  108. case spv::Op::OpUGreaterThan:
  109. return spv::Op::OpULessThanEqual;
  110. case spv::Op::OpUGreaterThanEqual:
  111. return spv::Op::OpULessThan;
  112. case spv::Op::OpULessThan:
  113. return spv::Op::OpUGreaterThanEqual;
  114. case spv::Op::OpULessThanEqual:
  115. return spv::Op::OpUGreaterThan;
  116. case spv::Op::OpIEqual:
  117. return spv::Op::OpINotEqual;
  118. case spv::Op::OpINotEqual:
  119. return spv::Op::OpIEqual;
  120. case spv::Op::OpFOrdEqual:
  121. return spv::Op::OpFUnordNotEqual;
  122. case spv::Op::OpFUnordEqual:
  123. return spv::Op::OpFOrdNotEqual;
  124. case spv::Op::OpFOrdNotEqual:
  125. return spv::Op::OpFUnordEqual;
  126. case spv::Op::OpFUnordNotEqual:
  127. return spv::Op::OpFOrdEqual;
  128. case spv::Op::OpFOrdLessThan:
  129. return spv::Op::OpFUnordGreaterThanEqual;
  130. case spv::Op::OpFUnordLessThan:
  131. return spv::Op::OpFOrdGreaterThanEqual;
  132. case spv::Op::OpFOrdLessThanEqual:
  133. return spv::Op::OpFUnordGreaterThan;
  134. case spv::Op::OpFUnordLessThanEqual:
  135. return spv::Op::OpFOrdGreaterThan;
  136. case spv::Op::OpFOrdGreaterThan:
  137. return spv::Op::OpFUnordLessThanEqual;
  138. case spv::Op::OpFUnordGreaterThan:
  139. return spv::Op::OpFOrdLessThanEqual;
  140. case spv::Op::OpFOrdGreaterThanEqual:
  141. return spv::Op::OpFUnordLessThan;
  142. case spv::Op::OpFUnordGreaterThanEqual:
  143. return spv::Op::OpFOrdLessThan;
  144. default:
  145. // The program will fail in the debug mode because of the assertion
  146. // at the beginning of the function.
  147. return spv::Op::OpNop;
  148. }
  149. }
  150. protobufs::Transformation TransformationInvertComparisonOperator::ToMessage()
  151. const {
  152. protobufs::Transformation result;
  153. *result.mutable_invert_comparison_operator() = message_;
  154. return result;
  155. }
  156. std::unordered_set<uint32_t>
  157. TransformationInvertComparisonOperator::GetFreshIds() const {
  158. return {message_.fresh_id()};
  159. }
  160. } // namespace fuzz
  161. } // namespace spvtools