mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
411 lines
12 KiB
LLVM
411 lines
12 KiB
LLVM
; 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_func1_merged(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_func1_merged(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_func1_merged(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_3_merged(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_3_merged(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_3_merged(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_3_merged(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_func1_merged(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_func1_merged(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_func1_merged(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_with3_merged(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_with4_merged(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_with3_merged(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_with3_merged(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_with4_merged(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_a_merged(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_a_merged(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_a_merged(i32, i32, i32*)
|
|
; CHECK: call i32 @callee2_a_merged(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_a_merged(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_a_merged(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_a_merged(i32, i32, i32*)
|
|
; CHECK: call fastcc i32 @callee1_a_merged(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 @recursive1_merged(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 @recursive1_merged(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 @recursive1_merged(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_func_merged(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_func_merged(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_func_merged(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
|
|
}
|
|
|