| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK |
| ; RUN: opt -passes=attributor -attributor-print-call-graph -S -disable-output < %s | FileCheck %s --check-prefixes=DOT |
| |
| define dso_local void @func1() { |
| ; CHECK-LABEL: @func1( |
| ; CHECK-NEXT: br label [[TMP2:%.*]] |
| ; CHECK: 1: |
| ; CHECK-NEXT: unreachable |
| ; CHECK: 2: |
| ; CHECK-NEXT: call void @func3() |
| ; CHECK-NEXT: ret void |
| ; |
| %1 = icmp ne i32 0, 0 |
| br i1 %1, label %2, label %3 |
| |
| 2: ; preds = %0 |
| call void @func2() |
| br label %3 |
| |
| 3: ; preds = %2, %0 |
| call void () @func3() |
| ret void |
| } |
| |
| declare void @func3() |
| declare void @func4() |
| |
| define dso_local void @func2() { |
| ; CHECK-LABEL: @func2( |
| ; CHECK-NEXT: call void @func4() |
| ; CHECK-NEXT: ret void |
| ; |
| call void @func4() |
| ret void |
| } |
| |
| |
| define void @func5(i32 %0) { |
| ; CHECK-LABEL: @func5( |
| ; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0 |
| ; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], void ()* @func4, void ()* @func3 |
| ; CHECK-NEXT: call void [[TMP3]]() |
| ; CHECK-NEXT: ret void |
| ; |
| %2 = icmp ne i32 %0, 0 |
| %3 = select i1 %2, void ()* @func4, void ()* @func3 |
| call void () %3() |
| ret void |
| } |
| |
| define void @broker(void ()* %unknown) !callback !0 { |
| ; CHECK-LABEL: @broker( |
| ; CHECK-NEXT: call void [[UNKNOWN:%.*]]() |
| ; CHECK-NEXT: ret void |
| ; |
| call void %unknown() |
| ret void |
| } |
| |
| define void @func6() { |
| ; CHECK-LABEL: @func6( |
| ; CHECK-NEXT: call void @broker(void ()* nocapture nofree noundef @func3) |
| ; CHECK-NEXT: ret void |
| ; |
| call void @broker(void ()* @func3) |
| ret void |
| } |
| |
| define void @func7(void ()* %unknown) { |
| ; CHECK-LABEL: @func7( |
| ; CHECK-NEXT: call void [[UNKNOWN:%.*]](), !callees !2 |
| ; CHECK-NEXT: ret void |
| ; |
| call void %unknown(), !callees !2 |
| ret void |
| } |
| |
| ; Check there's no crash if something that isn't a function appears in !callees |
| define void @undef_in_callees() { |
| ; CHECK-LABEL: @undef_in_callees( |
| ; CHECK-NEXT: cond.end.i: |
| ; CHECK-NEXT: call void undef(i8* undef, i32 undef, i8* undef), !callees !3 |
| ; CHECK-NEXT: ret void |
| ; |
| cond.end.i: |
| call void undef(i8* undef, i32 undef, i8* undef), !callees !3 |
| ret void |
| } |
| |
| !0 = !{!1} |
| !1 = !{i64 0, i1 false} |
| !2 = !{void ()* @func3, void ()* @func4} |
| !3 = distinct !{void (i8*, i32, i8*)* undef, void (i8*, i32, i8*)* null} |
| |
| ; UTC_ARGS: --disable |
| |
| ; DOT-DAG: Node[[FUNC1:0x[a-z0-9]+]] [shape=record,label="{func1}"]; |
| ; DOT-DAG: Node[[FUNC2:0x[a-z0-9]+]] [shape=record,label="{func2}"]; |
| ; DOT-DAG: Node[[FUNC3:0x[a-z0-9]+]] [shape=record,label="{func3}"]; |
| ; DOT-DAG: Node[[FUNC4:0x[a-z0-9]+]] [shape=record,label="{func4}"]; |
| ; DOT-DAG: Node[[FUNC5:0x[a-z0-9]+]] [shape=record,label="{func5}"]; |
| ; DOT-DAG: Node[[FUNC6:0x[a-z0-9]+]] [shape=record,label="{func6}"]; |
| ; DOT-DAG: Node[[FUNC7:0x[a-z0-9]+]] [shape=record,label="{func7}"]; |
| |
| ; DOT-DAG: Node[[BROKER:0x[a-z0-9]+]] [shape=record,label="{broker}"]; |
| |
| ; DOT-DAG: Node[[FUNC1]] -> Node[[FUNC3]]; |
| ; DOT-DAG: Node[[FUNC2]] -> Node[[FUNC4]]; |
| ; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC3]]; |
| ; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC4]]; |
| |
| ; DOT-DAG: Node[[FUNC6]] -> Node[[BROKER]]; |
| |
| ; This one gets added because of the callback metadata. |
| ; DOT-DAG: Node[[FUNC6]] -> Node[[FUNC3]]; |
| |
| ; These ones are added because of the callees metadata. |
| ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC3]]; |
| ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC4]]; |