transformation_add_opphi_synonym.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (c) 2020 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_add_opphi_synonym.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. namespace spvtools {
  17. namespace fuzz {
  18. TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
  19. protobufs::TransformationAddOpPhiSynonym message)
  20. : message_(std::move(message)) {}
  21. TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
  22. uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids,
  23. uint32_t fresh_id) {
  24. message_.set_block_id(block_id);
  25. *message_.mutable_pred_to_id() =
  26. fuzzerutil::MapToRepeatedUInt32Pair(preds_to_ids);
  27. message_.set_fresh_id(fresh_id);
  28. }
  29. bool TransformationAddOpPhiSynonym::IsApplicable(
  30. opt::IRContext* ir_context,
  31. const TransformationContext& transformation_context) const {
  32. // Check that |message_.block_id| is a block label id, and that it is not
  33. // dead.
  34. auto block = fuzzerutil::MaybeFindBlock(ir_context, message_.block_id());
  35. if (!block ||
  36. transformation_context.GetFactManager()->BlockIsDead(block->id())) {
  37. return false;
  38. }
  39. // Check that |message_.fresh_id| is actually fresh.
  40. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
  41. return false;
  42. }
  43. // Check that |message_.pred_to_id| contains a mapping for all of the block's
  44. // predecessors.
  45. std::vector<uint32_t> predecessors = ir_context->cfg()->preds(block->id());
  46. // There must be at least one predecessor.
  47. if (predecessors.empty()) {
  48. return false;
  49. }
  50. std::map<uint32_t, uint32_t> preds_to_ids =
  51. fuzzerutil::RepeatedUInt32PairToMap(message_.pred_to_id());
  52. // There must not be repeated key values in |message_.pred_to_id|.
  53. if (preds_to_ids.size() != static_cast<size_t>(message_.pred_to_id_size())) {
  54. return false;
  55. }
  56. // Check that each predecessor has a corresponding mapping and all of the
  57. // corresponding ids exist.
  58. for (uint32_t pred : predecessors) {
  59. if (preds_to_ids.count(pred) == 0) {
  60. return false;
  61. }
  62. // Check that the id exists in the module.
  63. if (!ir_context->get_def_use_mgr()->GetDef(preds_to_ids[pred])) {
  64. return false;
  65. }
  66. }
  67. // Get the first id and its type (which should be the same as all the other
  68. // ones) and check that the transformation supports this type.
  69. uint32_t first_id = preds_to_ids[predecessors[0]];
  70. uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id();
  71. if (!CheckTypeIsAllowed(ir_context, type_id)) {
  72. return false;
  73. }
  74. // Check that the ids corresponding to predecessors are all synonymous, have
  75. // the same type and are available to use at the end of the predecessor.
  76. for (uint32_t pred : predecessors) {
  77. auto id = preds_to_ids[pred];
  78. // Check that the id has the same type as the other ones.
  79. if (ir_context->get_def_use_mgr()->GetDef(id)->type_id() != type_id) {
  80. return false;
  81. }
  82. // Check that the id is synonymous with the others by checking that it is
  83. // synonymous with the first one (or it is the same id).
  84. if (id != first_id &&
  85. !transformation_context.GetFactManager()->IsSynonymous(
  86. MakeDataDescriptor(id, {}), MakeDataDescriptor(first_id, {}))) {
  87. return false;
  88. }
  89. // Check that the id is available at the end of the corresponding
  90. // predecessor block.
  91. auto pred_block = ir_context->get_instr_block(pred);
  92. // We should always be able to find the predecessor block, since it is in
  93. // the predecessors list of |block|.
  94. assert(pred_block && "Could not find one of the predecessor blocks.");
  95. if (!fuzzerutil::IdIsAvailableBeforeInstruction(
  96. ir_context, pred_block->terminator(), id)) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. void TransformationAddOpPhiSynonym::Apply(
  103. opt::IRContext* ir_context,
  104. TransformationContext* transformation_context) const {
  105. // Get the type id from one of the ids.
  106. uint32_t first_id = message_.pred_to_id(0).second();
  107. uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id();
  108. // Define the operand list.
  109. opt::Instruction::OperandList operand_list;
  110. // For each predecessor, add the corresponding operands.
  111. for (auto& pair : message_.pred_to_id()) {
  112. operand_list.emplace_back(
  113. opt::Operand{SPV_OPERAND_TYPE_ID, {pair.second()}});
  114. operand_list.emplace_back(
  115. opt::Operand{SPV_OPERAND_TYPE_ID, {pair.first()}});
  116. }
  117. // Add a new OpPhi instructions at the beginning of the block.
  118. ir_context->get_instr_block(message_.block_id())
  119. ->begin()
  120. .InsertBefore(MakeUnique<opt::Instruction>(ir_context, spv::Op::OpPhi,
  121. type_id, message_.fresh_id(),
  122. std::move(operand_list)));
  123. // Update the module id bound.
  124. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
  125. // Invalidate all analyses, since we added an instruction to the module.
  126. ir_context->InvalidateAnalysesExceptFor(
  127. opt::IRContext::Analysis::kAnalysisNone);
  128. // Record the fact that the new id is synonym with the other ones by declaring
  129. // that it is a synonym of the first one.
  130. transformation_context->GetFactManager()->AddFactDataSynonym(
  131. MakeDataDescriptor(message_.fresh_id(), {}),
  132. MakeDataDescriptor(first_id, {}));
  133. }
  134. protobufs::Transformation TransformationAddOpPhiSynonym::ToMessage() const {
  135. protobufs::Transformation result;
  136. *result.mutable_add_opphi_synonym() = message_;
  137. return result;
  138. }
  139. bool TransformationAddOpPhiSynonym::CheckTypeIsAllowed(
  140. opt::IRContext* ir_context, uint32_t type_id) {
  141. auto type = ir_context->get_type_mgr()->GetType(type_id);
  142. if (!type) {
  143. return false;
  144. }
  145. // We allow the following types: Bool, Integer, Float, Vector, Matrix, Array,
  146. // Struct.
  147. if (type->AsBool() || type->AsInteger() || type->AsFloat() ||
  148. type->AsVector() || type->AsMatrix() || type->AsArray() ||
  149. type->AsStruct()) {
  150. return true;
  151. }
  152. // We allow pointer types if the VariablePointers capability is enabled and
  153. // the pointer has the correct storage class (Workgroup or StorageBuffer).
  154. if (type->AsPointer()) {
  155. auto storage_class = type->AsPointer()->storage_class();
  156. return ir_context->get_feature_mgr()->HasCapability(
  157. spv::Capability::VariablePointers) &&
  158. (storage_class == spv::StorageClass::Workgroup ||
  159. storage_class == spv::StorageClass::StorageBuffer);
  160. }
  161. // We do not allow other types.
  162. return false;
  163. }
  164. std::unordered_set<uint32_t> TransformationAddOpPhiSynonym::GetFreshIds()
  165. const {
  166. return {message_.fresh_id()};
  167. }
  168. } // namespace fuzz
  169. } // namespace spvtools