1 // Copyright (c) 2020 André Perez Maselco
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.
14
15 #include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "source/fuzz/transformation_add_image_sample_unused_components.h"
20 #include "source/fuzz/transformation_composite_construct.h"
21
22 namespace spvtools {
23 namespace fuzz {
24
25 FuzzerPassAddImageSampleUnusedComponents::
FuzzerPassAddImageSampleUnusedComponents(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations)26 FuzzerPassAddImageSampleUnusedComponents(
27 opt::IRContext* ir_context,
28 TransformationContext* transformation_context,
29 FuzzerContext* fuzzer_context,
30 protobufs::TransformationSequence* transformations)
31 : FuzzerPass(ir_context, transformation_context, fuzzer_context,
32 transformations) {}
33
Apply()34 void FuzzerPassAddImageSampleUnusedComponents::Apply() {
35 // SPIR-V module to help understand the transformation.
36 //
37 // OpCapability Shader
38 // %1 = OpExtInstImport "GLSL.std.450"
39 // OpMemoryModel Logical GLSL450
40 // OpEntryPoint Fragment %15 "main" %12 %14
41 // OpExecutionMode %15 OriginUpperLeft
42 //
43 // ; Decorations
44 // OpDecorate %12 Location 0 ; Input color variable location
45 // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
46 // descriptor set OpDecorate %13 Binding 0 ; Image coordinate
47 // variable binding OpDecorate %14 Location 0 ; Fragment color
48 // variable location
49 //
50 // ; Types
51 // %2 = OpTypeVoid
52 // %3 = OpTypeFunction %2
53 // %4 = OpTypeFloat 32
54 // %5 = OpTypeVector %4 2
55 // %6 = OpTypeVector %4 4
56 // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
57 // %8 = OpTypeSampledImage %7
58 // %9 = OpTypePointer Input %5
59 // %10 = OpTypePointer UniformConstant %8
60 // %11 = OpTypePointer Output %6
61 //
62 // ; Variables
63 // %12 = OpVariable %9 Input ; Input image coordinate variable
64 // %13 = OpVariable %10 UniformConstant ; Image variable
65 // %14 = OpVariable %11 Output ; Fragment color variable
66 //
67 // ; main function
68 // %15 = OpFunction %2 None %3
69 // %16 = OpLabel
70 // %17 = OpLoad %5 %12
71 // %18 = OpLoad %8 %13
72 // %19 = OpImageSampleImplicitLod %6 %18 %17
73 // OpStore %14 %19
74 // OpReturn
75 // OpFunctionEnd
76
77 GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
78 // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
79 if (!spvOpcodeIsImageSample(instruction->opcode())) {
80 return;
81 }
82
83 if (!GetFuzzerContext()->ChoosePercentage(
84 GetFuzzerContext()
85 ->GetChanceOfAddingImageSampleUnusedComponents())) {
86 return;
87 }
88
89 // Gets image sample coordinate information.
90 // |coordinate_instruction| %17 = OpLoad %5 %12
91 uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
92 auto coordinate_instruction =
93 GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
94 auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
95 coordinate_instruction->type_id());
96
97 // If the coordinate is a 4-dimensional vector, then no unused components
98 // may be added.
99 if (coordinate_type->AsVector() &&
100 coordinate_type->AsVector()->element_count() == 4) {
101 return;
102 }
103
104 // If the coordinate is a scalar, then at most 3 unused components may be
105 // added. If the coordinate is a vector, then the maximum number of unused
106 // components depends on the vector size.
107 // For the sample module, the coordinate type instruction is %5 =
108 // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
109 uint32_t max_unused_component_count =
110 coordinate_type->AsInteger() || coordinate_type->AsFloat()
111 ? 3
112 : 4 - coordinate_type->AsVector()->element_count();
113
114 // |unused_component_count| may be 1 or 2.
115 uint32_t unused_component_count =
116 GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
117 max_unused_component_count);
118
119 // Gets a type for the zero-unused components.
120 uint32_t zero_constant_type_id;
121 switch (unused_component_count) {
122 case 1:
123 // If the coordinate is an integer or float, then the unused components
124 // type is the same as the coordinate. If the coordinate is a vector,
125 // then the unused components type is the same as the vector components
126 // type.
127 zero_constant_type_id =
128 coordinate_type->AsInteger() || coordinate_type->AsFloat()
129 ? coordinate_instruction->type_id()
130 : GetIRContext()->get_type_mgr()->GetId(
131 coordinate_type->AsVector()->element_type());
132 break;
133 case 2:
134 case 3:
135 // If the coordinate is an integer or float, then the unused components
136 // type is the same as the coordinate. If the coordinate is a vector,
137 // then the unused components type is the same as the coordinate
138 // components type.
139 // |zero_constant_type_id| %5 = OpTypeVector %4 2
140 zero_constant_type_id =
141 coordinate_type->AsInteger() || coordinate_type->AsFloat()
142 ? FindOrCreateVectorType(coordinate_instruction->type_id(),
143 unused_component_count)
144 : FindOrCreateVectorType(
145 GetIRContext()->get_type_mgr()->GetId(
146 coordinate_type->AsVector()->element_type()),
147 unused_component_count);
148 break;
149 default:
150 assert(false && "Should be unreachable.");
151 zero_constant_type_id = 0;
152 break;
153 }
154
155 // Gets |coordinate_type| again because the module may have changed due to
156 // the use of FindOrCreateVectorType above.
157 coordinate_type = GetIRContext()->get_type_mgr()->GetType(
158 coordinate_instruction->type_id());
159
160 // If the new vector type with unused components does not exist, then create
161 // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
162 uint32_t coordinate_with_unused_components_type_id =
163 coordinate_type->AsInteger() || coordinate_type->AsFloat()
164 ? FindOrCreateVectorType(coordinate_instruction->type_id(),
165 1 + unused_component_count)
166 : FindOrCreateVectorType(
167 GetIRContext()->get_type_mgr()->GetId(
168 coordinate_type->AsVector()->element_type()),
169 coordinate_type->AsVector()->element_count() +
170 unused_component_count);
171
172 // Inserts an OpCompositeConstruct instruction which
173 // represents the coordinate with unused components.
174 // |coordinate_with_unused_components_id|
175 // %22 = OpCompositeConstruct %6 %17 %21
176 uint32_t coordinate_with_unused_components_id =
177 GetFuzzerContext()->GetFreshId();
178 ApplyTransformation(TransformationCompositeConstruct(
179 coordinate_with_unused_components_type_id,
180 {coordinate_instruction->result_id(),
181 // FindOrCreateZeroConstant
182 // %20 = OpConstant %4 0
183 // %21 = OpConstantComposite %5 %20 %20
184 FindOrCreateZeroConstant(zero_constant_type_id, true)},
185 MakeInstructionDescriptor(GetIRContext(), instruction),
186 coordinate_with_unused_components_id));
187
188 // Tries to add unused components to the image sample coordinate.
189 // %19 = OpImageSampleImplicitLod %6 %18 %22
190 ApplyTransformation(TransformationAddImageSampleUnusedComponents(
191 coordinate_with_unused_components_id,
192 MakeInstructionDescriptor(GetIRContext(), instruction)));
193 });
194 }
195
196 } // namespace fuzz
197 } // namespace spvtools
198