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() == spv::Op::OpConstant);
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() == spv::Op::OpConstant &&
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(spv::Op::OpVariable == uniform_variable->opcode());
179 assert(spv::StorageClass::Uniform ==
180 spv::StorageClass(uniform_variable->GetSingleWordInOperand(0)));
181
182 auto should_be_uniform_pointer_type =
183 ir_context_->get_type_mgr()->GetType(uniform_variable->type_id());
184 if (!should_be_uniform_pointer_type->AsPointer()) {
185 return false;
186 }
187 if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
188 spv::StorageClass::Uniform) {
189 return false;
190 }
191 auto should_be_uniform_pointer_instruction =
192 ir_context_->get_def_use_mgr()->GetDef(uniform_variable->type_id());
193 auto composite_type =
194 should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
195
196 auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
197 ir_context_, composite_type,
198 fact.uniform_buffer_element_descriptor().index());
199 if (!final_element_type_id) {
200 return false;
201 }
202 auto final_element_type =
203 ir_context_->get_type_mgr()->GetType(final_element_type_id);
204 assert(final_element_type &&
205 "There should be a type corresponding to this id.");
206
207 if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
208 return false;
209 }
210 auto width = final_element_type->AsFloat()
211 ? final_element_type->AsFloat()->width()
212 : final_element_type->AsInteger()->width();
213
214 if (final_element_type->AsFloat() &&
215 !FloatingPointValueIsSuitable(fact, width)) {
216 return false;
217 }
218
219 auto required_words = (width + 32 - 1) / 32;
220 if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
221 return false;
222 }
223 facts_and_type_ids_.emplace_back(
224 std::pair<protobufs::FactConstantUniform, uint32_t>(
225 fact, final_element_type_id));
226 return true;
227 }
228
229 const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
GetConstantUniformFactsAndTypes() const230 ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
231 return facts_and_type_ids_;
232 }
233
234 } // namespace fact_manager
235 } // namespace fuzz
236 } // namespace spvtools
237