• 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_replace_id_with_synonym.h"
16 
17 #include <algorithm>
18 
19 #include "source/fuzz/data_descriptor.h"
20 #include "source/fuzz/fuzzer_util.h"
21 #include "source/fuzz/id_use_descriptor.h"
22 #include "source/opt/types.h"
23 #include "source/util/make_unique.h"
24 
25 namespace spvtools {
26 namespace fuzz {
27 
TransformationReplaceIdWithSynonym(const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym & message)28 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
29     const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym&
30         message)
31     : message_(message) {}
32 
TransformationReplaceIdWithSynonym(protobufs::IdUseDescriptor id_use_descriptor,uint32_t synonymous_id)33 TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
34     protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) {
35   *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
36   message_.set_synonymous_id(synonymous_id);
37 }
38 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const39 bool TransformationReplaceIdWithSynonym::IsApplicable(
40     opt::IRContext* ir_context,
41     const TransformationContext& transformation_context) const {
42   auto id_of_interest = message_.id_use_descriptor().id_of_interest();
43 
44   // Does the fact manager know about the synonym?
45   auto data_descriptor_for_synonymous_id =
46       MakeDataDescriptor(message_.synonymous_id(), {});
47   if (!transformation_context.GetFactManager()->IsSynonymous(
48           MakeDataDescriptor(id_of_interest, {}),
49           data_descriptor_for_synonymous_id, ir_context)) {
50     return false;
51   }
52 
53   // Does the id use descriptor in the transformation identify an instruction?
54   auto use_instruction =
55       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
56   if (!use_instruction) {
57     return false;
58   }
59 
60   // Is the use suitable for being replaced in principle?
61   if (!UseCanBeReplacedWithSynonym(
62           ir_context, use_instruction,
63           message_.id_use_descriptor().in_operand_index())) {
64     return false;
65   }
66 
67   // The transformation is applicable if the synonymous id is available at the
68   // use point.
69   return fuzzerutil::IdIsAvailableAtUse(
70       ir_context, use_instruction,
71       message_.id_use_descriptor().in_operand_index(),
72       message_.synonymous_id());
73 }
74 
Apply(spvtools::opt::IRContext * ir_context,TransformationContext *) const75 void TransformationReplaceIdWithSynonym::Apply(
76     spvtools::opt::IRContext* ir_context,
77     TransformationContext* /*unused*/) const {
78   auto instruction_to_change =
79       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
80   instruction_to_change->SetInOperand(
81       message_.id_use_descriptor().in_operand_index(),
82       {message_.synonymous_id()});
83   ir_context->InvalidateAnalysesExceptFor(
84       opt::IRContext::Analysis::kAnalysisNone);
85 }
86 
ToMessage() const87 protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
88     const {
89   protobufs::Transformation result;
90   *result.mutable_replace_id_with_synonym() = message_;
91   return result;
92 }
93 
UseCanBeReplacedWithSynonym(opt::IRContext * ir_context,opt::Instruction * use_instruction,uint32_t use_in_operand_index)94 bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
95     opt::IRContext* ir_context, opt::Instruction* use_instruction,
96     uint32_t use_in_operand_index) {
97   if (use_instruction->opcode() == SpvOpAccessChain &&
98       use_in_operand_index > 0) {
99     // This is an access chain index.  If the (sub-)object being accessed by the
100     // given index has struct type then we cannot replace the use with a
101     // synonym, as the use needs to be an OpConstant.
102 
103     // Get the top-level composite type that is being accessed.
104     auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef(
105         use_instruction->GetSingleWordInOperand(0));
106     auto pointer_type =
107         ir_context->get_type_mgr()->GetType(object_being_accessed->type_id());
108     assert(pointer_type->AsPointer());
109     auto composite_type_being_accessed =
110         pointer_type->AsPointer()->pointee_type();
111 
112     // Now walk the access chain, tracking the type of each sub-object of the
113     // composite that is traversed, until the index of interest is reached.
114     for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
115          index_in_operand++) {
116       // For vectors, matrices and arrays, getting the type of the sub-object is
117       // trivial. For the struct case, the sub-object type is field-sensitive,
118       // and depends on the constant index that is used.
119       if (composite_type_being_accessed->AsVector()) {
120         composite_type_being_accessed =
121             composite_type_being_accessed->AsVector()->element_type();
122       } else if (composite_type_being_accessed->AsMatrix()) {
123         composite_type_being_accessed =
124             composite_type_being_accessed->AsMatrix()->element_type();
125       } else if (composite_type_being_accessed->AsArray()) {
126         composite_type_being_accessed =
127             composite_type_being_accessed->AsArray()->element_type();
128       } else {
129         assert(composite_type_being_accessed->AsStruct());
130         auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef(
131             use_instruction->GetSingleWordInOperand(index_in_operand));
132         assert(constant_index_instruction->opcode() == SpvOpConstant);
133         uint32_t member_index =
134             constant_index_instruction->GetSingleWordInOperand(0);
135         composite_type_being_accessed =
136             composite_type_being_accessed->AsStruct()
137                 ->element_types()[member_index];
138       }
139     }
140 
141     // We have found the composite type being accessed by the index we are
142     // considering replacing. If it is a struct, then we cannot do the
143     // replacement as struct indices must be constants.
144     if (composite_type_being_accessed->AsStruct()) {
145       return false;
146     }
147   }
148 
149   if (use_instruction->opcode() == SpvOpFunctionCall &&
150       use_in_operand_index > 0) {
151     // This is a function call argument.  It is not allowed to have pointer
152     // type.
153 
154     // Get the definition of the function being called.
155     auto function = ir_context->get_def_use_mgr()->GetDef(
156         use_instruction->GetSingleWordInOperand(0));
157     // From the function definition, get the function type.
158     auto function_type = ir_context->get_def_use_mgr()->GetDef(
159         function->GetSingleWordInOperand(1));
160     // OpTypeFunction's 0-th input operand is the function return type, and the
161     // function argument types follow. Because the arguments to OpFunctionCall
162     // start from input operand 1, we can use |use_in_operand_index| to get the
163     // type associated with this function argument.
164     auto parameter_type = ir_context->get_type_mgr()->GetType(
165         function_type->GetSingleWordInOperand(use_in_operand_index));
166     if (parameter_type->AsPointer()) {
167       return false;
168     }
169   }
170   return true;
171 }
172 
173 }  // namespace fuzz
174 }  // namespace spvtools
175