| ; RUN: %swift-llvm-opt -swift-merge-functions -swiftmergefunc-threshold=4 %s | %FileCheck %s |
| |
| @g1 = external global i32 |
| @g2 = external global i32 |
| @g3 = external global i32 |
| @g4 = external global i32 |
| @g5 = external global i32 |
| |
| ; Test the most trivial example. |
| |
| ; CHECK-LABEL: define i32 @simple_func1(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call i32 @simple_func1Tm(i32 %x, i32 %y, i32* @g1) |
| ; CHECK: ret i32 %1 |
| define i32 @simple_func1(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %l = load i32, i32* @g1, align 4 |
| %sum3 = add i32 %sum2, %y |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define i32 @simple_func2(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call i32 @simple_func1Tm(i32 %x, i32 %y, i32* @g2) |
| ; CHECK: ret i32 %1 |
| define i32 @simple_func2(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %l = load i32, i32* @g2, align 4 |
| %sum3 = add i32 %sum2, %y |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define internal i32 @simple_func1Tm(i32, i32, i32*) |
| ; CHECK: %l = load i32, i32* %2 |
| ; CHECK: ret |
| |
| |
| ; Merge 3 functions with 3 types of differing instructions: load, store and call. |
| |
| ; CHECK-LABEL: define i32 @func1_of_3(i32 %x) |
| ; CHECK: %1 = tail call i32 @func1_of_3Tm(i32 %x, i32* @g1, i32* @g1, void (i32)* @callee1) |
| ; CHECK: ret i32 %1 |
| define i32 @func1_of_3(i32 %x) { |
| %l1 = load i32, i32* @g1, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g1, align 4 |
| %sum2 = add i32 %sum, %l2 |
| store i32 %sum2, i32 *@g1, align 4 |
| call void @callee1(i32 %sum2) |
| %sum3 = add i32 %sum2, %l2 |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define i32 @func2_of_3(i32 %x) |
| ; CHECK: %1 = tail call i32 @func1_of_3Tm(i32 %x, i32* @g2, i32* @g2, void (i32)* @callee2) |
| ; CHECK: ret i32 %1 |
| define i32 @func2_of_3(i32 %x) { |
| %l1 = load i32, i32* @g2, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g2, align 4 |
| %sum2 = add i32 %sum, %l2 |
| store i32 %sum2, i32 *@g2, align 4 |
| call void @callee2(i32 %sum2) |
| %sum3 = add i32 %sum2, %l2 |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define i32 @func3_of_3(i32 %x) |
| ; CHECK: %1 = tail call i32 @func1_of_3Tm(i32 %x, i32* @g3, i32* @g1, void (i32)* @callee3) |
| ; CHECK: ret i32 %1 |
| define i32 @func3_of_3(i32 %x) { |
| %l1 = load i32, i32* @g3, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g1, align 4 |
| %sum2 = add i32 %sum, %l2 |
| store i32 %sum2, i32 *@g3, align 4 |
| call void @callee3(i32 %sum2) |
| %sum3 = add i32 %sum2, %l2 |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define internal i32 @func1_of_3Tm(i32, i32*, i32*, void (i32)*) |
| ; CHECK: %l1 = load i32, i32* %1 |
| ; CHECK: %l2 = load i32, i32* %2 |
| ; CHECK: store i32 %sum2, i32* %1 |
| ; CHECK: call void %3(i32 %sum2) |
| ; CHECK: ret |
| |
| declare void @callee1(i32 %x) |
| declare void @callee2(i32 %x) |
| declare void @callee3(i32 %x) |
| |
| ; Preserve attributes |
| |
| ; CHECK-LABEL: define void @sret_func1(i32* sret %p, i32 %x, i32 %y) |
| ; CHECK: tail call void @sret_func1Tm(i32* sret %p, i32 %x, i32 %y, i32* @g1) |
| ; CHECK: ret void |
| define void @sret_func1(i32* sret %p, i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %l = load i32, i32* @g1, align 4 |
| %sum2 = add i32 %sum, %l |
| store i32 %sum2, i32* %p |
| ret void |
| } |
| |
| ; CHECK-LABEL: define void @sret_func2(i32* sret %p, i32 %x, i32 %y) |
| ; CHECK: tail call void @sret_func1Tm(i32* sret %p, i32 %x, i32 %y, i32* @g2) |
| ; CHECK: ret void |
| define void @sret_func2(i32* sret %p, i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %l = load i32, i32* @g2, align 4 |
| %sum2 = add i32 %sum, %l |
| store i32 %sum2, i32* %p |
| ret void |
| } |
| |
| ; CHECK-LABEL: define internal void @sret_func1Tm(i32* sret, i32, i32, i32*) |
| ; CHECK: %l = load i32, i32* %3, align 4 |
| ; CHECK: store i32 %sum2, i32* %0 |
| ; CHECK: ret |
| |
| |
| ; Don't merge all functions, because we would generate too many parameters. |
| ; Instead merge those functions which match best. |
| |
| ; CHECK-LABEL: define i32 @func1_merged_with3(i32 %x) |
| ; CHECK: %1 = tail call i32 @func1_merged_with3Tm(i32 %x, i32* @g1) |
| ; CHECK: ret i32 %1 |
| define i32 @func1_merged_with3(i32 %x) { |
| %l1 = load i32, i32* @g1, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g2, align 4 |
| %sum2 = add i32 %sum, %l2 |
| %l3 = load i32, i32* @g3, align 4 |
| %sum3 = add i32 %sum2, %l2 |
| %l4 = load i32, i32* @g4, align 4 |
| %sum4 = add i32 %sum3, %l2 |
| %l5 = load i32, i32* @g5, align 4 |
| %sum5 = add i32 %sum4, %l2 |
| ret i32 %sum5 |
| } |
| |
| ; CHECK-LABEL: define i32 @func2_merged_with4(i32 %x) |
| ; CHECK: %1 = tail call i32 @func2_merged_with4Tm(i32 %x, i32* @g2) |
| ; CHECK: ret i32 %1 |
| define i32 @func2_merged_with4(i32 %x) { |
| %l1 = load i32, i32* @g2, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g3, align 4 |
| %sum2 = add i32 %sum, %l2 |
| %l3 = load i32, i32* @g4, align 4 |
| %sum3 = add i32 %sum2, %l2 |
| %l4 = load i32, i32* @g5, align 4 |
| %sum4 = add i32 %sum3, %l2 |
| %l5 = load i32, i32* @g1, align 4 |
| %sum5 = add i32 %sum4, %l2 |
| ret i32 %sum5 |
| } |
| |
| ; CHECK-LABEL: define i32 @func3_merged_with1(i32 %x) |
| ; CHECK: %1 = tail call i32 @func1_merged_with3Tm(i32 %x, i32* @g2) |
| ; CHECK: ret i32 %1 |
| define i32 @func3_merged_with1(i32 %x) { |
| %l1 = load i32, i32* @g2, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g2, align 4 |
| %sum2 = add i32 %sum, %l2 |
| %l3 = load i32, i32* @g3, align 4 |
| %sum3 = add i32 %sum2, %l2 |
| %l4 = load i32, i32* @g4, align 4 |
| %sum4 = add i32 %sum3, %l2 |
| %l5 = load i32, i32* @g5, align 4 |
| %sum5 = add i32 %sum4, %l2 |
| ret i32 %sum5 |
| } |
| |
| ; CHECK-LABEL: define internal i32 @func1_merged_with3Tm(i32, i32*) |
| ; CHECK: load i32, i32* %1, align 4 |
| ; CHECK: load i32, i32* @g2, align 4 |
| ; CHECK: load i32, i32* @g3, align 4 |
| ; CHECK: load i32, i32* @g4, align 4 |
| ; CHECK: load i32, i32* @g5, align 4 |
| ; CHECK: ret i32 |
| |
| ; CHECK-LABEL: define i32 @func4_merged_with2(i32 %x) { |
| ; CHECK: %1 = tail call i32 @func2_merged_with4Tm(i32 %x, i32* @g1) |
| ; CHECK: ret i32 %1 |
| define i32 @func4_merged_with2(i32 %x) { |
| %l1 = load i32, i32* @g1, align 4 |
| %sum = add i32 %x, %l1 |
| %l2 = load i32, i32* @g3, align 4 |
| %sum2 = add i32 %sum, %l2 |
| %l3 = load i32, i32* @g4, align 4 |
| %sum3 = add i32 %sum2, %l2 |
| %l4 = load i32, i32* @g5, align 4 |
| %sum4 = add i32 %sum3, %l2 |
| %l5 = load i32, i32* @g1, align 4 |
| %sum5 = add i32 %sum4, %l2 |
| ret i32 %sum5 |
| } |
| |
| |
| ; Test a call chain: caller -> callee1 -> callee2. |
| ; Functions should be merged in bottom-up order: callee2, callee1, caller. |
| ; Also check that the calling convention is preserved. |
| |
| ; CHECK-LABEL: define fastcc i32 @callee1_a(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call fastcc i32 @callee1_aTm(i32 %x, i32 %y, i32* @g1) |
| ; CHECK: ret i32 %1 |
| define fastcc i32 @callee1_a(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %c = call i32 @callee2_a(i32 %sum2, i32 %y) |
| %sum3 = add i32 %sum2, %c |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define fastcc i32 @callee1_b(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call fastcc i32 @callee1_aTm(i32 %x, i32 %y, i32* @g2) |
| ; CHECK: ret i32 %1 |
| define fastcc i32 @callee1_b(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %c = call i32 @callee2_b(i32 %sum2, i32 %y) |
| %sum3 = add i32 %sum2, %c |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define internal fastcc i32 @callee1_aTm(i32, i32, i32*) |
| ; CHECK: call i32 @callee2_aTm(i32 %sum2, i32 %1, i32* %2) |
| ; CHECK: ret |
| |
| ; CHECK-NOT: @callee2_a( |
| define internal i32 @callee2_a(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = sub i32 %sum, %y |
| %l = load i32, i32* @g1, align 4 |
| %sum3 = add i32 %sum2, %y |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-NOT: @callee2_b( |
| define internal i32 @callee2_b(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = sub i32 %sum, %y |
| %l = load i32, i32* @g2, align 4 |
| %sum3 = add i32 %sum2, %y |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define i32 @caller_a(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call i32 @caller_aTm(i32 %x, i32 %y, i32* @g1) |
| ; CHECK: ret i32 %1 |
| define i32 @caller_a(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %c = call fastcc i32 @callee1_a(i32 %sum2, i32 %y) |
| %sum3 = add i32 %sum2, %c |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define i32 @caller_b(i32 %x, i32 %y) |
| ; CHECK: %1 = tail call i32 @caller_aTm(i32 %x, i32 %y, i32* @g2) |
| ; CHECK: ret i32 %1 |
| define i32 @caller_b(i32 %x, i32 %y) { |
| %sum = add i32 %x, %y |
| %sum2 = add i32 %sum, %y |
| %c = call fastcc i32 @callee1_b(i32 %sum2, i32 %y) |
| %sum3 = add i32 %sum2, %c |
| ret i32 %sum3 |
| } |
| |
| ; CHECK-LABEL: define internal i32 @caller_aTm(i32, i32, i32*) |
| ; CHECK: call fastcc i32 @callee1_aTm(i32 %sum2, i32 %1, i32* %2) |
| ; CHECK: ret |
| |
| |
| ; Ensure that we do not merge functions that are identical with the |
| ; exception of the order of the incoming blocks to a phi. |
| |
| ; CHECK-LABEL: define linkonce_odr hidden i1 @first(i2) |
| define linkonce_odr hidden i1 @first(i2) { |
| entry: |
| ; CHECK: switch i2 |
| switch i2 %0, label %default [ |
| i2 0, label %L1 |
| i2 1, label %L2 |
| i2 -2, label %L3 |
| ] |
| default: |
| unreachable |
| L1: |
| br label %done |
| L2: |
| br label %done |
| L3: |
| br label %done |
| done: |
| %result = phi i1 [ true, %L1 ], [ false, %L2 ], [ false, %L3 ] |
| ; CHECK: ret i1 |
| ret i1 %result |
| } |
| |
| ; CHECK-LABEL: define linkonce_odr hidden i1 @second(i2) |
| define linkonce_odr hidden i1 @second(i2) { |
| entry: |
| ; CHECK: switch i2 |
| switch i2 %0, label %default [ |
| i2 0, label %L1 |
| i2 1, label %L2 |
| i2 -2, label %L3 |
| ] |
| default: |
| unreachable |
| L1: |
| br label %done |
| L2: |
| br label %done |
| L3: |
| br label %done |
| done: |
| %result = phi i1 [ true, %L3 ], [ false, %L2 ], [ false, %L1 ] |
| ; CHECK: ret i1 |
| ret i1 %result |
| } |
| |
| ; Check self recursive functions |
| |
| ; CHECK-LABEL: define internal void @recursive1(i32 %x, i32 %y) |
| ; CHECK: tail call void @recursive1Tm(i32 %x, i32 %y, i32* @g1, void (i32, i32)* @recursive1) |
| ; CHECK: ret void |
| define internal void @recursive1(i32 %x, i32 %y) { |
| br i1 undef, label %bb1, label %bb2 |
| |
| bb1: |
| %l = load i32, i32* @g1, align 4 |
| call void @recursive1(i32 %x, i32 %y) |
| br label %bb2 |
| |
| bb2: |
| ret void |
| } |
| |
| ; CHECK-LABEL: define internal void @recursive2(i32 %x, i32 %y) |
| ; CHECK: tail call void @recursive1Tm(i32 %x, i32 %y, i32* @g2, void (i32, i32)* @recursive2) |
| ; CHECK: ret void |
| define internal void @recursive2(i32 %x, i32 %y) { |
| br i1 undef, label %bb1, label %bb2 |
| |
| bb1: |
| %l = load i32, i32* @g2, align 4 |
| call void @recursive2(i32 %x, i32 %y) |
| br label %bb2 |
| |
| bb2: |
| ret void |
| } |
| ; CHECK-LABEL: define internal void @recursive1Tm(i32, i32, i32*, void (i32, i32)*) |
| ; CHECK: load i32, i32* %2 |
| ; CHECK: call void %3(i32 %0, i32 %1) |
| ; CHECK: ret void |
| |
| |
| ; CHECK-LABEL: define internal void @another_recursive_func(i32 %x) |
| ; CHECK: tail call void @another_recursive_funcTm(i32 %x, i32* @g1, void (i32)* @another_recursive_func) |
| ; CHECK: ret void |
| define internal void @another_recursive_func(i32 %x) { |
| br i1 undef, label %bb1, label %bb2 |
| |
| bb1: |
| store i32 %x, i32 *@g1, align 4 |
| call void @another_recursive_func(i32 %x) |
| br label %bb2 |
| |
| bb2: |
| ret void |
| } |
| ; CHECK-NOT: @not_really_recursive( |
| |
| ; CHECK-LABEL: define internal void @another_recursive_funcTm(i32, i32*, void (i32)*) |
| ; CHECK: store i32 %0, i32* %1 |
| ; CHECK: call void %2(i32 %0) |
| ; CHECK: ret void |
| define internal void @not_really_recursive(i32 %x) { |
| br i1 undef, label %bb1, label %bb2 |
| |
| bb1: |
| store i32 %x, i32 *@g2, align 4 |
| call void @callee1(i32 %x) |
| br label %bb2 |
| |
| bb2: |
| ret void |
| } |
| ; CHECK-NOT: @not_really_recursive( |
| |
| ; CHECK-LABEL: define void @call_recursive_funcs(i32 %x) |
| ; CHECK: call void @recursive1(i32 %x, i32 %x) |
| ; CHECK: call void @recursive2(i32 %x, i32 %x) |
| ; CHECK: call void @another_recursive_func(i32 %x) |
| ; CHECK: call void @another_recursive_funcTm(i32 %x, i32* @g2, void (i32)* @callee1) |
| ; CHECK: ret void |
| define void @call_recursive_funcs(i32 %x) { |
| call void @recursive1(i32 %x, i32 %x) |
| call void @recursive2(i32 %x, i32 %x) |
| call void @another_recursive_func(i32 %x) |
| call void @not_really_recursive(i32 %x) |
| ret void |
| } |
| |