// 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 TransformationRecordSynonymousConstants::GetFreshIds() const { return std::unordered_set(); } } // namespace fuzz } // namespace spvtools