| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 |
| ; RUN: opt -S -passes=mergefunc < %s | FileCheck %s |
| ; |
| |
| ; Verify that MergeFunctions correctly merges value profile (VP) metadata |
| ; on indirect calls when combining structurally identical functions. |
| ; Because FunctionComparator only considers functions equivalent when |
| ; their control-flow structure and instruction ordering match exactly, |
| ; MergeFunctions can walk the two functions in parallel and safely merge |
| ; instruction-level metadata. |
| ; |
| ; Value profile counts are already absolute. The pass combines profiles by |
| ; adding counts for targets that appear in both functions and preserving |
| ; targets that appear in only one. |
| |
| ; -------------------------------------------------------------------------- |
| ; Both functions provide value profile metadata for the indirect call. |
| ; |
| ; vp_both_a has an entry count of 100 and a profile of: |
| ; {111:60, 222:40} |
| ; |
| ; vp_both_b has an entry count of 200 and a profile of: |
| ; {111:120, 333:80} |
| ; |
| ; After merging, counts for shared targets are summed while unique |
| ; targets are carried over: |
| ; |
| ; entry count = 300 |
| ; VP {111:180, 333:80, 222:40} |
| ; -------------------------------------------------------------------------- |
| define internal i32 @vp_both_a(ptr %fn) !prof !1 { |
| ; CHECK-LABEL: define internal i32 @vp_both_a( |
| ; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF0:![0-9]+]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8 |
| ; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF1:![0-9]+]] |
| ; CHECK-NEXT: ret i32 [[R]] |
| ; |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val(), !prof !2 |
| ret i32 %r |
| } |
| |
| define internal i32 @vp_both_b(ptr %fn) !prof !3 { |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val(), !prof !4 |
| ret i32 %r |
| } |
| |
| ; -------------------------------------------------------------------------- |
| ; Only one of the functions provides value profile metadata for the |
| ; indirect call. |
| ; |
| ; vp_one_a has an entry count of 30 but does not contain any VP data |
| ; on the call site. |
| ; |
| ; vp_one_b has an entry count of 70 and records the target profile: |
| ; {888:50} |
| ; |
| ; Since only vp_one_b contributes value profile information, the merged |
| ; function carries that profile forward unchanged while combining the |
| ; overall entry counts. |
| ; |
| ; entry count = 100 |
| ; VP {888:50} |
| ; -------------------------------------------------------------------------- |
| define internal i32 @vp_one_a(ptr %fn) !prof !5 { |
| ; CHECK-LABEL: define internal i32 @vp_one_a( |
| ; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2:![0-9]+]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8 |
| ; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF3:![0-9]+]] |
| ; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 3 |
| ; CHECK-NEXT: ret i32 [[ADJ]] |
| ; |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val() |
| %adj = add i32 %r, 3 |
| ret i32 %adj |
| } |
| |
| define internal i32 @vp_one_b(ptr %fn) !prof !6 { |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val(), !prof !7 |
| %adj = add i32 %r, 3 |
| ret i32 %adj |
| } |
| |
| ; -------------------------------------------------------------------------- |
| ; Both functions attach value profile metadata to the indirect call, but |
| ; they use different VP kinds. |
| ; |
| ; Because the profiles are not compatible, MergeFunctions cannot safely |
| ; merge them into a single representation. Rather than producing |
| ; potentially misleading profile data, the pass drops the call-site |
| ; !prof metadata during the merge. |
| ; -------------------------------------------------------------------------- |
| define internal i32 @vp_kind_a(ptr %fn) !prof !8 { |
| ; CHECK-LABEL: define internal i32 @vp_kind_a( |
| ; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8 |
| ; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]]() |
| ; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 2 |
| ; CHECK-NEXT: ret i32 [[ADJ]] |
| ; |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val(), !prof !9 |
| %adj = add i32 %r, 2 |
| ret i32 %adj |
| } |
| |
| define internal i32 @vp_kind_b(ptr %fn) !prof !10 { |
| entry: |
| %fn_val = load ptr, ptr %fn |
| %r = call i32 %fn_val(), !prof !11 |
| %adj = add i32 %r, 2 |
| ret i32 %adj |
| } |
| |
| !1 = !{!"function_entry_count", i64 100} |
| !2 = !{!"VP", i32 0, i64 100, i64 111, i64 60, i64 222, i64 40} |
| !3 = !{!"function_entry_count", i64 200} |
| !4 = !{!"VP", i32 0, i64 200, i64 111, i64 120, i64 333, i64 80} |
| !5 = !{!"function_entry_count", i64 30} |
| !6 = !{!"function_entry_count", i64 70} |
| !7 = !{!"VP", i32 0, i64 70, i64 888, i64 50} |
| !8 = !{!"function_entry_count", i64 30} |
| !9 = !{!"VP", i32 0, i64 30, i64 666, i64 20} |
| !10 = !{!"function_entry_count", i64 70} |
| !11 = !{!"VP", i32 1, i64 70, i64 777, i64 50} |
| ;. |
| ; CHECK: [[PROF0]] = !{!"function_entry_count", i64 300} |
| ; CHECK: [[PROF1]] = !{!"VP", i32 0, i64 300, i64 111, i64 180, i64 333, i64 80, i64 222, i64 40} |
| ; CHECK: [[PROF2]] = !{!"function_entry_count", i64 100} |
| ; CHECK: [[PROF3]] = !{!"VP", i32 0, i64 50, i64 888, i64 50} |
| ;. |