| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 |
| |
| ; RUN: opt < %s -passes='pgo-force-function-attrs,function(loop-mssa(simple-loop-unswitch<nontrivial>))' -pgo-kind=pgo-instr-use-pipeline -pgo-cold-func-opt=optsize -S | FileCheck %s |
| ; RUN: opt < %s -passes='pgo-force-function-attrs,function(loop-mssa(simple-loop-unswitch<nontrivial>))' -pgo-kind=pgo-instr-use-pipeline -pgo-cold-func-opt=minsize -S | FileCheck %s |
| |
| ;; Check that genuinely non-trivial loop unswitching - where the optimizer |
| ;; would have to duplicate the entire loop body - is suppressed for cold |
| ;; (optsize/minsize) functions. |
| ;; |
| ;; The branch on %cond in the loop header is loop-invariant, but both of its |
| ;; successors (if.then, if.else) stay inside the loop, so unswitching it |
| ;; requires producing two full copies of the loop. That code-size increase |
| ;; must be blocked when the function is cold. |
| ;; |
| ;; Contrast with unswitch-cold-func.ll, where the invariant condition can be |
| ;; hoisted via a latch-bypass rewrite that introduces no code duplication and |
| ;; is therefore allowed even for cold functions. |
| ;; |
| ;; Source: |
| ;; void function(bool cond, int N, int *A, int *B) { |
| ;; for (int i = 0; i < N; i++) { |
| ;; if (cond) A[i]++; |
| ;; else B[i]++; |
| ;; } |
| ;; } |
| ;; profiled when called with N=0 (cold). |
| |
| define void @function(i1 %cond, i32 %N, ptr %A, ptr %B) !prof !16 { |
| ; CHECK-LABEL: define void @function |
| ; CHECK-SAME: (i1 [[COND:%.*]], i32 [[N:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0:[0-9]+]] {{.*}}{ |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp sle i32 [[N]], 0 |
| ; CHECK-NEXT: br i1 [[CMP_NOT]], label [[EXIT:%.*]], label [[LOOP_PREHEADER:%.*]], !prof [[PROF17:![0-9]+]] |
| ; CHECK: loop.preheader: |
| ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] |
| ; CHECK: loop.header: |
| ; CHECK-NEXT: [[I:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[I_NEXT:%.*]], [[LOOP_LATCH:%.*]] ] |
| ; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] |
| ; CHECK: if.then: |
| ; CHECK-NEXT: [[GEP_A:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 [[I]] |
| ; CHECK-NEXT: [[VAL_A:%.*]] = load i32, ptr [[GEP_A]], align 4 |
| ; CHECK-NEXT: [[INC_A:%.*]] = add i32 [[VAL_A]], 1 |
| ; CHECK-NEXT: store i32 [[INC_A]], ptr [[GEP_A]], align 4 |
| ; CHECK-NEXT: br label [[LOOP_LATCH]] |
| ; CHECK: if.else: |
| ; CHECK-NEXT: [[GEP_B:%.*]] = getelementptr inbounds i32, ptr [[B]], i32 [[I]] |
| ; CHECK-NEXT: [[VAL_B:%.*]] = load i32, ptr [[GEP_B]], align 4 |
| ; CHECK-NEXT: [[INC_B:%.*]] = add i32 [[VAL_B]], 1 |
| ; CHECK-NEXT: store i32 [[INC_B]], ptr [[GEP_B]], align 4 |
| ; CHECK-NEXT: br label [[LOOP_LATCH]] |
| ; CHECK: loop.latch: |
| ; CHECK-NEXT: [[I_NEXT]] = add nuw i32 [[I]], 1 |
| ; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[I_NEXT]], [[N]] |
| ; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[EXIT_LOOPEXIT:%.*]], label [[LOOP_HEADER]], !prof [[PROF17]] |
| ; CHECK: exit.loopexit: |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| %cmp.not = icmp sle i32 %N, 0 |
| br i1 %cmp.not, label %exit, label %loop.preheader, !prof !17 |
| |
| loop.preheader: |
| br label %loop.header |
| |
| loop.header: |
| %i = phi i32 [ 0, %loop.preheader ], [ %i.next, %loop.latch ] |
| br i1 %cond, label %if.then, label %if.else |
| |
| if.then: |
| %gep.a = getelementptr inbounds i32, ptr %A, i32 %i |
| %val.a = load i32, ptr %gep.a, align 4 |
| %inc.a = add i32 %val.a, 1 |
| store i32 %inc.a, ptr %gep.a, align 4 |
| br label %loop.latch |
| |
| if.else: |
| %gep.b = getelementptr inbounds i32, ptr %B, i32 %i |
| %val.b = load i32, ptr %gep.b, align 4 |
| %inc.b = add i32 %val.b, 1 |
| store i32 %inc.b, ptr %gep.b, align 4 |
| br label %loop.latch |
| |
| loop.latch: |
| %i.next = add nuw i32 %i, 1 |
| %exitcond.not = icmp eq i32 %i.next, %N |
| br i1 %exitcond.not, label %exit, label %loop.header, !prof !17 |
| |
| exit: |
| ret void |
| } |
| |
| !llvm.module.flags = !{!0} |
| |
| !0 = !{i32 1, !"ProfileSummary", !1} |
| !1 = !{!2, !3, !4, !5, !6, !7, !8, !9, !10, !11} |
| !2 = !{!"ProfileFormat", !"InstrProf"} |
| !3 = !{!"TotalCount", i64 1002} |
| !4 = !{!"MaxCount", i64 1000} |
| !5 = !{!"MaxInternalCount", i64 1000} |
| !6 = !{!"MaxFunctionCount", i64 1} |
| !7 = !{!"NumCounts", i64 6} |
| !8 = !{!"NumFunctions", i64 3} |
| !9 = !{!"IsPartialProfile", i64 0} |
| !10 = !{!"PartialProfileRatio", double 0.000000e+00} |
| !11 = !{!"DetailedSummary", !12} |
| !12 = !{!13, !14, !15} |
| !13 = !{i32 10000, i64 1000, i32 1} |
| !14 = !{i32 999000, i64 1000, i32 1} |
| !15 = !{i32 999999, i64 1, i32 3} |
| !16 = !{!"function_entry_count", i64 1} |
| !17 = !{!"branch_weights", i32 1, i32 0} |
| |