1 /*
2  * Copyright (C) 2016 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_LOOP_OPTIMIZATION_H_
18 #define ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
19 
20 #include "base/macros.h"
21 #include "base/scoped_arena_allocator.h"
22 #include "base/scoped_arena_containers.h"
23 #include "induction_var_range.h"
24 #include "loop_analysis.h"
25 #include "nodes.h"
26 #include "optimization.h"
27 #include "superblock_cloner.h"
28 
29 namespace art HIDDEN {
30 
31 class CompilerOptions;
32 class ArchNoOptsLoopHelper;
33 
34 /**
35  * Loop optimizations. Builds a loop hierarchy and applies optimizations to
36  * the detected nested loops, such as removal of dead induction and empty loops
37  * and inner loop vectorization.
38  */
39 class HLoopOptimization : public HOptimization {
40  public:
41   HLoopOptimization(HGraph* graph,
42                     const CodeGenerator& codegen,    // Needs info about the target.
43                     HInductionVarAnalysis* induction_analysis,
44                     OptimizingCompilerStats* stats,
45                     const char* name = kLoopOptimizationPassName);
46 
47   bool Run() override;
48 
49   static constexpr const char* kLoopOptimizationPassName = "loop_optimization";
50 
51   // The maximum number of total instructions (trip_count * instruction_count),
52   // where the optimization of removing SuspendChecks from the loop header could
53   // be performed.
54   static constexpr int64_t kMaxTotalInstRemoveSuspendCheck = 128;
55 
56  private:
57   /**
58    * A single loop inside the loop hierarchy representation.
59    */
60   struct LoopNode : public ArenaObject<kArenaAllocLoopOptimization> {
LoopNodeLoopNode61     explicit LoopNode(HLoopInformation* lp_info)
62         : loop_info(lp_info),
63           outer(nullptr),
64           inner(nullptr),
65           previous(nullptr),
66           next(nullptr),
67           try_catch_kind(TryCatchKind::kUnknown) {}
68 
69     enum class TryCatchKind {
70       kUnknown,
71       // Either if we have a try catch in the loop, or if the loop is inside of an outer try catch,
72       // we set `kHasTryCatch`.
73       kHasTryCatch,
74       kNoTryCatch
75     };
76 
77     HLoopInformation* loop_info;
78     LoopNode* outer;
79     LoopNode* inner;
80     LoopNode* previous;
81     LoopNode* next;
82     TryCatchKind try_catch_kind;
83   };
84 
85   /*
86    * Vectorization restrictions (bit mask).
87    */
88   enum VectorRestrictions {
89     kNone            = 0,        // no restrictions
90     kNoMul           = 1 << 0,   // no multiplication
91     kNoDiv           = 1 << 1,   // no division
92     kNoShift         = 1 << 2,   // no shift
93     kNoShr           = 1 << 3,   // no arithmetic shift right
94     kNoHiBits        = 1 << 4,   // "wider" operations cannot bring in higher order bits
95     kNoSignedHAdd    = 1 << 5,   // no signed halving add
96     kNoUnsignedHAdd  = 1 << 6,   // no unsigned halving add
97     kNoUnroundedHAdd = 1 << 7,   // no unrounded halving add
98     kNoAbs           = 1 << 8,   // no absolute value
99     kNoStringCharAt  = 1 << 9,   // no StringCharAt
100     kNoReduction     = 1 << 10,  // no reduction
101     kNoSAD           = 1 << 11,  // no sum of absolute differences (SAD)
102     kNoWideSAD       = 1 << 12,  // no sum of absolute differences (SAD) with operand widening
103     kNoDotProd       = 1 << 13,  // no dot product
104     kNoIfCond        = 1 << 14,  // no if condition conversion
105   };
106 
107   /*
108    * Loop synthesis mode during vectorization
109    * (sequential peeling/cleanup loop or vector loop).
110    */
111   enum class LoopSynthesisMode {
112     kSequential,
113     kVector
114   };
115   friend std::ostream& operator<<(std::ostream& os, const LoopSynthesisMode& fd_logger);
116 
117   /*
118    * Representation of a unit-stride array reference.
119    */
120   struct ArrayReference {
121     ArrayReference(HInstruction* b, HInstruction* o, DataType::Type t, bool l, bool c = false)
baseArrayReference122         : base(b), offset(o), type(t), lhs(l), is_string_char_at(c) { }
123     bool operator<(const ArrayReference& other) const {
124       return
125           (base < other.base) ||
126           (base == other.base &&
127            (offset < other.offset || (offset == other.offset &&
128                                       (type < other.type ||
129                                        (type == other.type &&
130                                         (lhs < other.lhs ||
131                                          (lhs == other.lhs &&
132                                           is_string_char_at < other.is_string_char_at)))))));
133     }
134     HInstruction* base;      // base address
135     HInstruction* offset;    // offset + i
136     DataType::Type type;     // component type
137     bool lhs;                // def/use
138     bool is_string_char_at;  // compressed string read
139   };
140 
141   // This structure describes the control flow (CF) -> data flow (DF) conversion of the loop
142   // with control flow (see below) for the purpose of predicated autovectorization.
143   //
144   // Lets define "loops without control-flow" (or non-CF loops) as loops with two consecutive
145   // blocks and without the branching structure except for the loop exit. And
146   // "loop with control-flow" (or CF-loops) - all other loops.
147   //
148   // In the execution of the original CF-loop on each iteration some basic block Y will be
149   // either executed or not executed, depending on the control flow of the loop. More
150   // specifically, a block will be executed if all the conditional branches of the nodes in
151   // the control dependency graph for that block Y are taken according to the path from the loop
152   // header to that basic block.
153   //
154   // This is the key idea of CF->DF conversion: a boolean value
155   // 'ctrl_pred == cond1 && cond2 && ...' will determine whether the basic block Y will be
156   // executed, where cond_K is whether the branch of the node K in the control dependency
157   // graph upward traversal was taken in the 'right' direction.
158   //
159   // Def.: BB Y is control dependent on BB X iff
160   //   (1) there exists a directed path P from X to Y with any basic block Z in P (excluding X
161   //       and Y) post-dominated by Y and
162   //   (2) X is not post-dominated by Y.
163   //             ...
164   //              X
165   //     false /     \ true
166   //          /       \
167   //                  ...
168   //                   |
169   //                   Y
170   //                  ...
171   //
172   // When doing predicated autovectorization of a CF loop, we use the CF->DF conversion approach:
173   //  1) do the data analysis and vector operation creation as if it was a non-CF loop.
174   //  2) for each HIf block create two vector predicate setting instructions - for True and False
175   //     edges/paths.
176   //  3) assign a governing vector predicate (see comments near HVecPredSetOperation)
177   //     to each vector operation Alpha in the loop (including to those vector predicate setting
178   //     instructions created in #2); do this by:
179   //     - finding the immediate control dependent block of the instruction Alpha's block.
180   //     - choosing the True or False predicate setting instruction (created in #2) depending
181   //       on the path to the instruction.
182   //
183   // For more information check the papers:
184   //
185   //   - Allen, John R and Kennedy, Ken and Porterfield, Carrie and Warren, Joe,
186   //     “Conversion of Control Dependence to Data Dependence,” in Proceedings of the 10th ACM
187   //     SIGACT-SIGPLAN Symposium on Principles of Programming Languages, 1983, pp. 177–189.
188   //   - JEANNE FERRANTE, KARL J. OTTENSTEIN, JOE D. WARREN,
189   //     "The Program Dependence Graph and Its Use in Optimization"
190   //
191   class BlockPredicateInfo : public ArenaObject<kArenaAllocLoopOptimization> {
192    public:
BlockPredicateInfo()193     BlockPredicateInfo() :
194         control_predicate_(nullptr),
195         true_predicate_(nullptr),
196         false_predicate_(nullptr) {}
197 
SetControlFlowInfo(HVecPredSetOperation * true_predicate,HVecPredSetOperation * false_predicate)198     void SetControlFlowInfo(HVecPredSetOperation* true_predicate,
199                             HVecPredSetOperation* false_predicate) {
200       DCHECK(!HasControlFlowOps());
201       true_predicate_ = true_predicate;
202       false_predicate_ = false_predicate;
203     }
204 
HasControlFlowOps()205     bool HasControlFlowOps() const {
206       // Note: a block must have both T/F predicates set or none of them.
207       DCHECK_EQ(true_predicate_ == nullptr, false_predicate_ == nullptr);
208       return true_predicate_ != nullptr;
209     }
210 
GetControlPredicate()211     HVecPredSetOperation* GetControlPredicate() const { return control_predicate_; }
SetControlPredicate(HVecPredSetOperation * control_predicate)212     void SetControlPredicate(HVecPredSetOperation* control_predicate) {
213       control_predicate_ = control_predicate;
214     }
215 
GetTruePredicate()216     HVecPredSetOperation* GetTruePredicate() const { return true_predicate_; }
GetFalsePredicate()217     HVecPredSetOperation* GetFalsePredicate() const { return false_predicate_; }
218 
219    private:
220     // Vector control predicate operation, associated with the block which will determine
221     // the active lanes for all vector operations, originated from this block.
222     HVecPredSetOperation* control_predicate_;
223 
224     // Vector predicate instruction, associated with the true sucessor of the block.
225     HVecPredSetOperation* true_predicate_;
226     // Vector predicate instruction, associated with the false sucessor of the block.
227     HVecPredSetOperation* false_predicate_;
228   };
229 
230   //
231   // Loop setup and traversal.
232   //
233 
234   bool LocalRun();
235   void AddLoop(HLoopInformation* loop_info);
236   void RemoveLoop(LoopNode* node);
237 
238   // Traverses all loops inner to outer to perform simplifications and optimizations.
239   // Returns true if loops nested inside current loop (node) have changed.
240   bool TraverseLoopsInnerToOuter(LoopNode* node);
241 
242   // Calculates `node`'s `try_catch_kind` and sets it to:
243   // 1) kHasTryCatch if it has try catches (or if it's inside of an outer try catch)
244   // 2) kNoTryCatch otherwise.
245   void CalculateAndSetTryCatchKind(LoopNode* node);
246 
247   //
248   // Optimization.
249   //
250 
251   void SimplifyInduction(LoopNode* node);
252   void SimplifyBlocks(LoopNode* node);
253 
254   // Performs optimizations specific to inner loop with finite header logic (empty loop removal,
255   // unrolling, vectorization). Returns true if anything changed.
256   bool TryOptimizeInnerLoopFinite(LoopNode* node);
257 
258   // Performs optimizations specific to inner loop. Returns true if anything changed.
259   bool OptimizeInnerLoop(LoopNode* node);
260 
261   // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling
262   // opportunities. Returns whether transformation happened. 'generate_code' determines whether the
263   // optimization should be actually applied.
264   bool TryUnrollingForBranchPenaltyReduction(LoopAnalysisInfo* analysis_info,
265                                              bool generate_code = true);
266 
267   // Tries to apply loop peeling for loop invariant exits elimination. Returns whether
268   // transformation happened. 'generate_code' determines whether the optimization should be
269   // actually applied.
270   bool TryPeelingForLoopInvariantExitsElimination(LoopAnalysisInfo* analysis_info,
271                                                   bool generate_code = true);
272 
273   // Tries to perform whole loop unrolling for a small loop with a small trip count to eliminate
274   // the loop check overhead and to have more opportunities for inter-iteration optimizations.
275   // Returns whether transformation happened. 'generate_code' determines whether the optimization
276   // should be actually applied.
277   bool TryFullUnrolling(LoopAnalysisInfo* analysis_info, bool generate_code = true);
278 
279   // Tries to remove SuspendCheck for plain loops with a low trip count. The
280   // SuspendCheck in the codegen makes sure that the thread can be interrupted
281   // during execution for GC. Not being able to do so might decrease the
282   // responsiveness of GC when a very long loop or a long recursion is being
283   // executed. However, for plain loops with a small trip count, the removal of
284   // SuspendCheck should not affect the GC's responsiveness by a large margin.
285   // Consequently, since the thread won't be interrupted for plain loops, it is
286   // assumed that the performance might increase by removing SuspendCheck.
287   bool TryToRemoveSuspendCheckFromLoopHeader(LoopAnalysisInfo* analysis_info,
288                                              bool generate_code = true);
289 
290   // Tries to apply scalar loop optimizations.
291   bool TryLoopScalarOpts(LoopNode* node);
292 
293   //
294   // Vectorization analysis and synthesis.
295   //
296 
297   // Returns whether the data flow requirements are met for vectorization.
298   //
299   //   - checks whether instructions are vectorizable for the target.
300   //   - conducts data dependence analysis for array references.
301   //   - additionally, collects info on peeling and aligment strategy.
302   bool CanVectorizeDataFlow(LoopNode* node, HBasicBlock* header, bool collect_alignment_info);
303 
304   // Does the checks (common for predicated and traditional mode) for the loop.
305   bool ShouldVectorizeCommon(LoopNode* node, HPhi* main_phi, int64_t trip_count);
306 
307   // Try to vectorize the loop, returns whether it was successful.
308   //
309   // There are two versions/algorithms:
310   //  - Predicated: all the vector operations have governing predicates which control
311   //    which individual vector lanes will be active (see HVecPredSetOperation for more details).
312   //    Example: vectorization using AArch64 SVE.
313   //  - Traditional: a regular mode in which all vector operations lanes are unconditionally
314   //    active.
315   //    Example: vectoriation using AArch64 NEON.
316   bool TryVectorizePredicated(LoopNode* node,
317                               HBasicBlock* body,
318                               HBasicBlock* exit,
319                               HPhi* main_phi,
320                               int64_t trip_count);
321 
322   bool TryVectorizedTraditional(LoopNode* node,
323                                 HBasicBlock* body,
324                                 HBasicBlock* exit,
325                                 HPhi* main_phi,
326                                 int64_t trip_count);
327 
328   // Vectorizes the loop for which all checks have been already done.
329   void VectorizePredicated(LoopNode* node,
330                            HBasicBlock* block,
331                            HBasicBlock* exit);
332   void VectorizeTraditional(LoopNode* node,
333                             HBasicBlock* block,
334                             HBasicBlock* exit,
335                             int64_t trip_count);
336 
337   // Performs final steps for whole vectorization process: links reduction, removes the original
338   // scalar loop, updates loop info.
339   void FinalizeVectorization(LoopNode* node);
340 
341   // Helpers that do the vector instruction synthesis for the previously created loop; create
342   // and fill the loop body with instructions.
343   //
344   // A version to generate a vector loop in predicated mode.
345   void GenerateNewLoopPredicated(LoopNode* node,
346                                  HBasicBlock* new_preheader,
347                                  HInstruction* lo,
348                                  HInstruction* hi,
349                                  HInstruction* step);
350 
351   // A version to generate a vector loop in traditional mode or to generate
352   // a scalar loop for both modes.
353   void GenerateNewLoopScalarOrTraditional(LoopNode* node,
354                                           HBasicBlock* new_preheader,
355                                           HInstruction* lo,
356                                           HInstruction* hi,
357                                           HInstruction* step,
358                                           uint32_t unroll);
359 
360   //
361   // Helpers for GenerateNewLoop*.
362   //
363 
364   // Updates vectorization bookkeeping date for the new loop, creates and returns
365   // its main induction Phi.
366   HPhi* InitializeForNewLoop(HBasicBlock* new_preheader, HInstruction* lo);
367 
368   // Finalizes reduction and induction phis' inputs for the newly created loop.
369   void FinalizePhisForNewLoop(HPhi* phi, HInstruction* lo);
370 
371   // Creates empty predicate info object for each basic block and puts it into the map.
372   void PreparePredicateInfoMap(LoopNode* node);
373 
374   // Set up block true/false predicates using info, collected through data flow and control
375   // dependency analysis.
376   void InitPredicateInfoMap(LoopNode* node, HVecPredSetOperation* loop_main_pred);
377 
378   // Performs instruction synthesis for the loop body.
379   void GenerateNewLoopBodyOnce(LoopNode* node,
380                                DataType::Type induc_type,
381                                HInstruction* step);
382 
383   // Returns whether the vector loop needs runtime disambiguation test for array refs.
NeedsArrayRefsDisambiguationTest()384   bool NeedsArrayRefsDisambiguationTest() const { return vector_runtime_test_a_ != nullptr; }
385 
386   bool VectorizeDef(LoopNode* node, HInstruction* instruction, bool generate_code);
387   bool VectorizeUse(LoopNode* node,
388                     HInstruction* instruction,
389                     bool generate_code,
390                     DataType::Type type,
391                     uint64_t restrictions);
392   uint32_t GetVectorSizeInBytes();
393   bool TrySetVectorType(DataType::Type type, /*out*/ uint64_t* restrictions);
394   bool TrySetVectorLengthImpl(uint32_t length);
395 
TrySetVectorLength(DataType::Type type,uint32_t length)396   bool TrySetVectorLength(DataType::Type type, uint32_t length) {
397     bool res = TrySetVectorLengthImpl(length);
398     // Currently the vectorizer supports only the mode when full SIMD registers are used.
399     DCHECK_IMPLIES(res, DataType::Size(type) * length == GetVectorSizeInBytes());
400     return res;
401   }
402 
403   void GenerateVecInv(HInstruction* org, DataType::Type type);
404   void GenerateVecSub(HInstruction* org, HInstruction* offset);
405   void GenerateVecMem(HInstruction* org,
406                       HInstruction* opa,
407                       HInstruction* opb,
408                       HInstruction* offset,
409                       DataType::Type type);
410   void GenerateVecReductionPhi(HPhi* phi);
411   void GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* reduction);
412   HInstruction* ReduceAndExtractIfNeeded(HInstruction* instruction);
413   HInstruction* GenerateVecOp(HInstruction* org,
414                               HInstruction* opa,
415                               HInstruction* opb,
416                               DataType::Type type);
417 
418   // Vectorization idioms.
419   bool VectorizeSaturationIdiom(LoopNode* node,
420                                 HInstruction* instruction,
421                                 bool generate_code,
422                                 DataType::Type type,
423                                 uint64_t restrictions);
424   bool VectorizeHalvingAddIdiom(LoopNode* node,
425                                 HInstruction* instruction,
426                                 bool generate_code,
427                                 DataType::Type type,
428                                 uint64_t restrictions);
429   bool VectorizeSADIdiom(LoopNode* node,
430                          HInstruction* instruction,
431                          bool generate_code,
432                          DataType::Type type,
433                          uint64_t restrictions);
434   bool VectorizeDotProdIdiom(LoopNode* node,
435                              HInstruction* instruction,
436                              bool generate_code,
437                              DataType::Type type,
438                              uint64_t restrictions);
439   bool VectorizeIfCondition(LoopNode* node,
440                             HInstruction* instruction,
441                             bool generate_code,
442                             uint64_t restrictions);
443 
444   // Vectorization heuristics.
445   Alignment ComputeAlignment(HInstruction* offset,
446                              DataType::Type type,
447                              bool is_string_char_at,
448                              uint32_t peeling = 0);
449   void SetAlignmentStrategy(const ScopedArenaVector<uint32_t>& peeling_votes,
450                             const ArrayReference* peeling_candidate);
451   uint32_t MaxNumberPeeled();
452   bool IsVectorizationProfitable(int64_t trip_count);
453 
454   //
455   // Helpers.
456   //
457 
458   bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
459   bool TrySetPhiReduction(HPhi* phi);
460 
461   // Detects loop header with a single induction (returned in main_phi), possibly
462   // other phis for reductions, but no other side effects. Returns true on success.
463   bool TrySetSimpleLoopHeader(HBasicBlock* block, /*out*/ HPhi** main_phi);
464 
465   bool IsEmptyBody(HBasicBlock* block);
466   bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
467                            HInstruction* instruction,
468                            bool collect_loop_uses,
469                            /*out*/ uint32_t* use_count);
470   bool IsUsedOutsideLoop(HLoopInformation* loop_info,
471                          HInstruction* instruction);
472   bool TryReplaceWithLastValue(HLoopInformation* loop_info,
473                                HInstruction* instruction,
474                                HBasicBlock* block);
475   bool TryAssignLastValue(HLoopInformation* loop_info,
476                           HInstruction* instruction,
477                           HBasicBlock* block,
478                           bool collect_loop_uses);
479   void RemoveDeadInstructions(const HInstructionList& list);
480   bool CanRemoveCycle();  // Whether the current 'iset_' is removable.
481 
IsInPredicatedVectorizationMode()482   bool IsInPredicatedVectorizationMode() const { return predicated_vectorization_mode_; }
483   void MaybeInsertInVectorExternalSet(HInstruction* instruction);
484 
485   // Compiler options (to query ISA features).
486   const CompilerOptions* compiler_options_;
487 
488   // Cached target SIMD vector register size in bytes.
489   const size_t simd_register_size_;
490 
491   // Range information based on prior induction variable analysis.
492   InductionVarRange induction_range_;
493 
494   // Phase-local heap memory allocator for the loop optimizer. Storage obtained
495   // through this allocator is immediately released when the loop optimizer is done.
496   ScopedArenaAllocator* loop_allocator_;
497 
498   // Global heap memory allocator. Used to build HIR.
499   ArenaAllocator* global_allocator_;
500 
501   // Entries into the loop hierarchy representation. The hierarchy resides
502   // in phase-local heap memory.
503   LoopNode* top_loop_;
504   LoopNode* last_loop_;
505 
506   // Temporary bookkeeping of a set of instructions.
507   // Contents reside in phase-local heap memory.
508   ScopedArenaSet<HInstruction*>* iset_;
509 
510   // Temporary bookkeeping of reduction instructions. Mapping is two-fold:
511   // (1) reductions in the loop-body are mapped back to their phi definition,
512   // (2) phi definitions are mapped to their initial value (updated during
513   //     code generation to feed the proper values into the new chain).
514   // Contents reside in phase-local heap memory.
515   ScopedArenaSafeMap<HInstruction*, HInstruction*>* reductions_;
516 
517   // Flag that tracks if any simplifications have occurred.
518   bool simplified_;
519 
520   // Whether to use predicated loop vectorization (e.g. for arm64 SVE target).
521   bool predicated_vectorization_mode_;
522 
523   // Number of "lanes" for selected packed type.
524   uint32_t vector_length_;
525 
526   // Set of array references in the vector loop.
527   // Contents reside in phase-local heap memory.
528   ScopedArenaSet<ArrayReference>* vector_refs_;
529 
530   // Static or dynamic loop peeling for alignment.
531   uint32_t vector_static_peeling_factor_;
532   const ArrayReference* vector_dynamic_peeling_candidate_;
533 
534   // Dynamic data dependence test of the form a != b.
535   HInstruction* vector_runtime_test_a_;
536   HInstruction* vector_runtime_test_b_;
537 
538   // Mapping used during vectorization synthesis for both the scalar peeling/cleanup
539   // loop (mode is kSequential) and the actual vector loop (mode is kVector). The data
540   // structure maps original instructions into the new instructions.
541   // Contents reside in phase-local heap memory.
542   ScopedArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
543 
544   // Permanent mapping used during vectorization synthesis.
545   // Contents reside in phase-local heap memory.
546   ScopedArenaSafeMap<HInstruction*, HInstruction*>* vector_permanent_map_;
547 
548   // Tracks vector operations that are inserted outside of the loop (preheader, exit)
549   // as part of vectorization (e.g. replicate scalar for loop invariants and reduce ops
550   // for loop reductions).
551   //
552   // The instructions in the set are live for the whole vectorization process of the current
553   // loop, not just during generation of a particular loop version (as the sets above).
554   //
555   // Currently the set is being only used in the predicated mode - for assigning governing
556   // predicates.
557   ScopedArenaSet<HInstruction*>* vector_external_set_;
558 
559   // A mapping between a basic block of the original loop and its associated PredicateInfo.
560   //
561   // Only used in predicated loop vectorization mode.
562   ScopedArenaSafeMap<HBasicBlock*, BlockPredicateInfo*>* predicate_info_map_;
563 
564   // Temporary vectorization bookkeeping.
565   LoopSynthesisMode synthesis_mode_;  // synthesis mode
566   HBasicBlock* vector_preheader_;  // preheader of the new loop
567   HBasicBlock* vector_header_;  // header of the new loop
568   HBasicBlock* vector_body_;  // body of the new loop
569   HInstruction* vector_index_;  // normalized index of the new loop
570 
571   // Helper for target-specific behaviour for loop optimizations.
572   ArchNoOptsLoopHelper* arch_loop_helper_;
573 
574   friend class LoopOptimizationTest;
575 
576   DISALLOW_COPY_AND_ASSIGN(HLoopOptimization);
577 };
578 
579 }  // namespace art
580 
581 #endif  // ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
582