| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- // Copyright (c) 2020 Vasyl Teliman
- //
- // 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_add_synonym.h"
- #include <utility>
- #include "source/fuzz/fuzzer_util.h"
- #include "source/fuzz/instruction_descriptor.h"
- namespace spvtools {
- namespace fuzz {
- TransformationAddSynonym::TransformationAddSynonym(
- protobufs::TransformationAddSynonym message)
- : message_(std::move(message)) {}
- TransformationAddSynonym::TransformationAddSynonym(
- uint32_t result_id,
- protobufs::TransformationAddSynonym::SynonymType synonym_type,
- uint32_t synonym_fresh_id,
- const protobufs::InstructionDescriptor& insert_before) {
- message_.set_result_id(result_id);
- message_.set_synonym_type(synonym_type);
- message_.set_synonym_fresh_id(synonym_fresh_id);
- *message_.mutable_insert_before() = insert_before;
- }
- bool TransformationAddSynonym::IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const {
- assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
- message_.synonym_type()) &&
- "Synonym type is invalid");
- // |synonym_fresh_id| must be fresh.
- if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
- return false;
- }
- // Check that |message_.result_id| is valid.
- auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
- if (!synonym) {
- return false;
- }
- // Check that we can apply |synonym_type| to |result_id|.
- if (!IsInstructionValid(ir_context, transformation_context, synonym,
- message_.synonym_type())) {
- return false;
- }
- // Check that |insert_before| is valid.
- auto* insert_before_inst =
- FindInstruction(message_.insert_before(), ir_context);
- if (!insert_before_inst) {
- return false;
- }
- const auto* insert_before_inst_block =
- ir_context->get_instr_block(insert_before_inst);
- assert(insert_before_inst_block &&
- "|insert_before_inst| must be in some block");
- if (transformation_context.GetFactManager()->BlockIsDead(
- insert_before_inst_block->id())) {
- // We don't create synonyms in dead blocks.
- return false;
- }
- // Check that we can insert |message._synonymous_instruction| before
- // |message_.insert_before| instruction. We use OpIAdd to represent some
- // instruction that can produce a synonym.
- if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
- insert_before_inst)) {
- return false;
- }
- // A constant instruction must be present in the module if required.
- if (IsAdditionalConstantRequired(message_.synonym_type()) &&
- MaybeGetConstantId(ir_context, transformation_context) == 0) {
- return false;
- }
- // Domination rules must be satisfied.
- return fuzzerutil::IdIsAvailableBeforeInstruction(
- ir_context, insert_before_inst, message_.result_id());
- }
- void TransformationAddSynonym::Apply(
- opt::IRContext* ir_context,
- TransformationContext* transformation_context) const {
- // Add a synonymous instruction.
- auto new_instruction =
- MakeSynonymousInstruction(ir_context, *transformation_context);
- auto new_instruction_ptr = new_instruction.get();
- auto insert_before = FindInstruction(message_.insert_before(), ir_context);
- insert_before->InsertBefore(std::move(new_instruction));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
- // Inform the def-use manager about the new instruction and record its basic
- // block.
- ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
- ir_context->set_instr_block(new_instruction_ptr,
- ir_context->get_instr_block(insert_before));
- // Propagate PointeeValueIsIrrelevant fact.
- const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
- fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
- assert(new_synonym_type && "New synonym should have a valid type");
- if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
- message_.result_id()) &&
- new_synonym_type->AsPointer()) {
- transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
- message_.synonym_fresh_id());
- }
- // Mark two ids as synonymous.
- transformation_context->GetFactManager()->AddFactDataSynonym(
- MakeDataDescriptor(message_.result_id(), {}),
- MakeDataDescriptor(message_.synonym_fresh_id(), {}));
- }
- protobufs::Transformation TransformationAddSynonym::ToMessage() const {
- protobufs::Transformation result;
- *result.mutable_add_synonym() = message_;
- return result;
- }
- bool TransformationAddSynonym::IsInstructionValid(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context, opt::Instruction* inst,
- protobufs::TransformationAddSynonym::SynonymType synonym_type) {
- // Instruction must have a result id, type id. We skip OpUndef and
- // OpConstantNull.
- if (!inst || !inst->result_id() || !inst->type_id() ||
- inst->opcode() == spv::Op::OpUndef ||
- inst->opcode() == spv::Op::OpConstantNull) {
- return false;
- }
- if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
- *inst)) {
- return false;
- }
- switch (synonym_type) {
- case protobufs::TransformationAddSynonym::ADD_ZERO:
- case protobufs::TransformationAddSynonym::SUB_ZERO:
- case protobufs::TransformationAddSynonym::MUL_ONE: {
- // The instruction must be either scalar or vector of integers or floats.
- const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
- assert(type && "Instruction's result id is invalid");
- if (const auto* vector = type->AsVector()) {
- return vector->element_type()->AsInteger() ||
- vector->element_type()->AsFloat();
- }
- return type->AsInteger() || type->AsFloat();
- }
- case protobufs::TransformationAddSynonym::BITWISE_OR:
- case protobufs::TransformationAddSynonym::BITWISE_XOR: {
- // The instruction must be either an integer or a vector of integers.
- const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
- assert(type && "Instruction's result id is invalid");
- if (const auto* vector = type->AsVector()) {
- return vector->element_type()->AsInteger();
- }
- return type->AsInteger();
- }
- case protobufs::TransformationAddSynonym::COPY_OBJECT:
- // All checks for OpCopyObject are handled by
- // fuzzerutil::CanMakeSynonymOf.
- return true;
- case protobufs::TransformationAddSynonym::LOGICAL_AND:
- case protobufs::TransformationAddSynonym::LOGICAL_OR: {
- // The instruction must be either a scalar or a vector of booleans.
- const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
- assert(type && "Instruction's result id is invalid");
- return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
- type->AsBool();
- }
- default:
- assert(false && "Synonym type is not supported");
- return false;
- }
- }
- std::unique_ptr<opt::Instruction>
- TransformationAddSynonym::MakeSynonymousInstruction(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const {
- auto synonym_type_id =
- fuzzerutil::GetTypeId(ir_context, message_.result_id());
- assert(synonym_type_id && "Synonym has invalid type id");
- auto opcode = spv::Op::OpNop;
- const auto* synonym_type =
- ir_context->get_type_mgr()->GetType(synonym_type_id);
- assert(synonym_type && "Synonym has invalid type");
- auto is_integral = (synonym_type->AsVector() &&
- synonym_type->AsVector()->element_type()->AsInteger()) ||
- synonym_type->AsInteger();
- switch (message_.synonym_type()) {
- case protobufs::TransformationAddSynonym::SUB_ZERO:
- opcode = is_integral ? spv::Op::OpISub : spv::Op::OpFSub;
- break;
- case protobufs::TransformationAddSynonym::MUL_ONE:
- opcode = is_integral ? spv::Op::OpIMul : spv::Op::OpFMul;
- break;
- case protobufs::TransformationAddSynonym::ADD_ZERO:
- opcode = is_integral ? spv::Op::OpIAdd : spv::Op::OpFAdd;
- break;
- case protobufs::TransformationAddSynonym::LOGICAL_OR:
- opcode = spv::Op::OpLogicalOr;
- break;
- case protobufs::TransformationAddSynonym::LOGICAL_AND:
- opcode = spv::Op::OpLogicalAnd;
- break;
- case protobufs::TransformationAddSynonym::BITWISE_OR:
- opcode = spv::Op::OpBitwiseOr;
- break;
- case protobufs::TransformationAddSynonym::BITWISE_XOR:
- opcode = spv::Op::OpBitwiseXor;
- break;
- case protobufs::TransformationAddSynonym::COPY_OBJECT:
- return MakeUnique<opt::Instruction>(
- ir_context, spv::Op::OpCopyObject, synonym_type_id,
- message_.synonym_fresh_id(),
- opt::Instruction::OperandList{
- {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
- default:
- assert(false && "Unhandled synonym type");
- return nullptr;
- }
- return MakeUnique<opt::Instruction>(
- ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
- opt::Instruction::OperandList{
- {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
- {SPV_OPERAND_TYPE_ID,
- {MaybeGetConstantId(ir_context, transformation_context)}}});
- }
- uint32_t TransformationAddSynonym::MaybeGetConstantId(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const {
- assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
- "Synonym type doesn't require an additional constant");
- auto synonym_type_id =
- fuzzerutil::GetTypeId(ir_context, message_.result_id());
- assert(synonym_type_id && "Synonym has invalid type id");
- switch (message_.synonym_type()) {
- case protobufs::TransformationAddSynonym::ADD_ZERO:
- case protobufs::TransformationAddSynonym::SUB_ZERO:
- case protobufs::TransformationAddSynonym::LOGICAL_OR:
- case protobufs::TransformationAddSynonym::BITWISE_OR:
- case protobufs::TransformationAddSynonym::BITWISE_XOR:
- return fuzzerutil::MaybeGetZeroConstant(
- ir_context, transformation_context, synonym_type_id, false);
- case protobufs::TransformationAddSynonym::MUL_ONE:
- case protobufs::TransformationAddSynonym::LOGICAL_AND: {
- auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
- assert(synonym_type && "Synonym has invalid type");
- if (const auto* vector = synonym_type->AsVector()) {
- auto element_type_id =
- ir_context->get_type_mgr()->GetId(vector->element_type());
- assert(element_type_id && "Vector's element type is invalid");
- auto one_word =
- vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
- if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
- ir_context, transformation_context, {one_word}, element_type_id,
- false)) {
- return fuzzerutil::MaybeGetCompositeConstant(
- ir_context, transformation_context,
- std::vector<uint32_t>(vector->element_count(), scalar_one_id),
- synonym_type_id, false);
- }
- return 0;
- } else {
- return fuzzerutil::MaybeGetScalarConstant(
- ir_context, transformation_context,
- {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
- synonym_type_id, false);
- }
- }
- default:
- // The assertion at the beginning of the function will fail in the debug
- // mode.
- return 0;
- }
- }
- bool TransformationAddSynonym::IsAdditionalConstantRequired(
- protobufs::TransformationAddSynonym::SynonymType synonym_type) {
- switch (synonym_type) {
- case protobufs::TransformationAddSynonym::ADD_ZERO:
- case protobufs::TransformationAddSynonym::SUB_ZERO:
- case protobufs::TransformationAddSynonym::LOGICAL_OR:
- case protobufs::TransformationAddSynonym::MUL_ONE:
- case protobufs::TransformationAddSynonym::LOGICAL_AND:
- case protobufs::TransformationAddSynonym::BITWISE_OR:
- case protobufs::TransformationAddSynonym::BITWISE_XOR:
- return true;
- default:
- return false;
- }
- }
- std::unordered_set<uint32_t> TransformationAddSynonym::GetFreshIds() const {
- return {message_.synonym_fresh_id()};
- }
- } // namespace fuzz
- } // namespace spvtools
|