| // Copyright (c) 2020 Stefano Milizia |
| // Copyright (c) 2020 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 "transformation_record_synonymous_constants.h" |
| |
| #include "source/fuzz/fuzzer_util.h" |
| |
| namespace spvtools { |
| namespace fuzz { |
| |
| TransformationRecordSynonymousConstants:: |
| TransformationRecordSynonymousConstants( |
| protobufs::TransformationRecordSynonymousConstants message) |
| : message_(std::move(message)) {} |
| |
| TransformationRecordSynonymousConstants:: |
| TransformationRecordSynonymousConstants(uint32_t constant1_id, |
| uint32_t constant2_id) { |
| message_.set_constant1_id(constant1_id); |
| message_.set_constant2_id(constant2_id); |
| } |
| |
| bool TransformationRecordSynonymousConstants::IsApplicable( |
| opt::IRContext* ir_context, |
| const TransformationContext& transformation_context) const { |
| // The ids must be different |
| if (message_.constant1_id() == message_.constant2_id()) { |
| return false; |
| } |
| |
| if (transformation_context.GetFactManager()->IdIsIrrelevant( |
| message_.constant1_id()) || |
| transformation_context.GetFactManager()->IdIsIrrelevant( |
| message_.constant2_id())) { |
| return false; |
| } |
| |
| return AreEquivalentConstants(ir_context, message_.constant1_id(), |
| message_.constant2_id()); |
| } |
| |
| void TransformationRecordSynonymousConstants::Apply( |
| opt::IRContext* /*unused*/, |
| TransformationContext* transformation_context) const { |
| // Add the fact to the fact manager |
| transformation_context->GetFactManager()->AddFactDataSynonym( |
| MakeDataDescriptor(message_.constant1_id(), {}), |
| MakeDataDescriptor(message_.constant2_id(), {})); |
| } |
| |
| protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() |
| const { |
| protobufs::Transformation result; |
| *result.mutable_record_synonymous_constants() = message_; |
| return result; |
| } |
| |
| bool TransformationRecordSynonymousConstants::AreEquivalentConstants( |
| opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) { |
| const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1); |
| const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2); |
| |
| // Check that the definitions exist |
| if (!def_1 || !def_2) { |
| // We don't use an assertion since otherwise the shrinker fails. |
| return false; |
| } |
| |
| auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1); |
| auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2); |
| |
| // The ids must refer to constants. |
| if (!constant1 || !constant2) { |
| return false; |
| } |
| |
| // The types must be compatible. |
| if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(), |
| def_2->type_id())) { |
| return false; |
| } |
| |
| // If either constant is null, the other is equivalent iff it is zero-like |
| if (constant1->AsNullConstant()) { |
| return constant2->IsZero(); |
| } |
| |
| if (constant2->AsNullConstant()) { |
| return constant1->IsZero(); |
| } |
| |
| // If the constants are scalar, they are equal iff their words are the same |
| if (auto scalar1 = constant1->AsScalarConstant()) { |
| // Either both or neither constant is scalar since we've already checked |
| // that their types are compatible. |
| assert(constant2->AsScalarConstant() && "Both constants must be scalar"); |
| return scalar1->words() == constant2->AsScalarConstant()->words(); |
| } |
| |
| // The only remaining possibility is that the constants are composite |
| assert(constant1->AsCompositeConstant() && constant2->AsCompositeConstant() && |
| "Equivalence of constants can only be checked with scalar, composite " |
| "or null constants."); |
| |
| // Since the types match, we already know that the number of components is |
| // the same. We check that the input operands of the definitions are all |
| // constants and that they are pairwise equivalent. |
| for (uint32_t i = 0; i < def_1->NumInOperands(); i++) { |
| if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i), |
| def_2->GetSingleWordInOperand(i))) { |
| return false; |
| } |
| } |
| |
| // If we get here, all the components are equivalent |
| return true; |
| } |
| |
| std::unordered_set<uint32_t> |
| TransformationRecordSynonymousConstants::GetFreshIds() const { |
| return std::unordered_set<uint32_t>(); |
| } |
| |
| } // namespace fuzz |
| } // namespace spvtools |