[llvm] [FunctionAttrs] deduce attr `cold` on functions if all CG paths call a `cold` function (PR #101298)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 10 23:02:28 PDT 2024


https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/101298

>From ffcbfa46b9926bcb42dd7b1f778ada113672595c Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 31 Jul 2024 14:20:52 +0800
Subject: [PATCH 1/4] [FunctionAttrs] Add tests for deducing attr `cold` on
 functions; NFC

---
 llvm/test/Transforms/FunctionAttrs/cold.ll | 694 +++++++++++++++++++++
 1 file changed, 694 insertions(+)
 create mode 100644 llvm/test/Transforms/FunctionAttrs/cold.ll

diff --git a/llvm/test/Transforms/FunctionAttrs/cold.ll b/llvm/test/Transforms/FunctionAttrs/cold.ll
new file mode 100644
index 00000000000000..1fa8ae06797943
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/cold.ll
@@ -0,0 +1,694 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
+
+declare i32 @get_val()
+
+declare void @cold0() cold
+declare void @cold1() cold
+declare void @cold_at_cb()
+
+declare void @not_cold0()
+declare void @not_cold1()
+declare void @not_cold2()
+
+define void @test_no_exit_fail() {
+; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none)
+; COMMON-LABEL: define void @test_no_exit_fail
+; COMMON-SAME: () #[[ATTR1:[0-9]+]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
+; COMMON:       while.body:
+; COMMON-NEXT:    br label [[WHILE_BODY]]
+;
+entry:
+  br label %while.body
+
+while.body:
+  br label %while.body
+}
+
+define void @test_no_exit_fail2() {
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define void @test_no_exit_fail2
+; COMMON-SAME: () #[[ATTR2:[0-9]+]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
+; COMMON:       while.body:
+; COMMON-NEXT:    call void @not_cold0()
+; COMMON-NEXT:    br label [[WHILE_BODY2:%.*]]
+; COMMON:       while.body2:
+; COMMON-NEXT:    call void @not_cold1()
+; COMMON-NEXT:    br label [[WHILE_BODY]]
+;
+entry:
+  br label %while.body
+
+while.body:
+  call void @not_cold0()
+  br label %while.body2
+
+while.body2:
+  call void @not_cold1()
+  br label %while.body
+}
+
+define void @test_no_exit() {
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define void @test_no_exit
+; COMMON-SAME: () #[[ATTR2]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
+; COMMON:       while.body:
+; COMMON-NEXT:    call void @cold0()
+; COMMON-NEXT:    br label [[WHILE_BODY]]
+;
+entry:
+  br label %while.body
+
+while.body:
+  call void @cold0()
+  br label %while.body
+}
+
+define void @test_no_exit2() {
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define void @test_no_exit2
+; COMMON-SAME: () #[[ATTR2]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
+; COMMON:       while.body:
+; COMMON-NEXT:    call void @not_cold0()
+; COMMON-NEXT:    br label [[WHILE_BODY2:%.*]]
+; COMMON:       while.body2:
+; COMMON-NEXT:    call void @cold1()
+; COMMON-NEXT:    br label [[WHILE_BODY]]
+;
+entry:
+  br label %while.body
+
+while.body:
+  call void @not_cold0()
+  br label %while.body2
+
+while.body2:
+  call void @cold1()
+  br label %while.body
+}
+
+define dso_local void @test_entry(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_entry
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    br label [[IF_END]]
+; COMMON:       if.end:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  br i1 %tobool.not, label %if.end, label %if.then
+
+if.then:
+  tail call void @not_cold0()
+  br label %if.end
+
+if.end:
+  tail call void @not_cold1()
+  ret void
+}
+
+define dso_local void @test_hot_fail(i32 noundef %x) hot {
+; COMMON: Function Attrs: hot
+; COMMON-LABEL: define dso_local void @test_hot_fail
+; COMMON-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @cold0()
+  ret void
+}
+
+define dso_local void @test_br2(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_br2
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END]]
+; COMMON:       if.end:
+; COMMON-NEXT:    ret void
+;
+entry:
+  %tobool.not = icmp eq i32 %x, 0
+  br i1 %tobool.not, label %if.else, label %if.then
+
+if.then:
+  tail call void @cold0()
+  br label %if.end
+
+if.else:
+  tail call void @cold1()
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+define dso_local void @test_exit(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_exit
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[IF_END:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    tail call void @not_cold2()
+; COMMON-NEXT:    br label [[IF_END]]
+; COMMON:       if.end:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @not_cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  br i1 %tobool.not, label %if.else, label %if.then
+
+if.then:
+  tail call void @not_cold1()
+  br label %if.end
+
+if.else:
+  tail call void @not_cold2()
+  br label %if.end
+
+if.end:
+  tail call void @cold0()
+  ret void
+}
+
+define dso_local void @test_complex(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_complex
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    [[CALL:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; COMMON:       if.then2:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END12:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; COMMON:       if.then5:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.else6:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
+; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; COMMON-NEXT:    ]
+; COMMON:       sw.bb:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
+; COMMON:       sw.bb8:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.bb9:
+; COMMON-NEXT:    tail call void @not_cold2()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.default:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       call_cold:
+; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0:[0-9]+]]
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.else11:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.end12:
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @not_cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  br i1 %tobool.not, label %if.else11, label %if.then
+
+if.then:
+  %call = tail call i32 @get_val()
+  %tobool1.not = icmp eq i32 %call, 0
+  br i1 %tobool1.not, label %if.else, label %if.then2
+
+if.then2:
+  tail call void @cold1()
+  br label %if.end12
+
+if.else:
+  %call3 = tail call i32 @get_val()
+  %tobool4.not = icmp eq i32 %call3, 0
+  br i1 %tobool4.not, label %if.else6, label %if.then5
+
+if.then5:
+  tail call void @cold0()
+  br label %if.end12
+
+if.else6:
+  tail call void @not_cold0()
+  %call7 = tail call i32 @get_val()
+  switch i32 %call7, label %sw.default [
+  i32 0, label %sw.bb
+  i32 1, label %sw.bb8
+  i32 2, label %sw.bb9
+  ]
+
+sw.bb:
+  tail call void @not_cold0()
+  br label %call_cold
+
+sw.bb8:
+  tail call void @not_cold1()
+  br label %call_cold
+
+sw.bb9:
+  tail call void @not_cold2()
+  br label %call_cold
+
+sw.default:
+  tail call void @cold0()
+  br label %if.end12
+
+call_cold:
+  tail call void @cold_at_cb() cold
+  br label %if.end12
+
+if.else11:
+  tail call void @cold0()
+  br label %if.end12
+
+if.end12:
+  ret void
+}
+
+define dso_local void @test_complex2(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_complex2
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    [[CALL12:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL12]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; COMMON:       if.then2:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END16:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; COMMON:       if.then5:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       if.else6:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
+; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; COMMON-NEXT:    ]
+; COMMON:       sw.bb:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
+; COMMON:       sw.bb8:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.bb9:
+; COMMON-NEXT:    tail call void @not_cold2()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.default:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       call_cold:
+; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       if.else11:
+; COMMON-NEXT:    [[CMP:%.*]] = icmp slt i32 [[CALL12]], 1
+; COMMON-NEXT:    br i1 [[CMP]], label [[IF_END14:%.*]], label [[FOR_BODY:%.*]]
+; COMMON:       if.end14:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       for.body:
+; COMMON-NEXT:    [[I_021:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[IF_ELSE11]] ]
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    [[INC]] = add nuw nsw i32 [[I_021]], 1
+; COMMON-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[CALL12]]
+; COMMON-NEXT:    br i1 [[EXITCOND_NOT]], label [[IF_END16]], label [[FOR_BODY]]
+; COMMON:       if.end16:
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @not_cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  %call12 = tail call i32 @get_val()
+  br i1 %tobool.not, label %if.else11, label %if.then
+
+if.then:
+  %tobool1.not = icmp eq i32 %call12, 0
+  br i1 %tobool1.not, label %if.else, label %if.then2
+
+if.then2:
+  tail call void @cold1()
+  br label %if.end16
+
+if.else:
+  %call3 = tail call i32 @get_val()
+  %tobool4.not = icmp eq i32 %call3, 0
+  br i1 %tobool4.not, label %if.else6, label %if.then5
+
+if.then5:
+  tail call void @cold0()
+  br label %if.end16
+
+if.else6:
+  tail call void @not_cold0()
+  %call7 = tail call i32 @get_val()
+  switch i32 %call7, label %sw.default [
+  i32 0, label %sw.bb
+  i32 1, label %sw.bb8
+  i32 2, label %sw.bb9
+  ]
+
+sw.bb:
+  tail call void @not_cold0()
+  br label %call_cold
+
+sw.bb8:
+  tail call void @not_cold1()
+  br label %call_cold
+
+sw.bb9:
+  tail call void @not_cold2()
+  br label %call_cold
+
+sw.default:
+  tail call void @cold0()
+  br label %if.end16
+
+call_cold:
+  tail call void @cold_at_cb() cold
+  br label %if.end16
+
+if.else11:
+  %cmp = icmp slt i32 %call12, 1
+  br i1 %cmp, label %if.end14, label %for.body
+
+if.end14:
+  tail call void @cold1()
+  br label %if.end16
+
+for.body:
+  %i.021 = phi i32 [ %inc, %for.body ], [ 0, %if.else11 ]
+  tail call void @cold0()
+  %inc = add nuw nsw i32 %i.021, 1
+  %exitcond.not = icmp eq i32 %inc, %call12
+  br i1 %exitcond.not, label %if.end16, label %for.body
+
+if.end16:
+  ret void
+}
+
+define dso_local void @test_complex_fail(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_complex_fail
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    [[CALL:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; COMMON:       if.then2:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END12:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; COMMON:       if.then5:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.else6:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
+; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; COMMON-NEXT:    ]
+; COMMON:       sw.bb:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
+; COMMON:       sw.bb8:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.bb9:
+; COMMON-NEXT:    tail call void @not_cold2()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       sw.default:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       call_cold:
+; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.else11:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END12]]
+; COMMON:       if.end12:
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @not_cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  br i1 %tobool.not, label %if.else11, label %if.then
+
+if.then:
+  %call = tail call i32 @get_val()
+  %tobool1.not = icmp eq i32 %call, 0
+  br i1 %tobool1.not, label %if.else, label %if.then2
+
+if.then2:
+  tail call void @cold1()
+  br label %if.end12
+
+if.else:
+  %call3 = tail call i32 @get_val()
+  %tobool4.not = icmp eq i32 %call3, 0
+  br i1 %tobool4.not, label %if.else6, label %if.then5
+
+if.then5:
+  tail call void @cold0()
+  br label %if.end12
+
+if.else6:
+  tail call void @not_cold0()
+  %call7 = tail call i32 @get_val()
+  switch i32 %call7, label %sw.default [
+  i32 0, label %sw.bb
+  i32 1, label %sw.bb8
+  i32 2, label %sw.bb9
+  ]
+
+sw.bb:
+  tail call void @not_cold0()
+  br label %call_cold
+
+sw.bb8:
+  tail call void @not_cold1()
+  br label %call_cold
+
+sw.bb9:
+  tail call void @not_cold2()
+  br label %if.end12
+
+sw.default:
+  tail call void @cold0()
+  br label %if.end12
+
+call_cold:
+  tail call void @cold_at_cb() cold
+  br label %if.end12
+
+if.else11:
+  tail call void @cold0()
+  br label %if.end12
+
+if.end12:
+  ret void
+}
+
+define dso_local void @test_complex2_fail(i32 noundef %x) {
+; COMMON-LABEL: define dso_local void @test_complex2_fail
+; COMMON-SAME: (i32 noundef [[X:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; COMMON-NEXT:    [[CALL12:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; COMMON:       if.then:
+; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL12]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; COMMON:       if.then2:
+; COMMON-NEXT:    tail call void @cold1()
+; COMMON-NEXT:    br label [[IF_END16:%.*]]
+; COMMON:       if.else:
+; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; COMMON:       if.then5:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       if.else6:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
+; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; COMMON-NEXT:    ]
+; COMMON:       sw.bb:
+; COMMON-NEXT:    tail call void @not_cold0()
+; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
+; COMMON:       sw.bb8:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.bb9:
+; COMMON-NEXT:    tail call void @not_cold2()
+; COMMON-NEXT:    br label [[CALL_COLD]]
+; COMMON:       sw.default:
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       call_cold:
+; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       if.else11:
+; COMMON-NEXT:    [[CMP:%.*]] = icmp slt i32 [[CALL12]], 1
+; COMMON-NEXT:    br i1 [[CMP]], label [[IF_END14:%.*]], label [[FOR_BODY:%.*]]
+; COMMON:       if.end14:
+; COMMON-NEXT:    tail call void @not_cold1()
+; COMMON-NEXT:    br label [[IF_END16]]
+; COMMON:       for.body:
+; COMMON-NEXT:    [[I_021:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[IF_ELSE11]] ]
+; COMMON-NEXT:    tail call void @cold0()
+; COMMON-NEXT:    [[INC]] = add nuw nsw i32 [[I_021]], 1
+; COMMON-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[CALL12]]
+; COMMON-NEXT:    br i1 [[EXITCOND_NOT]], label [[IF_END16]], label [[FOR_BODY]]
+; COMMON:       if.end16:
+; COMMON-NEXT:    ret void
+;
+entry:
+  tail call void @not_cold0()
+  %tobool.not = icmp eq i32 %x, 0
+  %call12 = tail call i32 @get_val()
+  br i1 %tobool.not, label %if.else11, label %if.then
+
+if.then:
+  %tobool1.not = icmp eq i32 %call12, 0
+  br i1 %tobool1.not, label %if.else, label %if.then2
+
+if.then2:
+  tail call void @cold1()
+  br label %if.end16
+
+if.else:
+  %call3 = tail call i32 @get_val()
+  %tobool4.not = icmp eq i32 %call3, 0
+  br i1 %tobool4.not, label %if.else6, label %if.then5
+
+if.then5:
+  tail call void @cold0()
+  br label %if.end16
+
+if.else6:
+  tail call void @not_cold0()
+  %call7 = tail call i32 @get_val()
+  switch i32 %call7, label %sw.default [
+  i32 0, label %sw.bb
+  i32 1, label %sw.bb8
+  i32 2, label %sw.bb9
+  ]
+
+sw.bb:
+  tail call void @not_cold0()
+  br label %call_cold
+
+sw.bb8:
+  tail call void @not_cold1()
+  br label %call_cold
+
+sw.bb9:
+  tail call void @not_cold2()
+  br label %call_cold
+
+sw.default:
+  tail call void @cold0()
+  br label %if.end16
+
+call_cold:
+  tail call void @cold_at_cb() cold
+  br label %if.end16
+
+if.else11:
+  %cmp = icmp slt i32 %call12, 1
+  br i1 %cmp, label %if.end14, label %for.body
+
+if.end14:
+  tail call void @not_cold1()
+  br label %if.end16
+
+for.body:
+  %i.021 = phi i32 [ %inc, %for.body ], [ 0, %if.else11 ]
+  tail call void @cold0()
+  %inc = add nuw nsw i32 %i.021, 1
+  %exitcond.not = icmp eq i32 %inc, %call12
+  br i1 %exitcond.not, label %if.end16, label %for.body
+
+if.end16:
+  ret void
+}
+
+;.
+; COMMON: attributes #[[ATTR0]] = { cold }
+; COMMON: attributes #[[ATTR1]] = { nofree norecurse noreturn nosync nounwind memory(none) }
+; COMMON: attributes #[[ATTR2]] = { noreturn }
+; COMMON: attributes #[[ATTR3]] = { hot }
+;.
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; ATTRIBUTOR: {{.*}}
+; FNATTRS: {{.*}}

>From 85efc82366a64fbe5287b7bc9d20853b06d147f6 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 31 Jul 2024 14:21:58 +0800
Subject: [PATCH 2/4] [FunctionAttrs] deduce attr `cold` on functions if all CG
 paths call a `cold` function

---
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp  |  65 +++
 llvm/test/Transforms/FunctionAttrs/cold.ll | 542 ++++++++++++++-------
 2 files changed, 429 insertions(+), 178 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index d50218aaa3b6cc..918842a33f08f6 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -82,6 +82,7 @@ STATISTIC(NumNoUnwind, "Number of functions marked as nounwind");
 STATISTIC(NumNoFree, "Number of functions marked as nofree");
 STATISTIC(NumWillReturn, "Number of functions marked as willreturn");
 STATISTIC(NumNoSync, "Number of functions marked as nosync");
+STATISTIC(NumCold, "Number of functions marked as cold");
 
 STATISTIC(NumThinLinkNoRecurse,
           "Number of functions marked as norecurse during thinlink");
@@ -1745,6 +1746,7 @@ static bool canReturn(Function &F) {
   return false;
 }
 
+
 // Set the noreturn function attribute if possible.
 static void addNoReturnAttrs(const SCCNodeSet &SCCNodes,
                              SmallSet<Function *, 8> &Changed) {
@@ -1760,6 +1762,65 @@ static void addNoReturnAttrs(const SCCNodeSet &SCCNodes,
   }
 }
 
+static bool
+allBBPathsGoThroughCold(BasicBlock *BB,
+                        SmallDenseMap<BasicBlock *, bool, 16> &Visited) {
+  // If BB contains a cold callsite this path through the CG is cold.
+  if (any_of(*BB, [](Instruction &I) {
+        if (auto *CB = dyn_cast<CallBase>(&I))
+          return CB->hasFnAttr(Attribute::Cold);
+        return false;
+      })) {
+    Visited[BB] = true;
+    return true;
+  }
+
+  auto Succs = successors(BB);
+  // We found a path that doesn't go through any cold callsite.
+  if (Succs.empty())
+    return false;
+
+  for (auto *Succ : Succs) {
+    // Start with false, this is necessary to ensure we don't turn loops into
+    // cold.
+    auto R = Visited.try_emplace(Succ, false);
+    if (!R.second) {
+      if (R.first->second)
+        continue;
+      return false;
+    }
+    if (!allBBPathsGoThroughCold(Succ, Visited))
+      return false;
+    Visited[Succ] = true;
+  }
+
+  return true;
+}
+
+static bool allPathsGoThroughCold(Function &F) {
+  SmallDenseMap<BasicBlock *, bool, 16> Visited;
+  Visited[&F.front()] = false;
+  return allBBPathsGoThroughCold(&F.front(), Visited);
+}
+
+// Set the cold function attribute if possible.
+static void addColdAttrs(const SCCNodeSet &SCCNodes,
+                         SmallSet<Function *, 8> &Changed) {
+  for (Function *F : SCCNodes) {
+    if (!F || !F->hasExactDefinition() || F->hasFnAttribute(Attribute::Naked) ||
+        F->hasFnAttribute(Attribute::Cold) || F->hasFnAttribute(Attribute::Hot))
+      continue;
+
+    // Potential TODO: We could add attribute `cold` on functions with `coldcc`.
+    if (allPathsGoThroughCold(*F)) {
+      F->addFnAttr(Attribute::Cold);
+      ++NumCold;
+      Changed.insert(F);
+      continue;
+    }
+  }
+}
+
 static bool functionWillReturn(const Function &F) {
   // We can infer and propagate function attributes only when we know that the
   // definition we'll get at link time is *exactly* the definition we see now.
@@ -1789,6 +1850,7 @@ static bool functionWillReturn(const Function &F) {
   });
 }
 
+
 // Set the willreturn function attribute if possible.
 static void addWillReturn(const SCCNodeSet &SCCNodes,
                           SmallSet<Function *, 8> &Changed) {
@@ -1802,6 +1864,8 @@ static void addWillReturn(const SCCNodeSet &SCCNodes,
   }
 }
 
+
+
 static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
   SCCNodesResult Res;
   Res.HasUnknownCall = false;
@@ -1853,6 +1917,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
   addArgumentAttrs(Nodes.SCCNodes, Changed);
   inferConvergent(Nodes.SCCNodes, Changed);
   addNoReturnAttrs(Nodes.SCCNodes, Changed);
+  addColdAttrs(Nodes.SCCNodes, Changed);
   addWillReturn(Nodes.SCCNodes, Changed);
   addNoUndefAttrs(Nodes.SCCNodes, Changed);
 
diff --git a/llvm/test/Transforms/FunctionAttrs/cold.ll b/llvm/test/Transforms/FunctionAttrs/cold.ll
index 1fa8ae06797943..a205fbda062121 100644
--- a/llvm/test/Transforms/FunctionAttrs/cold.ll
+++ b/llvm/test/Transforms/FunctionAttrs/cold.ll
@@ -54,14 +54,23 @@ while.body2:
 }
 
 define void @test_no_exit() {
-; COMMON: Function Attrs: noreturn
-; COMMON-LABEL: define void @test_no_exit
-; COMMON-SAME: () #[[ATTR2]] {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
-; COMMON:       while.body:
-; COMMON-NEXT:    call void @cold0()
-; COMMON-NEXT:    br label [[WHILE_BODY]]
+; FNATTRS: Function Attrs: cold noreturn
+; FNATTRS-LABEL: define void @test_no_exit
+; FNATTRS-SAME: () #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
+; FNATTRS:       while.body:
+; FNATTRS-NEXT:    call void @cold0()
+; FNATTRS-NEXT:    br label [[WHILE_BODY]]
+;
+; ATTRIBUTOR: Function Attrs: noreturn
+; ATTRIBUTOR-LABEL: define void @test_no_exit
+; ATTRIBUTOR-SAME: () #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY:%.*]]
+; ATTRIBUTOR:       while.body:
+; ATTRIBUTOR-NEXT:    call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -72,17 +81,29 @@ while.body:
 }
 
 define void @test_no_exit2() {
-; COMMON: Function Attrs: noreturn
-; COMMON-LABEL: define void @test_no_exit2
-; COMMON-SAME: () #[[ATTR2]] {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
-; COMMON:       while.body:
-; COMMON-NEXT:    call void @not_cold0()
-; COMMON-NEXT:    br label [[WHILE_BODY2:%.*]]
-; COMMON:       while.body2:
-; COMMON-NEXT:    call void @cold1()
-; COMMON-NEXT:    br label [[WHILE_BODY]]
+; FNATTRS: Function Attrs: cold noreturn
+; FNATTRS-LABEL: define void @test_no_exit2
+; FNATTRS-SAME: () #[[ATTR3]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
+; FNATTRS:       while.body:
+; FNATTRS-NEXT:    call void @not_cold0()
+; FNATTRS-NEXT:    br label [[WHILE_BODY2:%.*]]
+; FNATTRS:       while.body2:
+; FNATTRS-NEXT:    call void @cold1()
+; FNATTRS-NEXT:    br label [[WHILE_BODY]]
+;
+; ATTRIBUTOR: Function Attrs: noreturn
+; ATTRIBUTOR-LABEL: define void @test_no_exit2
+; ATTRIBUTOR-SAME: () #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY:%.*]]
+; ATTRIBUTOR:       while.body:
+; ATTRIBUTOR-NEXT:    call void @not_cold0()
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY2:%.*]]
+; ATTRIBUTOR:       while.body2:
+; ATTRIBUTOR-NEXT:    call void @cold1()
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -97,18 +118,32 @@ while.body2:
 }
 
 define dso_local void @test_entry(i32 noundef %x) {
-; COMMON-LABEL: define dso_local void @test_entry
-; COMMON-SAME: (i32 noundef [[X:%.*]]) {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
-; COMMON:       if.then:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    br label [[IF_END]]
-; COMMON:       if.end:
-; COMMON-NEXT:    tail call void @not_cold1()
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: cold
+; FNATTRS-LABEL: define dso_local void @test_entry
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    br label [[IF_END]]
+; FNATTRS:       if.end:
+; FNATTRS-NEXT:    tail call void @not_cold1()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define dso_local void @test_entry
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END]]
+; ATTRIBUTOR:       if.end:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold1()
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   tail call void @cold0()
@@ -125,12 +160,19 @@ if.end:
 }
 
 define dso_local void @test_hot_fail(i32 noundef %x) hot {
-; COMMON: Function Attrs: hot
-; COMMON-LABEL: define dso_local void @test_hot_fail
-; COMMON-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3:[0-9]+]] {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: hot
+; FNATTRS-LABEL: define dso_local void @test_hot_fail
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: hot
+; ATTRIBUTOR-LABEL: define dso_local void @test_hot_fail
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   tail call void @cold0()
@@ -138,19 +180,34 @@ entry:
 }
 
 define dso_local void @test_br2(i32 noundef %x) {
-; COMMON-LABEL: define dso_local void @test_br2
-; COMMON-SAME: (i32 noundef [[X:%.*]]) {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
-; COMMON:       if.then:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END:%.*]]
-; COMMON:       if.else:
-; COMMON-NEXT:    tail call void @cold1()
-; COMMON-NEXT:    br label [[IF_END]]
-; COMMON:       if.end:
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: cold
+; FNATTRS-LABEL: define dso_local void @test_br2
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    tail call void @cold1()
+; FNATTRS-NEXT:    br label [[IF_END]]
+; FNATTRS:       if.end:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define dso_local void @test_br2
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    tail call void @cold1()
+; ATTRIBUTOR-NEXT:    br label [[IF_END]]
+; ATTRIBUTOR:       if.end:
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   %tobool.not = icmp eq i32 %x, 0
@@ -169,21 +226,38 @@ if.end:
 }
 
 define dso_local void @test_exit(i32 noundef %x) {
-; COMMON-LABEL: define dso_local void @test_exit
-; COMMON-SAME: (i32 noundef [[X:%.*]]) {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
-; COMMON:       if.then:
-; COMMON-NEXT:    tail call void @not_cold1()
-; COMMON-NEXT:    br label [[IF_END:%.*]]
-; COMMON:       if.else:
-; COMMON-NEXT:    tail call void @not_cold2()
-; COMMON-NEXT:    br label [[IF_END]]
-; COMMON:       if.end:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: cold
+; FNATTRS-LABEL: define dso_local void @test_exit
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @not_cold1()
+; FNATTRS-NEXT:    br label [[IF_END:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    tail call void @not_cold2()
+; FNATTRS-NEXT:    br label [[IF_END]]
+; FNATTRS:       if.end:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define dso_local void @test_exit
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold1()
+; ATTRIBUTOR-NEXT:    br label [[IF_END:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold2()
+; ATTRIBUTOR-NEXT:    br label [[IF_END]]
+; ATTRIBUTOR:       if.end:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   tail call void @not_cold0()
@@ -204,54 +278,104 @@ if.end:
 }
 
 define dso_local void @test_complex(i32 noundef %x) {
-; COMMON-LABEL: define dso_local void @test_complex
-; COMMON-SAME: (i32 noundef [[X:%.*]]) {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
-; COMMON:       if.then:
-; COMMON-NEXT:    [[CALL:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
-; COMMON:       if.then2:
-; COMMON-NEXT:    tail call void @cold1()
-; COMMON-NEXT:    br label [[IF_END12:%.*]]
-; COMMON:       if.else:
-; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
-; COMMON:       if.then5:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END12]]
-; COMMON:       if.else6:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
-; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
-; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
-; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
-; COMMON-NEXT:    ]
-; COMMON:       sw.bb:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
-; COMMON:       sw.bb8:
-; COMMON-NEXT:    tail call void @not_cold1()
-; COMMON-NEXT:    br label [[CALL_COLD]]
-; COMMON:       sw.bb9:
-; COMMON-NEXT:    tail call void @not_cold2()
-; COMMON-NEXT:    br label [[CALL_COLD]]
-; COMMON:       sw.default:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END12]]
-; COMMON:       call_cold:
-; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0:[0-9]+]]
-; COMMON-NEXT:    br label [[IF_END12]]
-; COMMON:       if.else11:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END12]]
-; COMMON:       if.end12:
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: cold
+; FNATTRS-LABEL: define dso_local void @test_complex
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    [[CALL:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; FNATTRS:       if.then2:
+; FNATTRS-NEXT:    tail call void @cold1()
+; FNATTRS-NEXT:    br label [[IF_END12:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; FNATTRS:       if.then5:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END12]]
+; FNATTRS:       if.else6:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; FNATTRS-NEXT:      i32 0, label [[SW_BB:%.*]]
+; FNATTRS-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; FNATTRS-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; FNATTRS-NEXT:    ]
+; FNATTRS:       sw.bb:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    br label [[CALL_COLD:%.*]]
+; FNATTRS:       sw.bb8:
+; FNATTRS-NEXT:    tail call void @not_cold1()
+; FNATTRS-NEXT:    br label [[CALL_COLD]]
+; FNATTRS:       sw.bb9:
+; FNATTRS-NEXT:    tail call void @not_cold2()
+; FNATTRS-NEXT:    br label [[CALL_COLD]]
+; FNATTRS:       sw.default:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END12]]
+; FNATTRS:       call_cold:
+; FNATTRS-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; FNATTRS-NEXT:    br label [[IF_END12]]
+; FNATTRS:       if.else11:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END12]]
+; FNATTRS:       if.end12:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define dso_local void @test_complex
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    [[CALL:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; ATTRIBUTOR:       if.then2:
+; ATTRIBUTOR-NEXT:    tail call void @cold1()
+; ATTRIBUTOR-NEXT:    br label [[IF_END12:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; ATTRIBUTOR:       if.then5:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END12]]
+; ATTRIBUTOR:       if.else6:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; ATTRIBUTOR-NEXT:      i32 0, label [[SW_BB:%.*]]
+; ATTRIBUTOR-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; ATTRIBUTOR-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; ATTRIBUTOR-NEXT:    ]
+; ATTRIBUTOR:       sw.bb:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD:%.*]]
+; ATTRIBUTOR:       sw.bb8:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold1()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD]]
+; ATTRIBUTOR:       sw.bb9:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold2()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD]]
+; ATTRIBUTOR:       sw.default:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END12]]
+; ATTRIBUTOR:       call_cold:
+; ATTRIBUTOR-NEXT:    tail call void @cold_at_cb() #[[ATTR0:[0-9]+]]
+; ATTRIBUTOR-NEXT:    br label [[IF_END12]]
+; ATTRIBUTOR:       if.else11:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END12]]
+; ATTRIBUTOR:       if.end12:
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   tail call void @not_cold0()
@@ -314,63 +438,122 @@ if.end12:
 }
 
 define dso_local void @test_complex2(i32 noundef %x) {
-; COMMON-LABEL: define dso_local void @test_complex2
-; COMMON-SAME: (i32 noundef [[X:%.*]]) {
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
-; COMMON-NEXT:    [[CALL12:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
-; COMMON:       if.then:
-; COMMON-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL12]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
-; COMMON:       if.then2:
-; COMMON-NEXT:    tail call void @cold1()
-; COMMON-NEXT:    br label [[IF_END16:%.*]]
-; COMMON:       if.else:
-; COMMON-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
-; COMMON-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
-; COMMON:       if.then5:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END16]]
-; COMMON:       if.else6:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
-; COMMON-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
-; COMMON-NEXT:      i32 0, label [[SW_BB:%.*]]
-; COMMON-NEXT:      i32 1, label [[SW_BB8:%.*]]
-; COMMON-NEXT:      i32 2, label [[SW_BB9:%.*]]
-; COMMON-NEXT:    ]
-; COMMON:       sw.bb:
-; COMMON-NEXT:    tail call void @not_cold0()
-; COMMON-NEXT:    br label [[CALL_COLD:%.*]]
-; COMMON:       sw.bb8:
-; COMMON-NEXT:    tail call void @not_cold1()
-; COMMON-NEXT:    br label [[CALL_COLD]]
-; COMMON:       sw.bb9:
-; COMMON-NEXT:    tail call void @not_cold2()
-; COMMON-NEXT:    br label [[CALL_COLD]]
-; COMMON:       sw.default:
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    br label [[IF_END16]]
-; COMMON:       call_cold:
-; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
-; COMMON-NEXT:    br label [[IF_END16]]
-; COMMON:       if.else11:
-; COMMON-NEXT:    [[CMP:%.*]] = icmp slt i32 [[CALL12]], 1
-; COMMON-NEXT:    br i1 [[CMP]], label [[IF_END14:%.*]], label [[FOR_BODY:%.*]]
-; COMMON:       if.end14:
-; COMMON-NEXT:    tail call void @cold1()
-; COMMON-NEXT:    br label [[IF_END16]]
-; COMMON:       for.body:
-; COMMON-NEXT:    [[I_021:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[IF_ELSE11]] ]
-; COMMON-NEXT:    tail call void @cold0()
-; COMMON-NEXT:    [[INC]] = add nuw nsw i32 [[I_021]], 1
-; COMMON-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[CALL12]]
-; COMMON-NEXT:    br i1 [[EXITCOND_NOT]], label [[IF_END16]], label [[FOR_BODY]]
-; COMMON:       if.end16:
-; COMMON-NEXT:    ret void
+; FNATTRS: Function Attrs: cold
+; FNATTRS-LABEL: define dso_local void @test_complex2
+; FNATTRS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; FNATTRS-NEXT:    [[CALL12:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL12]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; FNATTRS:       if.then2:
+; FNATTRS-NEXT:    tail call void @cold1()
+; FNATTRS-NEXT:    br label [[IF_END16:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; FNATTRS-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; FNATTRS:       if.then5:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END16]]
+; FNATTRS:       if.else6:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; FNATTRS-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; FNATTRS-NEXT:      i32 0, label [[SW_BB:%.*]]
+; FNATTRS-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; FNATTRS-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; FNATTRS-NEXT:    ]
+; FNATTRS:       sw.bb:
+; FNATTRS-NEXT:    tail call void @not_cold0()
+; FNATTRS-NEXT:    br label [[CALL_COLD:%.*]]
+; FNATTRS:       sw.bb8:
+; FNATTRS-NEXT:    tail call void @not_cold1()
+; FNATTRS-NEXT:    br label [[CALL_COLD]]
+; FNATTRS:       sw.bb9:
+; FNATTRS-NEXT:    tail call void @not_cold2()
+; FNATTRS-NEXT:    br label [[CALL_COLD]]
+; FNATTRS:       sw.default:
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    br label [[IF_END16]]
+; FNATTRS:       call_cold:
+; FNATTRS-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; FNATTRS-NEXT:    br label [[IF_END16]]
+; FNATTRS:       if.else11:
+; FNATTRS-NEXT:    [[CMP:%.*]] = icmp slt i32 [[CALL12]], 1
+; FNATTRS-NEXT:    br i1 [[CMP]], label [[IF_END14:%.*]], label [[FOR_BODY:%.*]]
+; FNATTRS:       if.end14:
+; FNATTRS-NEXT:    tail call void @cold1()
+; FNATTRS-NEXT:    br label [[IF_END16]]
+; FNATTRS:       for.body:
+; FNATTRS-NEXT:    [[I_021:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[IF_ELSE11]] ]
+; FNATTRS-NEXT:    tail call void @cold0()
+; FNATTRS-NEXT:    [[INC]] = add nuw nsw i32 [[I_021]], 1
+; FNATTRS-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[CALL12]]
+; FNATTRS-NEXT:    br i1 [[EXITCOND_NOT]], label [[IF_END16]], label [[FOR_BODY]]
+; FNATTRS:       if.end16:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define dso_local void @test_complex2
+; ATTRIBUTOR-SAME: (i32 noundef [[X:%.*]]) {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X]], 0
+; ATTRIBUTOR-NEXT:    [[CALL12:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_ELSE11:%.*]], label [[IF_THEN:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    [[TOBOOL1_NOT:%.*]] = icmp eq i32 [[CALL12]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL1_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN2:%.*]]
+; ATTRIBUTOR:       if.then2:
+; ATTRIBUTOR-NEXT:    tail call void @cold1()
+; ATTRIBUTOR-NEXT:    br label [[IF_END16:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    [[CALL3:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[CALL3]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TOBOOL4_NOT]], label [[IF_ELSE6:%.*]], label [[IF_THEN5:%.*]]
+; ATTRIBUTOR:       if.then5:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END16]]
+; ATTRIBUTOR:       if.else6:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    [[CALL7:%.*]] = tail call i32 @get_val()
+; ATTRIBUTOR-NEXT:    switch i32 [[CALL7]], label [[SW_DEFAULT:%.*]] [
+; ATTRIBUTOR-NEXT:      i32 0, label [[SW_BB:%.*]]
+; ATTRIBUTOR-NEXT:      i32 1, label [[SW_BB8:%.*]]
+; ATTRIBUTOR-NEXT:      i32 2, label [[SW_BB9:%.*]]
+; ATTRIBUTOR-NEXT:    ]
+; ATTRIBUTOR:       sw.bb:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold0()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD:%.*]]
+; ATTRIBUTOR:       sw.bb8:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold1()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD]]
+; ATTRIBUTOR:       sw.bb9:
+; ATTRIBUTOR-NEXT:    tail call void @not_cold2()
+; ATTRIBUTOR-NEXT:    br label [[CALL_COLD]]
+; ATTRIBUTOR:       sw.default:
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    br label [[IF_END16]]
+; ATTRIBUTOR:       call_cold:
+; ATTRIBUTOR-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; ATTRIBUTOR-NEXT:    br label [[IF_END16]]
+; ATTRIBUTOR:       if.else11:
+; ATTRIBUTOR-NEXT:    [[CMP:%.*]] = icmp slt i32 [[CALL12]], 1
+; ATTRIBUTOR-NEXT:    br i1 [[CMP]], label [[IF_END14:%.*]], label [[FOR_BODY:%.*]]
+; ATTRIBUTOR:       if.end14:
+; ATTRIBUTOR-NEXT:    tail call void @cold1()
+; ATTRIBUTOR-NEXT:    br label [[IF_END16]]
+; ATTRIBUTOR:       for.body:
+; ATTRIBUTOR-NEXT:    [[I_021:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[IF_ELSE11]] ]
+; ATTRIBUTOR-NEXT:    tail call void @cold0()
+; ATTRIBUTOR-NEXT:    [[INC]] = add nuw nsw i32 [[I_021]], 1
+; ATTRIBUTOR-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[CALL12]]
+; ATTRIBUTOR-NEXT:    br i1 [[EXITCOND_NOT]], label [[IF_END16]], label [[FOR_BODY]]
+; ATTRIBUTOR:       if.end16:
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   tail call void @not_cold0()
@@ -485,7 +668,7 @@ define dso_local void @test_complex_fail(i32 noundef %x) {
 ; COMMON-NEXT:    tail call void @cold0()
 ; COMMON-NEXT:    br label [[IF_END12]]
 ; COMMON:       call_cold:
-; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0]]
+; COMMON-NEXT:    tail call void @cold_at_cb() #[[ATTR0:[0-9]+]]
 ; COMMON-NEXT:    br label [[IF_END12]]
 ; COMMON:       if.else11:
 ; COMMON-NEXT:    tail call void @cold0()
