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