blob: 30ea94bb09ded36ab2dc59e3247a1cced72f5527 [file] [log] [blame]
// 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(
const protobufs::TransformationRecordSynonymousConstants& message)
: message_(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