| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453 |
- // Copyright (c) 2019 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "source/fuzz/fact_manager.h"
- #include <sstream>
- #include <unordered_map>
- #include <unordered_set>
- #include "source/fuzz/equivalence_relation.h"
- #include "source/fuzz/fuzzer_util.h"
- #include "source/fuzz/uniform_buffer_element_descriptor.h"
- #include "source/opt/ir_context.h"
- namespace spvtools {
- namespace fuzz {
- namespace {
- std::string ToString(const protobufs::FactConstantUniform& fact) {
- std::stringstream stream;
- stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
- << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
- bool first = true;
- for (auto index : fact.uniform_buffer_element_descriptor().index()) {
- if (first) {
- first = false;
- } else {
- stream << ", ";
- }
- stream << index;
- }
- stream << "] == [";
- first = true;
- for (auto constant_word : fact.constant_word()) {
- if (first) {
- first = false;
- } else {
- stream << ", ";
- }
- stream << constant_word;
- }
- stream << "]";
- return stream.str();
- }
- std::string ToString(const protobufs::FactDataSynonym& fact) {
- std::stringstream stream;
- stream << fact.data1() << " = " << fact.data2();
- return stream.str();
- }
- std::string ToString(const protobufs::FactIdEquation& fact) {
- std::stringstream stream;
- stream << fact.lhs_id();
- stream << " " << static_cast<SpvOp>(fact.opcode());
- for (auto rhs_id : fact.rhs_id()) {
- stream << " " << rhs_id;
- }
- return stream.str();
- }
- std::string ToString(const protobufs::Fact& fact) {
- switch (fact.fact_case()) {
- case protobufs::Fact::kConstantUniformFact:
- return ToString(fact.constant_uniform_fact());
- case protobufs::Fact::kDataSynonymFact:
- return ToString(fact.data_synonym_fact());
- case protobufs::Fact::kIdEquationFact:
- return ToString(fact.id_equation_fact());
- default:
- assert(false && "Stringification not supported for this fact.");
- return "";
- }
- }
- } // namespace
- //=======================
- // Constant uniform facts
- // The purpose of this class is to group the fields and data used to represent
- // facts about uniform constants.
- class FactManager::ConstantUniformFacts {
- public:
- // See method in FactManager which delegates to this method.
- bool AddFact(const protobufs::FactConstantUniform& fact,
- opt::IRContext* context);
- // See method in FactManager which delegates to this method.
- std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
- opt::IRContext* ir_context, uint32_t type_id) const;
- // See method in FactManager which delegates to this method.
- const std::vector<protobufs::UniformBufferElementDescriptor>
- GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
- uint32_t constant_id) const;
- // See method in FactManager which delegates to this method.
- uint32_t GetConstantFromUniformDescriptor(
- opt::IRContext* context,
- const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
- const;
- // See method in FactManager which delegates to this method.
- std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
- // See method in FactManager which delegates to this method.
- const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
- GetConstantUniformFactsAndTypes() const;
- private:
- // Returns true if and only if the words associated with
- // |constant_instruction| exactly match the words for the constant associated
- // with |constant_uniform_fact|.
- bool DataMatches(
- const opt::Instruction& constant_instruction,
- const protobufs::FactConstantUniform& constant_uniform_fact) const;
- // Yields the constant words associated with |constant_uniform_fact|.
- std::vector<uint32_t> GetConstantWords(
- const protobufs::FactConstantUniform& constant_uniform_fact) const;
- // Yields the id of a constant of type |type_id| whose data matches the
- // constant data in |constant_uniform_fact|, or 0 if no such constant is
- // declared.
- uint32_t GetConstantId(
- opt::IRContext* context,
- const protobufs::FactConstantUniform& constant_uniform_fact,
- uint32_t type_id) const;
- // Checks that the width of a floating-point constant is supported, and that
- // the constant is finite.
- bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact,
- uint32_t width) const;
- std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
- facts_and_type_ids_;
- };
- uint32_t FactManager::ConstantUniformFacts::GetConstantId(
- opt::IRContext* context,
- const protobufs::FactConstantUniform& constant_uniform_fact,
- uint32_t type_id) const {
- auto type = context->get_type_mgr()->GetType(type_id);
- assert(type != nullptr && "Unknown type id.");
- const opt::analysis::Constant* known_constant;
- if (type->AsInteger()) {
- opt::analysis::IntConstant candidate_constant(
- type->AsInteger(), GetConstantWords(constant_uniform_fact));
- known_constant =
- context->get_constant_mgr()->FindConstant(&candidate_constant);
- } else {
- assert(
- type->AsFloat() &&
- "Uniform constant facts are only supported for int and float types.");
- opt::analysis::FloatConstant candidate_constant(
- type->AsFloat(), GetConstantWords(constant_uniform_fact));
- known_constant =
- context->get_constant_mgr()->FindConstant(&candidate_constant);
- }
- if (!known_constant) {
- return 0;
- }
- return context->get_constant_mgr()->FindDeclaredConstant(known_constant,
- type_id);
- }
- std::vector<uint32_t> FactManager::ConstantUniformFacts::GetConstantWords(
- const protobufs::FactConstantUniform& constant_uniform_fact) const {
- std::vector<uint32_t> result;
- for (auto constant_word : constant_uniform_fact.constant_word()) {
- result.push_back(constant_word);
- }
- return result;
- }
- bool FactManager::ConstantUniformFacts::DataMatches(
- const opt::Instruction& constant_instruction,
- const protobufs::FactConstantUniform& constant_uniform_fact) const {
- assert(constant_instruction.opcode() == SpvOpConstant);
- std::vector<uint32_t> data_in_constant;
- for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
- data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
- }
- return data_in_constant == GetConstantWords(constant_uniform_fact);
- }
- std::vector<uint32_t>
- FactManager::ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
- opt::IRContext* ir_context, uint32_t type_id) const {
- std::vector<uint32_t> result;
- std::set<uint32_t> already_seen;
- for (auto& fact_and_type_id : facts_and_type_ids_) {
- if (fact_and_type_id.second != type_id) {
- continue;
- }
- if (auto constant_id =
- GetConstantId(ir_context, fact_and_type_id.first, type_id)) {
- if (already_seen.find(constant_id) == already_seen.end()) {
- result.push_back(constant_id);
- already_seen.insert(constant_id);
- }
- }
- }
- return result;
- }
- const std::vector<protobufs::UniformBufferElementDescriptor>
- FactManager::ConstantUniformFacts::GetUniformDescriptorsForConstant(
- opt::IRContext* ir_context, uint32_t constant_id) const {
- std::vector<protobufs::UniformBufferElementDescriptor> result;
- auto constant_inst = ir_context->get_def_use_mgr()->GetDef(constant_id);
- assert(constant_inst->opcode() == SpvOpConstant &&
- "The given id must be that of a constant");
- auto type_id = constant_inst->type_id();
- for (auto& fact_and_type_id : facts_and_type_ids_) {
- if (fact_and_type_id.second != type_id) {
- continue;
- }
- if (DataMatches(*constant_inst, fact_and_type_id.first)) {
- result.emplace_back(
- fact_and_type_id.first.uniform_buffer_element_descriptor());
- }
- }
- return result;
- }
- uint32_t FactManager::ConstantUniformFacts::GetConstantFromUniformDescriptor(
- opt::IRContext* context,
- const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
- // Consider each fact.
- for (auto& fact_and_type : facts_and_type_ids_) {
- // Check whether the uniform descriptor associated with the fact matches
- // |uniform_descriptor|.
- if (UniformBufferElementDescriptorEquals()(
- &uniform_descriptor,
- &fact_and_type.first.uniform_buffer_element_descriptor())) {
- return GetConstantId(context, fact_and_type.first, fact_and_type.second);
- }
- }
- // No fact associated with the given uniform descriptor was found.
- return 0;
- }
- std::vector<uint32_t>
- FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
- const {
- std::vector<uint32_t> result;
- for (auto& fact_and_type : facts_and_type_ids_) {
- if (std::find(result.begin(), result.end(), fact_and_type.second) ==
- result.end()) {
- result.push_back(fact_and_type.second);
- }
- }
- return result;
- }
- bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable(
- const protobufs::FactConstantUniform& fact, uint32_t width) const {
- const uint32_t kFloatWidth = 32;
- const uint32_t kDoubleWidth = 64;
- if (width != kFloatWidth && width != kDoubleWidth) {
- // Only 32- and 64-bit floating-point types are handled.
- return false;
- }
- std::vector<uint32_t> words = GetConstantWords(fact);
- if (width == 32) {
- float value;
- memcpy(&value, words.data(), sizeof(float));
- if (!std::isfinite(value)) {
- return false;
- }
- } else {
- double value;
- memcpy(&value, words.data(), sizeof(double));
- if (!std::isfinite(value)) {
- return false;
- }
- }
- return true;
- }
- bool FactManager::ConstantUniformFacts::AddFact(
- const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
- // Try to find a unique instruction that declares a variable such that the
- // variable is decorated with the descriptor set and binding associated with
- // the constant uniform fact.
- opt::Instruction* uniform_variable = FindUniformVariable(
- fact.uniform_buffer_element_descriptor(), context, true);
- if (!uniform_variable) {
- return false;
- }
- assert(SpvOpVariable == uniform_variable->opcode());
- assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
- auto should_be_uniform_pointer_type =
- context->get_type_mgr()->GetType(uniform_variable->type_id());
- if (!should_be_uniform_pointer_type->AsPointer()) {
- return false;
- }
- if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
- SpvStorageClassUniform) {
- return false;
- }
- auto should_be_uniform_pointer_instruction =
- context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
- auto composite_type =
- should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
- auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
- context, composite_type,
- fact.uniform_buffer_element_descriptor().index());
- if (!final_element_type_id) {
- return false;
- }
- auto final_element_type =
- context->get_type_mgr()->GetType(final_element_type_id);
- assert(final_element_type &&
- "There should be a type corresponding to this id.");
- if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
- return false;
- }
- auto width = final_element_type->AsFloat()
- ? final_element_type->AsFloat()->width()
- : final_element_type->AsInteger()->width();
- if (final_element_type->AsFloat() &&
- !FloatingPointValueIsSuitable(fact, width)) {
- return false;
- }
- auto required_words = (width + 32 - 1) / 32;
- if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
- return false;
- }
- facts_and_type_ids_.emplace_back(
- std::pair<protobufs::FactConstantUniform, uint32_t>(
- fact, final_element_type_id));
- return true;
- }
- const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
- FactManager::ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
- return facts_and_type_ids_;
- }
- // End of uniform constant facts
- //==============================
- //==============================
- // Data synonym and id equation facts
- // This helper struct represents the right hand side of an equation as an
- // operator applied to a number of data descriptor operands.
- struct Operation {
- SpvOp opcode;
- std::vector<const protobufs::DataDescriptor*> operands;
- };
- // Hashing for operations, to allow deterministic unordered sets.
- struct OperationHash {
- size_t operator()(const Operation& operation) const {
- std::u32string hash;
- hash.push_back(operation.opcode);
- for (auto operand : operation.operands) {
- hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
- }
- return std::hash<std::u32string>()(hash);
- }
- };
- // Equality for operations, to allow deterministic unordered sets.
- struct OperationEquals {
- bool operator()(const Operation& first, const Operation& second) const {
- // Equal operations require...
- //
- // Equal opcodes.
- if (first.opcode != second.opcode) {
- return false;
- }
- // Matching operand counds.
- if (first.operands.size() != second.operands.size()) {
- return false;
- }
- // Equal operands.
- for (uint32_t i = 0; i < first.operands.size(); i++) {
- if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
- return false;
- }
- }
- return true;
- }
- };
- // A helper, for debugging, to represent an operation as a string.
- std::string ToString(const Operation& operation) {
- std::stringstream stream;
- stream << operation.opcode;
- for (auto operand : operation.operands) {
- stream << " " << *operand;
- }
- return stream.str();
- }
- // The purpose of this class is to group the fields and data used to represent
- // facts about data synonyms and id equations.
- class FactManager::DataSynonymAndIdEquationFacts {
- public:
- // See method in FactManager which delegates to this method.
- void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
- // See method in FactManager which delegates to this method.
- void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
- // See method in FactManager which delegates to this method.
- std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
- const protobufs::DataDescriptor& data_descriptor) const;
- // See method in FactManager which delegates to this method.
- std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
- // See method in FactManager which delegates to this method.
- bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
- const protobufs::DataDescriptor& data_descriptor2) const;
- // See method in FactManager which delegates to this method.
- void ComputeClosureOfFacts(opt::IRContext* context,
- uint32_t maximum_equivalence_class_size);
- private:
- using OperationSet =
- std::unordered_set<Operation, OperationHash, OperationEquals>;
- // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
- // into sub-components of the data descriptors, if they are composites, to
- // record that their components are pairwise-synonymous.
- void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2,
- opt::IRContext* context);
- // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
- // of equations that are known about them.
- void MakeEquivalent(const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2);
- // Returns true if and only if |dd1| and |dd2| are valid data descriptors
- // whose associated data have the same type (modulo integer signedness).
- bool DataDescriptorsAreWellFormedAndComparable(
- opt::IRContext* context, const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2) const;
- OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
- // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
- // |synonymous_| equivalence relation, but is not necessarily its own
- // representative. Records the fact that the equation
- // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any
- // corollaries, in the form of data synonym or equation facts, that follow
- // from this and other known facts.
- void AddEquationFactRecursive(
- const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
- const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
- opt::IRContext* context);
- // The data descriptors that are known to be synonymous with one another are
- // captured by this equivalence relation.
- EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
- DataDescriptorEquals>
- synonymous_;
- // When a new synonym fact is added, it may be possible to deduce further
- // synonym facts by computing a closure of all known facts. However, this is
- // an expensive operation, so it should be performed sparingly and only there
- // is some chance of new facts being deduced. This boolean tracks whether a
- // closure computation is required - i.e., whether a new fact has been added
- // since the last time such a computation was performed.
- bool closure_computation_required_ = false;
- // Represents a set of equations on data descriptors as a map indexed by
- // left-hand-side, mapping a left-hand-side to a set of operations, each of
- // which (together with the left-hand-side) defines an equation.
- //
- // All data descriptors occurring in equations are required to be present in
- // the |synonymous_| equivalence relation, and to be their own representatives
- // in that relation.
- std::unordered_map<const protobufs::DataDescriptor*, OperationSet>
- id_equations_;
- };
- void FactManager::DataSynonymAndIdEquationFacts::AddFact(
- const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
- // Add the fact, including all facts relating sub-components of the data
- // descriptors that are involved.
- AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
- }
- void FactManager::DataSynonymAndIdEquationFacts::AddFact(
- const protobufs::FactIdEquation& fact, opt::IRContext* context) {
- protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
- // Register the LHS in the equivalence relation if needed.
- if (!synonymous_.Exists(lhs_dd)) {
- synonymous_.Register(lhs_dd);
- }
- // Get equivalence class representatives for all ids used on the RHS of the
- // equation.
- std::vector<const protobufs::DataDescriptor*> rhs_dd_ptrs;
- for (auto rhs_id : fact.rhs_id()) {
- // Register a data descriptor based on this id in the equivalence relation
- // if needed, and then record the equivalence class representative.
- protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
- if (!synonymous_.Exists(rhs_dd)) {
- synonymous_.Register(rhs_dd);
- }
- rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
- }
- // Now add the fact.
- AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()),
- rhs_dd_ptrs, context);
- }
- FactManager::DataSynonymAndIdEquationFacts::OperationSet
- FactManager::DataSynonymAndIdEquationFacts::GetEquations(
- const protobufs::DataDescriptor* lhs) const {
- auto existing = id_equations_.find(lhs);
- if (existing == id_equations_.end()) {
- return OperationSet();
- }
- return existing->second;
- }
- void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
- const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
- const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
- opt::IRContext* context) {
- assert(synonymous_.Exists(lhs_dd) &&
- "The LHS must be known to the equivalence relation.");
- for (auto rhs_dd : rhs_dds) {
- // Keep release compilers happy.
- (void)(rhs_dd);
- assert(synonymous_.Exists(*rhs_dd) &&
- "The RHS operands must be known to the equivalence relation.");
- }
- auto lhs_dd_representative = synonymous_.Find(&lhs_dd);
- if (id_equations_.count(lhs_dd_representative) == 0) {
- // We have not seen an equation with this LHS before, so associate the LHS
- // with an initially empty set.
- id_equations_.insert({lhs_dd_representative, OperationSet()});
- }
- {
- auto existing_equations = id_equations_.find(lhs_dd_representative);
- assert(existing_equations != id_equations_.end() &&
- "A set of operations should be present, even if empty.");
- Operation new_operation = {opcode, rhs_dds};
- if (existing_equations->second.count(new_operation)) {
- // This equation is known, so there is nothing further to be done.
- return;
- }
- // Add the equation to the set of known equations.
- existing_equations->second.insert(new_operation);
- }
- // Now try to work out corollaries implied by the new equation and existing
- // facts.
- switch (opcode) {
- case SpvOpIAdd: {
- // Equation form: "a = b + c"
- for (auto equation : GetEquations(rhs_dds[0])) {
- if (equation.opcode == SpvOpISub) {
- // Equation form: "a = (d - e) + c"
- if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
- // Equation form: "a = (d - c) + c"
- // We can thus infer "a = d"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
- }
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
- // Equation form: "a = (c - e) + c"
- // We can thus infer "a = -e"
- AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
- {equation.operands[1]}, context);
- }
- }
- }
- for (auto equation : GetEquations(rhs_dds[1])) {
- if (equation.opcode == SpvOpISub) {
- // Equation form: "a = b + (d - e)"
- if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
- // Equation form: "a = b + (d - b)"
- // We can thus infer "a = d"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
- }
- }
- }
- break;
- }
- case SpvOpISub: {
- // Equation form: "a = b - c"
- for (auto equation : GetEquations(rhs_dds[0])) {
- if (equation.opcode == SpvOpIAdd) {
- // Equation form: "a = (d + e) - c"
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
- // Equation form: "a = (c + e) - c"
- // We can thus infer "a = e"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
- }
- if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
- // Equation form: "a = (d + c) - c"
- // We can thus infer "a = d"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
- }
- }
- if (equation.opcode == SpvOpISub) {
- // Equation form: "a = (d - e) - c"
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
- // Equation form: "a = (c - e) - c"
- // We can thus infer "a = -e"
- AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
- {equation.operands[1]}, context);
- }
- }
- }
- for (auto equation : GetEquations(rhs_dds[1])) {
- if (equation.opcode == SpvOpIAdd) {
- // Equation form: "a = b - (d + e)"
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
- // Equation form: "a = b - (b + e)"
- // We can thus infer "a = -e"
- AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
- {equation.operands[1]}, context);
- }
- if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
- // Equation form: "a = b - (d + b)"
- // We can thus infer "a = -d"
- AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
- {equation.operands[0]}, context);
- }
- }
- if (equation.opcode == SpvOpISub) {
- // Equation form: "a = b - (d - e)"
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
- // Equation form: "a = b - (b - e)"
- // We can thus infer "a = e"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
- }
- }
- }
- break;
- }
- case SpvOpLogicalNot:
- case SpvOpSNegate: {
- // Equation form: "a = !b" or "a = -b"
- for (auto equation : GetEquations(rhs_dds[0])) {
- if (equation.opcode == opcode) {
- // Equation form: "a = !!b" or "a = -(-b)"
- // We can thus infer "a = b"
- AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
- }
- }
- break;
- }
- default:
- break;
- }
- }
- void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
- const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
- opt::IRContext* context) {
- assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
- // Record that the data descriptors provided in the fact are equivalent.
- MakeEquivalent(dd1, dd2);
- // We now check whether this is a synonym about composite objects. If it is,
- // we can recursively add synonym facts about their associated sub-components.
- // Get the type of the object referred to by the first data descriptor in the
- // synonym fact.
- uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
- context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
- dd1.index());
- auto type = context->get_type_mgr()->GetType(type_id);
- auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
- assert(type != nullptr &&
- "Invalid data synonym fact: one side has an unknown type.");
- // Check whether the type is composite, recording the number of elements
- // associated with the composite if so.
- uint32_t num_composite_elements;
- if (type->AsArray()) {
- num_composite_elements =
- fuzzerutil::GetArraySize(*type_instruction, context);
- } else if (type->AsMatrix()) {
- num_composite_elements = type->AsMatrix()->element_count();
- } else if (type->AsStruct()) {
- num_composite_elements =
- fuzzerutil::GetNumberOfStructMembers(*type_instruction);
- } else if (type->AsVector()) {
- num_composite_elements = type->AsVector()->element_count();
- } else {
- // The type is not a composite, so return.
- return;
- }
- // If the fact has the form:
- // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
- // then for each composite index i, we add a fact of the form:
- // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
- //
- // However, to avoid adding a large number of synonym facts e.g. in the case
- // of arrays, we bound the number of composite elements to which this is
- // applied. Nevertheless, we always add a synonym fact for the final
- // components, as this may be an interesting edge case.
- // The bound on the number of indices of the composite pair to note as being
- // synonymous.
- const uint32_t kCompositeElementBound = 10;
- for (uint32_t i = 0; i < num_composite_elements;) {
- std::vector<uint32_t> extended_indices1 =
- fuzzerutil::RepeatedFieldToVector(dd1.index());
- extended_indices1.push_back(i);
- std::vector<uint32_t> extended_indices2 =
- fuzzerutil::RepeatedFieldToVector(dd2.index());
- extended_indices2.push_back(i);
- AddDataSynonymFactRecursive(
- MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
- MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
- context);
- if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) {
- // We have not reached the bound yet, or have already skipped ahead to the
- // last element, so increment the loop counter as standard.
- i++;
- } else {
- // We have reached the bound, so skip ahead to the last element.
- assert(i == kCompositeElementBound - 1);
- i = num_composite_elements - 1;
- }
- }
- }
- void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
- opt::IRContext* context, uint32_t maximum_equivalence_class_size) {
- // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
- // data descriptors that describe objects of the same composite type, and that
- // the composite type is comprised of k components.
- //
- // For example, if m is a mat4x4 and v a vec4, we might consider:
- // m[2]: describes the 2nd column of m, a vec4
- // v[]: describes all of v, a vec4
- //
- // Suppose that we know, for every 0 <= i < k, that the fact:
- // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
- // holds - i.e. that the children of the two data descriptors are synonymous.
- //
- // Then we can conclude that:
- // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
- // holds.
- //
- // For instance, if we have the facts:
- // m[2, 0] == v[0]
- // m[2, 1] == v[1]
- // m[2, 2] == v[2]
- // m[2, 3] == v[3]
- // then we can conclude that:
- // m[2] == v.
- //
- // This method repeatedly searches the equivalence relation of data
- // descriptors, deducing and adding such facts, until a pass over the
- // relation leads to no further facts being deduced.
- // The method relies on working with pairs of data descriptors, and in
- // particular being able to hash and compare such pairs.
- using DataDescriptorPair =
- std::pair<protobufs::DataDescriptor, protobufs::DataDescriptor>;
- struct DataDescriptorPairHash {
- std::size_t operator()(const DataDescriptorPair& pair) const {
- return DataDescriptorHash()(&pair.first) ^
- DataDescriptorHash()(&pair.second);
- }
- };
- struct DataDescriptorPairEquals {
- bool operator()(const DataDescriptorPair& first,
- const DataDescriptorPair& second) const {
- return (DataDescriptorEquals()(&first.first, &second.first) &&
- DataDescriptorEquals()(&first.second, &second.second)) ||
- (DataDescriptorEquals()(&first.first, &second.second) &&
- DataDescriptorEquals()(&first.second, &second.first));
- }
- };
- // This map records, for a given pair of composite data descriptors of the
- // same type, all the indices at which the data descriptors are known to be
- // synonymous. A pair is a key to this map only if we have observed that
- // the pair are synonymous at *some* index, but not at *all* indices.
- // Once we find that a pair of data descriptors are equivalent at all indices
- // we record the fact that they are synonymous and remove them from the map.
- //
- // Using the m and v example from above, initially the pair (m[2], v) would
- // not be a key to the map. If we find that m[2, 2] == v[2] holds, we would
- // add an entry:
- // (m[2], v) -> [false, false, true, false]
- // to record that they are synonymous at index 2. If we then find that
- // m[2, 0] == v[0] holds, we would update this entry to:
- // (m[2], v) -> [true, false, true, false]
- // If we then find that m[2, 3] == v[3] holds, we would update this entry to:
- // (m[2], v) -> [true, false, true, true]
- // Finally, if we then find that m[2, 1] == v[1] holds, which would make the
- // boolean vector true at every index, we would add the fact:
- // m[2] == v
- // to the equivalence relation and remove (m[2], v) from the map.
- std::unordered_map<DataDescriptorPair, std::vector<bool>,
- DataDescriptorPairHash, DataDescriptorPairEquals>
- candidate_composite_synonyms;
- // We keep looking for new facts until we perform a complete pass over the
- // equivalence relation without finding any new facts.
- while (closure_computation_required_) {
- // We have not found any new facts yet during this pass; we set this to
- // 'true' if we do find a new fact.
- closure_computation_required_ = false;
- // Consider each class in the equivalence relation.
- for (auto representative :
- synonymous_.GetEquivalenceClassRepresentatives()) {
- auto equivalence_class = synonymous_.GetEquivalenceClass(*representative);
- if (equivalence_class.size() > maximum_equivalence_class_size) {
- // This equivalence class is larger than the maximum size we are willing
- // to consider, so we skip it. This potentially leads to missed fact
- // deductions, but avoids excessive runtime for closure computation.
- continue;
- }
- // Consider every data descriptor in the equivalence class.
- for (auto dd1_it = equivalence_class.begin();
- dd1_it != equivalence_class.end(); ++dd1_it) {
- // If this data descriptor has no indices then it does not have the form
- // obj_1[a_1, ..., a_m, i], so move on.
- auto dd1 = *dd1_it;
- if (dd1->index_size() == 0) {
- continue;
- }
- // Consider every other data descriptor later in the equivalence class
- // (due to symmetry, there is no need to compare with previous data
- // descriptors).
- auto dd2_it = dd1_it;
- for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) {
- auto dd2 = *dd2_it;
- // If this data descriptor has no indices then it does not have the
- // form obj_2[b_1, ..., b_n, i], so move on.
- if (dd2->index_size() == 0) {
- continue;
- }
- // At this point we know that:
- // - |dd1| has the form obj_1[a_1, ..., a_m, i]
- // - |dd2| has the form obj_2[b_1, ..., b_n, j]
- assert(dd1->index_size() > 0 && dd2->index_size() > 0 &&
- "Control should not reach here if either data descriptor has "
- "no indices.");
- // We are only interested if i == j.
- if (dd1->index(dd1->index_size() - 1) !=
- dd2->index(dd2->index_size() - 1)) {
- continue;
- }
- const uint32_t common_final_index = dd1->index(dd1->index_size() - 1);
- // Make data descriptors |dd1_prefix| and |dd2_prefix| for
- // obj_1[a_1, ..., a_m]
- // and
- // obj_2[b_1, ..., b_n]
- // These are the two data descriptors we might be getting closer to
- // deducing as being synonymous, due to knowing that they are
- // synonymous when extended by a particular index.
- protobufs::DataDescriptor dd1_prefix;
- dd1_prefix.set_object(dd1->object());
- for (uint32_t i = 0; i < static_cast<uint32_t>(dd1->index_size() - 1);
- i++) {
- dd1_prefix.add_index(dd1->index(i));
- }
- protobufs::DataDescriptor dd2_prefix;
- dd2_prefix.set_object(dd2->object());
- for (uint32_t i = 0; i < static_cast<uint32_t>(dd2->index_size() - 1);
- i++) {
- dd2_prefix.add_index(dd2->index(i));
- }
- assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) &&
- "By construction these prefixes should be different.");
- // If we already know that these prefixes are synonymous, move on.
- if (synonymous_.Exists(dd1_prefix) &&
- synonymous_.Exists(dd2_prefix) &&
- synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) {
- continue;
- }
- // Get the type of obj_1
- auto dd1_root_type_id =
- context->get_def_use_mgr()->GetDef(dd1->object())->type_id();
- // Use this type, together with a_1, ..., a_m, to get the type of
- // obj_1[a_1, ..., a_m].
- auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
- context, dd1_root_type_id, dd1_prefix.index());
- // Similarly, get the type of obj_2 and use it to get the type of
- // obj_2[b_1, ..., b_n].
- auto dd2_root_type_id =
- context->get_def_use_mgr()->GetDef(dd2->object())->type_id();
- auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
- context, dd2_root_type_id, dd2_prefix.index());
- // If the types of dd1_prefix and dd2_prefix are not the same, they
- // cannot be synonymous.
- if (dd1_prefix_type != dd2_prefix_type) {
- continue;
- }
- // At this point, we know we have synonymous data descriptors of the
- // form:
- // obj_1[a_1, ..., a_m, i]
- // obj_2[b_1, ..., b_n, i]
- // with the same last_index i, such that:
- // obj_1[a_1, ..., a_m]
- // and
- // obj_2[b_1, ..., b_n]
- // have the same type.
- // Work out how many components there are in the (common) commposite
- // type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n].
- // This depends on whether the composite type is array, matrix, struct
- // or vector.
- uint32_t num_components_in_composite;
- auto composite_type =
- context->get_type_mgr()->GetType(dd1_prefix_type);
- auto composite_type_instruction =
- context->get_def_use_mgr()->GetDef(dd1_prefix_type);
- if (composite_type->AsArray()) {
- num_components_in_composite =
- fuzzerutil::GetArraySize(*composite_type_instruction, context);
- if (num_components_in_composite == 0) {
- // This indicates that the array has an unknown size, in which
- // case we cannot be sure we have matched all of its elements with
- // synonymous elements of another array.
- continue;
- }
- } else if (composite_type->AsMatrix()) {
- num_components_in_composite =
- composite_type->AsMatrix()->element_count();
- } else if (composite_type->AsStruct()) {
- num_components_in_composite = fuzzerutil::GetNumberOfStructMembers(
- *composite_type_instruction);
- } else {
- assert(composite_type->AsVector());
- num_components_in_composite =
- composite_type->AsVector()->element_count();
- }
- // We are one step closer to being able to say that |dd1_prefix| and
- // |dd2_prefix| are synonymous.
- DataDescriptorPair candidate_composite_synonym(dd1_prefix,
- dd2_prefix);
- // We look up what we already know about this pair.
- auto existing_entry =
- candidate_composite_synonyms.find(candidate_composite_synonym);
- if (existing_entry == candidate_composite_synonyms.end()) {
- // If this is the first time we have seen the pair, we make a vector
- // of size |num_components_in_composite| that is 'true' at the
- // common final index associated with |dd1| and |dd2|, and 'false'
- // everywhere else, and register this vector as being associated
- // with the pair.
- std::vector<bool> entry;
- for (uint32_t i = 0; i < num_components_in_composite; i++) {
- entry.push_back(i == common_final_index);
- }
- candidate_composite_synonyms[candidate_composite_synonym] = entry;
- existing_entry =
- candidate_composite_synonyms.find(candidate_composite_synonym);
- } else {
- // We have seen this pair of data descriptors before, and we now
- // know that they are synonymous at one further index, so we
- // update the entry to record that.
- existing_entry->second[common_final_index] = true;
- }
- assert(existing_entry != candidate_composite_synonyms.end());
- // Check whether |dd1_prefix| and |dd2_prefix| are now known to match
- // at every sub-component.
- bool all_components_match = true;
- for (uint32_t i = 0; i < num_components_in_composite; i++) {
- if (!existing_entry->second[i]) {
- all_components_match = false;
- break;
- }
- }
- if (all_components_match) {
- // The two prefixes match on all sub-components, so we know that
- // they are synonymous. We add this fact *non-recursively*, as we
- // have deduced that |dd1_prefix| and |dd2_prefix| are synonymous
- // by observing that all their sub-components are already
- // synonymous.
- assert(DataDescriptorsAreWellFormedAndComparable(
- context, dd1_prefix, dd2_prefix));
- MakeEquivalent(dd1_prefix, dd2_prefix);
- // Now that we know this pair of data descriptors are synonymous,
- // there is no point recording how close they are to being
- // synonymous.
- candidate_composite_synonyms.erase(candidate_composite_synonym);
- }
- }
- }
- }
- }
- }
- void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
- const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2) {
- // Register the data descriptors if they are not already known to the
- // equivalence relation.
- for (const auto& dd : {dd1, dd2}) {
- if (!synonymous_.Exists(dd)) {
- synonymous_.Register(dd);
- }
- }
- if (synonymous_.IsEquivalent(dd1, dd2)) {
- // The data descriptors are already known to be equivalent, so there is
- // nothing to do.
- return;
- }
- // We must make the data descriptors equivalent, and also make sure any
- // equation facts known about their representatives are merged.
- // Record the original equivalence class representatives of the data
- // descriptors.
- auto dd1_original_representative = synonymous_.Find(&dd1);
- auto dd2_original_representative = synonymous_.Find(&dd2);
- // Make the data descriptors equivalent.
- synonymous_.MakeEquivalent(dd1, dd2);
- // As we have updated the equivalence relation, we might be able to deduce
- // more facts by performing a closure computation, so we record that such a
- // computation is required.
- closure_computation_required_ = true;
- // At this point, exactly one of |dd1_original_representative| and
- // |dd2_original_representative| will be the representative of the combined
- // equivalence class. We work out which one of them is still the class
- // representative and which one is no longer the class representative.
- auto still_representative = synonymous_.Find(dd1_original_representative) ==
- dd1_original_representative
- ? dd1_original_representative
- : dd2_original_representative;
- auto no_longer_representative =
- still_representative == dd1_original_representative
- ? dd2_original_representative
- : dd1_original_representative;
- assert(no_longer_representative != still_representative &&
- "The current and former representatives cannot be the same.");
- // We now need to add all equations about |no_longer_representative| to the
- // set of equations known about |still_representative|.
- // Get the equations associated with |no_longer_representative|.
- auto no_longer_representative_id_equations =
- id_equations_.find(no_longer_representative);
- if (no_longer_representative_id_equations != id_equations_.end()) {
- // There are some equations to transfer. There might not yet be any
- // equations about |still_representative|; create an empty set of equations
- // if this is the case.
- if (!id_equations_.count(still_representative)) {
- id_equations_.insert({still_representative, OperationSet()});
- }
- auto still_representative_id_equations =
- id_equations_.find(still_representative);
- assert(still_representative_id_equations != id_equations_.end() &&
- "At this point there must be a set of equations.");
- // Add all the equations known about |no_longer_representative| to the set
- // of equations known about |still_representative|.
- still_representative_id_equations->second.insert(
- no_longer_representative_id_equations->second.begin(),
- no_longer_representative_id_equations->second.end());
- }
- // Delete the no longer-relevant equations about |no_longer_representative|.
- id_equations_.erase(no_longer_representative);
- }
- bool FactManager::DataSynonymAndIdEquationFacts::
- DataDescriptorsAreWellFormedAndComparable(
- opt::IRContext* context, const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2) const {
- auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
- context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
- dd1.index());
- auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
- context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
- dd2.index());
- // The end types of the data descriptors must exist.
- if (end_type_id_1 == 0 || end_type_id_2 == 0) {
- return false;
- }
- // If the end types are the same, the data descriptors are comparable.
- if (end_type_id_1 == end_type_id_2) {
- return true;
- }
- // Otherwise they are only comparable if they are integer scalars or integer
- // vectors that differ only in signedness.
- // Get both types.
- const opt::analysis::Type* type_1 =
- context->get_type_mgr()->GetType(end_type_id_1);
- const opt::analysis::Type* type_2 =
- context->get_type_mgr()->GetType(end_type_id_2);
- // If the first type is a vector, check that the second type is a vector of
- // the same width, and drill down to the vector element types.
- if (type_1->AsVector()) {
- if (!type_2->AsVector()) {
- return false;
- }
- if (type_1->AsVector()->element_count() !=
- type_2->AsVector()->element_count()) {
- return false;
- }
- type_1 = type_1->AsVector()->element_type();
- type_2 = type_2->AsVector()->element_type();
- }
- // Check that type_1 and type_2 are both integer types of the same bit-width
- // (but with potentially different signedness).
- auto integer_type_1 = type_1->AsInteger();
- auto integer_type_2 = type_2->AsInteger();
- return integer_type_1 && integer_type_2 &&
- integer_type_1->width() == integer_type_2->width();
- }
- std::vector<const protobufs::DataDescriptor*>
- FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
- const protobufs::DataDescriptor& data_descriptor) const {
- if (synonymous_.Exists(data_descriptor)) {
- return synonymous_.GetEquivalenceClass(data_descriptor);
- }
- return std::vector<const protobufs::DataDescriptor*>();
- }
- std::vector<uint32_t>
- FactManager::DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown()
- const {
- std::vector<uint32_t> result;
- for (auto& data_descriptor : synonymous_.GetAllKnownValues()) {
- if (data_descriptor->index().empty()) {
- result.push_back(data_descriptor->object());
- }
- }
- return result;
- }
- bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
- const protobufs::DataDescriptor& data_descriptor1,
- const protobufs::DataDescriptor& data_descriptor2) const {
- return synonymous_.Exists(data_descriptor1) &&
- synonymous_.Exists(data_descriptor2) &&
- synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
- }
- // End of data synonym facts
- //==============================
- //==============================
- // Dead block facts
- // The purpose of this class is to group the fields and data used to represent
- // facts about data blocks.
- class FactManager::DeadBlockFacts {
- public:
- // See method in FactManager which delegates to this method.
- void AddFact(const protobufs::FactBlockIsDead& fact);
- // See method in FactManager which delegates to this method.
- bool BlockIsDead(uint32_t block_id) const;
- private:
- std::set<uint32_t> dead_block_ids_;
- };
- void FactManager::DeadBlockFacts::AddFact(
- const protobufs::FactBlockIsDead& fact) {
- dead_block_ids_.insert(fact.block_id());
- }
- bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
- return dead_block_ids_.count(block_id) != 0;
- }
- // End of dead block facts
- //==============================
- //==============================
- // Livesafe function facts
- // The purpose of this class is to group the fields and data used to represent
- // facts about livesafe functions.
- class FactManager::LivesafeFunctionFacts {
- public:
- // See method in FactManager which delegates to this method.
- void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
- // See method in FactManager which delegates to this method.
- bool FunctionIsLivesafe(uint32_t function_id) const;
- private:
- std::set<uint32_t> livesafe_function_ids_;
- };
- void FactManager::LivesafeFunctionFacts::AddFact(
- const protobufs::FactFunctionIsLivesafe& fact) {
- livesafe_function_ids_.insert(fact.function_id());
- }
- bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
- uint32_t function_id) const {
- return livesafe_function_ids_.count(function_id) != 0;
- }
- // End of livesafe function facts
- //==============================
- //==============================
- // Irrelevant pointee value facts
- // The purpose of this class is to group the fields and data used to represent
- // facts about pointers whose pointee values are irrelevant.
- class FactManager::IrrelevantPointeeValueFacts {
- public:
- // See method in FactManager which delegates to this method.
- void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
- // See method in FactManager which delegates to this method.
- bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
- private:
- std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
- };
- void FactManager::IrrelevantPointeeValueFacts::AddFact(
- const protobufs::FactPointeeValueIsIrrelevant& fact) {
- pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
- }
- bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
- uint32_t pointer_id) const {
- return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
- }
- // End of arbitrarily-valued variable facts
- //==============================
- FactManager::FactManager()
- : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
- data_synonym_and_id_equation_facts_(
- MakeUnique<DataSynonymAndIdEquationFacts>()),
- dead_block_facts_(MakeUnique<DeadBlockFacts>()),
- livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
- irrelevant_pointee_value_facts_(
- MakeUnique<IrrelevantPointeeValueFacts>()) {}
- FactManager::~FactManager() = default;
- void FactManager::AddFacts(const MessageConsumer& message_consumer,
- const protobufs::FactSequence& initial_facts,
- opt::IRContext* context) {
- for (auto& fact : initial_facts.fact()) {
- if (!AddFact(fact, context)) {
- message_consumer(
- SPV_MSG_WARNING, nullptr, {},
- ("Invalid fact " + ToString(fact) + " ignored.").c_str());
- }
- }
- }
- bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
- opt::IRContext* context) {
- switch (fact.fact_case()) {
- case protobufs::Fact::kConstantUniformFact:
- return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
- context);
- case protobufs::Fact::kDataSynonymFact:
- data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(),
- context);
- return true;
- case protobufs::Fact::kBlockIsDeadFact:
- dead_block_facts_->AddFact(fact.block_is_dead_fact());
- return true;
- case protobufs::Fact::kFunctionIsLivesafeFact:
- livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact());
- return true;
- default:
- assert(false && "Unknown fact type.");
- return false;
- }
- }
- void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
- const protobufs::DataDescriptor& data2,
- opt::IRContext* context) {
- protobufs::FactDataSynonym fact;
- *fact.mutable_data1() = data1;
- *fact.mutable_data2() = data2;
- data_synonym_and_id_equation_facts_->AddFact(fact, context);
- }
- std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
- opt::IRContext* ir_context, uint32_t type_id) const {
- return uniform_constant_facts_->GetConstantsAvailableFromUniformsForType(
- ir_context, type_id);
- }
- const std::vector<protobufs::UniformBufferElementDescriptor>
- FactManager::GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
- uint32_t constant_id) const {
- return uniform_constant_facts_->GetUniformDescriptorsForConstant(ir_context,
- constant_id);
- }
- uint32_t FactManager::GetConstantFromUniformDescriptor(
- opt::IRContext* context,
- const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
- return uniform_constant_facts_->GetConstantFromUniformDescriptor(
- context, uniform_descriptor);
- }
- std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
- const {
- return uniform_constant_facts_->GetTypesForWhichUniformValuesAreKnown();
- }
- const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
- FactManager::GetConstantUniformFactsAndTypes() const {
- return uniform_constant_facts_->GetConstantUniformFactsAndTypes();
- }
- std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
- return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown();
- }
- std::vector<const protobufs::DataDescriptor*>
- FactManager::GetSynonymsForDataDescriptor(
- const protobufs::DataDescriptor& data_descriptor) const {
- return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
- data_descriptor);
- }
- std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
- uint32_t id) const {
- return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}));
- }
- bool FactManager::IsSynonymous(
- const protobufs::DataDescriptor& data_descriptor1,
- const protobufs::DataDescriptor& data_descriptor2) const {
- return data_synonym_and_id_equation_facts_->IsSynonymous(data_descriptor1,
- data_descriptor2);
- }
- bool FactManager::BlockIsDead(uint32_t block_id) const {
- return dead_block_facts_->BlockIsDead(block_id);
- }
- void FactManager::AddFactBlockIsDead(uint32_t block_id) {
- protobufs::FactBlockIsDead fact;
- fact.set_block_id(block_id);
- dead_block_facts_->AddFact(fact);
- }
- bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
- return livesafe_function_facts_->FunctionIsLivesafe(function_id);
- }
- void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
- protobufs::FactFunctionIsLivesafe fact;
- fact.set_function_id(function_id);
- livesafe_function_facts_->AddFact(fact);
- }
- bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
- return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
- }
- void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
- protobufs::FactPointeeValueIsIrrelevant fact;
- fact.set_pointer_id(pointer_id);
- irrelevant_pointee_value_facts_->AddFact(fact);
- }
- void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
- const std::vector<uint32_t>& rhs_id,
- opt::IRContext* context) {
- protobufs::FactIdEquation fact;
- fact.set_lhs_id(lhs_id);
- fact.set_opcode(opcode);
- for (auto an_rhs_id : rhs_id) {
- fact.add_rhs_id(an_rhs_id);
- }
- data_synonym_and_id_equation_facts_->AddFact(fact, context);
- }
- void FactManager::ComputeClosureOfFacts(
- opt::IRContext* ir_context, uint32_t maximum_equivalence_class_size) {
- data_synonym_and_id_equation_facts_->ComputeClosureOfFacts(
- ir_context, maximum_equivalence_class_size);
- }
- } // namespace fuzz
- } // namespace spvtools
|