transformation_replace_id_with_synonym.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. spvtools::opt::IRContext* context,
  34. const spvtools::fuzz::FactManager& fact_manager) 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 (!fact_manager.IsSynonymous(MakeDataDescriptor(id_of_interest, {}),
  40. data_descriptor_for_synonymous_id, context)) {
  41. return false;
  42. }
  43. // Does the id use descriptor in the transformation identify an instruction?
  44. auto use_instruction =
  45. FindInstructionContainingUse(message_.id_use_descriptor(), context);
  46. if (!use_instruction) {
  47. return false;
  48. }
  49. // Is the use suitable for being replaced in principle?
  50. if (!UseCanBeReplacedWithSynonym(
  51. context, use_instruction,
  52. message_.id_use_descriptor().in_operand_index())) {
  53. return false;
  54. }
  55. // The transformation is applicable if the synonymous id is available at the
  56. // use point.
  57. return IdsIsAvailableAtUse(context, use_instruction,
  58. message_.id_use_descriptor().in_operand_index(),
  59. message_.synonymous_id());
  60. }
  61. void TransformationReplaceIdWithSynonym::Apply(
  62. spvtools::opt::IRContext* context,
  63. spvtools::fuzz::FactManager* /*unused*/) const {
  64. auto instruction_to_change =
  65. FindInstructionContainingUse(message_.id_use_descriptor(), context);
  66. instruction_to_change->SetInOperand(
  67. message_.id_use_descriptor().in_operand_index(),
  68. {message_.synonymous_id()});
  69. context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
  70. }
  71. protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
  72. const {
  73. protobufs::Transformation result;
  74. *result.mutable_replace_id_with_synonym() = message_;
  75. return result;
  76. }
  77. bool TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
  78. opt::IRContext* context, opt::Instruction* use_instruction,
  79. uint32_t use_input_operand_index, uint32_t id) {
  80. if (!context->get_instr_block(id)) {
  81. return true;
  82. }
  83. auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
  84. if (defining_instruction == use_instruction) {
  85. return false;
  86. }
  87. auto dominator_analysis = context->GetDominatorAnalysis(
  88. context->get_instr_block(use_instruction)->GetParent());
  89. if (use_instruction->opcode() == SpvOpPhi) {
  90. // In the case where the use is an operand to OpPhi, it is actually the
  91. // *parent* block associated with the operand that must be dominated by
  92. // the synonym.
  93. auto parent_block =
  94. use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
  95. return dominator_analysis->Dominates(
  96. context->get_instr_block(defining_instruction)->id(), parent_block);
  97. }
  98. return dominator_analysis->Dominates(defining_instruction, use_instruction);
  99. }
  100. bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
  101. opt::IRContext* context, opt::Instruction* use_instruction,
  102. uint32_t use_in_operand_index) {
  103. if (use_instruction->opcode() == SpvOpAccessChain &&
  104. use_in_operand_index > 0) {
  105. // This is an access chain index. If the (sub-)object being accessed by the
  106. // given index has struct type then we cannot replace the use with a
  107. // synonym, as the use needs to be an OpConstant.
  108. // Get the top-level composite type that is being accessed.
  109. auto object_being_accessed = context->get_def_use_mgr()->GetDef(
  110. use_instruction->GetSingleWordInOperand(0));
  111. auto pointer_type =
  112. context->get_type_mgr()->GetType(object_being_accessed->type_id());
  113. assert(pointer_type->AsPointer());
  114. auto composite_type_being_accessed =
  115. pointer_type->AsPointer()->pointee_type();
  116. // Now walk the access chain, tracking the type of each sub-object of the
  117. // composite that is traversed, until the index of interest is reached.
  118. for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
  119. index_in_operand++) {
  120. // For vectors, matrices and arrays, getting the type of the sub-object is
  121. // trivial. For the struct case, the sub-object type is field-sensitive,
  122. // and depends on the constant index that is used.
  123. if (composite_type_being_accessed->AsVector()) {
  124. composite_type_being_accessed =
  125. composite_type_being_accessed->AsVector()->element_type();
  126. } else if (composite_type_being_accessed->AsMatrix()) {
  127. composite_type_being_accessed =
  128. composite_type_being_accessed->AsMatrix()->element_type();
  129. } else if (composite_type_being_accessed->AsArray()) {
  130. composite_type_being_accessed =
  131. composite_type_being_accessed->AsArray()->element_type();
  132. } else {
  133. assert(composite_type_being_accessed->AsStruct());
  134. auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
  135. use_instruction->GetSingleWordInOperand(index_in_operand));
  136. assert(constant_index_instruction->opcode() == SpvOpConstant);
  137. uint32_t member_index =
  138. constant_index_instruction->GetSingleWordInOperand(0);
  139. composite_type_being_accessed =
  140. composite_type_being_accessed->AsStruct()
  141. ->element_types()[member_index];
  142. }
  143. }
  144. // We have found the composite type being accessed by the index we are
  145. // considering replacing. If it is a struct, then we cannot do the
  146. // replacement as struct indices must be constants.
  147. if (composite_type_being_accessed->AsStruct()) {
  148. return false;
  149. }
  150. }
  151. if (use_instruction->opcode() == SpvOpFunctionCall &&
  152. use_in_operand_index > 0) {
  153. // This is a function call argument. It is not allowed to have pointer
  154. // type.
  155. // Get the definition of the function being called.
  156. auto function = context->get_def_use_mgr()->GetDef(
  157. use_instruction->GetSingleWordInOperand(0));
  158. // From the function definition, get the function type.
  159. auto function_type =
  160. context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1));
  161. // OpTypeFunction's 0-th input operand is the function return type, and the
  162. // function argument types follow. Because the arguments to OpFunctionCall
  163. // start from input operand 1, we can use |use_in_operand_index| to get the
  164. // type associated with this function argument.
  165. auto parameter_type = context->get_type_mgr()->GetType(
  166. function_type->GetSingleWordInOperand(use_in_operand_index));
  167. if (parameter_type->AsPointer()) {
  168. return false;
  169. }
  170. }
  171. return true;
  172. }
  173. } // namespace fuzz
  174. } // namespace spvtools