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(const spvtools::fuzz::protobufs::TransformationSetLoopControl & message)20 TransformationSetLoopControl::TransformationSetLoopControl(
21 const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
22 : message_(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() != SpvOpLoopMerge) {
42 return false;
43 }
44
45 // We sanity-check that the transformation does not try to set any meaningless
46 // bits of the loop control mask.
47 uint32_t all_loop_control_mask_bits_set =
48 SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
49 SpvLoopControlDependencyInfiniteMask |
50 SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask |
51 SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask |
52 SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask;
53
54 // The variable is only used in an assertion; the following keeps release-mode
55 // compilers happy.
56 (void)(all_loop_control_mask_bits_set);
57
58 // No additional bits should be set.
59 assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
60
61 // Grab the loop control mask currently associated with the OpLoopMerge
62 // instruction.
63 auto existing_loop_control_mask =
64 merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
65
66 // Check that there is no attempt to set one of the loop controls that
67 // requires guarantees to hold.
68 for (SpvLoopControlMask mask :
69 {SpvLoopControlDependencyInfiniteMask,
70 SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
71 SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
72 // We have a problem if this loop control bit was not set in the original
73 // loop control mask but is set by the transformation.
74 if (LoopControlBitIsAddedByTransformation(mask,
75 existing_loop_control_mask)) {
76 return false;
77 }
78 }
79
80 if ((message_.loop_control() &
81 (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
82 !(PeelCountIsSupported(ir_context) &&
83 PartialCountIsSupported(ir_context))) {
84 // At least one of PeelCount or PartialCount is used, but the SPIR-V version
85 // in question does not support these loop controls.
86 return false;
87 }
88
89 if (message_.peel_count() > 0 &&
90 !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
91 // Peel count provided, but peel count mask bit not set.
92 return false;
93 }
94
95 if (message_.partial_count() > 0 &&
96 !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
97 // Partial count provided, but partial count mask bit not set.
98 return false;
99 }
100
101 // We must not set both 'don't unroll' and one of 'peel count' or 'partial
102 // count'.
103 return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
104 (message_.loop_control() &
105 (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
106 }
107
Apply(opt::IRContext * ir_context,TransformationContext *) const108 void TransformationSetLoopControl::Apply(
109 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
110 // Grab the loop merge instruction and its associated loop control mask.
111 auto merge_inst =
112 ir_context->get_instr_block(message_.block_id())->GetMergeInst();
113 auto existing_loop_control_mask =
114 merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
115
116 // We are going to replace the OpLoopMerge's operands with this list.
117 opt::Instruction::OperandList new_operands;
118 // We add the existing merge block and continue target ids.
119 new_operands.push_back(merge_inst->GetInOperand(0));
120 new_operands.push_back(merge_inst->GetInOperand(1));
121 // We use the loop control mask from the transformation.
122 new_operands.push_back(
123 {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
124
125 // It remains to determine what literals to provide, in association with
126 // the new loop control mask.
127 //
128 // For the loop controls that require guarantees to hold about the number
129 // of loop iterations, we need to keep, from the original OpLoopMerge, any
130 // literals associated with loop control bits that are still set.
131
132 uint32_t literal_index = 0; // Indexes into the literals from the original
133 // instruction.
134 for (SpvLoopControlMask mask :
135 {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
136 SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
137 // Check whether the bit was set in the original loop control mask.
138 if (existing_loop_control_mask & mask) {
139 // Check whether the bit is set in the new loop control mask.
140 if (message_.loop_control() & mask) {
141 // Add the associated literal to our sequence of replacement operands.
142 new_operands.push_back(
143 {SPV_OPERAND_TYPE_LITERAL_INTEGER,
144 {merge_inst->GetSingleWordInOperand(
145 kLoopControlFirstLiteralInOperandIndex + literal_index)}});
146 }
147 // Increment our index into the original loop control mask's literals,
148 // whether or not the bit was set in the new mask.
149 literal_index++;
150 }
151 }
152
153 // If PeelCount is set in the new mask, |message_.peel_count| provides the
154 // associated peel count.
155 if (message_.loop_control() & SpvLoopControlPeelCountMask) {
156 new_operands.push_back(
157 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
158 }
159
160 // Similar, but for PartialCount.
161 if (message_.loop_control() & SpvLoopControlPartialCountMask) {
162 new_operands.push_back(
163 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
164 }
165
166 // Replace the input operands of the OpLoopMerge with the new operands we have
167 // accumulated.
168 merge_inst->SetInOperands(std::move(new_operands));
169 }
170
ToMessage() const171 protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
172 protobufs::Transformation result;
173 *result.mutable_set_loop_control() = message_;
174 return result;
175 }
176
LoopControlBitIsAddedByTransformation(SpvLoopControlMask loop_control_single_bit_mask,uint32_t existing_loop_control_mask) const177 bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
178 SpvLoopControlMask loop_control_single_bit_mask,
179 uint32_t existing_loop_control_mask) const {
180 return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
181 (loop_control_single_bit_mask & message_.loop_control());
182 }
183
PartialCountIsSupported(opt::IRContext * ir_context)184 bool TransformationSetLoopControl::PartialCountIsSupported(
185 opt::IRContext* ir_context) {
186 // TODO(afd): We capture the universal environments for which this loop
187 // control is definitely not supported. The check should be refined on
188 // demand for other target environments.
189 switch (ir_context->grammar().target_env()) {
190 case SPV_ENV_UNIVERSAL_1_0:
191 case SPV_ENV_UNIVERSAL_1_1:
192 case SPV_ENV_UNIVERSAL_1_2:
193 case SPV_ENV_UNIVERSAL_1_3:
194 return false;
195 default:
196 return true;
197 }
198 }
199
PeelCountIsSupported(opt::IRContext * ir_context)200 bool TransformationSetLoopControl::PeelCountIsSupported(
201 opt::IRContext* ir_context) {
202 // TODO(afd): We capture the universal environments for which this loop
203 // control is definitely not supported. The check should be refined on
204 // demand for other target environments.
205 switch (ir_context->grammar().target_env()) {
206 case SPV_ENV_UNIVERSAL_1_0:
207 case SPV_ENV_UNIVERSAL_1_1:
208 case SPV_ENV_UNIVERSAL_1_2:
209 case SPV_ENV_UNIVERSAL_1_3:
210 return false;
211 default:
212 return true;
213 }
214 }
215
216 } // namespace fuzz
217 } // namespace spvtools
218