transformation_add_parameter.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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_add_parameter.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. namespace spvtools {
  17. namespace fuzz {
  18. TransformationAddParameter::TransformationAddParameter(
  19. protobufs::TransformationAddParameter message)
  20. : message_(std::move(message)) {}
  21. TransformationAddParameter::TransformationAddParameter(
  22. uint32_t function_id, uint32_t parameter_fresh_id,
  23. uint32_t parameter_type_id, std::map<uint32_t, uint32_t> call_parameter_ids,
  24. uint32_t function_type_fresh_id) {
  25. message_.set_function_id(function_id);
  26. message_.set_parameter_fresh_id(parameter_fresh_id);
  27. message_.set_parameter_type_id(parameter_type_id);
  28. *message_.mutable_call_parameter_ids() =
  29. fuzzerutil::MapToRepeatedUInt32Pair(call_parameter_ids);
  30. message_.set_function_type_fresh_id(function_type_fresh_id);
  31. }
  32. bool TransformationAddParameter::IsApplicable(
  33. opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
  34. // Check that function exists.
  35. const auto* function =
  36. fuzzerutil::FindFunction(ir_context, message_.function_id());
  37. if (!function ||
  38. fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
  39. return false;
  40. }
  41. // The type must be supported.
  42. if (ir_context->get_def_use_mgr()->GetDef(message_.parameter_type_id()) ==
  43. nullptr) {
  44. return false;
  45. }
  46. if (!IsParameterTypeSupported(ir_context, message_.parameter_type_id())) {
  47. return false;
  48. }
  49. // Iterate over all callers.
  50. std::map<uint32_t, uint32_t> call_parameter_ids_map =
  51. fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
  52. for (auto* instr :
  53. fuzzerutil::GetCallers(ir_context, message_.function_id())) {
  54. uint32_t caller_id = instr->result_id();
  55. // If there is no entry for this caller, return false.
  56. if (call_parameter_ids_map.find(caller_id) ==
  57. call_parameter_ids_map.end()) {
  58. return false;
  59. }
  60. uint32_t value_id = call_parameter_ids_map[caller_id];
  61. auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id);
  62. if (!value_instr) {
  63. return false;
  64. }
  65. // If the id of the value of the map is not available before the caller,
  66. // return false.
  67. if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, instr,
  68. value_id)) {
  69. return false;
  70. }
  71. // The type of the value must be defined.
  72. uint32_t value_type_id = fuzzerutil::GetTypeId(ir_context, value_id);
  73. if (!value_type_id) {
  74. return false;
  75. }
  76. // Type of every value of the map must be the same for all callers.
  77. if (message_.parameter_type_id() != value_type_id) {
  78. return false;
  79. }
  80. }
  81. return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
  82. fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
  83. message_.parameter_fresh_id() != message_.function_type_fresh_id();
  84. }
  85. void TransformationAddParameter::Apply(
  86. opt::IRContext* ir_context,
  87. TransformationContext* transformation_context) const {
  88. // Find the function that will be transformed.
  89. auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
  90. assert(function && "Can't find the function");
  91. std::map<uint32_t, uint32_t> call_parameter_ids_map =
  92. fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
  93. uint32_t new_parameter_type_id = message_.parameter_type_id();
  94. auto new_parameter_type =
  95. ir_context->get_type_mgr()->GetType(new_parameter_type_id);
  96. assert(new_parameter_type && "New parameter has invalid type.");
  97. // Add new parameters to the function.
  98. function->AddParameter(MakeUnique<opt::Instruction>(
  99. ir_context, spv::Op::OpFunctionParameter, new_parameter_type_id,
  100. message_.parameter_fresh_id(), opt::Instruction::OperandList()));
  101. fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
  102. // Fix all OpFunctionCall instructions.
  103. for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
  104. inst->AddOperand(
  105. {SPV_OPERAND_TYPE_ID, {call_parameter_ids_map[inst->result_id()]}});
  106. }
  107. // Update function's type.
  108. {
  109. // We use a separate scope here since |old_function_type| might become a
  110. // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
  111. const auto* old_function_type =
  112. fuzzerutil::GetFunctionType(ir_context, function);
  113. assert(old_function_type && "Function must have a valid type");
  114. std::vector<uint32_t> parameter_type_ids;
  115. for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
  116. parameter_type_ids.push_back(
  117. old_function_type->GetSingleWordInOperand(i));
  118. }
  119. parameter_type_ids.push_back(new_parameter_type_id);
  120. fuzzerutil::UpdateFunctionType(
  121. ir_context, function->result_id(), message_.function_type_fresh_id(),
  122. old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
  123. }
  124. auto new_parameter_kind = new_parameter_type->kind();
  125. // Make sure our changes are analyzed.
  126. ir_context->InvalidateAnalysesExceptFor(
  127. opt::IRContext::Analysis::kAnalysisNone);
  128. // If the |new_parameter_type_id| is not a pointer type, mark id as
  129. // irrelevant so that we can replace its use with some other id. If the
  130. // |new_parameter_type_id| is a pointer type, we cannot mark it with
  131. // IdIsIrrelevant, because this pointer might be replaced by a pointer from
  132. // original shader. This would change the semantics of the module. In the case
  133. // of a pointer type we mark it with PointeeValueIsIrrelevant.
  134. if (new_parameter_kind != opt::analysis::Type::kPointer) {
  135. transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
  136. message_.parameter_fresh_id());
  137. } else {
  138. transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
  139. message_.parameter_fresh_id());
  140. }
  141. }
  142. protobufs::Transformation TransformationAddParameter::ToMessage() const {
  143. protobufs::Transformation result;
  144. *result.mutable_add_parameter() = message_;
  145. return result;
  146. }
  147. bool TransformationAddParameter::IsParameterTypeSupported(
  148. opt::IRContext* ir_context, uint32_t type_id) {
  149. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
  150. // Think about other type instructions we can add here.
  151. opt::Instruction* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
  152. switch (type_inst->opcode()) {
  153. case spv::Op::OpTypeBool:
  154. case spv::Op::OpTypeInt:
  155. case spv::Op::OpTypeFloat:
  156. case spv::Op::OpTypeMatrix:
  157. case spv::Op::OpTypeVector:
  158. return true;
  159. case spv::Op::OpTypeArray:
  160. return IsParameterTypeSupported(ir_context,
  161. type_inst->GetSingleWordInOperand(0));
  162. case spv::Op::OpTypeStruct:
  163. if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) {
  164. return false;
  165. }
  166. for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) {
  167. if (!IsParameterTypeSupported(ir_context,
  168. type_inst->GetSingleWordInOperand(i))) {
  169. return false;
  170. }
  171. }
  172. return true;
  173. case spv::Op::OpTypePointer: {
  174. spv::StorageClass storage_class =
  175. static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0));
  176. switch (storage_class) {
  177. case spv::StorageClass::Private:
  178. case spv::StorageClass::Function:
  179. case spv::StorageClass::Workgroup: {
  180. return IsParameterTypeSupported(ir_context,
  181. type_inst->GetSingleWordInOperand(1));
  182. }
  183. default:
  184. return false;
  185. }
  186. }
  187. default:
  188. return false;
  189. }
  190. }
  191. std::unordered_set<uint32_t> TransformationAddParameter::GetFreshIds() const {
  192. return {message_.parameter_fresh_id(), message_.function_type_fresh_id()};
  193. }
  194. } // namespace fuzz
  195. } // namespace spvtools