1fd4e5da5Sopenharmony_ci// Copyright (c) 2020 Google LLC 2fd4e5da5Sopenharmony_ci// 3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License. 5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at 6fd4e5da5Sopenharmony_ci// 7fd4e5da5Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8fd4e5da5Sopenharmony_ci// 9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and 13fd4e5da5Sopenharmony_ci// limitations under the License. 14fd4e5da5Sopenharmony_ci 15fd4e5da5Sopenharmony_ci#include "source/fuzz/fuzzer_pass_add_stores.h" 16fd4e5da5Sopenharmony_ci 17fd4e5da5Sopenharmony_ci#include "source/fuzz/fuzzer_util.h" 18fd4e5da5Sopenharmony_ci#include "source/fuzz/transformation_store.h" 19fd4e5da5Sopenharmony_ci 20fd4e5da5Sopenharmony_cinamespace spvtools { 21fd4e5da5Sopenharmony_cinamespace fuzz { 22fd4e5da5Sopenharmony_ci 23fd4e5da5Sopenharmony_ciFuzzerPassAddStores::FuzzerPassAddStores( 24fd4e5da5Sopenharmony_ci opt::IRContext* ir_context, TransformationContext* transformation_context, 25fd4e5da5Sopenharmony_ci FuzzerContext* fuzzer_context, 26fd4e5da5Sopenharmony_ci protobufs::TransformationSequence* transformations, 27fd4e5da5Sopenharmony_ci bool ignore_inapplicable_transformations) 28fd4e5da5Sopenharmony_ci : FuzzerPass(ir_context, transformation_context, fuzzer_context, 29fd4e5da5Sopenharmony_ci transformations, ignore_inapplicable_transformations) {} 30fd4e5da5Sopenharmony_ci 31fd4e5da5Sopenharmony_civoid FuzzerPassAddStores::Apply() { 32fd4e5da5Sopenharmony_ci ForEachInstructionWithInstructionDescriptor( 33fd4e5da5Sopenharmony_ci [this](opt::Function* function, opt::BasicBlock* block, 34fd4e5da5Sopenharmony_ci opt::BasicBlock::iterator inst_it, 35fd4e5da5Sopenharmony_ci const protobufs::InstructionDescriptor& instruction_descriptor) 36fd4e5da5Sopenharmony_ci -> void { 37fd4e5da5Sopenharmony_ci assert( 38fd4e5da5Sopenharmony_ci inst_it->opcode() == 39fd4e5da5Sopenharmony_ci spv::Op(instruction_descriptor.target_instruction_opcode()) && 40fd4e5da5Sopenharmony_ci "The opcode of the instruction we might insert before must be " 41fd4e5da5Sopenharmony_ci "the same as the opcode in the descriptor for the instruction"); 42fd4e5da5Sopenharmony_ci 43fd4e5da5Sopenharmony_ci // Randomly decide whether to try inserting a store here. 44fd4e5da5Sopenharmony_ci if (!GetFuzzerContext()->ChoosePercentage( 45fd4e5da5Sopenharmony_ci GetFuzzerContext()->GetChanceOfAddingStore())) { 46fd4e5da5Sopenharmony_ci return; 47fd4e5da5Sopenharmony_ci } 48fd4e5da5Sopenharmony_ci 49fd4e5da5Sopenharmony_ci // Check whether it is legitimate to insert a store before this 50fd4e5da5Sopenharmony_ci // instruction. 51fd4e5da5Sopenharmony_ci if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore, 52fd4e5da5Sopenharmony_ci inst_it)) { 53fd4e5da5Sopenharmony_ci return; 54fd4e5da5Sopenharmony_ci } 55fd4e5da5Sopenharmony_ci if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( 56fd4e5da5Sopenharmony_ci spv::Op::OpAtomicStore, inst_it)) { 57fd4e5da5Sopenharmony_ci return; 58fd4e5da5Sopenharmony_ci } 59fd4e5da5Sopenharmony_ci 60fd4e5da5Sopenharmony_ci // Look for pointers we might consider storing to. 61fd4e5da5Sopenharmony_ci std::vector<opt::Instruction*> relevant_pointers = 62fd4e5da5Sopenharmony_ci FindAvailableInstructions( 63fd4e5da5Sopenharmony_ci function, block, inst_it, 64fd4e5da5Sopenharmony_ci [this, block](opt::IRContext* context, 65fd4e5da5Sopenharmony_ci opt::Instruction* instruction) -> bool { 66fd4e5da5Sopenharmony_ci if (!instruction->result_id() || !instruction->type_id()) { 67fd4e5da5Sopenharmony_ci return false; 68fd4e5da5Sopenharmony_ci } 69fd4e5da5Sopenharmony_ci auto type_inst = context->get_def_use_mgr()->GetDef( 70fd4e5da5Sopenharmony_ci instruction->type_id()); 71fd4e5da5Sopenharmony_ci if (type_inst->opcode() != spv::Op::OpTypePointer) { 72fd4e5da5Sopenharmony_ci // Not a pointer. 73fd4e5da5Sopenharmony_ci return false; 74fd4e5da5Sopenharmony_ci } 75fd4e5da5Sopenharmony_ci if (instruction->IsReadOnlyPointer()) { 76fd4e5da5Sopenharmony_ci // Read only: cannot store to it. 77fd4e5da5Sopenharmony_ci return false; 78fd4e5da5Sopenharmony_ci } 79fd4e5da5Sopenharmony_ci switch (instruction->opcode()) { 80fd4e5da5Sopenharmony_ci case spv::Op::OpConstantNull: 81fd4e5da5Sopenharmony_ci case spv::Op::OpUndef: 82fd4e5da5Sopenharmony_ci // Do not allow storing to a null or undefined pointer; 83fd4e5da5Sopenharmony_ci // this might be OK if the block is dead, but for now we 84fd4e5da5Sopenharmony_ci // conservatively avoid it. 85fd4e5da5Sopenharmony_ci return false; 86fd4e5da5Sopenharmony_ci default: 87fd4e5da5Sopenharmony_ci break; 88fd4e5da5Sopenharmony_ci } 89fd4e5da5Sopenharmony_ci return GetTransformationContext() 90fd4e5da5Sopenharmony_ci ->GetFactManager() 91fd4e5da5Sopenharmony_ci ->BlockIsDead(block->id()) || 92fd4e5da5Sopenharmony_ci GetTransformationContext() 93fd4e5da5Sopenharmony_ci ->GetFactManager() 94fd4e5da5Sopenharmony_ci ->PointeeValueIsIrrelevant( 95fd4e5da5Sopenharmony_ci instruction->result_id()); 96fd4e5da5Sopenharmony_ci }); 97fd4e5da5Sopenharmony_ci 98fd4e5da5Sopenharmony_ci // At this point, |relevant_pointers| contains all the pointers we might 99fd4e5da5Sopenharmony_ci // think of storing to. 100fd4e5da5Sopenharmony_ci if (relevant_pointers.empty()) { 101fd4e5da5Sopenharmony_ci return; 102fd4e5da5Sopenharmony_ci } 103fd4e5da5Sopenharmony_ci 104fd4e5da5Sopenharmony_ci auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex( 105fd4e5da5Sopenharmony_ci relevant_pointers)]; 106fd4e5da5Sopenharmony_ci 107fd4e5da5Sopenharmony_ci std::vector<opt::Instruction*> relevant_values = 108fd4e5da5Sopenharmony_ci FindAvailableInstructions( 109fd4e5da5Sopenharmony_ci function, block, inst_it, 110fd4e5da5Sopenharmony_ci [pointer](opt::IRContext* context, 111fd4e5da5Sopenharmony_ci opt::Instruction* instruction) -> bool { 112fd4e5da5Sopenharmony_ci if (!instruction->result_id() || !instruction->type_id()) { 113fd4e5da5Sopenharmony_ci return false; 114fd4e5da5Sopenharmony_ci } 115fd4e5da5Sopenharmony_ci return instruction->type_id() == 116fd4e5da5Sopenharmony_ci context->get_def_use_mgr() 117fd4e5da5Sopenharmony_ci ->GetDef(pointer->type_id()) 118fd4e5da5Sopenharmony_ci ->GetSingleWordInOperand(1); 119fd4e5da5Sopenharmony_ci }); 120fd4e5da5Sopenharmony_ci 121fd4e5da5Sopenharmony_ci if (relevant_values.empty()) { 122fd4e5da5Sopenharmony_ci return; 123fd4e5da5Sopenharmony_ci } 124fd4e5da5Sopenharmony_ci 125fd4e5da5Sopenharmony_ci bool is_atomic_store = false; 126fd4e5da5Sopenharmony_ci uint32_t memory_scope_id = 0; 127fd4e5da5Sopenharmony_ci uint32_t memory_semantics_id = 0; 128fd4e5da5Sopenharmony_ci 129fd4e5da5Sopenharmony_ci auto storage_class = 130fd4e5da5Sopenharmony_ci static_cast<spv::StorageClass>(GetIRContext() 131fd4e5da5Sopenharmony_ci ->get_def_use_mgr() 132fd4e5da5Sopenharmony_ci ->GetDef(pointer->type_id()) 133fd4e5da5Sopenharmony_ci ->GetSingleWordInOperand(0)); 134fd4e5da5Sopenharmony_ci 135fd4e5da5Sopenharmony_ci switch (storage_class) { 136fd4e5da5Sopenharmony_ci case spv::StorageClass::StorageBuffer: 137fd4e5da5Sopenharmony_ci case spv::StorageClass::PhysicalStorageBuffer: 138fd4e5da5Sopenharmony_ci case spv::StorageClass::Workgroup: 139fd4e5da5Sopenharmony_ci case spv::StorageClass::CrossWorkgroup: 140fd4e5da5Sopenharmony_ci case spv::StorageClass::AtomicCounter: 141fd4e5da5Sopenharmony_ci case spv::StorageClass::Image: 142fd4e5da5Sopenharmony_ci if (GetFuzzerContext()->ChoosePercentage( 143fd4e5da5Sopenharmony_ci GetFuzzerContext()->GetChanceOfAddingAtomicStore())) { 144fd4e5da5Sopenharmony_ci is_atomic_store = true; 145fd4e5da5Sopenharmony_ci 146fd4e5da5Sopenharmony_ci memory_scope_id = FindOrCreateConstant( 147fd4e5da5Sopenharmony_ci {uint32_t(spv::Scope::Invocation)}, 148fd4e5da5Sopenharmony_ci FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), 149fd4e5da5Sopenharmony_ci false); 150fd4e5da5Sopenharmony_ci 151fd4e5da5Sopenharmony_ci memory_semantics_id = FindOrCreateConstant( 152fd4e5da5Sopenharmony_ci {static_cast<uint32_t>( 153fd4e5da5Sopenharmony_ci fuzzerutil::GetMemorySemanticsForStorageClass( 154fd4e5da5Sopenharmony_ci storage_class))}, 155fd4e5da5Sopenharmony_ci FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), 156fd4e5da5Sopenharmony_ci false); 157fd4e5da5Sopenharmony_ci } 158fd4e5da5Sopenharmony_ci break; 159fd4e5da5Sopenharmony_ci 160fd4e5da5Sopenharmony_ci default: 161fd4e5da5Sopenharmony_ci break; 162fd4e5da5Sopenharmony_ci } 163fd4e5da5Sopenharmony_ci 164fd4e5da5Sopenharmony_ci // Create and apply the transformation. 165fd4e5da5Sopenharmony_ci ApplyTransformation(TransformationStore( 166fd4e5da5Sopenharmony_ci pointer->result_id(), is_atomic_store, memory_scope_id, 167fd4e5da5Sopenharmony_ci memory_semantics_id, 168fd4e5da5Sopenharmony_ci relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] 169fd4e5da5Sopenharmony_ci ->result_id(), 170fd4e5da5Sopenharmony_ci instruction_descriptor)); 171fd4e5da5Sopenharmony_ci }); 172fd4e5da5Sopenharmony_ci} 173fd4e5da5Sopenharmony_ci 174fd4e5da5Sopenharmony_ci} // namespace fuzz 175fd4e5da5Sopenharmony_ci} // namespace spvtools 176