transformation_replace_id_with_synonym.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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_replace_id_with_synonym.h"
  15. #include <algorithm>
  16. #include "source/fuzz/data_descriptor.h"
  17. #include "source/fuzz/fuzzer_util.h"
  18. #include "source/fuzz/id_use_descriptor.h"
  19. #include "source/opt/types.h"
  20. #include "source/util/make_unique.h"
  21. namespace spvtools {
  22. namespace fuzz {
  23. TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
  24. const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym&
  25. message)
  26. : message_(message) {}
  27. TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
  28. protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) {
  29. *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
  30. message_.set_synonymous_id(synonymous_id);
  31. }
  32. bool TransformationReplaceIdWithSynonym::IsApplicable(
  33. opt::IRContext* ir_context,
  34. const TransformationContext& transformation_context) const {
  35. auto id_of_interest = message_.id_use_descriptor().id_of_interest();
  36. // Does the fact manager know about the synonym?
  37. auto data_descriptor_for_synonymous_id =
  38. MakeDataDescriptor(message_.synonymous_id(), {});
  39. if (!transformation_context.GetFactManager()->IsSynonymous(
  40. MakeDataDescriptor(id_of_interest, {}),
  41. data_descriptor_for_synonymous_id)) {
  42. return false;
  43. }
  44. // Does the id use descriptor in the transformation identify an instruction?
  45. auto use_instruction =
  46. FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
  47. if (!use_instruction) {
  48. return false;
  49. }
  50. // Is the use suitable for being replaced in principle?
  51. if (!UseCanBeReplacedWithSynonym(
  52. ir_context, use_instruction,
  53. message_.id_use_descriptor().in_operand_index())) {
  54. return false;
  55. }
  56. // The transformation is applicable if the synonymous id is available at the
  57. // use point.
  58. return fuzzerutil::IdIsAvailableAtUse(
  59. ir_context, use_instruction,
  60. message_.id_use_descriptor().in_operand_index(),
  61. message_.synonymous_id());
  62. }
  63. void TransformationReplaceIdWithSynonym::Apply(
  64. spvtools::opt::IRContext* ir_context,
  65. TransformationContext* /*unused*/) const {
  66. auto instruction_to_change =
  67. FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
  68. instruction_to_change->SetInOperand(
  69. message_.id_use_descriptor().in_operand_index(),
  70. {message_.synonymous_id()});
  71. ir_context->InvalidateAnalysesExceptFor(
  72. opt::IRContext::Analysis::kAnalysisNone);
  73. }
  74. protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
  75. const {
  76. protobufs::Transformation result;
  77. *result.mutable_replace_id_with_synonym() = message_;
  78. return result;
  79. }
  80. bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
  81. opt::IRContext* ir_context, opt::Instruction* use_instruction,
  82. uint32_t use_in_operand_index) {
  83. if (use_instruction->opcode() == SpvOpAccessChain &&
  84. use_in_operand_index > 0) {
  85. // This is an access chain index. If the (sub-)object being accessed by the
  86. // given index has struct type then we cannot replace the use with a
  87. // synonym, as the use needs to be an OpConstant.
  88. // Get the top-level composite type that is being accessed.
  89. auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef(
  90. use_instruction->GetSingleWordInOperand(0));
  91. auto pointer_type =
  92. ir_context->get_type_mgr()->GetType(object_being_accessed->type_id());
  93. assert(pointer_type->AsPointer());
  94. auto composite_type_being_accessed =
  95. pointer_type->AsPointer()->pointee_type();
  96. // Now walk the access chain, tracking the type of each sub-object of the
  97. // composite that is traversed, until the index of interest is reached.
  98. for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
  99. index_in_operand++) {
  100. // For vectors, matrices and arrays, getting the type of the sub-object is
  101. // trivial. For the struct case, the sub-object type is field-sensitive,
  102. // and depends on the constant index that is used.
  103. if (composite_type_being_accessed->AsVector()) {
  104. composite_type_being_accessed =
  105. composite_type_being_accessed->AsVector()->element_type();
  106. } else if (composite_type_being_accessed->AsMatrix()) {
  107. composite_type_being_accessed =
  108. composite_type_being_accessed->AsMatrix()->element_type();
  109. } else if (composite_type_being_accessed->AsArray()) {
  110. composite_type_being_accessed =
  111. composite_type_being_accessed->AsArray()->element_type();
  112. } else if (composite_type_being_accessed->AsRuntimeArray()) {
  113. composite_type_being_accessed =
  114. composite_type_being_accessed->AsRuntimeArray()->element_type();
  115. } else {
  116. assert(composite_type_being_accessed->AsStruct());
  117. auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef(
  118. use_instruction->GetSingleWordInOperand(index_in_operand));
  119. assert(constant_index_instruction->opcode() == SpvOpConstant);
  120. uint32_t member_index =
  121. constant_index_instruction->GetSingleWordInOperand(0);
  122. composite_type_being_accessed =
  123. composite_type_being_accessed->AsStruct()
  124. ->element_types()[member_index];
  125. }
  126. }
  127. // We have found the composite type being accessed by the index we are
  128. // considering replacing. If it is a struct, then we cannot do the
  129. // replacement as struct indices must be constants.
  130. if (composite_type_being_accessed->AsStruct()) {
  131. return false;
  132. }
  133. }
  134. if (use_instruction->opcode() == SpvOpFunctionCall &&
  135. use_in_operand_index > 0) {
  136. // This is a function call argument. It is not allowed to have pointer
  137. // type.
  138. // Get the definition of the function being called.
  139. auto function = ir_context->get_def_use_mgr()->GetDef(
  140. use_instruction->GetSingleWordInOperand(0));
  141. // From the function definition, get the function type.
  142. auto function_type = ir_context->get_def_use_mgr()->GetDef(
  143. function->GetSingleWordInOperand(1));
  144. // OpTypeFunction's 0-th input operand is the function return type, and the
  145. // function argument types follow. Because the arguments to OpFunctionCall
  146. // start from input operand 1, we can use |use_in_operand_index| to get the
  147. // type associated with this function argument.
  148. auto parameter_type = ir_context->get_type_mgr()->GetType(
  149. function_type->GetSingleWordInOperand(use_in_operand_index));
  150. if (parameter_type->AsPointer()) {
  151. return false;
  152. }
  153. }
  154. if (use_instruction->opcode() == SpvOpImageTexelPointer &&
  155. use_in_operand_index == 2) {
  156. // The OpImageTexelPointer instruction has a Sample parameter that in some
  157. // situations must be an id for the value 0. To guard against disrupting
  158. // that requirement, we do not replace this argument to that instruction.
  159. return false;
  160. }
  161. return true;
  162. }
  163. } // namespace fuzz
  164. } // namespace spvtools