diff options
author | 2018-02-15 14:43:48 +0000 | |
---|---|---|
committer | 2018-05-15 19:33:45 +0100 | |
commit | cf43fb6a1e676cc6bbc04c6591640f18643b1839 (patch) | |
tree | 2573ba1024307763c54df655333f1e2477d0ea82 | |
parent | 9076eb66ad173933d7fbd5ce328d31c7f97fd202 (diff) |
ART: Enable scalar loop peeling and unrolling.
Turn on scalar loop peeling and unrolling by default.
Test: 482-checker-loop-back-edge-use, 530-checker-peel-unroll
Test: test-art-host, test-art-target, boot-to-gui
Change-Id: Ibfe1b54f790a97b281e85396da2985e0f22c2834
-rw-r--r-- | compiler/optimizing/loop_analysis.cc | 68 | ||||
-rw-r--r-- | compiler/optimizing/loop_analysis.h | 23 | ||||
-rw-r--r-- | compiler/optimizing/loop_optimization.cc | 17 | ||||
-rw-r--r-- | compiler/optimizing/loop_optimization.h | 4 | ||||
-rw-r--r-- | test/482-checker-loop-back-edge-use/src/Main.java | 67 | ||||
-rw-r--r-- | test/530-checker-peel-unroll/expected.txt | 1 | ||||
-rw-r--r-- | test/530-checker-peel-unroll/info.txt | 1 | ||||
-rw-r--r-- | test/530-checker-peel-unroll/src/Main.java | 822 |
8 files changed, 933 insertions, 70 deletions
diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc index a0760eff69..a2124455e2 100644 --- a/compiler/optimizing/loop_analysis.cc +++ b/compiler/optimizing/loop_analysis.cc @@ -35,6 +35,9 @@ void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info, for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); + if (it.Current()->GetType() == DataType::Type::kInt64) { + analysis_results->has_long_type_instructions_ = true; + } if (MakesScalarPeelingUnrollingNonBeneficial(instruction)) { analysis_results->has_instructions_preventing_scalar_peeling_ = true; analysis_results->has_instructions_preventing_scalar_unrolling_ = true; @@ -61,34 +64,29 @@ bool LoopAnalysis::HasLoopAtLeastOneInvariantExit(HLoopInformation* loop_info) { return false; } -class Arm64LoopHelper : public ArchDefaultLoopHelper { +// Default implementation of loop helper; used for all targets unless a custom implementation +// is provided. Enables scalar loop peeling and unrolling with the most conservative heuristics. +class ArchDefaultLoopHelper : public ArchNoOptsLoopHelper { public: // Scalar loop unrolling parameters and heuristics. // // Maximum possible unrolling factor. - static constexpr uint32_t kArm64ScalarMaxUnrollFactor = 2; + static constexpr uint32_t kScalarMaxUnrollFactor = 2; // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. - static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + static constexpr uint32_t kScalarHeuristicMaxBodySizeInstr = 17; // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. - static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + static constexpr uint32_t kScalarHeuristicMaxBodySizeBlocks = 6; - // SIMD loop unrolling parameters and heuristics. - // - // Maximum possible unrolling factor. - static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; - // Loop's maximum instruction count. Loops with higher count will not be unrolled. - static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; - - bool IsLoopTooBigForScalarPeelingUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { - size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); - size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); - return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr || - bb_num >= kArm64ScalarHeuristicMaxBodySizeBlocks); + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + return loop_analysis_info->HasLongTypeInstructions() || + IsLoopTooBig(loop_analysis_info, + kScalarHeuristicMaxBodySizeInstr, + kScalarHeuristicMaxBodySizeBlocks); } uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED, uint64_t trip_count) const OVERRIDE { - uint32_t desired_unrolling_factor = kArm64ScalarMaxUnrollFactor; + uint32_t desired_unrolling_factor = kScalarMaxUnrollFactor; if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) { return kNoUnrollingFactor; } @@ -98,6 +96,38 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { bool IsLoopPeelingEnabled() const OVERRIDE { return true; } + protected: + bool IsLoopTooBig(LoopAnalysisInfo* loop_analysis_info, + size_t instr_threshold, + size_t bb_threshold) const { + size_t instr_num = loop_analysis_info->GetNumberOfInstructions(); + size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks(); + return (instr_num >= instr_threshold || bb_num >= bb_threshold); + } +}; + +// Custom implementation of loop helper for arm64 target. Enables heuristics for scalar loop +// peeling and unrolling and supports SIMD loop unrolling. +class Arm64LoopHelper : public ArchDefaultLoopHelper { + public: + // SIMD loop unrolling parameters and heuristics. + // + // Maximum possible unrolling factor. + static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8; + // Loop's maximum instruction count. Loops with higher count will not be unrolled. + static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50; + + // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40; + // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled. + static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8; + + bool IsLoopNonBeneficialForScalarOpts(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE { + return IsLoopTooBig(loop_analysis_info, + kArm64ScalarHeuristicMaxBodySizeInstr, + kArm64ScalarHeuristicMaxBodySizeBlocks); + } + uint32_t GetSIMDUnrollingFactor(HBasicBlock* block, int64_t trip_count, uint32_t max_peel, @@ -126,8 +156,8 @@ class Arm64LoopHelper : public ArchDefaultLoopHelper { } }; -ArchDefaultLoopHelper* ArchDefaultLoopHelper::Create(InstructionSet isa, - ArenaAllocator* allocator) { +ArchNoOptsLoopHelper* ArchNoOptsLoopHelper::Create(InstructionSet isa, + ArenaAllocator* allocator) { switch (isa) { case InstructionSet::kArm64: { return new (allocator) Arm64LoopHelper; diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h index ece9858136..c09d3ff00f 100644 --- a/compiler/optimizing/loop_analysis.h +++ b/compiler/optimizing/loop_analysis.h @@ -35,6 +35,7 @@ class LoopAnalysisInfo : public ValueObject { exits_num_(0), has_instructions_preventing_scalar_peeling_(false), has_instructions_preventing_scalar_unrolling_(false), + has_long_type_instructions_(false), loop_info_(loop_info) {} size_t GetNumberOfBasicBlocks() const { return bb_num_; } @@ -49,6 +50,10 @@ class LoopAnalysisInfo : public ValueObject { return has_instructions_preventing_scalar_unrolling_; } + bool HasLongTypeInstructions() const { + return has_long_type_instructions_; + } + const HLoopInformation* GetLoopInfo() const { return loop_info_; } private: @@ -62,6 +67,9 @@ class LoopAnalysisInfo : public ValueObject { bool has_instructions_preventing_scalar_peeling_; // Whether the loop has instructions which make scalar loop unrolling non-beneficial. bool has_instructions_preventing_scalar_unrolling_; + // Whether the loop has instructions of primitive long type; unrolling these loop will + // likely introduce spill/fills on 32-bit targets. + bool has_long_type_instructions_; // Corresponding HLoopInformation. const HLoopInformation* loop_info_; @@ -117,22 +125,21 @@ class LoopAnalysis : public ValueObject { // To support peeling/unrolling for a new architecture one needs to create new helper class, // inherit it from this and add implementation for the following methods. // -class ArchDefaultLoopHelper : public ArenaObject<kArenaAllocOptimization> { +class ArchNoOptsLoopHelper : public ArenaObject<kArenaAllocOptimization> { public: - virtual ~ArchDefaultLoopHelper() {} + virtual ~ArchNoOptsLoopHelper() {} // Creates an instance of specialised helper for the target or default helper if the target // doesn't support loop peeling and unrolling. - static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); + static ArchNoOptsLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator); - // Returns whether the loop is too big for loop peeling/unrolling by checking its total number of - // basic blocks and instructions. + // Returns whether the loop is not beneficial for loop peeling/unrolling. // - // If the loop body has too many instructions then peeling/unrolling optimization will not bring - // any noticeable performance improvement however will increase the code size. + // For example, if the loop body has too many instructions then peeling/unrolling optimization + // will not bring any noticeable performance improvement however will increase the code size. // // Returns 'true' by default, should be overridden by particular target loop helper. - virtual bool IsLoopTooBigForScalarPeelingUnrolling( + virtual bool IsLoopNonBeneficialForScalarOpts( LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; } // Returns optimal scalar unrolling factor for the loop. diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1ce3524bd6..eda6bd1e86 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -33,9 +33,6 @@ namespace art { // Enables vectorization (SIMDization) in the loop optimizer. static constexpr bool kEnableVectorization = true; -// Enables scalar loop unrolling in the loop optimizer. -static constexpr bool kEnableScalarPeelingUnrolling = false; - // // Static helpers. // @@ -457,7 +454,7 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, vector_header_(nullptr), vector_body_(nullptr), vector_index_(nullptr), - arch_loop_helper_(ArchDefaultLoopHelper::Create(compiler_driver_ != nullptr + arch_loop_helper_(ArchNoOptsLoopHelper::Create(compiler_driver_ != nullptr ? compiler_driver_->GetInstructionSet() : InstructionSet::kNone, global_allocator_)) { @@ -761,7 +758,7 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) // as InstructionSet is needed. - if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { + if (compiler_driver_ == nullptr) { return false; } @@ -781,9 +778,9 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); // Check "IsLoopClonable" last as it can be time-consuming. - if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || + if (loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || + arch_loop_helper_->IsLoopNonBeneficialForScalarOpts(&loop_analysis_info) || (loop_analysis_info.GetNumberOfExits() > 1) || - loop_analysis_info.HasInstructionsPreventingScalarUnrolling() || !PeelUnrollHelper::IsLoopClonable(loop_info)) { return false; } @@ -807,7 +804,7 @@ bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* node) { bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopNode* node) { // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests) // as InstructionSet is needed. - if (!kEnableScalarPeelingUnrolling || compiler_driver_ == nullptr) { + if (compiler_driver_ == nullptr) { return false; } @@ -821,8 +818,8 @@ bool HLoopOptimization::TryPeelingForLoopInvariantExitsElimination(LoopNode* nod LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info); // Check "IsLoopClonable" last as it can be time-consuming. - if (arch_loop_helper_->IsLoopTooBigForScalarPeelingUnrolling(&loop_analysis_info) || - loop_analysis_info.HasInstructionsPreventingScalarPeeling() || + if (loop_analysis_info.HasInstructionsPreventingScalarPeeling() || + arch_loop_helper_->IsLoopNonBeneficialForScalarOpts(&loop_analysis_info) || !LoopAnalysis::HasLoopAtLeastOneInvariantExit(loop_info) || !PeelUnrollHelper::IsLoopClonable(loop_info)) { return false; diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 7807da15ed..191a93da26 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -28,7 +28,7 @@ namespace art { class CompilerDriver; -class ArchDefaultLoopHelper; +class ArchNoOptsLoopHelper; /** * Loop optimizations. Builds a loop hierarchy and applies optimizations to @@ -308,7 +308,7 @@ class HLoopOptimization : public HOptimization { HInstruction* vector_index_; // normalized index of the new loop // Helper for target-specific behaviour for loop optimizations. - ArchDefaultLoopHelper* arch_loop_helper_; + ArchNoOptsLoopHelper* arch_loop_helper_; friend class LoopOptimizationTest; diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 47823409a3..8311d8cc4f 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -18,26 +18,28 @@ public class Main { /// CHECK-START: void Main.loop1(boolean) liveness (after) - /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] - /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> - /// CHECK: Goto liveness:<<GotoLiv:\d+>> - /// CHECK: Exit + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgUse:\d+>>)} uses:[<<ArgUse>>] + /// CHECK-DAG: If [<<Arg>>] liveness:<<IfLiv:\d+>> loop:none + /// CHECK-DAG: Goto loop:B{{\d+}} + /// CHECK-DAG: Exit /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> - /// CHECK-EVAL: <<GotoLiv>> + 2 == <<ArgLoopUse>> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop1(boolean incoming) { while (incoming) {} } /// CHECK-START: void Main.loop2(boolean) liveness (after) - /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] - /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> - /// CHECK: Goto liveness:<<GotoLiv1:\d+>> - /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK-DAG: If [<<Arg>>] liveness:<<IfLiv:\d+>> loop:<<Loop1:B\d+>> + /// CHECK-DAG: Goto liveness:<<GotoLiv1:\d+>> loop:<<Loop1>> + /// CHECK-DAG: Goto liveness:<<GotoLiv2:\d+>> loop:<<Loop2:B\d+>> /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> - /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> - /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop2(boolean incoming) { // Add some code at entry to avoid having the entry block be a pre header. @@ -122,17 +124,18 @@ public class Main { } /// CHECK-START: void Main.loop7(boolean) liveness (after) - /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse1:\d+>>,<<ArgUse2:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] - /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> - /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> - /// CHECK: Goto liveness:<<GotoLiv1:\d+>> - /// CHECK: Goto liveness:<<GotoLiv2:\d+>> - /// CHECK: Exit + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse1:\d+>>,<<ArgUse2:\d+>>,<<ArgLoopUse>>] + /// CHECK-DAG: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK-DAG: If [<<Arg>>] liveness:<<IfLiv:\d+>> loop:<<Loop1:B\d+>> + /// CHECK-DAG: Goto liveness:<<GotoLiv1:\d+>> loop:<<Loop1>> + /// CHECK-DAG: Goto liveness:<<GotoLiv2:\d+>> loop:<<Loop2:B\d+>> + /// CHECK-DAG: Exit /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse1>> /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse2>> /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> - /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> - /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. @@ -144,15 +147,16 @@ public class Main { } /// CHECK-START: void Main.loop8() liveness (after) - /// CHECK: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] - /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> - /// CHECK: Goto liveness:<<GotoLiv1:\d+>> - /// CHECK: Goto liveness:<<GotoLiv2:\d+>> - /// CHECK: Exit + /// CHECK-DAG: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK-DAG: If [<<Arg>>] liveness:<<IfLiv:\d+>> loop:<<Loop1:B\d+>> + /// CHECK-DAG: Goto liveness:<<GotoLiv1:\d+>> loop:<<Loop1>> + /// CHECK-DAG: Goto liveness:<<GotoLiv2:\d+>> loop:<<Loop2:B\d+>> + /// CHECK-DAG: Exit /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> - /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> - /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop8() { // 'incoming' must have a use at both back edges. @@ -171,14 +175,15 @@ public class Main { } /// CHECK-START: void Main.loop9() liveness (after) - /// CHECK: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] - /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> - /// CHECK: Goto liveness:<<GotoLiv1:\d+>> - /// CHECK-DAG: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK-DAG: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgUse:\d+>>)} uses:[<<ArgUse>>] + /// CHECK-DAG: If [<<Arg>>] liveness:<<IfLiv:\d+>> loop:<<Loop1:B\d+>> + /// CHECK-DAG: Goto liveness:<<GotoLiv1:\d+>> loop:<<Loop1>> + /// CHECK-DAG: Goto liveness:<<GotoLiv2:\d+>> loop:<<Loop2:B\d+>> /// CHECK-DAG: Exit /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> - /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> + // + // Loop invariant exit check is hoisted from the loop by peeling. public static void loop9() { // Add some code at entry to avoid having the entry block be a pre header. diff --git a/test/530-checker-peel-unroll/expected.txt b/test/530-checker-peel-unroll/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/530-checker-peel-unroll/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/530-checker-peel-unroll/info.txt b/test/530-checker-peel-unroll/info.txt new file mode 100644 index 0000000000..0e10b36442 --- /dev/null +++ b/test/530-checker-peel-unroll/info.txt @@ -0,0 +1 @@ +Test on loop optimizations, peeling and unrolling. diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java new file mode 100644 index 0000000000..2051b47afe --- /dev/null +++ b/test/530-checker-peel-unroll/src/Main.java @@ -0,0 +1,822 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Test loop optimizations, in particular scalar loop peeling and unrolling. +public class Main { + + static final int LENGTH = 4 * 1024; + int[] a = new int[LENGTH]; + int[] b = new int[LENGTH]; + + private static final int LENGTH_A = LENGTH; + private static final int LENGTH_B = 16; + private static final int RESULT_POS = 4; + + double[][] mA; + double[][] mB; + double[][] mC; + + public Main() { + mA = new double[LENGTH_A][]; + mB = new double[LENGTH_B][]; + mC = new double[LENGTH_B][]; + for (int i = 0; i < LENGTH_A; i++) { + mA[i] = new double[LENGTH_B]; + } + + for (int i = 0; i < LENGTH_B; i++) { + mB[i] = new double[LENGTH_A]; + mC[i] = new double[LENGTH_B]; + } + } + + private static final void initMatrix(double[][] m) { + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[i].length; j++) { + m[i][j] = (double) (i * LENGTH / (j + 1)); + } + } + } + + private static final void initIntArray(int[] a) { + for (int i = 0; i < LENGTH; i++) { + a[i] = i % 4; + } + } + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<IndAdd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get0>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<IndAdd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get0>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<IndAdd>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0A:i\d+>> ArrayGet [<<Array>>,<<IndAdd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAddA:i\d+>> Add [<<IndAdd>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1A:i\d+>> ArrayGet [<<Array>>,<<IndAddA>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddA:i\d+>> Add [<<Get0A>>,<<Get1A>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<IndAdd>>,<<AddA>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + private static final void unrollingLoadStoreElimination(int[] a) { + for (int i = 0; i < LENGTH - 2; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Const128:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const128>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Rem:i\d+>> Rem [<<AddI>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Rem>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi [<<PhiS>>,<<AddS>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.unrollingWhile(int[]) loop_optimization (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Const128:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const128>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Rem:i\d+>> Rem [<<AddI>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Rem>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<PhiS>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<PhiSM:i\d+>> Phi [<<PhiS>>,<<AddS>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<AddIA:i\d+>> Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<RemA:i\d+>> Rem [<<AddIA>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NEA:z\d+>> NotEqual [<<RemA>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NEA>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddSA:i\d+>> Add [<<PhiSM>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<PhiSM>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi [<<AddSA>>,<<PhiSM>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + private static final void unrollingWhile(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + private static final void unrollingSwitch(int[] a) { + for (int i = 0; i < LENGTH; i++) { + switch (i % 3) { + case 2: + a[i]++; + break; + default: + break; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + private static final void unrollingSwapElements(int[] array) { + for (int i = 0; i < LENGTH - 2; i++) { + if (array[i] > array[i + 1]) { + int temp = array[i + 1]; + array[i + 1] = array[i]; + array[i] = temp; + } + } + } + + // Simple check that loop unrolling has happened. + // + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 16 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 16 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: ArraySet loop:<<Loop>> outer_loop:none + private static final void unrollingRInnerproduct(double[][] result, + double[][] a, + double[][] b, + int row, + int column) { + // computes the inner product of A[row,*] and B[*,column] + int i; + result[row][column] = 0.0f; + for (i = 0; i < LENGTH_B; i++) { + result[row][column] = result[row][column] + a[row][i] * b[i][column]; + } + } + + // nested loop + // [[[]]] + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop2:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Phi3:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop3:B\d+>> outer_loop:<<Loop2>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi3>>,<<Limit>>] loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: Add [<<Phi3>>,<<Const1>>] loop:<<Loop3>> outer_loop:<<Loop2>> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop2:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Phi3:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop3:B\d+>> outer_loop:<<Loop2>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi3>>,<<Limit>>] loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: <<AddI:i\d+>> Add [<<Phi3>>,<<Const1>>] loop:<<Loop3>> outer_loop:<<Loop2>> + // + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop2>> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 16; k++) { + for (int j = 0; j < 16; j++) { + for (int i = 0; i < 128; i++) { + b[x]++; + a[i] = a[i] + 1; + } + } + } + } + + // nested loop: + // [ + // if [] else [] + // ] + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<XThres:i\d+>> IntConstant 100 loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:none + // + /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop2:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Check2:z\d+>> GreaterThanOrEqual [<<Phi2>>,<<Limit>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Check2>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<AddI2:i\d+>> Add [<<Phi2>>,<<Const1>>] loop:<<Loop2>> outer_loop:<<Loop1>> + // + /// CHECK-DAG: <<Phi3:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop3:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Check3:z\d+>> GreaterThanOrEqual [<<Phi3>>,<<Limit>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Check3>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<AddI3:i\d+>> Add [<<Phi3>>,<<Const1>>] loop:<<Loop3>> outer_loop:<<Loop1>> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + + /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<XThres:i\d+>> IntConstant 100 loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:none + // + /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop2:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Check2:z\d+>> GreaterThanOrEqual [<<Phi2>>,<<Limit>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Check2>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<AddI2:i\d+>> Add [<<Phi2>>,<<Const1>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: Add [<<AddI2>>,<<Const1>>] loop:<<Loop2>> outer_loop:<<Loop1>> + // + /// CHECK-DAG: <<Phi3:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop3:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Check3:z\d+>> GreaterThanOrEqual [<<Phi3>>,<<Limit>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Check3>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<AddI3:i\d+>> Add [<<Phi3>>,<<Const1>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArrayGet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: ArraySet loop:<<Loop3>> outer_loop:<<Loop1>> + /// CHECK-DAG: Add [<<AddI3>>,<<Const1>>] loop:<<Loop3>> outer_loop:<<Loop1>> + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + /// CHECK-NOT: If + private static final void unrollingTwoLoopsInTheNest(int[] a, int[] b, int x) { + for (int k = 0; k < 128; k++) { + if (x > 100) { + for (int j = 0; j < 128; j++) { + a[x]++; + } + } else { + for (int i = 0; i < 128; i++) { + b[x]++; + } + } + } + } + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4095 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<IndAdd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get0>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingOddTripCount(int[] a) { + for (int i = 0; i < LENGTH - 1; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Limit:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<IndAdd>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get0>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: Phi + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void noUnrollingNotKnownTripCount(int[] a, int n) { + for (int i = 0; i < n; i++) { + a[i] += a[i + 1]; + } + } + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Param>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<Const0>>,<<Limit>>] loop:none + /// CHECK-DAG: If [<<CheckA>>] loop:none + /// CHECK-DAG: If [<<Param>>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <<IndAddA:i\d+>> Add [<<Const0>>,<<Const1>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<IndAddA>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: If [<<Param>>] loop:none + /// CHECK-DAG: ArrayGet loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: GreaterThanOrEqual + /// CHECK-NOT: If + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final void peelingSimple(int[] a, boolean f) { + for (int i = 0; i < LENGTH; i++) { + if (f) { + break; + } + a[i] += 1; + } + } + + // Often used idiom that, when not hoisted, prevents BCE and vectorization. + // + /// CHECK-START: void Main.peelingAddInts(int[]) loop_optimization (before) + /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<ConstNull:l\d+>> NullConstant loop:none + /// CHECK-DAG: <<Eq:z\d+>> Equal [<<Param>>,<<ConstNull>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If [<<Eq>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + + /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after) + /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<ConstNull:l\d+>> NullConstant loop:none + /// CHECK-DAG: <<Eq:z\d+>> Equal [<<Param>>,<<ConstNull>>] loop:none + /// CHECK-DAG: If [<<Eq>>] loop:none + /// CHECK-DAG: ArraySet loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If [<<Eq>>] loop:<<Loop>> outer_loop:none + private static final void peelingAddInts(int[] a) { + for (int i = 0; a != null && i < a.length; i++) { + a[i] += 1; + } + } + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) loop_optimization (before) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<Phi0:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop0:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi1>>,<<Limit>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: If [<<Param>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArrayGet loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArraySet loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<IndAdd1:i\d+>> Add [<<Phi1>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<IndAdd0:i\d+>> Add [<<Phi0>>,<<Const1>>] loop:<<Loop0>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4096 loop:none + /// CHECK-DAG: <<Phi0:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop0:B\d+>> outer_loop:none + /// CHECK-DAG: If [<<Param>>] loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: ArrayGet loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<Phi1>>,<<Limit>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArrayGet loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArraySet loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<IndAdd1:i\d+>> Add [<<Phi1>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<IndAdd0:i\d+>> Add [<<Phi0>>,<<Const1>>] loop:<<Loop0>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + private static final void peelingBreakFromNest(int[] a, boolean f) { + outer: + for (int i = 1; i < 32; i++) { + for (int j = 0; j < LENGTH; j++) { + if (f) { + break outer; + } + a[j] += 1; + } + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int) loop_optimization (before) + /// CHECK-DAG: <<Param:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Check:z\d+>> NotEqual [<<Param>>,<<Const0>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + + /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after) + /// CHECK-DAG: <<Param:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Check:z\d+>> NotEqual [<<Param>>,<<Const0>>] loop:none + /// CHECK-DAG: If [<<Check>>] loop:none + /// CHECK-DAG: SuspendCheck loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Goto loop:<<Loop>> outer_loop:none + // + // Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop). + /// CHECK-NOT: loop:<<Loop>> outer_loop:none + /// CHECK-NOT: If + /// CHECK-NOT: Phi + /// CHECK-NOT: Add + private static final int peelingHoistOneControl(int x) { + int i = 0; + while (true) { + if (x == 0) + return 1; + i++; + } + } + + /// CHECK-START: int Main.peelingHoistOneControl(int, int) loop_optimization (before) + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-NOT: If loop:<<Loop>> outer_loop:none + private static final int peelingHoistOneControl(int x, int y) { + while (true) { + if (x == 0) + return 1; + if (y == 0) // no longer invariant + return 2; + y--; + } + } + + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) loop_optimization (before) + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after) + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: If loop:<<Loop>> outer_loop:none + /// CHECK-NOT: If loop:<<Loop>> outer_loop:none + private static final int peelingHoistTwoControl(int x, int y, int z) { + while (true) { + if (x == 0) + return 1; + if (y == 0) + return 2; + if (z == 0) // no longer invariant + return 3; + z--; + } + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public void verifyUnrolling() { + initIntArray(a); + initIntArray(b); + + initMatrix(mA); + initMatrix(mB); + initMatrix(mC); + + int expected = 174291419; + int found = 0; + + unrollingWhile(a); + unrollingLoadStoreElimination(a); + unrollingSwitch(a); + unrollingSwapElements(a); + unrollingRInnerproduct(mC, mA, mB, RESULT_POS, RESULT_POS); + unrollingInTheNest(a, b, RESULT_POS); + unrollingTwoLoopsInTheNest(a, b, RESULT_POS); + + noUnrollingOddTripCount(b); + noUnrollingNotKnownTripCount(b, 128); + + for (int i = 0; i < LENGTH; i++) { + found += a[i]; + found += b[i]; + } + found += (int)mC[RESULT_POS][RESULT_POS]; + + expectEquals(expected, found); + } + + public void verifyPeeling() { + expectEquals(1, peelingHoistOneControl(0)); // anything else loops + expectEquals(1, peelingHoistOneControl(0, 0)); + expectEquals(1, peelingHoistOneControl(0, 1)); + expectEquals(2, peelingHoistOneControl(1, 0)); + expectEquals(2, peelingHoistOneControl(1, 1)); + expectEquals(1, peelingHoistTwoControl(0, 0, 0)); + expectEquals(1, peelingHoistTwoControl(0, 0, 1)); + expectEquals(1, peelingHoistTwoControl(0, 1, 0)); + expectEquals(1, peelingHoistTwoControl(0, 1, 1)); + expectEquals(2, peelingHoistTwoControl(1, 0, 0)); + expectEquals(2, peelingHoistTwoControl(1, 0, 1)); + expectEquals(3, peelingHoistTwoControl(1, 1, 0)); + expectEquals(3, peelingHoistTwoControl(1, 1, 1)); + + initIntArray(a); + peelingSimple(a, false); + peelingSimple(a, true); + peelingAddInts(a); + peelingAddInts(null); // okay + peelingBreakFromNest(a, false); + peelingBreakFromNest(a, true); + + int expected = 141312; + int found = 0; + for (int i = 0; i < a.length; i++) { + found += a[i]; + } + + expectEquals(expected, found); + } + + public static void main(String[] args) { + Main obj = new Main(); + + obj.verifyUnrolling(); + obj.verifyPeeling(); + + System.out.println("passed"); + } +} |