1 // Copyright (c) 2020 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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"
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()) {
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
) {
55 // The pointer must not be read only.
56 if (pointer
->IsReadOnlyPointer()) {
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
:
69 // Determine which instruction we should be inserting before.
71 FindInstruction(message_
.instruction_to_insert_before(), ir_context
);
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
)) {
81 if (message_
.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
82 spv::Op::OpAtomicStore
, insert_before
)) {
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())) {
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()) {
102 // The type of the value must match the pointee type.
103 if (pointer_type
->GetSingleWordInOperand(1) != value
->type_id()) {
107 // The pointer needs to be available at the insertion point.
108 if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context
, insert_before
,
109 message_
.pointer_id())) {
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
) {
123 if (!memory_semantics_instruction
) {
126 // The memory scope and memory semantics instructions must have the
127 // 'OpConstant' opcode.
128 if (memory_scope_instruction
->opcode() != spv::Op::OpConstant
) {
131 if (memory_semantics_instruction
->opcode() != spv::Op::OpConstant
) {
134 // The memory scope and memory semantics need to be available before
136 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
137 ir_context
, insert_before
, message_
.memory_scope_id())) {
140 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
141 ir_context
, insert_before
, message_
.memory_semantics_id())) {
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
) {
151 if (ir_context
->get_def_use_mgr()
152 ->GetDef(memory_semantics_instruction
->type_id())
153 ->opcode() != spv::Op::OpTypeInt
) {
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) {
171 if (memory_semantics_int_width
!= 32) {
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
) {
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)))) {
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.
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
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
));
222 // OpStore instruction.
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
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_
;
246 std::unordered_set
<uint32_t> TransformationStore::GetFreshIds() const {
247 return std::unordered_set
<uint32_t>();
251 } // namespace spvtools