transformation_replace_id_with_synonym.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 fuzzerutil::IdIsAvailableAtUse(
  58. context, use_instruction, 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::UseCanBeReplacedWithSynonym(
  78. opt::IRContext* context, opt::Instruction* use_instruction,
  79. uint32_t use_in_operand_index) {
  80. if (use_instruction->opcode() == SpvOpAccessChain &&
  81. use_in_operand_index > 0) {
  82. // This is an access chain index. If the (sub-)object being accessed by the
  83. // given index has struct type then we cannot replace the use with a
  84. // synonym, as the use needs to be an OpConstant.
  85. // Get the top-level composite type that is being accessed.
  86. auto object_being_accessed = context->get_def_use_mgr()->GetDef(
  87. use_instruction->GetSingleWordInOperand(0));
  88. auto pointer_type =
  89. context->get_type_mgr()->GetType(object_being_accessed->type_id());
  90. assert(pointer_type->AsPointer());
  91. auto composite_type_being_accessed =
  92. pointer_type->AsPointer()->pointee_type();
  93. // Now walk the access chain, tracking the type of each sub-object of the
  94. // composite that is traversed, until the index of interest is reached.
  95. for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
  96. index_in_operand++) {
  97. // For vectors, matrices and arrays, getting the type of the sub-object is
  98. // trivial. For the struct case, the sub-object type is field-sensitive,
  99. // and depends on the constant index that is used.
  100. if (composite_type_being_accessed->AsVector()) {
  101. composite_type_being_accessed =
  102. composite_type_being_accessed->AsVector()->element_type();
  103. } else if (composite_type_being_accessed->AsMatrix()) {
  104. composite_type_being_accessed =
  105. composite_type_being_accessed->AsMatrix()->element_type();
  106. } else if (composite_type_being_accessed->AsArray()) {
  107. composite_type_being_accessed =
  108. composite_type_being_accessed->AsArray()->element_type();
  109. } else {
  110. assert(composite_type_being_accessed->AsStruct());
  111. auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
  112. use_instruction->GetSingleWordInOperand(index_in_operand));
  113. assert(constant_index_instruction->opcode() == SpvOpConstant);
  114. uint32_t member_index =
  115. constant_index_instruction->GetSingleWordInOperand(0);
  116. composite_type_being_accessed =
  117. composite_type_being_accessed->AsStruct()
  118. ->element_types()[member_index];
  119. }
  120. }
  121. // We have found the composite type being accessed by the index we are
  122. // considering replacing. If it is a struct, then we cannot do the
  123. // replacement as struct indices must be constants.
  124. if (composite_type_being_accessed->AsStruct()) {
  125. return false;
  126. }
  127. }
  128. if (use_instruction->opcode() == SpvOpFunctionCall &&
  129. use_in_operand_index > 0) {
  130. // This is a function call argument. It is not allowed to have pointer
  131. // type.
  132. // Get the definition of the function being called.
  133. auto function = context->get_def_use_mgr()->GetDef(
  134. use_instruction->GetSingleWordInOperand(0));
  135. // From the function definition, get the function type.
  136. auto function_type =
  137. context->get_def_use_mgr()->GetDef(function->GetSingleWordInOperand(1));
  138. // OpTypeFunction's 0-th input operand is the function return type, and the
  139. // function argument types follow. Because the arguments to OpFunctionCall
  140. // start from input operand 1, we can use |use_in_operand_index| to get the
  141. // type associated with this function argument.
  142. auto parameter_type = context->get_type_mgr()->GetType(
  143. function_type->GetSingleWordInOperand(use_in_operand_index));
  144. if (parameter_type->AsPointer()) {
  145. return false;
  146. }
  147. }
  148. return true;
  149. }
  150. } // namespace fuzz
  151. } // namespace spvtools