• 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(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