• 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() != 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