// Copyright (c) 2020 André Perez Maselco // // 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/transformation_expand_vector_reduction.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" namespace spvtools { namespace fuzz { TransformationExpandVectorReduction::TransformationExpandVectorReduction( protobufs::TransformationExpandVectorReduction message) : message_(std::move(message)) {} TransformationExpandVectorReduction::TransformationExpandVectorReduction( const uint32_t instruction_result_id, const std::vector& fresh_ids) { message_.set_instruction_result_id(instruction_result_id); *message_.mutable_fresh_ids() = google::protobuf::RepeatedField( fresh_ids.begin(), fresh_ids.end()); } bool TransformationExpandVectorReduction::IsApplicable( opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { auto* instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); // |instruction| must be defined. if (!instruction) { return false; } // |instruction| must be OpAny or OpAll. if (instruction->opcode() != SpvOpAny && instruction->opcode() != SpvOpAll) { return false; } // |message_.fresh_ids.size| must have the exact number of fresh ids required // to apply the transformation. if (static_cast(message_.fresh_ids().size()) != GetRequiredFreshIdCount(ir_context, instruction)) { return false; } std::set ids_used_by_this_transformation; for (uint32_t fresh_id : message_.fresh_ids()) { // All ids in |message_.fresh_ids| must be fresh. if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { return false; } // All fresh ids need to be distinct. if (!CheckIdIsFreshAndNotUsedByThisTransformation( fresh_id, ir_context, &ids_used_by_this_transformation)) { return false; } } return true; } void TransformationExpandVectorReduction::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { auto* instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); auto* vector = ir_context->get_def_use_mgr()->GetDef( instruction->GetSingleWordInOperand(0)); uint32_t vector_component_count = ir_context->get_type_mgr() ->GetType(vector->type_id()) ->AsVector() ->element_count(); // Fresh id iterator. auto fresh_id = message_.fresh_ids().begin(); // |vector_components| are the ids of the extracted components from |vector|. std::vector vector_components; for (uint32_t i = 0; i < vector_component_count; i++) { // Extracts the i-th |vector| component. auto vector_component = opt::Instruction( ir_context, SpvOpCompositeExtract, instruction->type_id(), *fresh_id++, {{SPV_OPERAND_TYPE_ID, {vector->result_id()}}, {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}); instruction->InsertBefore(MakeUnique(vector_component)); fuzzerutil::UpdateModuleIdBound(ir_context, vector_component.result_id()); vector_components.push_back(vector_component.result_id()); } // The first two |vector| components are used in the first logical operation. auto logical_instruction = opt::Instruction( ir_context, instruction->opcode() == SpvOpAny ? SpvOpLogicalOr : SpvOpLogicalAnd, instruction->type_id(), *fresh_id++, {{SPV_OPERAND_TYPE_ID, {vector_components[0]}}, {SPV_OPERAND_TYPE_ID, {vector_components[1]}}}); instruction->InsertBefore(MakeUnique(logical_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, logical_instruction.result_id()); // Evaluates the remaining components. for (uint32_t i = 2; i < vector_components.size(); i++) { logical_instruction = opt::Instruction( ir_context, logical_instruction.opcode(), instruction->type_id(), *fresh_id++, {{SPV_OPERAND_TYPE_ID, {vector_components[i]}}, {SPV_OPERAND_TYPE_ID, {logical_instruction.result_id()}}}); instruction->InsertBefore( MakeUnique(logical_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, logical_instruction.result_id()); } ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); // If it's possible to make a synonym of |instruction|, then add the fact that // the last |logical_instruction| is a synonym of |instruction|. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, *instruction)) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(logical_instruction.result_id(), {}), MakeDataDescriptor(instruction->result_id(), {})); } } protobufs::Transformation TransformationExpandVectorReduction::ToMessage() const { protobufs::Transformation result; *result.mutable_expand_vector_reduction() = message_; return result; } uint32_t TransformationExpandVectorReduction::GetRequiredFreshIdCount( opt::IRContext* ir_context, opt::Instruction* instruction) { // For each vector component, 1 OpCompositeExtract and 1 OpLogical* (except // for the first component) instructions will be inserted. return 2 * ir_context->get_type_mgr() ->GetType(ir_context->get_def_use_mgr() ->GetDef(instruction->GetSingleWordInOperand(0)) ->type_id()) ->AsVector() ->element_count() - 1; } std::unordered_set TransformationExpandVectorReduction::GetFreshIds() const { std::unordered_set result; for (auto id : message_.fresh_ids()) { result.insert(id); } return result; } } // namespace fuzz } // namespace spvtools