• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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