| // 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_replace_load_store_with_copy_memory.h" |
| |
| #include "source/fuzz/fuzzer_util.h" |
| #include "source/fuzz/instruction_descriptor.h" |
| #include "source/opcode.h" |
| |
| namespace spvtools { |
| namespace fuzz { |
| |
| namespace { |
| const uint32_t kOpStoreOperandIndexTargetVariable = 0; |
| const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1; |
| const uint32_t kOpLoadOperandIndexSourceVariable = 2; |
| } // namespace |
| |
| TransformationReplaceLoadStoreWithCopyMemory:: |
| TransformationReplaceLoadStoreWithCopyMemory( |
| protobufs::TransformationReplaceLoadStoreWithCopyMemory message) |
| : message_(std::move(message)) {} |
| |
| TransformationReplaceLoadStoreWithCopyMemory:: |
| TransformationReplaceLoadStoreWithCopyMemory( |
| const protobufs::InstructionDescriptor& load_instruction_descriptor, |
| const protobufs::InstructionDescriptor& store_instruction_descriptor) { |
| *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor; |
| *message_.mutable_store_instruction_descriptor() = |
| store_instruction_descriptor; |
| } |
| bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable( |
| opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { |
| // This transformation is only applicable to the pair of OpLoad and OpStore |
| // instructions. |
| |
| // The OpLoad instruction must be defined. |
| auto load_instruction = |
| FindInstruction(message_.load_instruction_descriptor(), ir_context); |
| if (!load_instruction || load_instruction->opcode() != SpvOpLoad) { |
| return false; |
| } |
| |
| // The OpStore instruction must be defined. |
| auto store_instruction = |
| FindInstruction(message_.store_instruction_descriptor(), ir_context); |
| if (!store_instruction || store_instruction->opcode() != SpvOpStore) { |
| return false; |
| } |
| |
| // Intermediate values of the OpLoad and the OpStore must match. |
| if (load_instruction->result_id() != |
| store_instruction->GetSingleWordOperand( |
| kOpStoreOperandIndexIntermediateIdToWrite)) { |
| return false; |
| } |
| |
| // Get storage class of the variable pointed by the source operand in OpLoad. |
| opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef( |
| load_instruction->GetSingleWordOperand(2)); |
| SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType( |
| ir_context, source_id->type_id()); |
| |
| // Iterate over all instructions between |load_instruction| and |
| // |store_instruction|. |
| for (auto it = load_instruction; it != store_instruction; |
| it = it->NextNode()) { |
| //|load_instruction| and |store_instruction| are not in the same block. |
| if (it == nullptr) { |
| return false; |
| } |
| |
| // We need to make sure that the value pointed to by the source of the |
| // OpLoad hasn't changed by the time we see the matching OpStore |
| // instruction. |
| if (IsMemoryWritingOpCode(it->opcode())) { |
| return false; |
| } else if (IsMemoryBarrierOpCode(it->opcode()) && |
| !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void TransformationReplaceLoadStoreWithCopyMemory::Apply( |
| opt::IRContext* ir_context, TransformationContext* /*unused*/) const { |
| // OpLoad and OpStore instructions must be defined. |
| auto load_instruction = |
| FindInstruction(message_.load_instruction_descriptor(), ir_context); |
| assert(load_instruction && load_instruction->opcode() == SpvOpLoad && |
| "The required OpLoad instruction must be defined."); |
| auto store_instruction = |
| FindInstruction(message_.store_instruction_descriptor(), ir_context); |
| assert(store_instruction && store_instruction->opcode() == SpvOpStore && |
| "The required OpStore instruction must be defined."); |
| |
| // Intermediate values of the OpLoad and the OpStore must match. |
| assert(load_instruction->result_id() == |
| store_instruction->GetSingleWordOperand( |
| kOpStoreOperandIndexIntermediateIdToWrite) && |
| "OpLoad and OpStore must refer to the same value."); |
| |
| // Get the ids of the source operand of the OpLoad and the target operand of |
| // the OpStore. |
| uint32_t source_variable_id = |
| load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable); |
| uint32_t target_variable_id = store_instruction->GetSingleWordOperand( |
| kOpStoreOperandIndexTargetVariable); |
| |
| // Insert the OpCopyMemory instruction before the OpStore instruction. |
| store_instruction->InsertBefore(MakeUnique<opt::Instruction>( |
| ir_context, SpvOpCopyMemory, 0, 0, |
| opt::Instruction::OperandList( |
| {{SPV_OPERAND_TYPE_ID, {target_variable_id}}, |
| {SPV_OPERAND_TYPE_ID, {source_variable_id}}}))); |
| |
| // Remove the OpStore instruction. |
| ir_context->KillInst(store_instruction); |
| |
| ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); |
| } |
| |
| bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode( |
| SpvOp op_code) { |
| if (spvOpcodeIsAtomicOp(op_code)) { |
| return op_code != SpvOpAtomicLoad; |
| } |
| switch (op_code) { |
| case SpvOpStore: |
| case SpvOpCopyMemory: |
| case SpvOpCopyMemorySized: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode( |
| SpvOp op_code) { |
| switch (op_code) { |
| case SpvOpMemoryBarrier: |
| case SpvOpMemoryNamedBarrier: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool TransformationReplaceLoadStoreWithCopyMemory:: |
| IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) { |
| switch (storage_class) { |
| case SpvStorageClassUniformConstant: |
| case SpvStorageClassInput: |
| case SpvStorageClassUniform: |
| case SpvStorageClassPrivate: |
| case SpvStorageClassFunction: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| protobufs::Transformation |
| TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const { |
| protobufs::Transformation result; |
| *result.mutable_replace_load_store_with_copy_memory() = message_; |
| return result; |
| } |
| |
| std::unordered_set<uint32_t> |
| TransformationReplaceLoadStoreWithCopyMemory::GetFreshIds() const { |
| return std::unordered_set<uint32_t>(); |
| } |
| |
| } // namespace fuzz |
| } // namespace spvtools |