• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
18 #define ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
19 
20 // This #include should never be used by compilation, because this header file (nodes_vector.h)
21 // is included in the header file nodes.h itself. However it gives editing tools better context.
22 #include "nodes.h"
23 
24 namespace art {
25 
26 // Memory alignment, represented as an offset relative to a base, where 0 <= offset < base,
27 // and base is a power of two. For example, the value Alignment(16, 0) means memory is
28 // perfectly aligned at a 16-byte boundary, whereas the value Alignment(16, 4) means
29 // memory is always exactly 4 bytes above such a boundary.
30 class Alignment {
31  public:
Alignment(size_t base,size_t offset)32   Alignment(size_t base, size_t offset) : base_(base), offset_(offset) {
33     DCHECK_LT(offset, base);
34     DCHECK(IsPowerOfTwo(base));
35   }
36 
37   // Returns true if memory is at least aligned at the given boundary.
38   // Assumes requested base is power of two.
IsAlignedAt(size_t base)39   bool IsAlignedAt(size_t base) const {
40     DCHECK_NE(0u, base);
41     DCHECK(IsPowerOfTwo(base));
42     return ((offset_ | base_) & (base - 1u)) == 0;
43   }
44 
Base()45   size_t Base() const { return base_; }
46 
Offset()47   size_t Offset() const { return offset_; }
48 
ToString()49   std::string ToString() const {
50     return "ALIGN(" + std::to_string(base_) + "," + std::to_string(offset_) + ")";
51   }
52 
53   bool operator==(const Alignment& other) const {
54     return base_ == other.base_ && offset_ == other.offset_;
55   }
56 
57  private:
58   size_t base_;
59   size_t offset_;
60 };
61 
62 //
63 // Definitions of abstract vector operations in HIR.
64 //
65 
66 // Abstraction of a vector operation, i.e., an operation that performs
67 // GetVectorLength() x GetPackedType() operations simultaneously.
68 class HVecOperation : public HVariableInputSizeInstruction {
69  public:
70   // A SIMD operation looks like a FPU location.
71   // TODO: we could introduce SIMD types in HIR.
72   static constexpr DataType::Type kSIMDType = DataType::Type::kFloat64;
73 
HVecOperation(InstructionKind kind,ArenaAllocator * allocator,DataType::Type packed_type,SideEffects side_effects,size_t number_of_inputs,size_t vector_length,uint32_t dex_pc)74   HVecOperation(InstructionKind kind,
75                 ArenaAllocator* allocator,
76                 DataType::Type packed_type,
77                 SideEffects side_effects,
78                 size_t number_of_inputs,
79                 size_t vector_length,
80                 uint32_t dex_pc)
81       : HVariableInputSizeInstruction(kind,
82                                       side_effects,
83                                       dex_pc,
84                                       allocator,
85                                       number_of_inputs,
86                                       kArenaAllocVectorNode),
87         vector_length_(vector_length) {
88     SetPackedField<TypeField>(packed_type);
89     DCHECK_LT(1u, vector_length);
90   }
91 
92   // Returns the number of elements packed in a vector.
GetVectorLength()93   size_t GetVectorLength() const {
94     return vector_length_;
95   }
96 
97   // Returns the number of bytes in a full vector.
GetVectorNumberOfBytes()98   size_t GetVectorNumberOfBytes() const {
99     return vector_length_ * DataType::Size(GetPackedType());
100   }
101 
102   // Returns the type of the vector operation.
GetType()103   DataType::Type GetType() const OVERRIDE {
104     return kSIMDType;
105   }
106 
107   // Returns the true component type packed in a vector.
GetPackedType()108   DataType::Type GetPackedType() const {
109     return GetPackedField<TypeField>();
110   }
111 
112   // Assumes vector nodes cannot be moved by default. Each concrete implementation
113   // that can be moved should override this method and return true.
114   //
115   // Note: similar approach is used for instruction scheduling (if it is turned on for the target):
116   // by default HScheduler::IsSchedulable returns false for a particular HVecOperation.
117   // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see
118   // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also
119   // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction.
120   //
121   // Note: For newly introduced vector instructions HScheduler${ARCH}::IsSchedulingBarrier must be
122   // altered to return true if the instruction might reside outside the SIMD loop body since SIMD
123   // registers are not kept alive across vector loop boundaries (yet).
CanBeMoved()124   bool CanBeMoved() const OVERRIDE { return false; }
125 
126   // Tests if all data of a vector node (vector length and packed type) is equal.
127   // Each concrete implementation that adds more fields should test equality of
128   // those fields in its own method *and* call all super methods.
InstructionDataEquals(const HInstruction * other)129   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
130     DCHECK(other->IsVecOperation());
131     const HVecOperation* o = other->AsVecOperation();
132     return GetVectorLength() == o->GetVectorLength() && GetPackedType() == o->GetPackedType();
133   }
134 
135   // Maps an integral type to the same-size signed type and leaves other types alone.
ToSignedType(DataType::Type type)136   static DataType::Type ToSignedType(DataType::Type type) {
137     switch (type) {
138       case DataType::Type::kBool:  // 1-byte storage unit
139       case DataType::Type::kUint8:
140         return DataType::Type::kInt8;
141       case DataType::Type::kUint16:
142         return DataType::Type::kInt16;
143       default:
144         DCHECK(type != DataType::Type::kVoid && type != DataType::Type::kReference) << type;
145         return type;
146     }
147   }
148 
149   // Maps an integral type to the same-size unsigned type and leaves other types alone.
ToUnsignedType(DataType::Type type)150   static DataType::Type ToUnsignedType(DataType::Type type) {
151     switch (type) {
152       case DataType::Type::kBool:  // 1-byte storage unit
153       case DataType::Type::kInt8:
154         return DataType::Type::kUint8;
155       case DataType::Type::kInt16:
156         return DataType::Type::kUint16;
157       default:
158         DCHECK(type != DataType::Type::kVoid && type != DataType::Type::kReference) << type;
159         return type;
160     }
161   }
162 
163   // Maps an integral type to the same-size (un)signed type. Leaves other types alone.
ToProperType(DataType::Type type,bool is_unsigned)164   static DataType::Type ToProperType(DataType::Type type, bool is_unsigned) {
165     return is_unsigned ? ToUnsignedType(type) : ToSignedType(type);
166   }
167 
168   // Helper method to determine if an instruction returns a SIMD value.
169   // TODO: This method is needed until we introduce SIMD as proper type.
ReturnsSIMDValue(HInstruction * instruction)170   static bool ReturnsSIMDValue(HInstruction* instruction) {
171     if (instruction->IsVecOperation()) {
172       return !instruction->IsVecExtractScalar();  // only scalar returning vec op
173     } else if (instruction->IsPhi()) {
174       // Vectorizer only uses Phis in reductions, so checking for a 2-way phi
175       // with a direct vector operand as second argument suffices.
176       return
177           instruction->GetType() == kSIMDType &&
178           instruction->InputCount() == 2 &&
179           instruction->InputAt(1)->IsVecOperation();
180     }
181     return false;
182   }
183 
184   DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
185 
186  protected:
187   // Additional packed bits.
188   static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
189   static constexpr size_t kFieldTypeSize =
190       MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
191   static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize;
192   static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
193   using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
194 
195   DEFAULT_COPY_CONSTRUCTOR(VecOperation);
196 
197  private:
198   const size_t vector_length_;
199 };
200 
201 // Abstraction of a unary vector operation.
202 class HVecUnaryOperation : public HVecOperation {
203  public:
HVecUnaryOperation(InstructionKind kind,ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)204   HVecUnaryOperation(InstructionKind kind,
205                      ArenaAllocator* allocator,
206                      HInstruction* input,
207                      DataType::Type packed_type,
208                      size_t vector_length,
209                      uint32_t dex_pc)
210       : HVecOperation(kind,
211                       allocator,
212                       packed_type,
213                       SideEffects::None(),
214                       /* number_of_inputs */ 1,
215                       vector_length,
216                       dex_pc) {
217     SetRawInputAt(0, input);
218   }
219 
GetInput()220   HInstruction* GetInput() const { return InputAt(0); }
221 
222   DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation);
223 
224  protected:
225   DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation);
226 };
227 
228 // Abstraction of a binary vector operation.
229 class HVecBinaryOperation : public HVecOperation {
230  public:
HVecBinaryOperation(InstructionKind kind,ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)231   HVecBinaryOperation(InstructionKind kind,
232                       ArenaAllocator* allocator,
233                       HInstruction* left,
234                       HInstruction* right,
235                       DataType::Type packed_type,
236                       size_t vector_length,
237                       uint32_t dex_pc)
238       : HVecOperation(kind,
239                       allocator,
240                       packed_type,
241                       SideEffects::None(),
242                       /* number_of_inputs */ 2,
243                       vector_length,
244                       dex_pc) {
245     SetRawInputAt(0, left);
246     SetRawInputAt(1, right);
247   }
248 
GetLeft()249   HInstruction* GetLeft() const { return InputAt(0); }
GetRight()250   HInstruction* GetRight() const { return InputAt(1); }
251 
252   DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation);
253 
254  protected:
255   DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation);
256 };
257 
258 // Abstraction of a vector operation that references memory, with an alignment.
259 // The Android runtime guarantees elements have at least natural alignment.
260 class HVecMemoryOperation : public HVecOperation {
261  public:
HVecMemoryOperation(InstructionKind kind,ArenaAllocator * allocator,DataType::Type packed_type,SideEffects side_effects,size_t number_of_inputs,size_t vector_length,uint32_t dex_pc)262   HVecMemoryOperation(InstructionKind kind,
263                       ArenaAllocator* allocator,
264                       DataType::Type packed_type,
265                       SideEffects side_effects,
266                       size_t number_of_inputs,
267                       size_t vector_length,
268                       uint32_t dex_pc)
269       : HVecOperation(kind,
270                       allocator,
271                       packed_type,
272                       side_effects,
273                       number_of_inputs,
274                       vector_length,
275                       dex_pc),
276         alignment_(DataType::Size(packed_type), 0) {
277     DCHECK_GE(number_of_inputs, 2u);
278   }
279 
SetAlignment(Alignment alignment)280   void SetAlignment(Alignment alignment) { alignment_ = alignment; }
281 
GetAlignment()282   Alignment GetAlignment() const { return alignment_; }
283 
GetArray()284   HInstruction* GetArray() const { return InputAt(0); }
GetIndex()285   HInstruction* GetIndex() const { return InputAt(1); }
286 
InstructionDataEquals(const HInstruction * other)287   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
288     DCHECK(other->IsVecMemoryOperation());
289     const HVecMemoryOperation* o = other->AsVecMemoryOperation();
290     return HVecOperation::InstructionDataEquals(o) && GetAlignment() == o->GetAlignment();
291   }
292 
293   DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
294 
295  protected:
296   DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation);
297 
298  private:
299   Alignment alignment_;
300 };
301 
302 // Packed type consistency checker ("same vector length" integral types may mix freely).
303 // Tests relaxed type consistency in which packed same-size integral types can co-exist,
304 // but other type mixes are an error.
HasConsistentPackedTypes(HInstruction * input,DataType::Type type)305 inline static bool HasConsistentPackedTypes(HInstruction* input, DataType::Type type) {
306   if (input->IsPhi()) {
307     return input->GetType() == HVecOperation::kSIMDType;  // carries SIMD
308   }
309   DCHECK(input->IsVecOperation());
310   DataType::Type input_type = input->AsVecOperation()->GetPackedType();
311   DCHECK_EQ(HVecOperation::ToUnsignedType(input_type) == HVecOperation::ToUnsignedType(type),
312             HVecOperation::ToSignedType(input_type) == HVecOperation::ToSignedType(type));
313   return HVecOperation::ToSignedType(input_type) == HVecOperation::ToSignedType(type);
314 }
315 
316 //
317 // Definitions of concrete unary vector operations in HIR.
318 //
319 
320 // Replicates the given scalar into a vector,
321 // viz. replicate(x) = [ x, .. , x ].
322 class HVecReplicateScalar FINAL : public HVecUnaryOperation {
323  public:
HVecReplicateScalar(ArenaAllocator * allocator,HInstruction * scalar,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)324   HVecReplicateScalar(ArenaAllocator* allocator,
325                       HInstruction* scalar,
326                       DataType::Type packed_type,
327                       size_t vector_length,
328                       uint32_t dex_pc)
329       : HVecUnaryOperation(
330             kVecReplicateScalar, allocator, scalar, packed_type, vector_length, dex_pc) {
331     DCHECK(!ReturnsSIMDValue(scalar));
332   }
333 
334   // A replicate needs to stay in place, since SIMD registers are not
335   // kept alive across vector loop boundaries (yet).
CanBeMoved()336   bool CanBeMoved() const OVERRIDE { return false; }
337 
338   DECLARE_INSTRUCTION(VecReplicateScalar);
339 
340  protected:
341   DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar);
342 };
343 
344 // Extracts a particular scalar from the given vector,
345 // viz. extract[ x1, .. , xn ] = x_i.
346 //
347 // TODO: for now only i == 1 case supported.
348 class HVecExtractScalar FINAL : public HVecUnaryOperation {
349  public:
HVecExtractScalar(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,size_t index,uint32_t dex_pc)350   HVecExtractScalar(ArenaAllocator* allocator,
351                     HInstruction* input,
352                     DataType::Type packed_type,
353                     size_t vector_length,
354                     size_t index,
355                     uint32_t dex_pc)
356       : HVecUnaryOperation(
357             kVecExtractScalar, allocator, input, packed_type, vector_length, dex_pc) {
358     DCHECK(HasConsistentPackedTypes(input, packed_type));
359     DCHECK_LT(index, vector_length);
360     DCHECK_EQ(index, 0u);
361   }
362 
363   // Yields a single component in the vector.
GetType()364   DataType::Type GetType() const OVERRIDE {
365     return GetPackedType();
366   }
367 
368   // An extract needs to stay in place, since SIMD registers are not
369   // kept alive across vector loop boundaries (yet).
CanBeMoved()370   bool CanBeMoved() const OVERRIDE { return false; }
371 
372   DECLARE_INSTRUCTION(VecExtractScalar);
373 
374  protected:
375   DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar);
376 };
377 
378 // Reduces the given vector into the first element as sum/min/max,
379 // viz. sum-reduce[ x1, .. , xn ] = [ y, ---- ], where y = sum xi
380 // and the "-" denotes "don't care" (implementation dependent).
381 class HVecReduce FINAL : public HVecUnaryOperation {
382  public:
383   enum ReductionKind {
384     kSum = 1,
385     kMin = 2,
386     kMax = 3
387   };
388 
HVecReduce(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,ReductionKind kind,uint32_t dex_pc)389   HVecReduce(ArenaAllocator* allocator,
390              HInstruction* input,
391              DataType::Type packed_type,
392              size_t vector_length,
393              ReductionKind kind,
394              uint32_t dex_pc)
395       : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
396         kind_(kind) {
397     DCHECK(HasConsistentPackedTypes(input, packed_type));
398   }
399 
GetKind()400   ReductionKind GetKind() const { return kind_; }
401 
CanBeMoved()402   bool CanBeMoved() const OVERRIDE { return true; }
403 
InstructionDataEquals(const HInstruction * other)404   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
405     DCHECK(other->IsVecReduce());
406     const HVecReduce* o = other->AsVecReduce();
407     return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
408   }
409 
410   DECLARE_INSTRUCTION(VecReduce);
411 
412  protected:
413   DEFAULT_COPY_CONSTRUCTOR(VecReduce);
414 
415  private:
416   const ReductionKind kind_;
417 };
418 
419 // Converts every component in the vector,
420 // viz. cnv[ x1, .. , xn ]  = [ cnv(x1), .. , cnv(xn) ].
421 class HVecCnv FINAL : public HVecUnaryOperation {
422  public:
HVecCnv(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)423   HVecCnv(ArenaAllocator* allocator,
424           HInstruction* input,
425           DataType::Type packed_type,
426           size_t vector_length,
427           uint32_t dex_pc)
428       : HVecUnaryOperation(kVecCnv, allocator, input, packed_type, vector_length, dex_pc) {
429     DCHECK(input->IsVecOperation());
430     DCHECK_NE(GetInputType(), GetResultType());  // actual convert
431   }
432 
GetInputType()433   DataType::Type GetInputType() const { return InputAt(0)->AsVecOperation()->GetPackedType(); }
GetResultType()434   DataType::Type GetResultType() const { return GetPackedType(); }
435 
CanBeMoved()436   bool CanBeMoved() const OVERRIDE { return true; }
437 
438   DECLARE_INSTRUCTION(VecCnv);
439 
440  protected:
441   DEFAULT_COPY_CONSTRUCTOR(VecCnv);
442 };
443 
444 // Negates every component in the vector,
445 // viz. neg[ x1, .. , xn ]  = [ -x1, .. , -xn ].
446 class HVecNeg FINAL : public HVecUnaryOperation {
447  public:
HVecNeg(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)448   HVecNeg(ArenaAllocator* allocator,
449           HInstruction* input,
450           DataType::Type packed_type,
451           size_t vector_length,
452           uint32_t dex_pc)
453       : HVecUnaryOperation(kVecNeg, allocator, input, packed_type, vector_length, dex_pc) {
454     DCHECK(HasConsistentPackedTypes(input, packed_type));
455   }
456 
CanBeMoved()457   bool CanBeMoved() const OVERRIDE { return true; }
458 
459   DECLARE_INSTRUCTION(VecNeg);
460 
461  protected:
462   DEFAULT_COPY_CONSTRUCTOR(VecNeg);
463 };
464 
465 // Takes absolute value of every component in the vector,
466 // viz. abs[ x1, .. , xn ]  = [ |x1|, .. , |xn| ]
467 // for signed operand x.
468 class HVecAbs FINAL : public HVecUnaryOperation {
469  public:
HVecAbs(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)470   HVecAbs(ArenaAllocator* allocator,
471           HInstruction* input,
472           DataType::Type packed_type,
473           size_t vector_length,
474           uint32_t dex_pc)
475       : HVecUnaryOperation(kVecAbs, allocator, input, packed_type, vector_length, dex_pc) {
476     DCHECK(HasConsistentPackedTypes(input, packed_type));
477   }
478 
CanBeMoved()479   bool CanBeMoved() const OVERRIDE { return true; }
480 
481   DECLARE_INSTRUCTION(VecAbs);
482 
483  protected:
484   DEFAULT_COPY_CONSTRUCTOR(VecAbs);
485 };
486 
487 // Bitwise- or boolean-nots every component in the vector,
488 // viz. not[ x1, .. , xn ]  = [ ~x1, .. , ~xn ], or
489 //      not[ x1, .. , xn ]  = [ !x1, .. , !xn ] for boolean.
490 class HVecNot FINAL : public HVecUnaryOperation {
491  public:
HVecNot(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)492   HVecNot(ArenaAllocator* allocator,
493           HInstruction* input,
494           DataType::Type packed_type,
495           size_t vector_length,
496           uint32_t dex_pc)
497       : HVecUnaryOperation(kVecNot, allocator, input, packed_type, vector_length, dex_pc) {
498     DCHECK(input->IsVecOperation());
499   }
500 
CanBeMoved()501   bool CanBeMoved() const OVERRIDE { return true; }
502 
503   DECLARE_INSTRUCTION(VecNot);
504 
505  protected:
506   DEFAULT_COPY_CONSTRUCTOR(VecNot);
507 };
508 
509 //
510 // Definitions of concrete binary vector operations in HIR.
511 //
512 
513 // Adds every component in the two vectors,
514 // viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 + y1, .. , xn + yn ].
515 class HVecAdd FINAL : public HVecBinaryOperation {
516  public:
HVecAdd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)517   HVecAdd(ArenaAllocator* allocator,
518           HInstruction* left,
519           HInstruction* right,
520           DataType::Type packed_type,
521           size_t vector_length,
522           uint32_t dex_pc)
523       : HVecBinaryOperation(kVecAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
524     DCHECK(HasConsistentPackedTypes(left, packed_type));
525     DCHECK(HasConsistentPackedTypes(right, packed_type));
526   }
527 
CanBeMoved()528   bool CanBeMoved() const OVERRIDE { return true; }
529 
530   DECLARE_INSTRUCTION(VecAdd);
531 
532  protected:
533   DEFAULT_COPY_CONSTRUCTOR(VecAdd);
534 };
535 
536 // Performs halving add on every component in the two vectors, viz.
537 // rounded   [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ]
538 // truncated [ x1, .. , xn ] hadd  [ y1, .. , yn ] = [ (x1 + y1)     >> 1, .. , (xn + yn )    >> 1 ]
539 // for either both signed or both unsigned operands x, y (reflected in packed_type).
540 class HVecHalvingAdd FINAL : public HVecBinaryOperation {
541  public:
HVecHalvingAdd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,bool is_rounded,uint32_t dex_pc)542   HVecHalvingAdd(ArenaAllocator* allocator,
543                  HInstruction* left,
544                  HInstruction* right,
545                  DataType::Type packed_type,
546                  size_t vector_length,
547                  bool is_rounded,
548                  uint32_t dex_pc)
549       : HVecBinaryOperation(
550             kVecHalvingAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
551     DCHECK(HasConsistentPackedTypes(left, packed_type));
552     DCHECK(HasConsistentPackedTypes(right, packed_type));
553     SetPackedFlag<kFieldHAddIsRounded>(is_rounded);
554   }
555 
IsRounded()556   bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
557 
CanBeMoved()558   bool CanBeMoved() const OVERRIDE { return true; }
559 
InstructionDataEquals(const HInstruction * other)560   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
561     DCHECK(other->IsVecHalvingAdd());
562     const HVecHalvingAdd* o = other->AsVecHalvingAdd();
563     return HVecOperation::InstructionDataEquals(o) && IsRounded() == o->IsRounded();
564   }
565 
566   DECLARE_INSTRUCTION(VecHalvingAdd);
567 
568  protected:
569   DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd);
570 
571  private:
572   // Additional packed bits.
573   static constexpr size_t kFieldHAddIsRounded = HVecOperation::kNumberOfVectorOpPackedBits;
574   static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1;
575   static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
576 };
577 
578 // Subtracts every component in the two vectors,
579 // viz. [ x1, .. , xn ] - [ y1, .. , yn ] = [ x1 - y1, .. , xn - yn ].
580 class HVecSub FINAL : public HVecBinaryOperation {
581  public:
HVecSub(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)582   HVecSub(ArenaAllocator* allocator,
583           HInstruction* left,
584           HInstruction* right,
585           DataType::Type packed_type,
586           size_t vector_length,
587           uint32_t dex_pc)
588       : HVecBinaryOperation(kVecSub, allocator, left, right, packed_type, vector_length, dex_pc) {
589     DCHECK(HasConsistentPackedTypes(left, packed_type));
590     DCHECK(HasConsistentPackedTypes(right, packed_type));
591   }
592 
CanBeMoved()593   bool CanBeMoved() const OVERRIDE { return true; }
594 
595   DECLARE_INSTRUCTION(VecSub);
596 
597  protected:
598   DEFAULT_COPY_CONSTRUCTOR(VecSub);
599 };
600 
601 // Multiplies every component in the two vectors,
602 // viz. [ x1, .. , xn ] * [ y1, .. , yn ] = [ x1 * y1, .. , xn * yn ].
603 class HVecMul FINAL : public HVecBinaryOperation {
604  public:
HVecMul(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)605   HVecMul(ArenaAllocator* allocator,
606           HInstruction* left,
607           HInstruction* right,
608           DataType::Type packed_type,
609           size_t vector_length,
610           uint32_t dex_pc)
611       : HVecBinaryOperation(kVecMul, allocator, left, right, packed_type, vector_length, dex_pc) {
612     DCHECK(HasConsistentPackedTypes(left, packed_type));
613     DCHECK(HasConsistentPackedTypes(right, packed_type));
614   }
615 
CanBeMoved()616   bool CanBeMoved() const OVERRIDE { return true; }
617 
618   DECLARE_INSTRUCTION(VecMul);
619 
620  protected:
621   DEFAULT_COPY_CONSTRUCTOR(VecMul);
622 };
623 
624 // Divides every component in the two vectors,
625 // viz. [ x1, .. , xn ] / [ y1, .. , yn ] = [ x1 / y1, .. , xn / yn ].
626 class HVecDiv FINAL : public HVecBinaryOperation {
627  public:
HVecDiv(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)628   HVecDiv(ArenaAllocator* allocator,
629           HInstruction* left,
630           HInstruction* right,
631           DataType::Type packed_type,
632           size_t vector_length,
633           uint32_t dex_pc)
634       : HVecBinaryOperation(kVecDiv, allocator, left, right, packed_type, vector_length, dex_pc) {
635     DCHECK(HasConsistentPackedTypes(left, packed_type));
636     DCHECK(HasConsistentPackedTypes(right, packed_type));
637   }
638 
CanBeMoved()639   bool CanBeMoved() const OVERRIDE { return true; }
640 
641   DECLARE_INSTRUCTION(VecDiv);
642 
643  protected:
644   DEFAULT_COPY_CONSTRUCTOR(VecDiv);
645 };
646 
647 // Takes minimum of every component in the two vectors,
648 // viz. MIN( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ min(x1, y1), .. , min(xn, yn) ]
649 // for either both signed or both unsigned operands x, y (reflected in packed_type).
650 class HVecMin FINAL : public HVecBinaryOperation {
651  public:
HVecMin(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)652   HVecMin(ArenaAllocator* allocator,
653           HInstruction* left,
654           HInstruction* right,
655           DataType::Type packed_type,
656           size_t vector_length,
657           uint32_t dex_pc)
658       : HVecBinaryOperation(kVecMin, allocator, left, right, packed_type, vector_length, dex_pc) {
659     DCHECK(HasConsistentPackedTypes(left, packed_type));
660     DCHECK(HasConsistentPackedTypes(right, packed_type));
661   }
662 
CanBeMoved()663   bool CanBeMoved() const OVERRIDE { return true; }
664 
665   DECLARE_INSTRUCTION(VecMin);
666 
667  protected:
668   DEFAULT_COPY_CONSTRUCTOR(VecMin);
669 };
670 
671 // Takes maximum of every component in the two vectors,
672 // viz. MAX( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ max(x1, y1), .. , max(xn, yn) ]
673 // for either both signed or both unsigned operands x, y (reflected in packed_type).
674 class HVecMax FINAL : public HVecBinaryOperation {
675  public:
HVecMax(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)676   HVecMax(ArenaAllocator* allocator,
677           HInstruction* left,
678           HInstruction* right,
679           DataType::Type packed_type,
680           size_t vector_length,
681           uint32_t dex_pc)
682       : HVecBinaryOperation(kVecMax, allocator, left, right, packed_type, vector_length, dex_pc) {
683     DCHECK(HasConsistentPackedTypes(left, packed_type));
684     DCHECK(HasConsistentPackedTypes(right, packed_type));
685   }
686 
CanBeMoved()687   bool CanBeMoved() const OVERRIDE { return true; }
688 
689   DECLARE_INSTRUCTION(VecMax);
690 
691  protected:
692   DEFAULT_COPY_CONSTRUCTOR(VecMax);
693 };
694 
695 // Bitwise-ands every component in the two vectors,
696 // viz. [ x1, .. , xn ] & [ y1, .. , yn ] = [ x1 & y1, .. , xn & yn ].
697 class HVecAnd FINAL : public HVecBinaryOperation {
698  public:
HVecAnd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)699   HVecAnd(ArenaAllocator* allocator,
700           HInstruction* left,
701           HInstruction* right,
702           DataType::Type packed_type,
703           size_t vector_length,
704           uint32_t dex_pc)
705       : HVecBinaryOperation(kVecAnd, allocator, left, right, packed_type, vector_length, dex_pc) {
706     DCHECK(left->IsVecOperation() && right->IsVecOperation());
707   }
708 
CanBeMoved()709   bool CanBeMoved() const OVERRIDE { return true; }
710 
711   DECLARE_INSTRUCTION(VecAnd);
712 
713  protected:
714   DEFAULT_COPY_CONSTRUCTOR(VecAnd);
715 };
716 
717 // Bitwise-and-nots every component in the two vectors,
718 // viz. [ x1, .. , xn ] and-not [ y1, .. , yn ] = [ ~x1 & y1, .. , ~xn & yn ].
719 class HVecAndNot FINAL : public HVecBinaryOperation {
720  public:
HVecAndNot(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)721   HVecAndNot(ArenaAllocator* allocator,
722              HInstruction* left,
723              HInstruction* right,
724              DataType::Type packed_type,
725              size_t vector_length,
726              uint32_t dex_pc)
727          : HVecBinaryOperation(
728                kVecAndNot, allocator, left, right, packed_type, vector_length, dex_pc) {
729     DCHECK(left->IsVecOperation() && right->IsVecOperation());
730   }
731 
CanBeMoved()732   bool CanBeMoved() const OVERRIDE { return true; }
733 
734   DECLARE_INSTRUCTION(VecAndNot);
735 
736  protected:
737   DEFAULT_COPY_CONSTRUCTOR(VecAndNot);
738 };
739 
740 // Bitwise-ors every component in the two vectors,
741 // viz. [ x1, .. , xn ] | [ y1, .. , yn ] = [ x1 | y1, .. , xn | yn ].
742 class HVecOr FINAL : public HVecBinaryOperation {
743  public:
HVecOr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)744   HVecOr(ArenaAllocator* allocator,
745          HInstruction* left,
746          HInstruction* right,
747          DataType::Type packed_type,
748          size_t vector_length,
749          uint32_t dex_pc)
750       : HVecBinaryOperation(kVecOr, allocator, left, right, packed_type, vector_length, dex_pc) {
751     DCHECK(left->IsVecOperation() && right->IsVecOperation());
752   }
753 
CanBeMoved()754   bool CanBeMoved() const OVERRIDE { return true; }
755 
756   DECLARE_INSTRUCTION(VecOr);
757 
758  protected:
759   DEFAULT_COPY_CONSTRUCTOR(VecOr);
760 };
761 
762 // Bitwise-xors every component in the two vectors,
763 // viz. [ x1, .. , xn ] ^ [ y1, .. , yn ] = [ x1 ^ y1, .. , xn ^ yn ].
764 class HVecXor FINAL : public HVecBinaryOperation {
765  public:
HVecXor(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)766   HVecXor(ArenaAllocator* allocator,
767           HInstruction* left,
768           HInstruction* right,
769           DataType::Type packed_type,
770           size_t vector_length,
771           uint32_t dex_pc)
772       : HVecBinaryOperation(kVecXor, allocator, left, right, packed_type, vector_length, dex_pc) {
773     DCHECK(left->IsVecOperation() && right->IsVecOperation());
774   }
775 
CanBeMoved()776   bool CanBeMoved() const OVERRIDE { return true; }
777 
778   DECLARE_INSTRUCTION(VecXor);
779 
780  protected:
781   DEFAULT_COPY_CONSTRUCTOR(VecXor);
782 };
783 
784 // Logically shifts every component in the vector left by the given distance,
785 // viz. [ x1, .. , xn ] << d = [ x1 << d, .. , xn << d ].
786 class HVecShl FINAL : public HVecBinaryOperation {
787  public:
HVecShl(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)788   HVecShl(ArenaAllocator* allocator,
789           HInstruction* left,
790           HInstruction* right,
791           DataType::Type packed_type,
792           size_t vector_length,
793           uint32_t dex_pc)
794       : HVecBinaryOperation(kVecShl, allocator, left, right, packed_type, vector_length, dex_pc) {
795     DCHECK(HasConsistentPackedTypes(left, packed_type));
796   }
797 
CanBeMoved()798   bool CanBeMoved() const OVERRIDE { return true; }
799 
800   DECLARE_INSTRUCTION(VecShl);
801 
802  protected:
803   DEFAULT_COPY_CONSTRUCTOR(VecShl);
804 };
805 
806 // Arithmetically shifts every component in the vector right by the given distance,
807 // viz. [ x1, .. , xn ] >> d = [ x1 >> d, .. , xn >> d ].
808 class HVecShr FINAL : public HVecBinaryOperation {
809  public:
HVecShr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)810   HVecShr(ArenaAllocator* allocator,
811           HInstruction* left,
812           HInstruction* right,
813           DataType::Type packed_type,
814           size_t vector_length,
815           uint32_t dex_pc)
816       : HVecBinaryOperation(kVecShr, allocator, left, right, packed_type, vector_length, dex_pc) {
817     DCHECK(HasConsistentPackedTypes(left, packed_type));
818   }
819 
CanBeMoved()820   bool CanBeMoved() const OVERRIDE { return true; }
821 
822   DECLARE_INSTRUCTION(VecShr);
823 
824  protected:
825   DEFAULT_COPY_CONSTRUCTOR(VecShr);
826 };
827 
828 // Logically shifts every component in the vector right by the given distance,
829 // viz. [ x1, .. , xn ] >>> d = [ x1 >>> d, .. , xn >>> d ].
830 class HVecUShr FINAL : public HVecBinaryOperation {
831  public:
HVecUShr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)832   HVecUShr(ArenaAllocator* allocator,
833            HInstruction* left,
834            HInstruction* right,
835            DataType::Type packed_type,
836            size_t vector_length,
837            uint32_t dex_pc)
838       : HVecBinaryOperation(kVecUShr, allocator, left, right, packed_type, vector_length, dex_pc) {
839     DCHECK(HasConsistentPackedTypes(left, packed_type));
840   }
841 
CanBeMoved()842   bool CanBeMoved() const OVERRIDE { return true; }
843 
844   DECLARE_INSTRUCTION(VecUShr);
845 
846  protected:
847   DEFAULT_COPY_CONSTRUCTOR(VecUShr);
848 };
849 
850 //
851 // Definitions of concrete miscellaneous vector operations in HIR.
852 //
853 
854 // Assigns the given scalar elements to a vector,
855 // viz. set( array(x1, .. , xn) ) = [ x1, .. ,            xn ] if n == m,
856 //      set( array(x1, .. , xm) ) = [ x1, .. , xm, 0, .. , 0 ] if m <  n.
857 class HVecSetScalars FINAL : public HVecOperation {
858  public:
HVecSetScalars(ArenaAllocator * allocator,HInstruction * scalars[],DataType::Type packed_type,size_t vector_length,size_t number_of_scalars,uint32_t dex_pc)859   HVecSetScalars(ArenaAllocator* allocator,
860                  HInstruction* scalars[],
861                  DataType::Type packed_type,
862                  size_t vector_length,
863                  size_t number_of_scalars,
864                  uint32_t dex_pc)
865       : HVecOperation(kVecSetScalars,
866                       allocator,
867                       packed_type,
868                       SideEffects::None(),
869                       number_of_scalars,
870                       vector_length,
871                       dex_pc) {
872     for (size_t i = 0; i < number_of_scalars; i++) {
873       DCHECK(!ReturnsSIMDValue(scalars[i]));
874       SetRawInputAt(0, scalars[i]);
875     }
876   }
877 
878   // Setting scalars needs to stay in place, since SIMD registers are not
879   // kept alive across vector loop boundaries (yet).
CanBeMoved()880   bool CanBeMoved() const OVERRIDE { return false; }
881 
882   DECLARE_INSTRUCTION(VecSetScalars);
883 
884  protected:
885   DEFAULT_COPY_CONSTRUCTOR(VecSetScalars);
886 };
887 
888 // Multiplies every component in the two vectors, adds the result vector to the accumulator vector,
889 // viz. [ a1, .. , an ] + [ x1, .. , xn ] * [ y1, .. , yn ] = [ a1 + x1 * y1, .. , an + xn * yn ].
890 class HVecMultiplyAccumulate FINAL : public HVecOperation {
891  public:
HVecMultiplyAccumulate(ArenaAllocator * allocator,InstructionKind op,HInstruction * accumulator,HInstruction * mul_left,HInstruction * mul_right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)892   HVecMultiplyAccumulate(ArenaAllocator* allocator,
893                          InstructionKind op,
894                          HInstruction* accumulator,
895                          HInstruction* mul_left,
896                          HInstruction* mul_right,
897                          DataType::Type packed_type,
898                          size_t vector_length,
899                          uint32_t dex_pc)
900       : HVecOperation(kVecMultiplyAccumulate,
901                       allocator,
902                       packed_type,
903                       SideEffects::None(),
904                       /* number_of_inputs */ 3,
905                       vector_length,
906                       dex_pc),
907         op_kind_(op) {
908     DCHECK(op == InstructionKind::kAdd || op == InstructionKind::kSub);
909     DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
910     DCHECK(HasConsistentPackedTypes(mul_left, packed_type));
911     DCHECK(HasConsistentPackedTypes(mul_right, packed_type));
912     SetRawInputAt(0, accumulator);
913     SetRawInputAt(1, mul_left);
914     SetRawInputAt(2, mul_right);
915   }
916 
CanBeMoved()917   bool CanBeMoved() const OVERRIDE { return true; }
918 
InstructionDataEquals(const HInstruction * other)919   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
920     DCHECK(other->IsVecMultiplyAccumulate());
921     const HVecMultiplyAccumulate* o = other->AsVecMultiplyAccumulate();
922     return HVecOperation::InstructionDataEquals(o) && GetOpKind() == o->GetOpKind();
923   }
924 
GetOpKind()925   InstructionKind GetOpKind() const { return op_kind_; }
926 
927   DECLARE_INSTRUCTION(VecMultiplyAccumulate);
928 
929  protected:
930   DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate);
931 
932  private:
933   // Indicates if this is a MADD or MSUB.
934   const InstructionKind op_kind_;
935 };
936 
937 // Takes the absolute difference of two vectors, and adds the results to
938 // same-precision or wider-precision components in the accumulator,
939 // viz. SAD([ a1, .. , am ], [ x1, .. , xn ], [ y1, .. , yn ]) =
940 //          [ a1 + sum abs(xi-yi), .. , am + sum abs(xj-yj) ],
941 //      for m <= n, non-overlapping sums, and signed operands x, y.
942 class HVecSADAccumulate FINAL : public HVecOperation {
943  public:
HVecSADAccumulate(ArenaAllocator * allocator,HInstruction * accumulator,HInstruction * sad_left,HInstruction * sad_right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)944   HVecSADAccumulate(ArenaAllocator* allocator,
945                     HInstruction* accumulator,
946                     HInstruction* sad_left,
947                     HInstruction* sad_right,
948                     DataType::Type packed_type,
949                     size_t vector_length,
950                     uint32_t dex_pc)
951       : HVecOperation(kVecSADAccumulate,
952                       allocator,
953                       packed_type,
954                       SideEffects::None(),
955                       /* number_of_inputs */ 3,
956                       vector_length,
957                       dex_pc) {
958     DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
959     DCHECK(sad_left->IsVecOperation());
960     DCHECK(sad_right->IsVecOperation());
961     DCHECK_EQ(ToSignedType(sad_left->AsVecOperation()->GetPackedType()),
962               ToSignedType(sad_right->AsVecOperation()->GetPackedType()));
963     SetRawInputAt(0, accumulator);
964     SetRawInputAt(1, sad_left);
965     SetRawInputAt(2, sad_right);
966   }
967 
968   DECLARE_INSTRUCTION(VecSADAccumulate);
969 
970  protected:
971   DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate);
972 };
973 
974 // Loads a vector from memory, viz. load(mem, 1)
975 // yield the vector [ mem(1), .. , mem(n) ].
976 class HVecLoad FINAL : public HVecMemoryOperation {
977  public:
HVecLoad(ArenaAllocator * allocator,HInstruction * base,HInstruction * index,DataType::Type packed_type,SideEffects side_effects,size_t vector_length,bool is_string_char_at,uint32_t dex_pc)978   HVecLoad(ArenaAllocator* allocator,
979            HInstruction* base,
980            HInstruction* index,
981            DataType::Type packed_type,
982            SideEffects side_effects,
983            size_t vector_length,
984            bool is_string_char_at,
985            uint32_t dex_pc)
986       : HVecMemoryOperation(kVecLoad,
987                             allocator,
988                             packed_type,
989                             side_effects,
990                             /* number_of_inputs */ 2,
991                             vector_length,
992                             dex_pc) {
993     SetRawInputAt(0, base);
994     SetRawInputAt(1, index);
995     SetPackedFlag<kFieldIsStringCharAt>(is_string_char_at);
996   }
997 
IsStringCharAt()998   bool IsStringCharAt() const { return GetPackedFlag<kFieldIsStringCharAt>(); }
999 
CanBeMoved()1000   bool CanBeMoved() const OVERRIDE { return true; }
1001 
InstructionDataEquals(const HInstruction * other)1002   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
1003     DCHECK(other->IsVecLoad());
1004     const HVecLoad* o = other->AsVecLoad();
1005     return HVecMemoryOperation::InstructionDataEquals(o) && IsStringCharAt() == o->IsStringCharAt();
1006   }
1007 
1008   DECLARE_INSTRUCTION(VecLoad);
1009 
1010  protected:
1011   DEFAULT_COPY_CONSTRUCTOR(VecLoad);
1012 
1013  private:
1014   // Additional packed bits.
1015   static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
1016   static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1;
1017   static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
1018 };
1019 
1020 // Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] )
1021 // sets mem(1) = x1, .. , mem(n) = xn.
1022 class HVecStore FINAL : public HVecMemoryOperation {
1023  public:
HVecStore(ArenaAllocator * allocator,HInstruction * base,HInstruction * index,HInstruction * value,DataType::Type packed_type,SideEffects side_effects,size_t vector_length,uint32_t dex_pc)1024   HVecStore(ArenaAllocator* allocator,
1025             HInstruction* base,
1026             HInstruction* index,
1027             HInstruction* value,
1028             DataType::Type packed_type,
1029             SideEffects side_effects,
1030             size_t vector_length,
1031             uint32_t dex_pc)
1032       : HVecMemoryOperation(kVecStore,
1033                             allocator,
1034                             packed_type,
1035                             side_effects,
1036                             /* number_of_inputs */ 3,
1037                             vector_length,
1038                             dex_pc) {
1039     DCHECK(HasConsistentPackedTypes(value, packed_type));
1040     SetRawInputAt(0, base);
1041     SetRawInputAt(1, index);
1042     SetRawInputAt(2, value);
1043   }
1044 
1045   // A store needs to stay in place.
CanBeMoved()1046   bool CanBeMoved() const OVERRIDE { return false; }
1047 
1048   DECLARE_INSTRUCTION(VecStore);
1049 
1050  protected:
1051   DEFAULT_COPY_CONSTRUCTOR(VecStore)
1052 };
1053 
1054 }  // namespace art
1055 
1056 #endif  // ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
1057