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