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() != SpvOpLoopMerge) {
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 =
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 // Check that PeelCount and PartialCount are supported if used.
81 if ((message_.loop_control() & SpvLoopControlPeelCountMask) &&
82 !PeelCountIsSupported(ir_context)) {
83 return false;
84 }
85
86 if ((message_.loop_control() & SpvLoopControlPartialCountMask) &&
87 !PartialCountIsSupported(ir_context)) {
88 return false;
89 }
90
91 if (message_.peel_count() > 0 &&
92 !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
93 // Peel count provided, but peel count mask bit not set.
94 return false;
95 }
96
97 if (message_.partial_count() > 0 &&
98 !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
99 // Partial count provided, but partial count mask bit not set.
100 return false;
101 }
102
103 // We must not set both 'don't unroll' and one of 'peel count' or 'partial
104 // count'.
105 return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
106 (message_.loop_control() &
107 (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
108 }
109
Apply(opt::IRContext * ir_context,TransformationContext *) const110 void TransformationSetLoopControl::Apply(
111 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
112 // Grab the loop merge instruction and its associated loop control mask.
113 auto merge_inst =
114 ir_context->get_instr_block(message_.block_id())->GetMergeInst();
115 auto existing_loop_control_mask =
116 merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
117
118 // We are going to replace the OpLoopMerge's operands with this list.
119 opt::Instruction::OperandList new_operands;
120 // We add the existing merge block and continue target ids.
121 new_operands.push_back(merge_inst->GetInOperand(0));
122 new_operands.push_back(merge_inst->GetInOperand(1));
123 // We use the loop control mask from the transformation.
124 new_operands.push_back(
125 {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
126
127 // It remains to determine what literals to provide, in association with
128 // the new loop control mask.
129 //
130 // For the loop controls that require guarantees to hold about the number
131 // of loop iterations, we need to keep, from the original OpLoopMerge, any
132 // literals associated with loop control bits that are still set.
133
134 uint32_t literal_index = 0; // Indexes into the literals from the original
135 // instruction.
136 for (SpvLoopControlMask mask :
137 {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
138 SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
139 // Check whether the bit was set in the original loop control mask.
140 if (existing_loop_control_mask & mask) {
141 // Check whether the bit is set in the new loop control mask.
142 if (message_.loop_control() & mask) {
143 // Add the associated literal to our sequence of replacement operands.
144 new_operands.push_back(
145 {SPV_OPERAND_TYPE_LITERAL_INTEGER,
146 {merge_inst->GetSingleWordInOperand(
147 kLoopControlFirstLiteralInOperandIndex + literal_index)}});
148 }
149 // Increment our index into the original loop control mask's literals,
150 // whether or not the bit was set in the new mask.
151 literal_index++;
152 }
153 }
154
155 // If PeelCount is set in the new mask, |message_.peel_count| provides the
156 // associated peel count.
157 if (message_.loop_control() & SpvLoopControlPeelCountMask) {
158 new_operands.push_back(
159 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
160 }
161
162 // Similar, but for PartialCount.
163 if (message_.loop_control() & SpvLoopControlPartialCountMask) {
164 new_operands.push_back(
165 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
166 }
167
168 // Replace the input operands of the OpLoopMerge with the new operands we have
169 // accumulated.
170 merge_inst->SetInOperands(std::move(new_operands));
171 }
172
ToMessage() const173 protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
174 protobufs::Transformation result;
175 *result.mutable_set_loop_control() = message_;
176 return result;
177 }
178
LoopControlBitIsAddedByTransformation(SpvLoopControlMask loop_control_single_bit_mask,uint32_t existing_loop_control_mask) const179 bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
180 SpvLoopControlMask loop_control_single_bit_mask,
181 uint32_t existing_loop_control_mask) const {
182 return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
183 (loop_control_single_bit_mask & message_.loop_control());
184 }
185
PartialCountIsSupported(opt::IRContext * ir_context)186 bool TransformationSetLoopControl::PartialCountIsSupported(
187 opt::IRContext* ir_context) {
188 // TODO(afd): We capture the environments for which this loop control is
189 // definitely not supported. The check should be refined on demand for other
190 // target environments.
191 switch (ir_context->grammar().target_env()) {
192 case SPV_ENV_UNIVERSAL_1_0:
193 case SPV_ENV_UNIVERSAL_1_1:
194 case SPV_ENV_UNIVERSAL_1_2:
195 case SPV_ENV_UNIVERSAL_1_3:
196 case SPV_ENV_VULKAN_1_0:
197 case SPV_ENV_VULKAN_1_1:
198 return false;
199 default:
200 return true;
201 }
202 }
203
PeelCountIsSupported(opt::IRContext * ir_context)204 bool TransformationSetLoopControl::PeelCountIsSupported(
205 opt::IRContext* ir_context) {
206 // TODO(afd): We capture the environments for which this loop control is
207 // definitely not supported. The check should be refined on demand for other
208 // target environments.
209 switch (ir_context->grammar().target_env()) {
210 case SPV_ENV_UNIVERSAL_1_0:
211 case SPV_ENV_UNIVERSAL_1_1:
212 case SPV_ENV_UNIVERSAL_1_2:
213 case SPV_ENV_UNIVERSAL_1_3:
214 case SPV_ENV_VULKAN_1_0:
215 case SPV_ENV_VULKAN_1_1:
216 return false;
217 default:
218 return true;
219 }
220 }
221
GetFreshIds() const222 std::unordered_set<uint32_t> TransformationSetLoopControl::GetFreshIds() const {
223 return std::unordered_set<uint32_t>();
224 }
225
226 } // namespace fuzz
227 } // namespace spvtools
228