| // 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 "fact_manager.h" |
| |
| #include <sstream> |
| #include <unordered_map> |
| |
| #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 << " " << 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 |
| |
| FactManager::FactManager(opt::IRContext* ir_context) |
| : constant_uniform_facts_(ir_context), |
| data_synonym_and_id_equation_facts_(ir_context), |
| dead_block_facts_(ir_context), |
| livesafe_function_facts_(ir_context), |
| irrelevant_value_facts_(ir_context) {} |
| |
| void FactManager::AddInitialFacts(const MessageConsumer& message_consumer, |
| const protobufs::FactSequence& facts) { |
| for (auto& fact : facts.fact()) { |
| if (!MaybeAddFact(fact)) { |
| auto message = "Invalid fact " + ToString(fact) + " ignored."; |
| message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str()); |
| } |
| } |
| } |
| |
| bool FactManager::MaybeAddFact(const fuzz::protobufs::Fact& fact) { |
| switch (fact.fact_case()) { |
| case protobufs::Fact::kBlockIsDeadFact: |
| return dead_block_facts_.MaybeAddFact(fact.block_is_dead_fact()); |
| case protobufs::Fact::kConstantUniformFact: |
| return constant_uniform_facts_.MaybeAddFact(fact.constant_uniform_fact()); |
| case protobufs::Fact::kDataSynonymFact: |
| return data_synonym_and_id_equation_facts_.MaybeAddFact( |
| fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_); |
| case protobufs::Fact::kFunctionIsLivesafeFact: |
| return livesafe_function_facts_.MaybeAddFact( |
| fact.function_is_livesafe_fact()); |
| case protobufs::Fact::kIdEquationFact: |
| return data_synonym_and_id_equation_facts_.MaybeAddFact( |
| fact.id_equation_fact(), dead_block_facts_, irrelevant_value_facts_); |
| case protobufs::Fact::kIdIsIrrelevant: |
| return irrelevant_value_facts_.MaybeAddFact( |
| fact.id_is_irrelevant(), data_synonym_and_id_equation_facts_); |
| case protobufs::Fact::kPointeeValueIsIrrelevantFact: |
| return irrelevant_value_facts_.MaybeAddFact( |
| fact.pointee_value_is_irrelevant_fact(), |
| data_synonym_and_id_equation_facts_); |
| case protobufs::Fact::FACT_NOT_SET: |
| assert(false && "The fact must be set"); |
| return false; |
| } |
| |
| assert(false && "Unreachable"); |
| return false; |
| } |
| |
| void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1, |
| const protobufs::DataDescriptor& data2) { |
| protobufs::FactDataSynonym fact; |
| *fact.mutable_data1() = data1; |
| *fact.mutable_data2() = data2; |
| auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( |
| fact, dead_block_facts_, irrelevant_value_facts_); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "Unable to create DataSynonym fact"); |
| } |
| |
| std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType( |
| uint32_t type_id) const { |
| return constant_uniform_facts_.GetConstantsAvailableFromUniformsForType( |
| type_id); |
| } |
| |
| std::vector<protobufs::UniformBufferElementDescriptor> |
| FactManager::GetUniformDescriptorsForConstant(uint32_t constant_id) const { |
| return constant_uniform_facts_.GetUniformDescriptorsForConstant(constant_id); |
| } |
| |
| uint32_t FactManager::GetConstantFromUniformDescriptor( |
| const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const { |
| return constant_uniform_facts_.GetConstantFromUniformDescriptor( |
| uniform_descriptor); |
| } |
| |
| std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown() |
| const { |
| return constant_uniform_facts_.GetTypesForWhichUniformValuesAreKnown(); |
| } |
| |
| const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>& |
| FactManager::GetConstantUniformFactsAndTypes() const { |
| return constant_uniform_facts_.GetConstantUniformFactsAndTypes(); |
| } |
| |
| std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const { |
| return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown(); |
| } |
| |
| std::vector<const protobufs::DataDescriptor*> FactManager::GetAllSynonyms() |
| const { |
| return data_synonym_and_id_equation_facts_.GetAllKnownSynonyms(); |
| } |
| |
| 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 data_synonym_and_id_equation_facts_.GetSynonymsForId(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); |
| auto success = dead_block_facts_.MaybeAddFact(fact); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "|block_id| is invalid"); |
| } |
| |
| 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); |
| auto success = livesafe_function_facts_.MaybeAddFact(fact); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "|function_id| is invalid"); |
| } |
| |
| bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const { |
| return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id); |
| } |
| |
| bool FactManager::IdIsIrrelevant(uint32_t result_id) const { |
| return irrelevant_value_facts_.IdIsIrrelevant(result_id, dead_block_facts_); |
| } |
| |
| std::unordered_set<uint32_t> FactManager::GetIrrelevantIds() const { |
| return irrelevant_value_facts_.GetIrrelevantIds(dead_block_facts_); |
| } |
| |
| void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { |
| protobufs::FactPointeeValueIsIrrelevant fact; |
| fact.set_pointer_id(pointer_id); |
| auto success = irrelevant_value_facts_.MaybeAddFact( |
| fact, data_synonym_and_id_equation_facts_); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "|pointer_id| is invalid"); |
| } |
| |
| void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) { |
| protobufs::FactIdIsIrrelevant fact; |
| fact.set_result_id(result_id); |
| auto success = irrelevant_value_facts_.MaybeAddFact( |
| fact, data_synonym_and_id_equation_facts_); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "|result_id| is invalid"); |
| } |
| |
| void FactManager::AddFactIdEquation(uint32_t lhs_id, spv::Op opcode, |
| const std::vector<uint32_t>& rhs_id) { |
| protobufs::FactIdEquation fact; |
| fact.set_lhs_id(lhs_id); |
| fact.set_opcode(uint32_t(opcode)); |
| for (auto an_rhs_id : rhs_id) { |
| fact.add_rhs_id(an_rhs_id); |
| } |
| auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( |
| fact, dead_block_facts_, irrelevant_value_facts_); |
| (void)success; // Keep compilers happy in release mode. |
| assert(success && "Can't create IdIsIrrelevant fact"); |
| } |
| |
| void FactManager::ComputeClosureOfFacts( |
| uint32_t maximum_equivalence_class_size) { |
| data_synonym_and_id_equation_facts_.ComputeClosureOfFacts( |
| maximum_equivalence_class_size); |
| } |
| |
| } // namespace fuzz |
| } // namespace spvtools |