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