@@ -684,11 +867,14 @@ if.end16:
 }
 
 ;.
-; COMMON: attributes #[[ATTR0]] = { cold }
-; COMMON: attributes #[[ATTR1]] = { nofree norecurse noreturn nosync nounwind memory(none) }
-; COMMON: attributes #[[ATTR2]] = { noreturn }
-; COMMON: attributes #[[ATTR3]] = { hot }
+; FNATTRS: attributes #[[ATTR0]] = { cold }
+; FNATTRS: attributes #[[ATTR1]] = { nofree norecurse noreturn nosync nounwind memory(none) }
+; FNATTRS: attributes #[[ATTR2]] = { noreturn }
+; FNATTRS: attributes #[[ATTR3]] = { cold noreturn }
+; FNATTRS: attributes #[[ATTR4]] = { hot }
+;.
+; ATTRIBUTOR: attributes #[[ATTR0]] = { cold }
+; ATTRIBUTOR: attributes #[[ATTR1]] = { nofree norecurse noreturn nosync nounwind memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR2]] = { noreturn }
+; ATTRIBUTOR: attributes #[[ATTR3]] = { hot }
 ;.
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; ATTRIBUTOR: {{.*}}
-; FNATTRS: {{.*}}

>From 3df796947d8845f8cda56e1fec60c5f6047a3a63 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 31 Jul 2024 15:51:24 +0800
Subject: [PATCH 3/4] fixup fmt

