1 // Copyright (c) 2019 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.
14
15 #include "source/fuzz/transformation_set_loop_control.h"
16
17 namespace spvtools {
18 namespace fuzz {
19
TransformationSetLoopControl(protobufs::TransformationSetLoopControl message)20 TransformationSetLoopControl::TransformationSetLoopControl(
21 protobufs::TransformationSetLoopControl message)
22 : message_(std::move(message)) {}
23
TransformationSetLoopControl(uint32_t block_id,uint32_t loop_control,uint32_t peel_count,uint32_t partial_count)24 TransformationSetLoopControl::TransformationSetLoopControl(
25 uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
26 uint32_t partial_count) {
27 message_.set_block_id(block_id);
28 message_.set_loop_control(loop_control);
29 message_.set_peel_count(peel_count);
30 message_.set_partial_count(partial_count);
31 }
32
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const33 bool TransformationSetLoopControl::IsApplicable(
34 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
35 // |message_.block_id| must identify a block that ends with OpLoopMerge.
36 auto block = ir_context->get_instr_block(message_.block_id());
37 if (!block) {
38 return false;
39 }
40 auto merge_inst = block->GetMergeInst();
41 if (!merge_inst || merge_inst->opcode() != spv::Op::OpLoopMerge) {
42 return false;
43 }
44
45 // We assert that the transformation does not try to set any meaningless bits
46 // of the loop control mask.
47 uint32_t all_loop_control_mask_bits_set = uint32_t(
48 spv::LoopControlMask::Unroll | spv::LoopControlMask::DontUnroll |
49 spv::LoopControlMask::DependencyInfinite |
50 spv::LoopControlMask::DependencyLength |
51 spv::LoopControlMask::MinIterations |
52 spv::LoopControlMask::MaxIterations |
53 spv::LoopControlMask::IterationMultiple |
54 spv::LoopControlMask::PeelCount | spv::LoopControlMask::PartialCount);
55
56 // The variable is only used in an assertion; the following keeps release-mode
57 // compilers happy.
58 (void)(all_loop_control_mask_bits_set);
59
60 // No additional bits should be set.
61 assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
62
63 // Grab the loop control mask currently associated with the OpLoopMerge
64 // instruction.
65 auto existing_loop_control_mask =
66 merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
67
68 // Check that there is no attempt to set one of the loop controls that
69 // requires guarantees to hold.
70 for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyInfinite,
71 spv::LoopControlMask::DependencyLength,
72 spv::LoopControlMask::MinIterations,
73 spv::LoopControlMask::MaxIterations,
74 spv::LoopControlMask::IterationMultiple}) {
75 // We have a problem if this loop control bit was not set in the original
76 // loop control mask but is set by the transformation.
77 if (LoopControlBitIsAddedByTransformation(mask,
78 existing_loop_control_mask)) {
79 return false;
80 }
81 }
82
83 // Check that PeelCount and PartialCount are supported if used.
84 if ((message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) &&
85 !PeelCountIsSupported(ir_context)) {
86 return false;
87 }
88
89 if ((message_.loop_control() &
90 uint32_t(spv::LoopControlMask::PartialCount)) &&
91 !PartialCountIsSupported(ir_context)) {
92 return false;
93 }
94
95 if (message_.peel_count() > 0 &&
96 !(message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount))) {
97 // Peel count provided, but peel count mask bit not set.
98 return false;
99 }
100
101 if (message_.partial_count() > 0 &&
102 !(message_.loop_control() &
103 uint32_t(spv::LoopControlMask::PartialCount))) {
104 // Partial count provided, but partial count mask bit not set.
105 return false;
106 }
107
108 // We must not set both 'don't unroll' and one of 'peel count' or 'partial
109 // count'.
110 return !(
111 (message_.loop_control() & uint32_t(spv::LoopControlMask::DontUnroll)) &&
112 (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount |
113 spv::LoopControlMask::PartialCount)));
114 }
115
Apply(opt::IRContext * ir_context,TransformationContext *) const116 void TransformationSetLoopControl::Apply(
117 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
118 // Grab the loop merge instruction and its associated loop control mask.
119 auto merge_inst =
120 ir_context->get_instr_block(message_.block_id())->GetMergeInst();
121 auto existing_loop_control_mask =
122 merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
123
124 // We are going to replace the OpLoopMerge's operands with this list.
125 opt::Instruction::OperandList new_operands;
126 // We add the existing merge block and continue target ids.
127 new_operands.push_back(merge_inst->GetInOperand(0));
128 new_operands.push_back(merge_inst->GetInOperand(1));
129 // We use the loop control mask from the transformation.
130 new_operands.push_back(
131 {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
132
133 // It remains to determine what literals to provide, in association with
134 // the new loop control mask.
135 //
136 // For the loop controls that require guarantees to hold about the number
137 // of loop iterations, we need to keep, from the original OpLoopMerge, any
138 // literals associated with loop control bits that are still set.
139
140 uint32_t literal_index = 0; // Indexes into the literals from the original
141 // instruction.
142 for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyLength,
143 spv::LoopControlMask::MinIterations,
144 spv::LoopControlMask::MaxIterations,
145 spv::LoopControlMask::IterationMultiple}) {
146 // Check whether the bit was set in the original loop control mask.
147 if (existing_loop_control_mask & uint32_t(mask)) {
148 // Check whether the bit is set in the new loop control mask.
149 if (message_.loop_control() & uint32_t(mask)) {
150 // Add the associated literal to our sequence of replacement operands.
151 new_operands.push_back(
152 {SPV_OPERAND_TYPE_LITERAL_INTEGER,
153 {merge_inst->GetSingleWordInOperand(
154 kLoopControlFirstLiteralInOperandIndex + literal_index)}});
155 }
156 // Increment our index into the original loop control mask's literals,
157 // whether or not the bit was set in the new mask.
158 literal_index++;
159 }
160 }
161
162 // If PeelCount is set in the new mask, |message_.peel_count| provides the
163 // associated peel count.
164 if (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) {
165 new_operands.push_back(
166 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
167 }
168
169 // Similar, but for PartialCount.
170 if (message_.loop_control() & uint32_t(spv::LoopControlMask::PartialCount)) {
171 new_operands.push_back(
172 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
173 }
174
175 // Replace the input operands of the OpLoopMerge with the new operands we have
176 // accumulated.
177 merge_inst->SetInOperands(std::move(new_operands));
178 }
179
ToMessage() const180 protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
181 protobufs::Transformation result;
182 *result.mutable_set_loop_control() = message_;
183 return result;
184 }
185
LoopControlBitIsAddedByTransformation(spv::LoopControlMask loop_control_single_bit_mask,uint32_t existing_loop_control_mask) const186 bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
187 spv::LoopControlMask loop_control_single_bit_mask,
188 uint32_t existing_loop_control_mask) const {
189 return !(uint32_t(loop_control_single_bit_mask) &
190 existing_loop_control_mask) &&
191 (uint32_t(loop_control_single_bit_mask) & message_.loop_control());
192 }
193
PartialCountIsSupported(opt::IRContext * ir_context)194 bool TransformationSetLoopControl::PartialCountIsSupported(
195 opt::IRContext* ir_context) {
196 // TODO(afd): We capture the environments for which this loop control is
197 // definitely not supported. The check should be refined on demand for other
198 // target environments.
199 switch (ir_context->grammar().target_env()) {
200 case SPV_ENV_UNIVERSAL_1_0:
201 case SPV_ENV_UNIVERSAL_1_1:
202 case SPV_ENV_UNIVERSAL_1_2:
203 case SPV_ENV_UNIVERSAL_1_3:
204 case SPV_ENV_VULKAN_1_0:
205 case SPV_ENV_VULKAN_1_1:
206 return false;
207 default:
208 return true;
209 }
210 }
211
PeelCountIsSupported(opt::IRContext * ir_context)212 bool TransformationSetLoopControl::PeelCountIsSupported(
213 opt::IRContext* ir_context) {
214 // TODO(afd): We capture the environments for which this loop control is
215 // definitely not supported. The check should be refined on demand for other
216 // target environments.
217 switch (ir_context->grammar().target_env()) {
218 case SPV_ENV_UNIVERSAL_1_0:
219 case SPV_ENV_UNIVERSAL_1_1:
220 case SPV_ENV_UNIVERSAL_1_2:
221 case SPV_ENV_UNIVERSAL_1_3:
222 case SPV_ENV_VULKAN_1_0:
223 case SPV_ENV_VULKAN_1_1:
224 return false;
225 default:
226 return true;
227 }
228 }
229
GetFreshIds() const230 std::unordered_set<uint32_t> TransformationSetLoopControl::GetFreshIds() const {
231 return std::unordered_set<uint32_t>();
232 }
233
234 } // namespace fuzz
235 } // namespace spvtools
236