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/fact_manager/constant_uniform_facts.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/uniform_buffer_element_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 namespace fact_manager {
23 
ConstantUniformFacts(opt::IRContext * ir_context)24 ConstantUniformFacts::ConstantUniformFacts(opt::IRContext* ir_context)
25     : ir_context_(ir_context) {}
26 
GetConstantId(const protobufs::FactConstantUniform & constant_uniform_fact,uint32_t type_id) const27 uint32_t ConstantUniformFacts::GetConstantId(
28     const protobufs::FactConstantUniform& constant_uniform_fact,
29     uint32_t type_id) const {
30   auto type = ir_context_->get_type_mgr()->GetType(type_id);
31   assert(type != nullptr && "Unknown type id.");
32   const opt::analysis::Constant* known_constant;
33   if (type->AsInteger()) {
34     opt::analysis::IntConstant candidate_constant(
35         type->AsInteger(), GetConstantWords(constant_uniform_fact));
36     known_constant =
37         ir_context_->get_constant_mgr()->FindConstant(&candidate_constant);
38   } else {
39     assert(
40         type->AsFloat() &&
41         "Uniform constant facts are only supported for int and float types.");
42     opt::analysis::FloatConstant candidate_constant(
43         type->AsFloat(), GetConstantWords(constant_uniform_fact));
44     known_constant =
45         ir_context_->get_constant_mgr()->FindConstant(&candidate_constant);
46   }
47   if (!known_constant) {
48     return 0;
49   }
50   return ir_context_->get_constant_mgr()->FindDeclaredConstant(known_constant,
51                                                                type_id);
52 }
53 
GetConstantWords(const protobufs::FactConstantUniform & constant_uniform_fact)54 std::vector<uint32_t> ConstantUniformFacts::GetConstantWords(
55     const protobufs::FactConstantUniform& constant_uniform_fact) {
56   std::vector<uint32_t> result;
57   for (auto constant_word : constant_uniform_fact.constant_word()) {
58     result.push_back(constant_word);
59   }
60   return result;
61 }
62 
DataMatches(const opt::Instruction & constant_instruction,const protobufs::FactConstantUniform & constant_uniform_fact)63 bool ConstantUniformFacts::DataMatches(
64     const opt::Instruction& constant_instruction,
65     const protobufs::FactConstantUniform& constant_uniform_fact) {
66   assert(constant_instruction.opcode() == SpvOpConstant);
67   std::vector<uint32_t> data_in_constant;
68   for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
69     data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
70   }
71   return data_in_constant == GetConstantWords(constant_uniform_fact);
72 }
73 
74 std::vector<uint32_t>
GetConstantsAvailableFromUniformsForType(uint32_t type_id) const75 ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
76     uint32_t type_id) const {
77   std::vector<uint32_t> result;
78   std::set<uint32_t> already_seen;
79   for (auto& fact_and_type_id : facts_and_type_ids_) {
80     if (fact_and_type_id.second != type_id) {
81       continue;
82     }
83     if (auto constant_id = GetConstantId(fact_and_type_id.first, type_id)) {
84       if (already_seen.find(constant_id) == already_seen.end()) {
85         result.push_back(constant_id);
86         already_seen.insert(constant_id);
87       }
88     }
89   }
90   return result;
91 }
92 
93 std::vector<protobufs::UniformBufferElementDescriptor>
GetUniformDescriptorsForConstant(uint32_t constant_id) const94 ConstantUniformFacts::GetUniformDescriptorsForConstant(
95     uint32_t constant_id) const {
96   std::vector<protobufs::UniformBufferElementDescriptor> result;
97   auto constant_inst = ir_context_->get_def_use_mgr()->GetDef(constant_id);
98   assert(constant_inst->opcode() == SpvOpConstant &&
99          "The given id must be that of a constant");
100   auto type_id = constant_inst->type_id();
101   for (auto& fact_and_type_id : facts_and_type_ids_) {
102     if (fact_and_type_id.second != type_id) {
103       continue;
104     }
105     if (DataMatches(*constant_inst, fact_and_type_id.first)) {
106       result.emplace_back(
107           fact_and_type_id.first.uniform_buffer_element_descriptor());
108     }
109   }
110   return result;
111 }
112 
GetConstantFromUniformDescriptor(const protobufs::UniformBufferElementDescriptor & uniform_descriptor) const113 uint32_t ConstantUniformFacts::GetConstantFromUniformDescriptor(
114     const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
115   // Consider each fact.
116   for (auto& fact_and_type : facts_and_type_ids_) {
117     // Check whether the uniform descriptor associated with the fact matches
118     // |uniform_descriptor|.
119     if (UniformBufferElementDescriptorEquals()(
120             &uniform_descriptor,
121             &fact_and_type.first.uniform_buffer_element_descriptor())) {
122       return GetConstantId(fact_and_type.first, fact_and_type.second);
123     }
124   }
125   // No fact associated with the given uniform descriptor was found.
126   return 0;
127 }
128 
129 std::vector<uint32_t>
GetTypesForWhichUniformValuesAreKnown() const130 ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown() const {
131   std::vector<uint32_t> result;
132   for (auto& fact_and_type : facts_and_type_ids_) {
133     if (std::find(result.begin(), result.end(), fact_and_type.second) ==
134         result.end()) {
135       result.push_back(fact_and_type.second);
136     }
137   }
138   return result;
139 }
140 
FloatingPointValueIsSuitable(const protobufs::FactConstantUniform & fact,uint32_t width)141 bool ConstantUniformFacts::FloatingPointValueIsSuitable(
142     const protobufs::FactConstantUniform& fact, uint32_t width) {
143   const uint32_t kFloatWidth = 32;
144   const uint32_t kDoubleWidth = 64;
145   if (width != kFloatWidth && width != kDoubleWidth) {
146     // Only 32- and 64-bit floating-point types are handled.
147     return false;
148   }
149   std::vector<uint32_t> words = GetConstantWords(fact);
150   if (width == 32) {
151     float value;
152     memcpy(&value, words.data(), sizeof(float));
153     if (!std::isfinite(value)) {
154       return false;
155     }
156   } else {
157     double value;
158     memcpy(&value, words.data(), sizeof(double));
159     if (!std::isfinite(value)) {
160       return false;
161     }
162   }
163   return true;
164 }
165 
MaybeAddFact(const protobufs::FactConstantUniform & fact)166 bool ConstantUniformFacts::MaybeAddFact(
167     const protobufs::FactConstantUniform& fact) {
168   // Try to find a unique instruction that declares a variable such that the
169   // variable is decorated with the descriptor set and binding associated with
170   // the constant uniform fact.
171   opt::Instruction* uniform_variable = FindUniformVariable(
172       fact.uniform_buffer_element_descriptor(), ir_context_, true);
173 
174   if (!uniform_variable) {
175     return false;
176   }
177 
178   assert(SpvOpVariable == uniform_variable->opcode());
179   assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
180 
181   auto should_be_uniform_pointer_type =
182       ir_context_->get_type_mgr()->GetType(uniform_variable->type_id());
183   if (!should_be_uniform_pointer_type->AsPointer()) {
184     return false;
185   }
186   if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
187       SpvStorageClassUniform) {
188     return false;
189   }
190   auto should_be_uniform_pointer_instruction =
191       ir_context_->get_def_use_mgr()->GetDef(uniform_variable->type_id());
192   auto composite_type =
193       should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
194 
195   auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
196       ir_context_, composite_type,
197       fact.uniform_buffer_element_descriptor().index());
198   if (!final_element_type_id) {
199     return false;
200   }
201   auto final_element_type =
202       ir_context_->get_type_mgr()->GetType(final_element_type_id);
203   assert(final_element_type &&
204          "There should be a type corresponding to this id.");
205 
206   if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
207     return false;
208   }
209   auto width = final_element_type->AsFloat()
210                    ? final_element_type->AsFloat()->width()
211                    : final_element_type->AsInteger()->width();
212 
213   if (final_element_type->AsFloat() &&
214       !FloatingPointValueIsSuitable(fact, width)) {
215     return false;
216   }
217 
218   auto required_words = (width + 32 - 1) / 32;
219   if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
220     return false;
221   }
222   facts_and_type_ids_.emplace_back(
223       std::pair<protobufs::FactConstantUniform, uint32_t>(
224           fact, final_element_type_id));
225   return true;
226 }
227 
228 const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
GetConstantUniformFactsAndTypes() const229 ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
230   return facts_and_type_ids_;
231 }
232 
233 }  // namespace fact_manager
234 }  // namespace fuzz
235 }  // namespace spvtools
236