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