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 HIDDEN {
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 kSIMDType,
83 side_effects,
84 dex_pc,
85 allocator,
86 number_of_inputs,
87 kArenaAllocVectorNode),
88 vector_length_(vector_length) {
89 SetPackedField<PackedTypeField>(packed_type);
90 // By default vector operations are not predicated.
91 SetPackedField<PredicationKindField>(PredicationKind::kNotPredicated);
92 DCHECK_LT(1u, vector_length);
93 }
94
95 // Predicated instructions execute a corresponding operation only on vector elements which are
96 // active (governing predicate is true for that element); the following modes determine what
97 // is happening with inactive elements.
98 //
99 // See HVecPredSetOperation.
100 enum class PredicationKind {
101 kNotPredicated, // Instruction doesn't take any predicate as an input.
102 kZeroingForm, // Inactive elements are reset to zero.
103 kMergingForm, // Inactive elements keep their value.
104 kLast = kMergingForm,
105 };
106
GetPredicationKind()107 PredicationKind GetPredicationKind() const { return GetPackedField<PredicationKindField>(); }
108
109 // Returns whether the vector operation must be predicated in predicated SIMD mode
110 // (see CodeGenerator::SupportsPredicatedSIMD). The method reflects semantics of
111 // the instruction class rather than the state of a particular instruction instance.
112 //
113 // This property is introduced for robustness purpose - to maintain and check the invariant:
114 // all instructions of the same vector operation class must be either all predicated or all
115 // not predicated (depending on the predicated SIMD support) in a correct graph.
MustBePredicatedInPredicatedSIMDMode()116 virtual bool MustBePredicatedInPredicatedSIMDMode() {
117 return true;
118 }
119
IsPredicated()120 bool IsPredicated() const {
121 return GetPredicationKind() != PredicationKind::kNotPredicated;
122 }
123
124 // See HVecPredSetOperation.
SetGoverningPredicate(HInstruction * input,PredicationKind pred_kind)125 void SetGoverningPredicate(HInstruction* input, PredicationKind pred_kind) {
126 DCHECK(!IsPredicated());
127 DCHECK(input->IsVecPredSetOperation());
128 AddInput(input);
129 SetPackedField<PredicationKindField>(pred_kind);
130 DCHECK(IsPredicated());
131 }
132
SetMergingGoverningPredicate(HInstruction * input)133 void SetMergingGoverningPredicate(HInstruction* input) {
134 SetGoverningPredicate(input, PredicationKind::kMergingForm);
135 }
SetZeroingGoverningPredicate(HInstruction * input)136 void SetZeroingGoverningPredicate(HInstruction* input) {
137 SetGoverningPredicate(input, PredicationKind::kZeroingForm);
138 }
139
140 // See HVecPredSetOperation.
GetGoverningPredicate()141 HVecPredSetOperation* GetGoverningPredicate() const {
142 DCHECK(IsPredicated());
143 HInstruction* pred_input = InputAt(InputCount() - 1);
144 DCHECK(pred_input->IsVecPredSetOperation());
145 return pred_input->AsVecPredSetOperation();
146 }
147
148 // Returns whether two vector operations are predicated by the same vector predicate
149 // with the same predication type.
HaveSamePredicate(HVecOperation * instr0,HVecOperation * instr1)150 static bool HaveSamePredicate(HVecOperation* instr0, HVecOperation* instr1) {
151 HVecPredSetOperation* instr0_predicate = instr0->GetGoverningPredicate();
152 HVecOperation::PredicationKind instr0_predicate_kind = instr0->GetPredicationKind();
153 return instr1->GetGoverningPredicate() == instr0_predicate &&
154 instr1->GetPredicationKind() == instr0_predicate_kind;
155 }
156
157 // Returns the number of elements packed in a vector.
GetVectorLength()158 size_t GetVectorLength() const {
159 return vector_length_;
160 }
161
162 // Returns the number of bytes in a full vector.
GetVectorNumberOfBytes()163 size_t GetVectorNumberOfBytes() const {
164 return vector_length_ * DataType::Size(GetPackedType());
165 }
166
167 // Returns the true component type packed in a vector.
GetPackedType()168 DataType::Type GetPackedType() const {
169 return GetPackedField<PackedTypeField>();
170 }
171
172 // Assumes vector nodes cannot be moved by default. Each concrete implementation
173 // that can be moved should override this method and return true.
174 //
175 // Note: similar approach is used for instruction scheduling (if it is turned on for the target):
176 // by default HScheduler::IsSchedulable returns false for a particular HVecOperation.
177 // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see
178 // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also
179 // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction.
180 //
181 // Note: For newly introduced vector instructions HScheduler${ARCH}::IsSchedulingBarrier must be
182 // altered to return true if the instruction might reside outside the SIMD loop body since SIMD
183 // registers are not kept alive across vector loop boundaries (yet).
CanBeMoved()184 bool CanBeMoved() const override { return false; }
185
186 // Tests if all data of a vector node (vector length and packed type) is equal.
187 // Each concrete implementation that adds more fields should test equality of
188 // those fields in its own method *and* call all super methods.
InstructionDataEquals(const HInstruction * other)189 bool InstructionDataEquals(const HInstruction* other) const override {
190 DCHECK(other->IsVecOperation());
191 const HVecOperation* o = other->AsVecOperation();
192 return GetVectorLength() == o->GetVectorLength() && GetPackedType() == o->GetPackedType();
193 }
194
195 // Maps an integral type to the same-size signed type and leaves other types alone.
ToSignedType(DataType::Type type)196 static DataType::Type ToSignedType(DataType::Type type) {
197 switch (type) {
198 case DataType::Type::kBool: // 1-byte storage unit
199 case DataType::Type::kUint8:
200 return DataType::Type::kInt8;
201 case DataType::Type::kUint16:
202 return DataType::Type::kInt16;
203 default:
204 DCHECK(type != DataType::Type::kVoid && type != DataType::Type::kReference) << type;
205 return type;
206 }
207 }
208
209 // Maps an integral type to the same-size unsigned type and leaves other types alone.
ToUnsignedType(DataType::Type type)210 static DataType::Type ToUnsignedType(DataType::Type type) {
211 switch (type) {
212 case DataType::Type::kBool: // 1-byte storage unit
213 case DataType::Type::kInt8:
214 return DataType::Type::kUint8;
215 case DataType::Type::kInt16:
216 return DataType::Type::kUint16;
217 default:
218 DCHECK(type != DataType::Type::kVoid && type != DataType::Type::kReference) << type;
219 return type;
220 }
221 }
222
223 // Maps an integral type to the same-size (un)signed type. Leaves other types alone.
ToProperType(DataType::Type type,bool is_unsigned)224 static DataType::Type ToProperType(DataType::Type type, bool is_unsigned) {
225 return is_unsigned ? ToUnsignedType(type) : ToSignedType(type);
226 }
227
228 // Helper method to determine if an instruction returns a SIMD value.
229 // TODO: This method is needed until we introduce SIMD as proper type.
ReturnsSIMDValue(HInstruction * instruction)230 static bool ReturnsSIMDValue(HInstruction* instruction) {
231 if (instruction->IsVecOperation()) {
232 return !instruction->IsVecExtractScalar(); // only scalar returning vec op
233 } else if (instruction->IsPhi()) {
234 // Vectorizer only uses Phis in reductions, so checking for a 2-way phi
235 // with a direct vector operand as second argument suffices.
236 return
237 instruction->GetType() == kSIMDType &&
238 instruction->InputCount() == 2 &&
239 instruction->InputAt(1)->IsVecOperation();
240 }
241 return false;
242 }
243
244 DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
245
246 protected:
247 // Additional packed bits.
248 static constexpr size_t kPredicationKind = HInstruction::kNumberOfGenericPackedBits;
249 static constexpr size_t kPredicationKindSize =
250 MinimumBitsToStore(static_cast<size_t>(PredicationKind::kLast));
251 static constexpr size_t kFieldPackedType = kPredicationKind + kPredicationKindSize;
252 static constexpr size_t kFieldPackedTypeSize =
253 MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
254 static constexpr size_t kNumberOfVectorOpPackedBits = kFieldPackedType + kFieldPackedTypeSize;
255 static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
256 using PackedTypeField = BitField<DataType::Type, kFieldPackedType, kFieldPackedTypeSize>;
257 using PredicationKindField = BitField<PredicationKind, kPredicationKind, kPredicationKindSize>;
258
259 DEFAULT_COPY_CONSTRUCTOR(VecOperation);
260
261 private:
262 const size_t vector_length_;
263 };
264
265 // Abstraction of a unary vector operation.
266 class HVecUnaryOperation : public HVecOperation {
267 public:
HVecUnaryOperation(InstructionKind kind,ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)268 HVecUnaryOperation(InstructionKind kind,
269 ArenaAllocator* allocator,
270 HInstruction* input,
271 DataType::Type packed_type,
272 size_t vector_length,
273 uint32_t dex_pc)
274 : HVecOperation(kind,
275 allocator,
276 packed_type,
277 SideEffects::None(),
278 /* number_of_inputs= */ 1,
279 vector_length,
280 dex_pc) {
281 SetRawInputAt(0, input);
282 }
283
GetInput()284 HInstruction* GetInput() const { return InputAt(0); }
285
286 DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation);
287
288 protected:
289 DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation);
290 };
291
292 // Abstraction of a binary vector operation.
293 class HVecBinaryOperation : public HVecOperation {
294 public:
HVecBinaryOperation(InstructionKind kind,ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)295 HVecBinaryOperation(InstructionKind kind,
296 ArenaAllocator* allocator,
297 HInstruction* left,
298 HInstruction* right,
299 DataType::Type packed_type,
300 size_t vector_length,
301 uint32_t dex_pc)
302 : HVecOperation(kind,
303 allocator,
304 packed_type,
305 SideEffects::None(),
306 /* number_of_inputs= */ 2,
307 vector_length,
308 dex_pc) {
309 SetRawInputAt(0, left);
310 SetRawInputAt(1, right);
311 }
312
GetLeft()313 HInstruction* GetLeft() const { return InputAt(0); }
GetRight()314 HInstruction* GetRight() const { return InputAt(1); }
315
316 DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation);
317
318 protected:
319 DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation);
320 };
321
322 // Abstraction of a vector operation that references memory, with an alignment.
323 // The Android runtime guarantees elements have at least natural alignment.
324 class HVecMemoryOperation : public HVecOperation {
325 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)326 HVecMemoryOperation(InstructionKind kind,
327 ArenaAllocator* allocator,
328 DataType::Type packed_type,
329 SideEffects side_effects,
330 size_t number_of_inputs,
331 size_t vector_length,
332 uint32_t dex_pc)
333 : HVecOperation(kind,
334 allocator,
335 packed_type,
336 side_effects,
337 number_of_inputs,
338 vector_length,
339 dex_pc),
340 alignment_(DataType::Size(packed_type), 0) {
341 DCHECK_GE(number_of_inputs, 2u);
342 }
343
SetAlignment(Alignment alignment)344 void SetAlignment(Alignment alignment) { alignment_ = alignment; }
345
GetAlignment()346 Alignment GetAlignment() const { return alignment_; }
347
GetArray()348 HInstruction* GetArray() const { return InputAt(0); }
GetIndex()349 HInstruction* GetIndex() const { return InputAt(1); }
350
InstructionDataEquals(const HInstruction * other)351 bool InstructionDataEquals(const HInstruction* other) const override {
352 DCHECK(other->IsVecMemoryOperation());
353 const HVecMemoryOperation* o = other->AsVecMemoryOperation();
354 return HVecOperation::InstructionDataEquals(o) && GetAlignment() == o->GetAlignment();
355 }
356
357 DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
358
359 protected:
360 DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation);
361
362 private:
363 Alignment alignment_;
364 };
365
366 // Packed type consistency checker ("same vector length" integral types may mix freely).
367 // Tests relaxed type consistency in which packed same-size integral types can co-exist,
368 // but other type mixes are an error.
HasConsistentPackedTypes(HInstruction * input,DataType::Type type)369 inline static bool HasConsistentPackedTypes(HInstruction* input, DataType::Type type) {
370 if (input->IsPhi()) {
371 return input->GetType() == HVecOperation::kSIMDType; // carries SIMD
372 }
373 DCHECK(input->IsVecOperation());
374 DataType::Type input_type = input->AsVecOperation()->GetPackedType();
375 DCHECK_EQ(HVecOperation::ToUnsignedType(input_type) == HVecOperation::ToUnsignedType(type),
376 HVecOperation::ToSignedType(input_type) == HVecOperation::ToSignedType(type));
377 return HVecOperation::ToSignedType(input_type) == HVecOperation::ToSignedType(type);
378 }
379
380 //
381 // Definitions of concrete unary vector operations in HIR.
382 //
383
384 // Replicates the given scalar into a vector,
385 // viz. replicate(x) = [ x, .. , x ].
386 class HVecReplicateScalar final : public HVecUnaryOperation {
387 public:
HVecReplicateScalar(ArenaAllocator * allocator,HInstruction * scalar,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)388 HVecReplicateScalar(ArenaAllocator* allocator,
389 HInstruction* scalar,
390 DataType::Type packed_type,
391 size_t vector_length,
392 uint32_t dex_pc)
393 : HVecUnaryOperation(
394 kVecReplicateScalar, allocator, scalar, packed_type, vector_length, dex_pc) {
395 DCHECK(!ReturnsSIMDValue(scalar));
396 }
397
398 // A replicate needs to stay in place, since SIMD registers are not
399 // kept alive across vector loop boundaries (yet).
CanBeMoved()400 bool CanBeMoved() const override { return false; }
401
402 DECLARE_INSTRUCTION(VecReplicateScalar);
403
404 protected:
405 DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar);
406 };
407
408 // Extracts a particular scalar from the given vector,
409 // viz. extract[ x1, .. , xn ] = x_i.
410 //
411 // TODO: for now only i == 1 case supported.
412 class HVecExtractScalar final : public HVecUnaryOperation {
413 public:
HVecExtractScalar(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,size_t index,uint32_t dex_pc)414 HVecExtractScalar(ArenaAllocator* allocator,
415 HInstruction* input,
416 DataType::Type packed_type,
417 size_t vector_length,
418 size_t index,
419 uint32_t dex_pc)
420 : HVecUnaryOperation(
421 kVecExtractScalar, allocator, input, packed_type, vector_length, dex_pc) {
422 DCHECK(HasConsistentPackedTypes(input, packed_type));
423 DCHECK_LT(index, vector_length);
424 DCHECK_EQ(index, 0u);
425 // Yields a single component in the vector.
426 // Overrides the kSIMDType set by the VecOperation constructor.
427 SetPackedField<TypeField>(packed_type);
428 }
429
430 // An extract needs to stay in place, since SIMD registers are not
431 // kept alive across vector loop boundaries (yet).
CanBeMoved()432 bool CanBeMoved() const override { return false; }
433
434 DECLARE_INSTRUCTION(VecExtractScalar);
435
436 protected:
437 DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar);
438 };
439
440 // Reduces the given vector into the first element as sum/min/max,
441 // viz. sum-reduce[ x1, .. , xn ] = [ y, ---- ], where y = sum xi
442 // and the "-" denotes "don't care" (implementation dependent).
443 class HVecReduce final : public HVecUnaryOperation {
444 public:
445 enum ReductionKind {
446 kSum = 1,
447 kMin = 2,
448 kMax = 3
449 };
450
HVecReduce(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,ReductionKind reduction_kind,uint32_t dex_pc)451 HVecReduce(ArenaAllocator* allocator,
452 HInstruction* input,
453 DataType::Type packed_type,
454 size_t vector_length,
455 ReductionKind reduction_kind,
456 uint32_t dex_pc)
457 : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
458 reduction_kind_(reduction_kind) {
459 DCHECK(HasConsistentPackedTypes(input, packed_type));
460 }
461
GetReductionKind()462 ReductionKind GetReductionKind() const { return reduction_kind_; }
463
CanBeMoved()464 bool CanBeMoved() const override { return true; }
465
InstructionDataEquals(const HInstruction * other)466 bool InstructionDataEquals(const HInstruction* other) const override {
467 DCHECK(other->IsVecReduce());
468 const HVecReduce* o = other->AsVecReduce();
469 return HVecOperation::InstructionDataEquals(o) && GetReductionKind() == o->GetReductionKind();
470 }
471
472 DECLARE_INSTRUCTION(VecReduce);
473
474 protected:
475 DEFAULT_COPY_CONSTRUCTOR(VecReduce);
476
477 private:
478 const ReductionKind reduction_kind_;
479 };
480
481 // Converts every component in the vector,
482 // viz. cnv[ x1, .. , xn ] = [ cnv(x1), .. , cnv(xn) ].
483 class HVecCnv final : public HVecUnaryOperation {
484 public:
HVecCnv(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)485 HVecCnv(ArenaAllocator* allocator,
486 HInstruction* input,
487 DataType::Type packed_type,
488 size_t vector_length,
489 uint32_t dex_pc)
490 : HVecUnaryOperation(kVecCnv, allocator, input, packed_type, vector_length, dex_pc) {
491 DCHECK(input->IsVecOperation());
492 DCHECK_NE(GetInputType(), GetResultType()); // actual convert
493 }
494
GetInputType()495 DataType::Type GetInputType() const { return InputAt(0)->AsVecOperation()->GetPackedType(); }
GetResultType()496 DataType::Type GetResultType() const { return GetPackedType(); }
497
CanBeMoved()498 bool CanBeMoved() const override { return true; }
499
500 DECLARE_INSTRUCTION(VecCnv);
501
502 protected:
503 DEFAULT_COPY_CONSTRUCTOR(VecCnv);
504 };
505
506 // Negates every component in the vector,
507 // viz. neg[ x1, .. , xn ] = [ -x1, .. , -xn ].
508 class HVecNeg final : public HVecUnaryOperation {
509 public:
HVecNeg(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)510 HVecNeg(ArenaAllocator* allocator,
511 HInstruction* input,
512 DataType::Type packed_type,
513 size_t vector_length,
514 uint32_t dex_pc)
515 : HVecUnaryOperation(kVecNeg, allocator, input, packed_type, vector_length, dex_pc) {
516 DCHECK(HasConsistentPackedTypes(input, packed_type));
517 }
518
CanBeMoved()519 bool CanBeMoved() const override { return true; }
520
521 DECLARE_INSTRUCTION(VecNeg);
522
523 protected:
524 DEFAULT_COPY_CONSTRUCTOR(VecNeg);
525 };
526
527 // Takes absolute value of every component in the vector,
528 // viz. abs[ x1, .. , xn ] = [ |x1|, .. , |xn| ]
529 // for signed operand x.
530 class HVecAbs final : public HVecUnaryOperation {
531 public:
HVecAbs(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)532 HVecAbs(ArenaAllocator* allocator,
533 HInstruction* input,
534 DataType::Type packed_type,
535 size_t vector_length,
536 uint32_t dex_pc)
537 : HVecUnaryOperation(kVecAbs, allocator, input, packed_type, vector_length, dex_pc) {
538 DCHECK(HasConsistentPackedTypes(input, packed_type));
539 }
540
CanBeMoved()541 bool CanBeMoved() const override { return true; }
542
543 DECLARE_INSTRUCTION(VecAbs);
544
545 protected:
546 DEFAULT_COPY_CONSTRUCTOR(VecAbs);
547 };
548
549 // Bitwise- or boolean-nots every component in the vector,
550 // viz. not[ x1, .. , xn ] = [ ~x1, .. , ~xn ], or
551 // not[ x1, .. , xn ] = [ !x1, .. , !xn ] for boolean.
552 class HVecNot final : public HVecUnaryOperation {
553 public:
HVecNot(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)554 HVecNot(ArenaAllocator* allocator,
555 HInstruction* input,
556 DataType::Type packed_type,
557 size_t vector_length,
558 uint32_t dex_pc)
559 : HVecUnaryOperation(kVecNot, allocator, input, packed_type, vector_length, dex_pc) {
560 DCHECK(input->IsVecOperation());
561 }
562
CanBeMoved()563 bool CanBeMoved() const override { return true; }
564
565 DECLARE_INSTRUCTION(VecNot);
566
567 protected:
568 DEFAULT_COPY_CONSTRUCTOR(VecNot);
569 };
570
571 //
572 // Definitions of concrete binary vector operations in HIR.
573 //
574
575 // Adds every component in the two vectors,
576 // viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 + y1, .. , xn + yn ].
577 class HVecAdd final : public HVecBinaryOperation {
578 public:
HVecAdd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)579 HVecAdd(ArenaAllocator* allocator,
580 HInstruction* left,
581 HInstruction* right,
582 DataType::Type packed_type,
583 size_t vector_length,
584 uint32_t dex_pc)
585 : HVecBinaryOperation(kVecAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
586 DCHECK(HasConsistentPackedTypes(left, packed_type));
587 DCHECK(HasConsistentPackedTypes(right, packed_type));
588 }
589
CanBeMoved()590 bool CanBeMoved() const override { return true; }
591
592 DECLARE_INSTRUCTION(VecAdd);
593
594 protected:
595 DEFAULT_COPY_CONSTRUCTOR(VecAdd);
596 };
597
598 // Adds every component in the two vectors using saturation arithmetic,
599 // viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 +_sat y1, .. , xn +_sat yn ]
600 // for either both signed or both unsigned operands x, y (reflected in packed_type).
601 class HVecSaturationAdd final : public HVecBinaryOperation {
602 public:
HVecSaturationAdd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)603 HVecSaturationAdd(ArenaAllocator* allocator,
604 HInstruction* left,
605 HInstruction* right,
606 DataType::Type packed_type,
607 size_t vector_length,
608 uint32_t dex_pc)
609 : HVecBinaryOperation(
610 kVecSaturationAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
611 DCHECK(HasConsistentPackedTypes(left, packed_type));
612 DCHECK(HasConsistentPackedTypes(right, packed_type));
613 }
614
CanBeMoved()615 bool CanBeMoved() const override { return true; }
616
617 DECLARE_INSTRUCTION(VecSaturationAdd);
618
619 protected:
620 DEFAULT_COPY_CONSTRUCTOR(VecSaturationAdd);
621 };
622
623 // Performs halving add on every component in the two vectors, viz.
624 // rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ]
625 // truncated [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ]
626 // for either both signed or both unsigned operands x, y (reflected in packed_type).
627 class HVecHalvingAdd final : public HVecBinaryOperation {
628 public:
HVecHalvingAdd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,bool is_rounded,uint32_t dex_pc)629 HVecHalvingAdd(ArenaAllocator* allocator,
630 HInstruction* left,
631 HInstruction* right,
632 DataType::Type packed_type,
633 size_t vector_length,
634 bool is_rounded,
635 uint32_t dex_pc)
636 : HVecBinaryOperation(
637 kVecHalvingAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
638 DCHECK(HasConsistentPackedTypes(left, packed_type));
639 DCHECK(HasConsistentPackedTypes(right, packed_type));
640 SetPackedFlag<kFieldHAddIsRounded>(is_rounded);
641 }
642
IsRounded()643 bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
644
CanBeMoved()645 bool CanBeMoved() const override { return true; }
646
InstructionDataEquals(const HInstruction * other)647 bool InstructionDataEquals(const HInstruction* other) const override {
648 DCHECK(other->IsVecHalvingAdd());
649 const HVecHalvingAdd* o = other->AsVecHalvingAdd();
650 return HVecOperation::InstructionDataEquals(o) && IsRounded() == o->IsRounded();
651 }
652
653 DECLARE_INSTRUCTION(VecHalvingAdd);
654
655 protected:
656 DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd);
657
658 private:
659 // Additional packed bits.
660 static constexpr size_t kFieldHAddIsRounded = HVecOperation::kNumberOfVectorOpPackedBits;
661 static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1;
662 static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
663 };
664
665 // Subtracts every component in the two vectors,
666 // viz. [ x1, .. , xn ] - [ y1, .. , yn ] = [ x1 - y1, .. , xn - yn ].
667 class HVecSub final : public HVecBinaryOperation {
668 public:
HVecSub(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)669 HVecSub(ArenaAllocator* allocator,
670 HInstruction* left,
671 HInstruction* right,
672 DataType::Type packed_type,
673 size_t vector_length,
674 uint32_t dex_pc)
675 : HVecBinaryOperation(kVecSub, allocator, left, right, packed_type, vector_length, dex_pc) {
676 DCHECK(HasConsistentPackedTypes(left, packed_type));
677 DCHECK(HasConsistentPackedTypes(right, packed_type));
678 }
679
CanBeMoved()680 bool CanBeMoved() const override { return true; }
681
682 DECLARE_INSTRUCTION(VecSub);
683
684 protected:
685 DEFAULT_COPY_CONSTRUCTOR(VecSub);
686 };
687
688 // Subtracts every component in the two vectors using saturation arithmetic,
689 // viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 -_sat y1, .. , xn -_sat yn ]
690 // for either both signed or both unsigned operands x, y (reflected in packed_type).
691 class HVecSaturationSub final : public HVecBinaryOperation {
692 public:
HVecSaturationSub(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)693 HVecSaturationSub(ArenaAllocator* allocator,
694 HInstruction* left,
695 HInstruction* right,
696 DataType::Type packed_type,
697 size_t vector_length,
698 uint32_t dex_pc)
699 : HVecBinaryOperation(
700 kVecSaturationSub, allocator, left, right, packed_type, vector_length, dex_pc) {
701 DCHECK(HasConsistentPackedTypes(left, packed_type));
702 DCHECK(HasConsistentPackedTypes(right, packed_type));
703 }
704
CanBeMoved()705 bool CanBeMoved() const override { return true; }
706
707 DECLARE_INSTRUCTION(VecSaturationSub);
708
709 protected:
710 DEFAULT_COPY_CONSTRUCTOR(VecSaturationSub);
711 };
712
713 // Multiplies every component in the two vectors,
714 // viz. [ x1, .. , xn ] * [ y1, .. , yn ] = [ x1 * y1, .. , xn * yn ].
715 class HVecMul final : public HVecBinaryOperation {
716 public:
HVecMul(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)717 HVecMul(ArenaAllocator* allocator,
718 HInstruction* left,
719 HInstruction* right,
720 DataType::Type packed_type,
721 size_t vector_length,
722 uint32_t dex_pc)
723 : HVecBinaryOperation(kVecMul, allocator, left, right, packed_type, vector_length, dex_pc) {
724 DCHECK(HasConsistentPackedTypes(left, packed_type));
725 DCHECK(HasConsistentPackedTypes(right, packed_type));
726 }
727
CanBeMoved()728 bool CanBeMoved() const override { return true; }
729
730 DECLARE_INSTRUCTION(VecMul);
731
732 protected:
733 DEFAULT_COPY_CONSTRUCTOR(VecMul);
734 };
735
736 // Divides every component in the two vectors,
737 // viz. [ x1, .. , xn ] / [ y1, .. , yn ] = [ x1 / y1, .. , xn / yn ].
738 class HVecDiv final : public HVecBinaryOperation {
739 public:
HVecDiv(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)740 HVecDiv(ArenaAllocator* allocator,
741 HInstruction* left,
742 HInstruction* right,
743 DataType::Type packed_type,
744 size_t vector_length,
745 uint32_t dex_pc)
746 : HVecBinaryOperation(kVecDiv, allocator, left, right, packed_type, vector_length, dex_pc) {
747 DCHECK(HasConsistentPackedTypes(left, packed_type));
748 DCHECK(HasConsistentPackedTypes(right, packed_type));
749 }
750
CanBeMoved()751 bool CanBeMoved() const override { return true; }
752
753 DECLARE_INSTRUCTION(VecDiv);
754
755 protected:
756 DEFAULT_COPY_CONSTRUCTOR(VecDiv);
757 };
758
759 // Takes minimum of every component in the two vectors,
760 // viz. MIN( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ min(x1, y1), .. , min(xn, yn) ]
761 // for either both signed or both unsigned operands x, y (reflected in packed_type).
762 class HVecMin final : public HVecBinaryOperation {
763 public:
HVecMin(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)764 HVecMin(ArenaAllocator* allocator,
765 HInstruction* left,
766 HInstruction* right,
767 DataType::Type packed_type,
768 size_t vector_length,
769 uint32_t dex_pc)
770 : HVecBinaryOperation(kVecMin, allocator, left, right, packed_type, vector_length, dex_pc) {
771 DCHECK(HasConsistentPackedTypes(left, packed_type));
772 DCHECK(HasConsistentPackedTypes(right, packed_type));
773 }
774
CanBeMoved()775 bool CanBeMoved() const override { return true; }
776
777 DECLARE_INSTRUCTION(VecMin);
778
779 protected:
780 DEFAULT_COPY_CONSTRUCTOR(VecMin);
781 };
782
783 // Takes maximum of every component in the two vectors,
784 // viz. MAX( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ max(x1, y1), .. , max(xn, yn) ]
785 // for either both signed or both unsigned operands x, y (reflected in packed_type).
786 class HVecMax final : public HVecBinaryOperation {
787 public:
HVecMax(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)788 HVecMax(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(kVecMax, allocator, left, right, packed_type, vector_length, dex_pc) {
795 DCHECK(HasConsistentPackedTypes(left, packed_type));
796 DCHECK(HasConsistentPackedTypes(right, packed_type));
797 }
798
CanBeMoved()799 bool CanBeMoved() const override { return true; }
800
801 DECLARE_INSTRUCTION(VecMax);
802
803 protected:
804 DEFAULT_COPY_CONSTRUCTOR(VecMax);
805 };
806
807 // Bitwise-ands every component in the two vectors,
808 // viz. [ x1, .. , xn ] & [ y1, .. , yn ] = [ x1 & y1, .. , xn & yn ].
809 class HVecAnd final : public HVecBinaryOperation {
810 public:
HVecAnd(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)811 HVecAnd(ArenaAllocator* allocator,
812 HInstruction* left,
813 HInstruction* right,
814 DataType::Type packed_type,
815 size_t vector_length,
816 uint32_t dex_pc)
817 : HVecBinaryOperation(kVecAnd, allocator, left, right, packed_type, vector_length, dex_pc) {
818 DCHECK(left->IsVecOperation() && right->IsVecOperation());
819 }
820
CanBeMoved()821 bool CanBeMoved() const override { return true; }
822
823 DECLARE_INSTRUCTION(VecAnd);
824
825 protected:
826 DEFAULT_COPY_CONSTRUCTOR(VecAnd);
827 };
828
829 // Bitwise-and-nots every component in the two vectors,
830 // viz. [ x1, .. , xn ] and-not [ y1, .. , yn ] = [ ~x1 & y1, .. , ~xn & yn ].
831 class HVecAndNot final : public HVecBinaryOperation {
832 public:
HVecAndNot(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)833 HVecAndNot(ArenaAllocator* allocator,
834 HInstruction* left,
835 HInstruction* right,
836 DataType::Type packed_type,
837 size_t vector_length,
838 uint32_t dex_pc)
839 : HVecBinaryOperation(
840 kVecAndNot, allocator, left, right, packed_type, vector_length, dex_pc) {
841 DCHECK(left->IsVecOperation() && right->IsVecOperation());
842 }
843
CanBeMoved()844 bool CanBeMoved() const override { return true; }
845
846 DECLARE_INSTRUCTION(VecAndNot);
847
848 protected:
849 DEFAULT_COPY_CONSTRUCTOR(VecAndNot);
850 };
851
852 // Bitwise-ors every component in the two vectors,
853 // viz. [ x1, .. , xn ] | [ y1, .. , yn ] = [ x1 | y1, .. , xn | yn ].
854 class HVecOr final : public HVecBinaryOperation {
855 public:
HVecOr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)856 HVecOr(ArenaAllocator* allocator,
857 HInstruction* left,
858 HInstruction* right,
859 DataType::Type packed_type,
860 size_t vector_length,
861 uint32_t dex_pc)
862 : HVecBinaryOperation(kVecOr, allocator, left, right, packed_type, vector_length, dex_pc) {
863 DCHECK(left->IsVecOperation() && right->IsVecOperation());
864 }
865
CanBeMoved()866 bool CanBeMoved() const override { return true; }
867
868 DECLARE_INSTRUCTION(VecOr);
869
870 protected:
871 DEFAULT_COPY_CONSTRUCTOR(VecOr);
872 };
873
874 // Bitwise-xors every component in the two vectors,
875 // viz. [ x1, .. , xn ] ^ [ y1, .. , yn ] = [ x1 ^ y1, .. , xn ^ yn ].
876 class HVecXor final : public HVecBinaryOperation {
877 public:
HVecXor(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)878 HVecXor(ArenaAllocator* allocator,
879 HInstruction* left,
880 HInstruction* right,
881 DataType::Type packed_type,
882 size_t vector_length,
883 uint32_t dex_pc)
884 : HVecBinaryOperation(kVecXor, allocator, left, right, packed_type, vector_length, dex_pc) {
885 DCHECK(left->IsVecOperation() && right->IsVecOperation());
886 }
887
CanBeMoved()888 bool CanBeMoved() const override { return true; }
889
890 DECLARE_INSTRUCTION(VecXor);
891
892 protected:
893 DEFAULT_COPY_CONSTRUCTOR(VecXor);
894 };
895
896 // Logically shifts every component in the vector left by the given distance,
897 // viz. [ x1, .. , xn ] << d = [ x1 << d, .. , xn << d ].
898 class HVecShl final : public HVecBinaryOperation {
899 public:
HVecShl(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)900 HVecShl(ArenaAllocator* allocator,
901 HInstruction* left,
902 HInstruction* right,
903 DataType::Type packed_type,
904 size_t vector_length,
905 uint32_t dex_pc)
906 : HVecBinaryOperation(kVecShl, allocator, left, right, packed_type, vector_length, dex_pc) {
907 DCHECK(HasConsistentPackedTypes(left, packed_type));
908 }
909
CanBeMoved()910 bool CanBeMoved() const override { return true; }
911
912 DECLARE_INSTRUCTION(VecShl);
913
914 protected:
915 DEFAULT_COPY_CONSTRUCTOR(VecShl);
916 };
917
918 // Arithmetically shifts every component in the vector right by the given distance,
919 // viz. [ x1, .. , xn ] >> d = [ x1 >> d, .. , xn >> d ].
920 class HVecShr final : public HVecBinaryOperation {
921 public:
HVecShr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)922 HVecShr(ArenaAllocator* allocator,
923 HInstruction* left,
924 HInstruction* right,
925 DataType::Type packed_type,
926 size_t vector_length,
927 uint32_t dex_pc)
928 : HVecBinaryOperation(kVecShr, allocator, left, right, packed_type, vector_length, dex_pc) {
929 DCHECK(HasConsistentPackedTypes(left, packed_type));
930 }
931
CanBeMoved()932 bool CanBeMoved() const override { return true; }
933
934 DECLARE_INSTRUCTION(VecShr);
935
936 protected:
937 DEFAULT_COPY_CONSTRUCTOR(VecShr);
938 };
939
940 // Logically shifts every component in the vector right by the given distance,
941 // viz. [ x1, .. , xn ] >>> d = [ x1 >>> d, .. , xn >>> d ].
942 class HVecUShr final : public HVecBinaryOperation {
943 public:
HVecUShr(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)944 HVecUShr(ArenaAllocator* allocator,
945 HInstruction* left,
946 HInstruction* right,
947 DataType::Type packed_type,
948 size_t vector_length,
949 uint32_t dex_pc)
950 : HVecBinaryOperation(kVecUShr, allocator, left, right, packed_type, vector_length, dex_pc) {
951 DCHECK(HasConsistentPackedTypes(left, packed_type));
952 }
953
CanBeMoved()954 bool CanBeMoved() const override { return true; }
955
956 DECLARE_INSTRUCTION(VecUShr);
957
958 protected:
959 DEFAULT_COPY_CONSTRUCTOR(VecUShr);
960 };
961
962 //
963 // Definitions of concrete miscellaneous vector operations in HIR.
964 //
965
966 // Assigns the given scalar elements to a vector,
967 // viz. set( array(x1, .. , xn) ) = [ x1, .. , xn ] if n == m,
968 // set( array(x1, .. , xm) ) = [ x1, .. , xm, 0, .. , 0 ] if m < n.
969 class HVecSetScalars final : public HVecOperation {
970 public:
HVecSetScalars(ArenaAllocator * allocator,HInstruction * scalars[],DataType::Type packed_type,size_t vector_length,size_t number_of_scalars,uint32_t dex_pc)971 HVecSetScalars(ArenaAllocator* allocator,
972 HInstruction* scalars[],
973 DataType::Type packed_type,
974 size_t vector_length,
975 size_t number_of_scalars,
976 uint32_t dex_pc)
977 : HVecOperation(kVecSetScalars,
978 allocator,
979 packed_type,
980 SideEffects::None(),
981 number_of_scalars,
982 vector_length,
983 dex_pc) {
984 for (size_t i = 0; i < number_of_scalars; i++) {
985 DCHECK(!ReturnsSIMDValue(scalars[i]));
986 SetRawInputAt(0, scalars[i]);
987 }
988 }
989
990 // Setting scalars needs to stay in place, since SIMD registers are not
991 // kept alive across vector loop boundaries (yet).
CanBeMoved()992 bool CanBeMoved() const override { return false; }
993
994 DECLARE_INSTRUCTION(VecSetScalars);
995
996 protected:
997 DEFAULT_COPY_CONSTRUCTOR(VecSetScalars);
998 };
999
1000 // Multiplies every component in the two vectors, adds the result vector to the accumulator vector,
1001 // viz. [ a1, .. , an ] + [ x1, .. , xn ] * [ y1, .. , yn ] = [ a1 + x1 * y1, .. , an + xn * yn ].
1002 // For floating point types, Java rounding behavior must be preserved; the products are rounded to
1003 // the proper precision before being added. "Fused" multiply-add operations available on several
1004 // architectures are not usable since they would violate Java language rules.
1005 class HVecMultiplyAccumulate final : public HVecOperation {
1006 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)1007 HVecMultiplyAccumulate(ArenaAllocator* allocator,
1008 InstructionKind op,
1009 HInstruction* accumulator,
1010 HInstruction* mul_left,
1011 HInstruction* mul_right,
1012 DataType::Type packed_type,
1013 size_t vector_length,
1014 uint32_t dex_pc)
1015 : HVecOperation(kVecMultiplyAccumulate,
1016 allocator,
1017 packed_type,
1018 SideEffects::None(),
1019 /* number_of_inputs= */ 3,
1020 vector_length,
1021 dex_pc),
1022 op_kind_(op) {
1023 DCHECK(op == InstructionKind::kAdd || op == InstructionKind::kSub);
1024 DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
1025 DCHECK(HasConsistentPackedTypes(mul_left, packed_type));
1026 DCHECK(HasConsistentPackedTypes(mul_right, packed_type));
1027 // Remove the following if we add an architecture that supports floating point multiply-add
1028 // with Java-compatible rounding.
1029 DCHECK(DataType::IsIntegralType(packed_type));
1030 SetRawInputAt(0, accumulator);
1031 SetRawInputAt(1, mul_left);
1032 SetRawInputAt(2, mul_right);
1033 }
1034
CanBeMoved()1035 bool CanBeMoved() const override { return true; }
1036
InstructionDataEquals(const HInstruction * other)1037 bool InstructionDataEquals(const HInstruction* other) const override {
1038 DCHECK(other->IsVecMultiplyAccumulate());
1039 const HVecMultiplyAccumulate* o = other->AsVecMultiplyAccumulate();
1040 return HVecOperation::InstructionDataEquals(o) && GetOpKind() == o->GetOpKind();
1041 }
1042
GetOpKind()1043 InstructionKind GetOpKind() const { return op_kind_; }
1044
1045 DECLARE_INSTRUCTION(VecMultiplyAccumulate);
1046
1047 protected:
1048 DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate);
1049
1050 private:
1051 // Indicates if this is a MADD or MSUB.
1052 const InstructionKind op_kind_;
1053 };
1054
1055 // Takes the absolute difference of two vectors, and adds the results to
1056 // same-precision or wider-precision components in the accumulator,
1057 // viz. SAD([ a1, .. , am ], [ x1, .. , xn ], [ y1, .. , yn ]) =
1058 // [ a1 + sum abs(xi-yi), .. , am + sum abs(xj-yj) ],
1059 // for m <= n, non-overlapping sums, and signed operands x, y.
1060 class HVecSADAccumulate final : public HVecOperation {
1061 public:
HVecSADAccumulate(ArenaAllocator * allocator,HInstruction * accumulator,HInstruction * sad_left,HInstruction * sad_right,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)1062 HVecSADAccumulate(ArenaAllocator* allocator,
1063 HInstruction* accumulator,
1064 HInstruction* sad_left,
1065 HInstruction* sad_right,
1066 DataType::Type packed_type,
1067 size_t vector_length,
1068 uint32_t dex_pc)
1069 : HVecOperation(kVecSADAccumulate,
1070 allocator,
1071 packed_type,
1072 SideEffects::None(),
1073 /* number_of_inputs= */ 3,
1074 vector_length,
1075 dex_pc) {
1076 DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
1077 DCHECK(sad_left->IsVecOperation());
1078 DCHECK(sad_right->IsVecOperation());
1079 DCHECK_EQ(ToSignedType(sad_left->AsVecOperation()->GetPackedType()),
1080 ToSignedType(sad_right->AsVecOperation()->GetPackedType()));
1081 SetRawInputAt(0, accumulator);
1082 SetRawInputAt(1, sad_left);
1083 SetRawInputAt(2, sad_right);
1084 }
1085
1086 DECLARE_INSTRUCTION(VecSADAccumulate);
1087
1088 protected:
1089 DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate);
1090 };
1091
1092 // Performs dot product of two vectors and adds the result to wider precision components in
1093 // the accumulator.
1094 //
1095 // viz. DOT_PRODUCT([ a1, .. , am], [ x1, .. , xn ], [ y1, .. , yn ]) =
1096 // [ a1 + sum(xi * yi), .. , am + sum(xj * yj) ],
1097 // for m <= n, non-overlapping sums,
1098 // for either both signed or both unsigned operands x, y.
1099 //
1100 // Notes:
1101 // - packed type reflects the type of sum reduction, not the type of the operands.
1102 // - IsZeroExtending() is used to determine the kind of signed/zero extension to be
1103 // performed for the operands.
1104 //
1105 // TODO: Support types other than kInt32 for packed type.
1106 class HVecDotProd final : public HVecOperation {
1107 public:
HVecDotProd(ArenaAllocator * allocator,HInstruction * accumulator,HInstruction * left,HInstruction * right,DataType::Type packed_type,bool is_zero_extending,size_t vector_length,uint32_t dex_pc)1108 HVecDotProd(ArenaAllocator* allocator,
1109 HInstruction* accumulator,
1110 HInstruction* left,
1111 HInstruction* right,
1112 DataType::Type packed_type,
1113 bool is_zero_extending,
1114 size_t vector_length,
1115 uint32_t dex_pc)
1116 : HVecOperation(kVecDotProd,
1117 allocator,
1118 packed_type,
1119 SideEffects::None(),
1120 /* number_of_inputs= */ 3,
1121 vector_length,
1122 dex_pc) {
1123 DCHECK(HasConsistentPackedTypes(accumulator, packed_type));
1124 DCHECK(DataType::IsIntegralType(packed_type));
1125 DCHECK(left->IsVecOperation());
1126 DCHECK(right->IsVecOperation());
1127 DCHECK_EQ(ToSignedType(left->AsVecOperation()->GetPackedType()),
1128 ToSignedType(right->AsVecOperation()->GetPackedType()));
1129 SetRawInputAt(0, accumulator);
1130 SetRawInputAt(1, left);
1131 SetRawInputAt(2, right);
1132 SetPackedFlag<kFieldHDotProdIsZeroExtending>(is_zero_extending);
1133 }
1134
IsZeroExtending()1135 bool IsZeroExtending() const { return GetPackedFlag<kFieldHDotProdIsZeroExtending>(); }
1136
CanBeMoved()1137 bool CanBeMoved() const override { return true; }
1138
1139 DECLARE_INSTRUCTION(VecDotProd);
1140
1141 protected:
1142 DEFAULT_COPY_CONSTRUCTOR(VecDotProd);
1143
1144 private:
1145 // Additional packed bits.
1146 static constexpr size_t kFieldHDotProdIsZeroExtending =
1147 HVecOperation::kNumberOfVectorOpPackedBits;
1148 static constexpr size_t kNumberOfHDotProdPackedBits = kFieldHDotProdIsZeroExtending + 1;
1149 static_assert(kNumberOfHDotProdPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
1150 };
1151
1152 // Loads a vector from memory, viz. load(mem, 1)
1153 // yield the vector [ mem(1), .. , mem(n) ].
1154 class HVecLoad final : public HVecMemoryOperation {
1155 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)1156 HVecLoad(ArenaAllocator* allocator,
1157 HInstruction* base,
1158 HInstruction* index,
1159 DataType::Type packed_type,
1160 SideEffects side_effects,
1161 size_t vector_length,
1162 bool is_string_char_at,
1163 uint32_t dex_pc)
1164 : HVecMemoryOperation(kVecLoad,
1165 allocator,
1166 packed_type,
1167 side_effects,
1168 /* number_of_inputs= */ 2,
1169 vector_length,
1170 dex_pc) {
1171 SetRawInputAt(0, base);
1172 SetRawInputAt(1, index);
1173 SetPackedFlag<kFieldIsStringCharAt>(is_string_char_at);
1174 }
1175
IsStringCharAt()1176 bool IsStringCharAt() const { return GetPackedFlag<kFieldIsStringCharAt>(); }
1177
CanBeMoved()1178 bool CanBeMoved() const override { return true; }
1179
InstructionDataEquals(const HInstruction * other)1180 bool InstructionDataEquals(const HInstruction* other) const override {
1181 DCHECK(other->IsVecLoad());
1182 const HVecLoad* o = other->AsVecLoad();
1183 return HVecMemoryOperation::InstructionDataEquals(o) && IsStringCharAt() == o->IsStringCharAt();
1184 }
1185
1186 DECLARE_INSTRUCTION(VecLoad);
1187
1188 protected:
1189 DEFAULT_COPY_CONSTRUCTOR(VecLoad);
1190
1191 private:
1192 // Additional packed bits.
1193 static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
1194 static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1;
1195 static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
1196 };
1197
1198 // Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] )
1199 // sets mem(1) = x1, .. , mem(n) = xn.
1200 class HVecStore final : public HVecMemoryOperation {
1201 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)1202 HVecStore(ArenaAllocator* allocator,
1203 HInstruction* base,
1204 HInstruction* index,
1205 HInstruction* value,
1206 DataType::Type packed_type,
1207 SideEffects side_effects,
1208 size_t vector_length,
1209 uint32_t dex_pc)
1210 : HVecMemoryOperation(kVecStore,
1211 allocator,
1212 packed_type,
1213 side_effects,
1214 /* number_of_inputs= */ 3,
1215 vector_length,
1216 dex_pc) {
1217 DCHECK(HasConsistentPackedTypes(value, packed_type));
1218 SetRawInputAt(0, base);
1219 SetRawInputAt(1, index);
1220 SetRawInputAt(2, value);
1221 }
1222
1223 // A store needs to stay in place.
CanBeMoved()1224 bool CanBeMoved() const override { return false; }
1225
GetValue()1226 HInstruction* GetValue() const { return InputAt(2); }
1227
1228 DECLARE_INSTRUCTION(VecStore);
1229
1230 protected:
1231 DEFAULT_COPY_CONSTRUCTOR(VecStore)
1232 };
1233
1234 //
1235 // 'Predicate-setting' instructions.
1236 //
1237
1238 // An abstract class for instructions for which the output value is a vector predicate -
1239 // a special kind of vector value:
1240 //
1241 // viz. [ p1, .. , pn ], where p_i is from { 0, 1 }.
1242 //
1243 // A VecOperation OP executes the same operation (e.g. ADD) on multiple elements of the vector.
1244 // It can be either unpredicated (operation is done on ALL of the elements) or predicated (only
1245 // on SOME elements, determined by a special extra input - vector predicate).
1246 // Implementations can vary depending on the ISA; the general idea is that for each element of the
1247 // regular vector a vector predicate has a corresponding element with either 0 or 1.
1248 // The value determines whether a vector element will be involved in OP calculations or not
1249 // (active or inactive). A vector predicate is referred as governing one if it is used to
1250 // control the execution of a predicated instruction.
1251 //
1252 // Note: vector predicate value type is introduced alongside existing vectors of booleans and
1253 // vectors of bytes to reflect their special semantics.
1254 //
1255 // TODO: we could introduce SIMD types in HIR.
1256 class HVecPredSetOperation : public HVecOperation {
1257 public:
1258 // A vector predicate-setting operation looks like a Int64 location.
1259 // TODO: we could introduce vector types in HIR.
1260 static constexpr DataType::Type kSIMDPredType = DataType::Type::kInt64;
1261
HVecPredSetOperation(InstructionKind kind,ArenaAllocator * allocator,DataType::Type packed_type,SideEffects side_effects,size_t number_of_inputs,size_t vector_length,uint32_t dex_pc)1262 HVecPredSetOperation(InstructionKind kind,
1263 ArenaAllocator* allocator,
1264 DataType::Type packed_type,
1265 SideEffects side_effects,
1266 size_t number_of_inputs,
1267 size_t vector_length,
1268 uint32_t dex_pc)
1269 : HVecOperation(kind,
1270 allocator,
1271 packed_type,
1272 side_effects,
1273 number_of_inputs,
1274 vector_length,
1275 dex_pc) {
1276 // Overrides the kSIMDType set by the VecOperation constructor.
1277 SetPackedField<TypeField>(kSIMDPredType);
1278 }
1279
CanBeMoved()1280 bool CanBeMoved() const override { return true; }
1281
1282 DECLARE_ABSTRACT_INSTRUCTION(VecPredSetOperation);
1283
1284 protected:
1285 DEFAULT_COPY_CONSTRUCTOR(VecPredSetOperation);
1286 };
1287
1288 // Sets all the vector predicate elements as active or inactive.
1289 //
1290 // viz. [ p1, .. , pn ] = [ val, .. , val ] where val is from { 1, 0 }.
1291 class HVecPredSetAll final : public HVecPredSetOperation {
1292 public:
HVecPredSetAll(ArenaAllocator * allocator,HInstruction * input,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)1293 HVecPredSetAll(ArenaAllocator* allocator,
1294 HInstruction* input,
1295 DataType::Type packed_type,
1296 size_t vector_length,
1297 uint32_t dex_pc) :
1298 HVecPredSetOperation(kVecPredSetAll,
1299 allocator,
1300 packed_type,
1301 SideEffects::None(),
1302 /* number_of_inputs= */ 1,
1303 vector_length,
1304 dex_pc) {
1305 DCHECK(input->IsIntConstant());
1306 SetRawInputAt(0, input);
1307 MarkEmittedAtUseSite();
1308 }
1309
1310 // Having governing predicate doesn't make sense for set all TRUE/FALSE instruction.
MustBePredicatedInPredicatedSIMDMode()1311 bool MustBePredicatedInPredicatedSIMDMode() override { return false; }
1312
IsSetTrue()1313 bool IsSetTrue() const { return InputAt(0)->AsIntConstant()->IsTrue(); }
1314
1315 // Vector predicates are not kept alive across vector loop boundaries.
CanBeMoved()1316 bool CanBeMoved() const override { return false; }
1317
1318 DECLARE_INSTRUCTION(VecPredSetAll);
1319
1320 protected:
1321 DEFAULT_COPY_CONSTRUCTOR(VecPredSetAll);
1322 };
1323
1324 //
1325 // Arm64 SVE-specific instructions.
1326 //
1327 // Classes of instructions which are specific to Arm64 SVE (though could be adopted
1328 // by other targets, possibly being lowered to a number of ISA instructions) and
1329 // implement SIMD loop predicated execution idiom.
1330 //
1331
1332 // Takes two scalar values x and y, creates a vector S: s(n) = x + n, compares (OP) each s(n)
1333 // with y and set the corresponding element of the predicate register to the result of the
1334 // comparison.
1335 //
1336 // viz. [ p1, .. , pn ] = [ x OP y , (x + 1) OP y, .. , (x + n) OP y ] where OP is CondKind
1337 // condition.
1338 class HVecPredWhile final : public HVecPredSetOperation {
1339 public:
1340 enum class CondKind {
1341 kLE, // signed less than or equal.
1342 kLO, // unsigned lower.
1343 kLS, // unsigned lower or same.
1344 kLT, // signed less.
1345 kLast = kLT,
1346 };
1347
HVecPredWhile(ArenaAllocator * allocator,HInstruction * left,HInstruction * right,CondKind cond,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)1348 HVecPredWhile(ArenaAllocator* allocator,
1349 HInstruction* left,
1350 HInstruction* right,
1351 CondKind cond,
1352 DataType::Type packed_type,
1353 size_t vector_length,
1354 uint32_t dex_pc) :
1355 HVecPredSetOperation(kVecPredWhile,
1356 allocator,
1357 packed_type,
1358 SideEffects::None(),
1359 /* number_of_inputs= */ 2,
1360 vector_length,
1361 dex_pc) {
1362 DCHECK(!left->IsVecOperation());
1363 DCHECK(!left->IsVecPredSetOperation());
1364 DCHECK(!right->IsVecOperation());
1365 DCHECK(!right->IsVecPredSetOperation());
1366 DCHECK(DataType::IsIntegralType(left->GetType()));
1367 DCHECK(DataType::IsIntegralType(right->GetType()));
1368 SetRawInputAt(0, left);
1369 SetRawInputAt(1, right);
1370 SetPackedField<CondKindField>(cond);
1371 }
1372
1373 // This is a special loop control instruction which must not be predicated.
MustBePredicatedInPredicatedSIMDMode()1374 bool MustBePredicatedInPredicatedSIMDMode() override { return false; }
1375
GetCondKind()1376 CondKind GetCondKind() const {
1377 return GetPackedField<CondKindField>();
1378 }
1379
1380 DECLARE_INSTRUCTION(VecPredWhile);
1381
1382 protected:
1383 // Additional packed bits.
1384 static constexpr size_t kCondKind = HVecOperation::kNumberOfVectorOpPackedBits;
1385 static constexpr size_t kCondKindSize =
1386 MinimumBitsToStore(static_cast<size_t>(CondKind::kLast));
1387 static constexpr size_t kNumberOfVecPredConditionPackedBits = kCondKind + kCondKindSize;
1388 static_assert(kNumberOfVecPredConditionPackedBits <= kMaxNumberOfPackedBits,
1389 "Too many packed fields.");
1390 using CondKindField = BitField<CondKind, kCondKind, kCondKindSize>;
1391
1392 DEFAULT_COPY_CONSTRUCTOR(VecPredWhile);
1393 };
1394
1395 // Evaluates the predicate condition (PCondKind) for a vector predicate; outputs
1396 // a scalar boolean value result.
1397 //
1398 // Note: as VecPredCondition can be also predicated, only active elements (determined by the
1399 // instruction's governing predicate) of the input vector predicate are used for condition
1400 // evaluation.
1401 //
1402 // Note: this instruction is currently used as a workaround for the fact that IR instructions
1403 // can't have more than one output.
1404 class HVecPredCondition final : public HVecOperation {
1405 public:
1406 // To get more info on the condition kinds please see "2.2 Process state, PSTATE" section of
1407 // "ARM Architecture Reference Manual Supplement. The Scalable Vector Extension (SVE),
1408 // for ARMv8-A".
1409 enum class PCondKind {
1410 kNone, // No active elements were TRUE.
1411 kAny, // An active element was TRUE.
1412 kNLast, // The last active element was not TRUE.
1413 kLast, // The last active element was TRUE.
1414 kFirst, // The first active element was TRUE.
1415 kNFirst, // The first active element was not TRUE.
1416 kPMore, // An active element was TRUE but not the last active element.
1417 kPLast, // The last active element was TRUE or no active elements were TRUE.
1418 kEnumLast = kPLast
1419 };
1420
HVecPredCondition(ArenaAllocator * allocator,HInstruction * input,PCondKind pred_cond,DataType::Type packed_type,size_t vector_length,uint32_t dex_pc)1421 HVecPredCondition(ArenaAllocator* allocator,
1422 HInstruction* input,
1423 PCondKind pred_cond,
1424 DataType::Type packed_type,
1425 size_t vector_length,
1426 uint32_t dex_pc)
1427 : HVecOperation(kVecPredCondition,
1428 allocator,
1429 packed_type,
1430 SideEffects::None(),
1431 /* number_of_inputs */ 1,
1432 vector_length,
1433 dex_pc) {
1434 DCHECK(input->IsVecPredSetOperation());
1435 SetRawInputAt(0, input);
1436 // Overrides the kSIMDType set by the VecOperation constructor.
1437 SetPackedField<TypeField>(DataType::Type::kBool);
1438 SetPackedField<CondKindField>(pred_cond);
1439 }
1440
1441 // This instruction is currently used only as a special loop control instruction
1442 // which must not be predicated.
1443 // TODO: Remove the constraint.
MustBePredicatedInPredicatedSIMDMode()1444 bool MustBePredicatedInPredicatedSIMDMode() override { return false; }
1445
GetPCondKind()1446 PCondKind GetPCondKind() const {
1447 return GetPackedField<CondKindField>();
1448 }
1449
1450 DECLARE_INSTRUCTION(VecPredCondition);
1451
1452 protected:
1453 // Additional packed bits.
1454 static constexpr size_t kCondKind = HVecOperation::kNumberOfVectorOpPackedBits;
1455 static constexpr size_t kCondKindSize =
1456 MinimumBitsToStore(static_cast<size_t>(PCondKind::kEnumLast));
1457 static constexpr size_t kNumberOfVecPredConditionPackedBits = kCondKind + kCondKindSize;
1458 static_assert(kNumberOfVecPredConditionPackedBits <= kMaxNumberOfPackedBits,
1459 "Too many packed fields.");
1460 using CondKindField = BitField<PCondKind, kCondKind, kCondKindSize>;
1461
1462 DEFAULT_COPY_CONSTRUCTOR(VecPredCondition);
1463 };
1464
1465 } // namespace art
1466
1467 #endif // ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
1468