---
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 918842a33f08f6..6c032117dd9f48 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1780,6 +1780,11 @@ allBBPathsGoThroughCold(BasicBlock *BB,
   if (Succs.empty())
     return false;
 
+  // We didn't find a cold callsite in this BB, so check that all successors
+  // contain a cold callsite (or that their successors do).
+  // Potential TODO: We could use static branch hints to assume certain
+  // successor paths are inherently cold, irrespective of if they contain a cold
+  // callsite.
   for (auto *Succ : Succs) {
     // Start with false, this is necessary to ensure we don't turn loops into
     // cold.

>From 7d22a7e4fd88d1022cf45cfb0d9c22c1d47647fd Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sun, 11 Aug 2024 14:02:03 +0800
Subject: [PATCH 4/4] Fix fmt 2

---
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 6c032117dd9f48..31316ebbd4594c 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1855,7 +1855,6 @@ static bool functionWillReturn(const Function &F) {
   });
 }
 
-
 // Set the willreturn function attribute if possible.
 static void addWillReturn(const SCCNodeSet &SCCNodes,
                           SmallSet<Function *, 8> &Changed) {
@@ -1869,8 +1868,6 @@ static void addWillReturn(const SCCNodeSet &SCCNodes,
   }
 }
 
-
-
 static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
   SCCNodesResult Res;
   Res.HasUnknownCall = false;



More information about the llvm-commits mailing list