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