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 "fact_manager.h"
16
17 #include <sstream>
18 #include <unordered_map>
19
20 #include "source/fuzz/uniform_buffer_element_descriptor.h"
21 #include "source/opt/ir_context.h"
22
23 namespace spvtools {
24 namespace fuzz {
25 namespace {
26
ToString(const protobufs::FactConstantUniform & fact)27 std::string ToString(const protobufs::FactConstantUniform& fact) {
28 std::stringstream stream;
29 stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
30 << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
31
32 bool first = true;
33 for (auto index : fact.uniform_buffer_element_descriptor().index()) {
34 if (first) {
35 first = false;
36 } else {
37 stream << ", ";
38 }
39 stream << index;
40 }
41
42 stream << "] == [";
43
44 first = true;
45 for (auto constant_word : fact.constant_word()) {
46 if (first) {
47 first = false;
48 } else {
49 stream << ", ";
50 }
51 stream << constant_word;
52 }
53
54 stream << "]";
55 return stream.str();
56 }
57
ToString(const protobufs::FactDataSynonym & fact)58 std::string ToString(const protobufs::FactDataSynonym& fact) {
59 std::stringstream stream;
60 stream << fact.data1() << " = " << fact.data2();
61 return stream.str();
62 }
63
ToString(const protobufs::FactIdEquation & fact)64 std::string ToString(const protobufs::FactIdEquation& fact) {
65 std::stringstream stream;
66 stream << fact.lhs_id();
67 stream << " " << static_cast<SpvOp>(fact.opcode());
68 for (auto rhs_id : fact.rhs_id()) {
69 stream << " " << rhs_id;
70 }
71 return stream.str();
72 }
73
ToString(const protobufs::Fact & fact)74 std::string ToString(const protobufs::Fact& fact) {
75 switch (fact.fact_case()) {
76 case protobufs::Fact::kConstantUniformFact:
77 return ToString(fact.constant_uniform_fact());
78 case protobufs::Fact::kDataSynonymFact:
79 return ToString(fact.data_synonym_fact());
80 case protobufs::Fact::kIdEquationFact:
81 return ToString(fact.id_equation_fact());
82 default:
83 assert(false && "Stringification not supported for this fact.");
84 return "";
85 }
86 }
87
88 } // namespace
89
FactManager(opt::IRContext * ir_context)90 FactManager::FactManager(opt::IRContext* ir_context)
91 : constant_uniform_facts_(ir_context),
92 data_synonym_and_id_equation_facts_(ir_context),
93 dead_block_facts_(ir_context),
94 livesafe_function_facts_(ir_context),
95 irrelevant_value_facts_(ir_context) {}
96
AddInitialFacts(const MessageConsumer & message_consumer,const protobufs::FactSequence & facts)97 void FactManager::AddInitialFacts(const MessageConsumer& message_consumer,
98 const protobufs::FactSequence& facts) {
99 for (auto& fact : facts.fact()) {
100 if (!MaybeAddFact(fact)) {
101 auto message = "Invalid fact " + ToString(fact) + " ignored.";
102 message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str());
103 }
104 }
105 }
106
MaybeAddFact(const fuzz::protobufs::Fact & fact)107 bool FactManager::MaybeAddFact(const fuzz::protobufs::Fact& fact) {
108 switch (fact.fact_case()) {
109 case protobufs::Fact::kBlockIsDeadFact:
110 return dead_block_facts_.MaybeAddFact(fact.block_is_dead_fact());
111 case protobufs::Fact::kConstantUniformFact:
112 return constant_uniform_facts_.MaybeAddFact(fact.constant_uniform_fact());
113 case protobufs::Fact::kDataSynonymFact:
114 return data_synonym_and_id_equation_facts_.MaybeAddFact(
115 fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_);
116 case protobufs::Fact::kFunctionIsLivesafeFact:
117 return livesafe_function_facts_.MaybeAddFact(
118 fact.function_is_livesafe_fact());
119 case protobufs::Fact::kIdEquationFact:
120 return data_synonym_and_id_equation_facts_.MaybeAddFact(
121 fact.id_equation_fact(), dead_block_facts_, irrelevant_value_facts_);
122 case protobufs::Fact::kIdIsIrrelevant:
123 return irrelevant_value_facts_.MaybeAddFact(
124 fact.id_is_irrelevant(), data_synonym_and_id_equation_facts_);
125 case protobufs::Fact::kPointeeValueIsIrrelevantFact:
126 return irrelevant_value_facts_.MaybeAddFact(
127 fact.pointee_value_is_irrelevant_fact(),
128 data_synonym_and_id_equation_facts_);
129 case protobufs::Fact::FACT_NOT_SET:
130 assert(false && "The fact must be set");
131 return false;
132 }
133
134 assert(false && "Unreachable");
135 return false;
136 }
137
AddFactDataSynonym(const protobufs::DataDescriptor & data1,const protobufs::DataDescriptor & data2)138 void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
139 const protobufs::DataDescriptor& data2) {
140 protobufs::FactDataSynonym fact;
141 *fact.mutable_data1() = data1;
142 *fact.mutable_data2() = data2;
143 auto success = data_synonym_and_id_equation_facts_.MaybeAddFact(
144 fact, dead_block_facts_, irrelevant_value_facts_);
145 (void)success; // Keep compilers happy in release mode.
146 assert(success && "Unable to create DataSynonym fact");
147 }
148
GetConstantsAvailableFromUniformsForType(uint32_t type_id) const149 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
150 uint32_t type_id) const {
151 return constant_uniform_facts_.GetConstantsAvailableFromUniformsForType(
152 type_id);
153 }
154
155 std::vector<protobufs::UniformBufferElementDescriptor>
GetUniformDescriptorsForConstant(uint32_t constant_id) const156 FactManager::GetUniformDescriptorsForConstant(uint32_t constant_id) const {
157 return constant_uniform_facts_.GetUniformDescriptorsForConstant(constant_id);
158 }
159
GetConstantFromUniformDescriptor(const protobufs::UniformBufferElementDescriptor & uniform_descriptor) const160 uint32_t FactManager::GetConstantFromUniformDescriptor(
161 const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
162 return constant_uniform_facts_.GetConstantFromUniformDescriptor(
163 uniform_descriptor);
164 }
165
GetTypesForWhichUniformValuesAreKnown() const166 std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
167 const {
168 return constant_uniform_facts_.GetTypesForWhichUniformValuesAreKnown();
169 }
170
171 const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
GetConstantUniformFactsAndTypes() const172 FactManager::GetConstantUniformFactsAndTypes() const {
173 return constant_uniform_facts_.GetConstantUniformFactsAndTypes();
174 }
175
GetIdsForWhichSynonymsAreKnown() const176 std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
177 return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown();
178 }
179
GetAllSynonyms() const180 std::vector<const protobufs::DataDescriptor*> FactManager::GetAllSynonyms()
181 const {
182 return data_synonym_and_id_equation_facts_.GetAllKnownSynonyms();
183 }
184
185 std::vector<const protobufs::DataDescriptor*>
GetSynonymsForDataDescriptor(const protobufs::DataDescriptor & data_descriptor) const186 FactManager::GetSynonymsForDataDescriptor(
187 const protobufs::DataDescriptor& data_descriptor) const {
188 return data_synonym_and_id_equation_facts_.GetSynonymsForDataDescriptor(
189 data_descriptor);
190 }
191
GetSynonymsForId(uint32_t id) const192 std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
193 uint32_t id) const {
194 return data_synonym_and_id_equation_facts_.GetSynonymsForId(id);
195 }
196
IsSynonymous(const protobufs::DataDescriptor & data_descriptor1,const protobufs::DataDescriptor & data_descriptor2) const197 bool FactManager::IsSynonymous(
198 const protobufs::DataDescriptor& data_descriptor1,
199 const protobufs::DataDescriptor& data_descriptor2) const {
200 return data_synonym_and_id_equation_facts_.IsSynonymous(data_descriptor1,
201 data_descriptor2);
202 }
203
BlockIsDead(uint32_t block_id) const204 bool FactManager::BlockIsDead(uint32_t block_id) const {
205 return dead_block_facts_.BlockIsDead(block_id);
206 }
207
AddFactBlockIsDead(uint32_t block_id)208 void FactManager::AddFactBlockIsDead(uint32_t block_id) {
209 protobufs::FactBlockIsDead fact;
210 fact.set_block_id(block_id);
211 auto success = dead_block_facts_.MaybeAddFact(fact);
212 (void)success; // Keep compilers happy in release mode.
213 assert(success && "|block_id| is invalid");
214 }
215
FunctionIsLivesafe(uint32_t function_id) const216 bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
217 return livesafe_function_facts_.FunctionIsLivesafe(function_id);
218 }
219
AddFactFunctionIsLivesafe(uint32_t function_id)220 void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
221 protobufs::FactFunctionIsLivesafe fact;
222 fact.set_function_id(function_id);
223 auto success = livesafe_function_facts_.MaybeAddFact(fact);
224 (void)success; // Keep compilers happy in release mode.
225 assert(success && "|function_id| is invalid");
226 }
227
PointeeValueIsIrrelevant(uint32_t pointer_id) const228 bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
229 return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id);
230 }
231
IdIsIrrelevant(uint32_t result_id) const232 bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
233 return irrelevant_value_facts_.IdIsIrrelevant(result_id, dead_block_facts_);
234 }
235
GetIrrelevantIds() const236 std::unordered_set<uint32_t> FactManager::GetIrrelevantIds() const {
237 return irrelevant_value_facts_.GetIrrelevantIds(dead_block_facts_);
238 }
239
AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id)240 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
241 protobufs::FactPointeeValueIsIrrelevant fact;
242 fact.set_pointer_id(pointer_id);
243 auto success = irrelevant_value_facts_.MaybeAddFact(
244 fact, data_synonym_and_id_equation_facts_);
245 (void)success; // Keep compilers happy in release mode.
246 assert(success && "|pointer_id| is invalid");
247 }
248
AddFactIdIsIrrelevant(uint32_t result_id)249 void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
250 protobufs::FactIdIsIrrelevant fact;
251 fact.set_result_id(result_id);
252 auto success = irrelevant_value_facts_.MaybeAddFact(
253 fact, data_synonym_and_id_equation_facts_);
254 (void)success; // Keep compilers happy in release mode.
255 assert(success && "|result_id| is invalid");
256 }
257
AddFactIdEquation(uint32_t lhs_id,SpvOp opcode,const std::vector<uint32_t> & rhs_id)258 void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
259 const std::vector<uint32_t>& rhs_id) {
260 protobufs::FactIdEquation fact;
261 fact.set_lhs_id(lhs_id);
262 fact.set_opcode(opcode);
263 for (auto an_rhs_id : rhs_id) {
264 fact.add_rhs_id(an_rhs_id);
265 }
266 auto success = data_synonym_and_id_equation_facts_.MaybeAddFact(
267 fact, dead_block_facts_, irrelevant_value_facts_);
268 (void)success; // Keep compilers happy in release mode.
269 assert(success && "Can't create IdIsIrrelevant fact");
270 }
271
ComputeClosureOfFacts(uint32_t maximum_equivalence_class_size)272 void FactManager::ComputeClosureOfFacts(
273 uint32_t maximum_equivalence_class_size) {
274 data_synonym_and_id_equation_facts_.ComputeClosureOfFacts(
275 maximum_equivalence_class_size);
276 }
277
278 } // namespace fuzz
279 } // namespace spvtools
280