| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- // 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_add_bit_instruction_synonym.h"
- #include "source/fuzz/fuzzer_util.h"
- #include "source/fuzz/instruction_descriptor.h"
- namespace spvtools {
- namespace fuzz {
- TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
- protobufs::TransformationAddBitInstructionSynonym message)
- : message_(std::move(message)) {}
- TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym(
- const uint32_t instruction_result_id,
- const std::vector<uint32_t>& fresh_ids) {
- message_.set_instruction_result_id(instruction_result_id);
- *message_.mutable_fresh_ids() =
- google::protobuf::RepeatedField<google::protobuf::uint32>(
- fresh_ids.begin(), fresh_ids.end());
- }
- bool TransformationAddBitInstructionSynonym::IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const {
- auto instruction =
- ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
- // Checks on: only integer operands are supported, instructions are bitwise
- // operations only. Signedness of the operands must be the same.
- if (!IsInstructionSupported(ir_context, instruction)) {
- return false;
- }
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3791):
- // This condition could be relaxed if the index exists as another integer
- // type.
- // All bit indexes must be defined as 32-bit unsigned integers.
- uint32_t width = ir_context->get_type_mgr()
- ->GetType(instruction->type_id())
- ->AsInteger()
- ->width();
- for (uint32_t i = 0; i < width; i++) {
- if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context,
- {i}, 32, false, false)) {
- return false;
- }
- }
- // |message_.fresh_ids.size| must have the exact number of fresh ids required
- // to apply the transformation.
- if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
- GetRequiredFreshIdCount(ir_context, instruction)) {
- return false;
- }
- // All ids in |message_.fresh_ids| must be fresh.
- for (uint32_t fresh_id : message_.fresh_ids()) {
- if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
- return false;
- }
- }
- return true;
- }
- void TransformationAddBitInstructionSynonym::Apply(
- opt::IRContext* ir_context,
- TransformationContext* transformation_context) const {
- auto bit_instruction =
- ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
- // Use an appropriate helper function to add the new instruction and new
- // synonym fact. The helper function should take care of invalidating
- // analyses before adding facts.
- switch (bit_instruction->opcode()) {
- case spv::Op::OpBitwiseOr:
- case spv::Op::OpBitwiseXor:
- case spv::Op::OpBitwiseAnd:
- case spv::Op::OpNot:
- AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context,
- bit_instruction);
- break;
- default:
- assert(false && "Should be unreachable.");
- break;
- }
- }
- bool TransformationAddBitInstructionSynonym::IsInstructionSupported(
- opt::IRContext* ir_context, opt::Instruction* instruction) {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
- // Right now we only support certain operations. When this issue is addressed
- // the following conditional can use the function |spvOpcodeIsBit|.
- // |instruction| must be defined and must be a supported bit instruction.
- if (!instruction || (instruction->opcode() != spv::Op::OpBitwiseOr &&
- instruction->opcode() != spv::Op::OpBitwiseXor &&
- instruction->opcode() != spv::Op::OpBitwiseAnd &&
- instruction->opcode() != spv::Op::OpNot)) {
- return false;
- }
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792):
- // Right now, only integer operands are supported.
- if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) {
- return false;
- }
- if (instruction->opcode() == spv::Op::OpNot) {
- auto operand = instruction->GetInOperand(0).words[0];
- auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand);
- auto operand_type =
- ir_context->get_type_mgr()->GetType(operand_inst->type_id());
- auto operand_sign = operand_type->AsInteger()->IsSigned();
- auto type_id_sign = ir_context->get_type_mgr()
- ->GetType(instruction->type_id())
- ->AsInteger()
- ->IsSigned();
- return operand_sign == type_id_sign;
- } else {
- // Other BitWise operations that takes two operands.
- auto first_operand = instruction->GetInOperand(0).words[0];
- auto first_operand_inst =
- ir_context->get_def_use_mgr()->GetDef(first_operand);
- auto first_operand_type =
- ir_context->get_type_mgr()->GetType(first_operand_inst->type_id());
- auto first_operand_sign = first_operand_type->AsInteger()->IsSigned();
- auto second_operand = instruction->GetInOperand(1).words[0];
- auto second_operand_inst =
- ir_context->get_def_use_mgr()->GetDef(second_operand);
- auto second_operand_type =
- ir_context->get_type_mgr()->GetType(second_operand_inst->type_id());
- auto second_operand_sign = second_operand_type->AsInteger()->IsSigned();
- auto type_id_sign = ir_context->get_type_mgr()
- ->GetType(instruction->type_id())
- ->AsInteger()
- ->IsSigned();
- return first_operand_sign == second_operand_sign &&
- first_operand_sign == type_id_sign;
- }
- }
- protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
- const {
- protobufs::Transformation result;
- *result.mutable_add_bit_instruction_synonym() = message_;
- return result;
- }
- uint32_t TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount(
- opt::IRContext* ir_context, opt::Instruction* bit_instruction) {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557):
- // Right now, only certain operations are supported.
- switch (bit_instruction->opcode()) {
- case spv::Op::OpBitwiseOr:
- case spv::Op::OpBitwiseXor:
- case spv::Op::OpBitwiseAnd:
- case spv::Op::OpNot:
- return (2 + bit_instruction->NumInOperands()) *
- ir_context->get_type_mgr()
- ->GetType(bit_instruction->type_id())
- ->AsInteger()
- ->width() -
- 1;
- default:
- assert(false && "Unsupported bit instruction.");
- return 0;
- }
- }
- void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym(
- opt::IRContext* ir_context, TransformationContext* transformation_context,
- opt::Instruction* bit_instruction) const {
- // Fresh id iterator.
- auto fresh_id = message_.fresh_ids().begin();
- // |width| is the bit width of operands (8, 16, 32 or 64).
- const uint32_t width = ir_context->get_type_mgr()
- ->GetType(bit_instruction->type_id())
- ->AsInteger()
- ->width();
- // |count| is the number of bits to be extracted and inserted at a time.
- const uint32_t count = fuzzerutil::MaybeGetIntegerConstant(
- ir_context, *transformation_context, {1}, 32, false, false);
- // |extracted_bit_instructions| is the collection of OpBiwise* or OpNot
- // instructions that evaluate the extracted bits. Those ids will be used to
- // insert the result bits.
- std::vector<uint32_t> extracted_bit_instructions(width);
- for (uint32_t i = 0; i < width; i++) {
- // |offset| is the current bit index.
- uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
- ir_context, *transformation_context, {i}, 32, false, false);
- // |bit_extract_ids| are the two extracted bits from the operands.
- opt::Instruction::OperandList bit_extract_ids;
- // Extracts the i-th bit from operands.
- for (auto operand = bit_instruction->begin() + 2;
- operand != bit_instruction->end(); operand++) {
- auto bit_extract =
- opt::Instruction(ir_context, spv::Op::OpBitFieldUExtract,
- bit_instruction->type_id(), *fresh_id++,
- {{SPV_OPERAND_TYPE_ID, operand->words},
- {SPV_OPERAND_TYPE_ID, {offset}},
- {SPV_OPERAND_TYPE_ID, {count}}});
- bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_extract));
- fuzzerutil::UpdateModuleIdBound(ir_context, bit_extract.result_id());
- bit_extract_ids.push_back(
- {SPV_OPERAND_TYPE_ID, {bit_extract.result_id()}});
- }
- // Applies |bit_instruction| to the extracted bits.
- auto extracted_bit_instruction = opt::Instruction(
- ir_context, bit_instruction->opcode(), bit_instruction->type_id(),
- *fresh_id++, bit_extract_ids);
- bit_instruction->InsertBefore(
- MakeUnique<opt::Instruction>(extracted_bit_instruction));
- fuzzerutil::UpdateModuleIdBound(ir_context,
- extracted_bit_instruction.result_id());
- extracted_bit_instructions[i] = extracted_bit_instruction.result_id();
- }
- // The first two ids in |extracted_bit_instructions| are used to insert the
- // first two bits of the result.
- uint32_t offset = fuzzerutil::MaybeGetIntegerConstant(
- ir_context, *transformation_context, {1}, 32, false, false);
- auto bit_insert =
- opt::Instruction(ir_context, spv::Op::OpBitFieldInsert,
- bit_instruction->type_id(), *fresh_id++,
- {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}},
- {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}},
- {SPV_OPERAND_TYPE_ID, {offset}},
- {SPV_OPERAND_TYPE_ID, {count}}});
- bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
- fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
- // Inserts the remaining bits.
- for (uint32_t i = 2; i < width; i++) {
- offset = fuzzerutil::MaybeGetIntegerConstant(
- ir_context, *transformation_context, {i}, 32, false, false);
- bit_insert = opt::Instruction(
- ir_context, spv::Op::OpBitFieldInsert, bit_instruction->type_id(),
- *fresh_id++,
- {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}},
- {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}},
- {SPV_OPERAND_TYPE_ID, {offset}},
- {SPV_OPERAND_TYPE_ID, {count}}});
- bit_instruction->InsertBefore(MakeUnique<opt::Instruction>(bit_insert));
- fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id());
- }
- ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
- // We only add a synonym fact if the bit instruction is not irrelevant, and if
- // the new result id we would make it synonymous with is not irrelevant. (It
- // could be irrelevant if we are in a dead block.)
- if (!transformation_context->GetFactManager()->IdIsIrrelevant(
- bit_instruction->result_id()) &&
- !transformation_context->GetFactManager()->IdIsIrrelevant(
- bit_insert.result_id())) {
- // Adds the fact that the last |bit_insert| instruction is synonymous of
- // |bit_instruction|.
- transformation_context->GetFactManager()->AddFactDataSynonym(
- MakeDataDescriptor(bit_insert.result_id(), {}),
- MakeDataDescriptor(bit_instruction->result_id(), {}));
- }
- }
- std::unordered_set<uint32_t>
- TransformationAddBitInstructionSynonym::GetFreshIds() const {
- std::unordered_set<uint32_t> result;
- for (auto id : message_.fresh_ids()) {
- result.insert(id);
- }
- return result;
- }
- } // namespace fuzz
- } // namespace spvtools
|