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
15 #include "source/fuzz/transformation_add_constant_composite.h"
16
17 #include <vector>
18
19 #include "source/fuzz/fuzzer_util.h"
20
21 namespace spvtools {
22 namespace fuzz {
23
TransformationAddConstantComposite(spvtools::fuzz::protobufs::TransformationAddConstantComposite message)24 TransformationAddConstantComposite::TransformationAddConstantComposite(
25 spvtools::fuzz::protobufs::TransformationAddConstantComposite message)
26 : message_(std::move(message)) {}
27
TransformationAddConstantComposite(uint32_t fresh_id,uint32_t type_id,const std::vector<uint32_t> & constituent_ids,bool is_irrelevant)28 TransformationAddConstantComposite::TransformationAddConstantComposite(
29 uint32_t fresh_id, uint32_t type_id,
30 const std::vector<uint32_t>& constituent_ids, bool is_irrelevant) {
31 message_.set_fresh_id(fresh_id);
32 message_.set_type_id(type_id);
33 message_.set_is_irrelevant(is_irrelevant);
34 for (auto constituent_id : constituent_ids) {
35 message_.add_constituent_id(constituent_id);
36 }
37 }
38
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const39 bool TransformationAddConstantComposite::IsApplicable(
40 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
41 // Check that the given id is fresh.
42 if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
43 return false;
44 }
45 // Check that the composite type id is an instruction id.
46 auto composite_type_instruction =
47 ir_context->get_def_use_mgr()->GetDef(message_.type_id());
48 if (!composite_type_instruction) {
49 return false;
50 }
51 // Gather up the operands for the composite constant, in the process checking
52 // whether the given type really defines a composite and - in the case of a
53 // struct - whether its decorations are OK.
54 std::vector<uint32_t> constituent_type_ids;
55 switch (composite_type_instruction->opcode()) {
56 case SpvOpTypeArray:
57 for (uint32_t index = 0;
58 index <
59 fuzzerutil::GetArraySize(*composite_type_instruction, ir_context);
60 index++) {
61 constituent_type_ids.push_back(
62 composite_type_instruction->GetSingleWordInOperand(0));
63 }
64 break;
65 case SpvOpTypeMatrix:
66 case SpvOpTypeVector:
67 for (uint32_t index = 0;
68 index < composite_type_instruction->GetSingleWordInOperand(1);
69 index++) {
70 constituent_type_ids.push_back(
71 composite_type_instruction->GetSingleWordInOperand(0));
72 }
73 break;
74 case SpvOpTypeStruct:
75 // We do not create constants of structs decorated with Block nor
76 // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it
77 // seems like a strange thing to do, so we disallow it to avoid triggering
78 // low priorty edge case issues related to it.
79 if (fuzzerutil::HasBlockOrBufferBlockDecoration(
80 ir_context, composite_type_instruction->result_id())) {
81 return false;
82 }
83 composite_type_instruction->ForEachInOperand(
84 [&constituent_type_ids](const uint32_t* member_type_id) {
85 constituent_type_ids.push_back(*member_type_id);
86 });
87 break;
88 default:
89 // Not a composite type.
90 return false;
91 }
92
93 // Check that the number of provided operands matches the number of
94 // constituents required by the type.
95 if (constituent_type_ids.size() !=
96 static_cast<uint32_t>(message_.constituent_id().size())) {
97 return false;
98 }
99
100 // Check that every provided operand refers to an instruction of the
101 // corresponding constituent type.
102 for (uint32_t index = 0; index < constituent_type_ids.size(); index++) {
103 auto constituent_instruction =
104 ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index));
105 if (!constituent_instruction) {
106 return false;
107 }
108 if (constituent_instruction->type_id() != constituent_type_ids.at(index)) {
109 return false;
110 }
111 }
112 return true;
113 }
114
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const115 void TransformationAddConstantComposite::Apply(
116 opt::IRContext* ir_context,
117 TransformationContext* transformation_context) const {
118 opt::Instruction::OperandList in_operands;
119 for (auto constituent_id : message_.constituent_id()) {
120 in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
121 }
122 auto new_instruction = MakeUnique<opt::Instruction>(
123 ir_context, SpvOpConstantComposite, message_.type_id(),
124 message_.fresh_id(), in_operands);
125 auto new_instruction_ptr = new_instruction.get();
126 ir_context->module()->AddGlobalValue(std::move(new_instruction));
127 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
128
129 // Inform the def-use manager of the new instruction. Invalidate the constant
130 // manager as we have added a new constant.
131 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
132 ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants);
133
134 if (message_.is_irrelevant()) {
135 transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
136 message_.fresh_id());
137 }
138 }
139
ToMessage() const140 protobufs::Transformation TransformationAddConstantComposite::ToMessage()
141 const {
142 protobufs::Transformation result;
143 *result.mutable_add_constant_composite() = message_;
144 return result;
145 }
146
GetFreshIds() const147 std::unordered_set<uint32_t> TransformationAddConstantComposite::GetFreshIds()
148 const {
149 return {message_.fresh_id()};
150 }
151
152 } // namespace fuzz
153 } // namespace spvtools
154