fuzzer_pass_construct_composites.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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/fuzzer_pass_construct_composites.h"
  15. #include <memory>
  16. #include "source/fuzz/available_instructions.h"
  17. #include "source/fuzz/fuzzer_util.h"
  18. #include "source/fuzz/transformation_composite_construct.h"
  19. namespace spvtools {
  20. namespace fuzz {
  21. FuzzerPassConstructComposites::FuzzerPassConstructComposites(
  22. opt::IRContext* ir_context, TransformationContext* transformation_context,
  23. FuzzerContext* fuzzer_context,
  24. protobufs::TransformationSequence* transformations,
  25. bool ignore_inapplicable_transformations)
  26. : FuzzerPass(ir_context, transformation_context, fuzzer_context,
  27. transformations, ignore_inapplicable_transformations) {}
  28. void FuzzerPassConstructComposites::Apply() {
  29. // Gather up the ids of all composite types, but skip block-/buffer
  30. // block-decorated struct types.
  31. std::vector<uint32_t> composite_type_ids;
  32. for (auto& inst : GetIRContext()->types_values()) {
  33. if (fuzzerutil::IsCompositeType(
  34. GetIRContext()->get_type_mgr()->GetType(inst.result_id())) &&
  35. !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(),
  36. inst.result_id())) {
  37. composite_type_ids.push_back(inst.result_id());
  38. }
  39. }
  40. if (composite_type_ids.empty()) {
  41. // There are no composite types, so this fuzzer pass cannot do anything.
  42. return;
  43. }
  44. AvailableInstructions available_composite_constituents(
  45. GetIRContext(),
  46. [this](opt::IRContext* ir_context, opt::Instruction* inst) -> bool {
  47. if (!inst->result_id() || !inst->type_id()) {
  48. return false;
  49. }
  50. // If the id is irrelevant, we can use it since it will not
  51. // participate in DataSynonym fact. Otherwise, we should be able
  52. // to produce a synonym out of the id.
  53. return GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
  54. inst->result_id()) ||
  55. fuzzerutil::CanMakeSynonymOf(ir_context,
  56. *GetTransformationContext(), *inst);
  57. });
  58. ForEachInstructionWithInstructionDescriptor(
  59. [this, &available_composite_constituents, &composite_type_ids](
  60. opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
  61. opt::BasicBlock::iterator inst_it,
  62. const protobufs::InstructionDescriptor& instruction_descriptor)
  63. -> void {
  64. // Randomly decide whether to try inserting a composite construction
  65. // here.
  66. if (!GetFuzzerContext()->ChoosePercentage(
  67. GetFuzzerContext()->GetChanceOfConstructingComposite())) {
  68. return;
  69. }
  70. // Check whether it is legitimate to insert a composite construction
  71. // before the instruction.
  72. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
  73. spv::Op::OpCompositeConstruct, inst_it)) {
  74. return;
  75. }
  76. // For each instruction that is available at this program point (i.e. an
  77. // instruction that is global or whose definition strictly dominates the
  78. // program point) and suitable for making a synonym of, associate it
  79. // with the id of its result type.
  80. TypeIdToInstructions type_id_to_available_instructions;
  81. auto available_instructions =
  82. available_composite_constituents.GetAvailableBeforeInstruction(
  83. &*inst_it);
  84. for (uint32_t available_instruction_index = 0;
  85. available_instruction_index < available_instructions.size();
  86. available_instruction_index++) {
  87. opt::Instruction* inst =
  88. available_instructions[available_instruction_index];
  89. type_id_to_available_instructions[inst->type_id()].push_back(
  90. inst->result_id());
  91. }
  92. // At this point, |composite_type_ids| captures all the composite types
  93. // we could try to create, while |type_id_to_available_instructions|
  94. // captures all the available result ids we might use, organized by
  95. // type.
  96. // Now we choose a composite type to construct, building it from
  97. // available constituent components and using zero constants if suitable
  98. // components are not available.
  99. std::vector<uint32_t> constructor_arguments;
  100. uint32_t chosen_composite_type =
  101. composite_type_ids[GetFuzzerContext()->RandomIndex(
  102. composite_type_ids)];
  103. // Construct a composite of this type, using an appropriate helper
  104. // method depending on the kind of composite type.
  105. auto composite_type_inst =
  106. GetIRContext()->get_def_use_mgr()->GetDef(chosen_composite_type);
  107. switch (composite_type_inst->opcode()) {
  108. case spv::Op::OpTypeArray:
  109. constructor_arguments = FindComponentsToConstructArray(
  110. *composite_type_inst, type_id_to_available_instructions);
  111. break;
  112. case spv::Op::OpTypeMatrix:
  113. constructor_arguments = FindComponentsToConstructMatrix(
  114. *composite_type_inst, type_id_to_available_instructions);
  115. break;
  116. case spv::Op::OpTypeStruct:
  117. constructor_arguments = FindComponentsToConstructStruct(
  118. *composite_type_inst, type_id_to_available_instructions);
  119. break;
  120. case spv::Op::OpTypeVector:
  121. constructor_arguments = FindComponentsToConstructVector(
  122. *composite_type_inst, type_id_to_available_instructions);
  123. break;
  124. default:
  125. assert(false &&
  126. "The space of possible composite types should be covered "
  127. "by the above cases.");
  128. break;
  129. }
  130. assert(!constructor_arguments.empty());
  131. // Make and apply a transformation.
  132. ApplyTransformation(TransformationCompositeConstruct(
  133. chosen_composite_type, constructor_arguments,
  134. instruction_descriptor, GetFuzzerContext()->GetFreshId()));
  135. });
  136. }
  137. std::vector<uint32_t>
  138. FuzzerPassConstructComposites::FindComponentsToConstructArray(
  139. const opt::Instruction& array_type_instruction,
  140. const TypeIdToInstructions& type_id_to_available_instructions) {
  141. assert(array_type_instruction.opcode() == spv::Op::OpTypeArray &&
  142. "Precondition: instruction must be an array type.");
  143. // Get the element type for the array.
  144. auto element_type_id = array_type_instruction.GetSingleWordInOperand(0);
  145. // Get all instructions at our disposal that compute something of this element
  146. // type.
  147. auto available_instructions =
  148. type_id_to_available_instructions.find(element_type_id);
  149. uint32_t array_length =
  150. GetIRContext()
  151. ->get_def_use_mgr()
  152. ->GetDef(array_type_instruction.GetSingleWordInOperand(1))
  153. ->GetSingleWordInOperand(0);
  154. std::vector<uint32_t> result;
  155. for (uint32_t index = 0; index < array_length; index++) {
  156. if (available_instructions == type_id_to_available_instructions.cend()) {
  157. // No suitable instructions are available, so use a zero constant
  158. result.push_back(FindOrCreateZeroConstant(element_type_id, true));
  159. } else {
  160. result.push_back(
  161. available_instructions->second[GetFuzzerContext()->RandomIndex(
  162. available_instructions->second)]);
  163. }
  164. }
  165. return result;
  166. }
  167. std::vector<uint32_t>
  168. FuzzerPassConstructComposites::FindComponentsToConstructMatrix(
  169. const opt::Instruction& matrix_type_instruction,
  170. const TypeIdToInstructions& type_id_to_available_instructions) {
  171. assert(matrix_type_instruction.opcode() == spv::Op::OpTypeMatrix &&
  172. "Precondition: instruction must be a matrix type.");
  173. // Get the element type for the matrix.
  174. auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0);
  175. // Get all instructions at our disposal that compute something of this element
  176. // type.
  177. auto available_instructions =
  178. type_id_to_available_instructions.find(element_type_id);
  179. std::vector<uint32_t> result;
  180. for (uint32_t index = 0;
  181. index < matrix_type_instruction.GetSingleWordInOperand(1); index++) {
  182. if (available_instructions == type_id_to_available_instructions.cend()) {
  183. // No suitable components are available, so use a zero constant.
  184. result.push_back(FindOrCreateZeroConstant(element_type_id, true));
  185. } else {
  186. result.push_back(
  187. available_instructions->second[GetFuzzerContext()->RandomIndex(
  188. available_instructions->second)]);
  189. }
  190. }
  191. return result;
  192. }
  193. std::vector<uint32_t>
  194. FuzzerPassConstructComposites::FindComponentsToConstructStruct(
  195. const opt::Instruction& struct_type_instruction,
  196. const TypeIdToInstructions& type_id_to_available_instructions) {
  197. assert(struct_type_instruction.opcode() == spv::Op::OpTypeStruct &&
  198. "Precondition: instruction must be a struct type.");
  199. std::vector<uint32_t> result;
  200. // Consider the type of each field of the struct.
  201. for (uint32_t in_operand_index = 0;
  202. in_operand_index < struct_type_instruction.NumInOperands();
  203. in_operand_index++) {
  204. auto element_type_id =
  205. struct_type_instruction.GetSingleWordInOperand(in_operand_index);
  206. // Find the instructions at our disposal that compute something of the field
  207. // type.
  208. auto available_instructions =
  209. type_id_to_available_instructions.find(element_type_id);
  210. if (available_instructions == type_id_to_available_instructions.cend()) {
  211. // No suitable component is available for this element type, so use a zero
  212. // constant.
  213. result.push_back(FindOrCreateZeroConstant(element_type_id, true));
  214. } else {
  215. result.push_back(
  216. available_instructions->second[GetFuzzerContext()->RandomIndex(
  217. available_instructions->second)]);
  218. }
  219. }
  220. return result;
  221. }
  222. std::vector<uint32_t>
  223. FuzzerPassConstructComposites::FindComponentsToConstructVector(
  224. const opt::Instruction& vector_type_instruction,
  225. const TypeIdToInstructions& type_id_to_available_instructions) {
  226. assert(vector_type_instruction.opcode() == spv::Op::OpTypeVector &&
  227. "Precondition: instruction must be a vector type.");
  228. // Get details of the type underlying the vector, and the width of the vector,
  229. // for convenience.
  230. auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0);
  231. auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
  232. auto element_count = vector_type_instruction.GetSingleWordInOperand(1);
  233. // Collect a mapping, from type id to width, for scalar/vector types that are
  234. // smaller in width than |vector_type|, but that have the same underlying
  235. // type. For example, if |vector_type| is vec4, the mapping will be:
  236. // { float -> 1, vec2 -> 2, vec3 -> 3 }
  237. // The mapping will have missing entries if some of these types do not exist.
  238. std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
  239. // Add the underlying type. This id must exist, in order for |vector_type| to
  240. // exist.
  241. smaller_vector_type_id_to_width[element_type_id] = 1;
  242. // Now add every vector type with width at least 2, and less than the width of
  243. // |vector_type|.
  244. for (uint32_t width = 2; width < element_count; width++) {
  245. opt::analysis::Vector smaller_vector_type(element_type, width);
  246. auto smaller_vector_type_id =
  247. GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
  248. // We might find that there is no declared type of this smaller width.
  249. // For example, a module can declare vec4 without having declared vec2 or
  250. // vec3.
  251. if (smaller_vector_type_id) {
  252. smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
  253. }
  254. }
  255. // Now we know the types that are available to us, we set about populating a
  256. // vector of the right length. We do this by deciding, with no order in mind,
  257. // which instructions we will use to populate the vector, and subsequently
  258. // randomly choosing an order. This is to avoid biasing construction of
  259. // vectors with smaller vectors to the left and scalars to the right. That is
  260. // a concern because, e.g. in the case of populating a vec4, if we populate
  261. // the constructor instructions left-to-right, we can always choose a vec3 to
  262. // construct the first three elements, but can only choose a vec3 to construct
  263. // the last three elements if we chose a float to construct the first element
  264. // (otherwise there will not be space left for a vec3).
  265. uint32_t vector_slots_used = 0;
  266. // The instructions result ids we will use to construct the vector, in no
  267. // particular order at this stage.
  268. std::vector<uint32_t> result;
  269. while (vector_slots_used < element_count) {
  270. std::vector<uint32_t> instructions_to_choose_from;
  271. for (auto& entry : smaller_vector_type_id_to_width) {
  272. if (entry.second >
  273. std::min(element_count - 1, element_count - vector_slots_used)) {
  274. continue;
  275. }
  276. auto available_instructions =
  277. type_id_to_available_instructions.find(entry.first);
  278. if (available_instructions == type_id_to_available_instructions.cend()) {
  279. continue;
  280. }
  281. instructions_to_choose_from.insert(instructions_to_choose_from.end(),
  282. available_instructions->second.begin(),
  283. available_instructions->second.end());
  284. }
  285. // If there are no instructions to choose from then use a zero constant,
  286. // otherwise select one of the instructions at random.
  287. uint32_t id_of_instruction_to_use =
  288. instructions_to_choose_from.empty()
  289. ? FindOrCreateZeroConstant(element_type_id, true)
  290. : instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
  291. instructions_to_choose_from)];
  292. opt::Instruction* instruction_to_use =
  293. GetIRContext()->get_def_use_mgr()->GetDef(id_of_instruction_to_use);
  294. result.push_back(instruction_to_use->result_id());
  295. auto chosen_type =
  296. GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
  297. if (chosen_type->AsVector()) {
  298. assert(chosen_type->AsVector()->element_type() == element_type);
  299. assert(chosen_type->AsVector()->element_count() < element_count);
  300. assert(chosen_type->AsVector()->element_count() <=
  301. element_count - vector_slots_used);
  302. vector_slots_used += chosen_type->AsVector()->element_count();
  303. } else {
  304. assert(chosen_type == element_type);
  305. vector_slots_used += 1;
  306. }
  307. }
  308. assert(vector_slots_used == element_count);
  309. GetFuzzerContext()->Shuffle(&result);
  310. return result;
  311. }
  312. } // namespace fuzz
  313. } // namespace spvtools