Roll external/abseil_cpp/ 8f739d18b..917bfee46 (2 commits) (#5887)
[KhronosGroup/SPIRV-Tools.git] / source / fuzz / transformation_store.cpp
blob6ba1c551a9ee3bec2148930a46df6b024dc4c031
1 // Copyright (c) 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #include "source/fuzz/transformation_store.h"
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
20 namespace spvtools {
21 namespace fuzz {
23 TransformationStore::TransformationStore(protobufs::TransformationStore message)
24 : message_(std::move(message)) {}
26 TransformationStore::TransformationStore(
27 uint32_t pointer_id, bool is_atomic, uint32_t memory_scope,
28 uint32_t memory_semantics, uint32_t value_id,
29 const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30 message_.set_pointer_id(pointer_id);
31 message_.set_is_atomic(is_atomic);
32 message_.set_memory_scope_id(memory_scope);
33 message_.set_memory_semantics_id(memory_semantics);
34 message_.set_value_id(value_id);
35 *message_.mutable_instruction_to_insert_before() =
36 instruction_to_insert_before;
39 bool TransformationStore::IsApplicable(
40 opt::IRContext* ir_context,
41 const TransformationContext& transformation_context) const {
42 // The pointer must exist and have a type.
43 auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
44 if (!pointer || !pointer->type_id()) {
45 return false;
48 // The pointer type must indeed be a pointer.
49 auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
50 assert(pointer_type && "Type id must be defined.");
51 if (pointer_type->opcode() != spv::Op::OpTypePointer) {
52 return false;
55 // The pointer must not be read only.
56 if (pointer->IsReadOnlyPointer()) {
57 return false;
60 // We do not want to allow storing to null or undefined pointers.
61 switch (pointer->opcode()) {
62 case spv::Op::OpConstantNull:
63 case spv::Op::OpUndef:
64 return false;
65 default:
66 break;
69 // Determine which instruction we should be inserting before.
70 auto insert_before =
71 FindInstruction(message_.instruction_to_insert_before(), ir_context);
72 // It must exist, ...
73 if (!insert_before) {
74 return false;
76 // ... and it must be legitimate to insert a store before it.
77 if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
78 spv::Op::OpStore, insert_before)) {
79 return false;
81 if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
82 spv::Op::OpAtomicStore, insert_before)) {
83 return false;
86 // The block we are inserting into needs to be dead, or else the pointee type
87 // of the pointer we are storing to needs to be irrelevant (otherwise the
88 // store could impact on the observable behaviour of the module).
89 if (!transformation_context.GetFactManager()->BlockIsDead(
90 ir_context->get_instr_block(insert_before)->id()) &&
91 !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
92 message_.pointer_id())) {
93 return false;
96 // The value being stored needs to exist and have a type.
97 auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
98 if (!value || !value->type_id()) {
99 return false;
102 // The type of the value must match the pointee type.
103 if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
104 return false;
107 // The pointer needs to be available at the insertion point.
108 if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
109 message_.pointer_id())) {
110 return false;
113 if (message_.is_atomic()) {
114 // Check the exists of memory scope and memory semantics ids.
115 auto memory_scope_instruction =
116 ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
117 auto memory_semantics_instruction =
118 ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
120 if (!memory_scope_instruction) {
121 return false;
123 if (!memory_semantics_instruction) {
124 return false;
126 // The memory scope and memory semantics instructions must have the
127 // 'OpConstant' opcode.
128 if (memory_scope_instruction->opcode() != spv::Op::OpConstant) {
129 return false;
131 if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) {
132 return false;
134 // The memory scope and memory semantics need to be available before
135 // |insert_before|.
136 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
137 ir_context, insert_before, message_.memory_scope_id())) {
138 return false;
140 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
141 ir_context, insert_before, message_.memory_semantics_id())) {
142 return false;
144 // The memory scope and memory semantics instructions must have an Integer
145 // operand type with signedness does not matters.
146 if (ir_context->get_def_use_mgr()
147 ->GetDef(memory_scope_instruction->type_id())
148 ->opcode() != spv::Op::OpTypeInt) {
149 return false;
151 if (ir_context->get_def_use_mgr()
152 ->GetDef(memory_semantics_instruction->type_id())
153 ->opcode() != spv::Op::OpTypeInt) {
154 return false;
157 // The size of the integer for memory scope and memory semantics
158 // instructions must be equal to 32 bits.
159 auto memory_scope_int_width =
160 ir_context->get_def_use_mgr()
161 ->GetDef(memory_scope_instruction->type_id())
162 ->GetSingleWordInOperand(0);
163 auto memory_semantics_int_width =
164 ir_context->get_def_use_mgr()
165 ->GetDef(memory_semantics_instruction->type_id())
166 ->GetSingleWordInOperand(0);
168 if (memory_scope_int_width != 32) {
169 return false;
171 if (memory_semantics_int_width != 32) {
172 return false;
175 // The memory scope constant value must be that of spv::Scope::Invocation.
176 auto memory_scope_const_value =
177 memory_scope_instruction->GetSingleWordInOperand(0);
178 if (spv::Scope(memory_scope_const_value) != spv::Scope::Invocation) {
179 return false;
182 // The memory semantics constant value must match the storage class of the
183 // pointer being loaded from.
184 auto memory_semantics_const_value = static_cast<spv::MemorySemanticsMask>(
185 memory_semantics_instruction->GetSingleWordInOperand(0));
186 if (memory_semantics_const_value !=
187 fuzzerutil::GetMemorySemanticsForStorageClass(
188 static_cast<spv::StorageClass>(
189 pointer_type->GetSingleWordInOperand(0)))) {
190 return false;
194 // The value needs to be available at the insertion point.
195 return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
196 message_.value_id());
199 void TransformationStore::Apply(opt::IRContext* ir_context,
200 TransformationContext* /*unused*/) const {
201 if (message_.is_atomic()) {
202 // OpAtomicStore instruction.
203 auto insert_before =
204 FindInstruction(message_.instruction_to_insert_before(), ir_context);
205 auto new_instruction = MakeUnique<opt::Instruction>(
206 ir_context, spv::Op::OpAtomicStore, 0, 0,
207 opt::Instruction::OperandList(
208 {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
209 {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
210 {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
211 {message_.memory_semantics_id()}},
212 {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
213 auto new_instruction_ptr = new_instruction.get();
214 insert_before->InsertBefore(std::move(new_instruction));
215 // Inform the def-use manager about the new instruction and record its basic
216 // block.
217 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
218 ir_context->set_instr_block(new_instruction_ptr,
219 ir_context->get_instr_block(insert_before));
221 } else {
222 // OpStore instruction.
223 auto insert_before =
224 FindInstruction(message_.instruction_to_insert_before(), ir_context);
225 auto new_instruction = MakeUnique<opt::Instruction>(
226 ir_context, spv::Op::OpStore, 0, 0,
227 opt::Instruction::OperandList(
228 {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
229 {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
230 auto new_instruction_ptr = new_instruction.get();
231 insert_before->InsertBefore(std::move(new_instruction));
232 // Inform the def-use manager about the new instruction and record its basic
233 // block.
234 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
235 ir_context->set_instr_block(new_instruction_ptr,
236 ir_context->get_instr_block(insert_before));
240 protobufs::Transformation TransformationStore::ToMessage() const {
241 protobufs::Transformation result;
242 *result.mutable_store() = message_;
243 return result;
246 std::unordered_set<uint32_t> TransformationStore::GetFreshIds() const {
247 return std::unordered_set<uint32_t>();
250 } // namespace fuzz
251 } // namespace spvtools