[llvm] [CFI] Fix Direct Call Issues in CFI Dispatch Table (PR #69663)

Oskar Wirga via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 21 14:01:11 PST 2023


https://github.com/oskarwirga updated https://github.com/llvm/llvm-project/pull/69663

>From 79b21ab739aa52ce73460987beffac1da0a17551 Mon Sep 17 00:00:00 2001
From: Oskar Wirga <10386631+oskarwirga at users.noreply.github.com>
Date: Thu, 19 Oct 2023 17:27:54 -0700
Subject: [PATCH] [CFI] Fix Direct Call Issues in CFI Dispatch Table

I discovered two issues for when a CFI dispatch table entry is used as a direct call.
Inlining
There is the possibility that the dispatch table entry contains only a single function pointer:
; Function Attrs: naked nocf_check
define private void @.cfi.jumptable() #6 align 8 {
entry:
  call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @_Z7throw_ei)
  unreachable
}
If this function is inlined, the unreachable follows and ruins the containing function.
Exception Handling
The dispatch table is always marked NoUnwind. This is fine if the entries are never used directly, but if a direct call is used which the containing function expects to throw, it will no longer throw and the exception handling code will be lost.
---
 llvm/lib/Transforms/IPO/LowerTypeTests.cpp    |  21 +-
 .../LowerTypeTests/aarch64-jumptable.ll       |  35 ++-
 .../cfi-nounwind-direct-call.ll               | 160 ++++++++++++
 .../LowerTypeTests/cfi-unwind-direct-call.ll  | 228 ++++++++++++++++++
 .../LowerTypeTests/function-arm-thumb.ll      |   4 +-
 .../LowerTypeTests/function-thumb-bti.ll      |   4 +-
 .../Transforms/LowerTypeTests/function.ll     |  16 +-
 .../LowerTypeTests/x86-jumptable.ll           |   2 +-
 8 files changed, 446 insertions(+), 24 deletions(-)
 create mode 100644 llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
 create mode 100644 llvm/test/Transforms/LowerTypeTests/cfi-unwind-direct-call.ll

diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 5fcb518cf37c130..733f290b1bc93a8 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1467,9 +1467,19 @@ void LowerTypeTestsModule::createJumpTable(
   SmallVector<Value *, 16> AsmArgs;
   AsmArgs.reserve(Functions.size() * 2);
 
-  for (GlobalTypeMember *GTM : Functions)
+  // Check if all entries have the NoUnwind attribute.
+  // If all entries have it, we can safely mark the
+  // cfi.jumptable as NoUnwind, otherwise, direct calls
+  // to the jump table will not handle exceptions properly
+  bool areAllEntriesNounwind = true;
+  for (GlobalTypeMember *GTM : Functions) {
+    if (!llvm::cast<llvm::Function>(GTM->getGlobal())
+             ->hasFnAttribute(llvm::Attribute::NoUnwind)) {
+      areAllEntriesNounwind = false;
+    }
     createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs,
                          cast<Function>(GTM->getGlobal()));
+  }
 
   // Align the whole table by entry size.
   F->setAlignment(Align(getJumpTableEntrySize()));
@@ -1512,8 +1522,13 @@ void LowerTypeTestsModule::createJumpTable(
   // -fcf-protection=.
   if (JumpTableArch == Triple::x86 || JumpTableArch == Triple::x86_64)
     F->addFnAttr(Attribute::NoCfCheck);
-  // Make sure we don't emit .eh_frame for this function.
-  F->addFnAttr(Attribute::NoUnwind);
+
+  // Make sure we don't emit .eh_frame for this function if it isn't needed.
+  if (areAllEntriesNounwind)
+    F->addFnAttr(Attribute::NoUnwind);
+
+  // Make sure we do not inline any calls to the cfi.jumptable.
+  F->addFnAttr(Attribute::NoInline);
 
   BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
   IRBuilder<> IRB(BB);
diff --git a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable.ll b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable.ll
index 139df60a7b916f4..3464a748778b668 100644
--- a/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable.ll
+++ b/llvm/test/Transforms/LowerTypeTests/aarch64-jumptable.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
 ; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64 %s
 
 ; Test for the jump table generation with branch protection on AArch64
@@ -6,7 +7,6 @@ target datalayout = "e-p:64:64"
 
 @0 = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16
 
-; AARCH64: @f = alias void (), ptr @[[JT:.*]]
 
 define void @f() !type !0 {
   ret void
@@ -29,11 +29,30 @@ define i1 @foo(ptr %p) {
 
 !1 = !{i32 4, !"branch-target-enforcement", i32 1}
 
-; AARCH64:   define private void @[[JT]]() #[[ATTR:.*]] align 8 {
 
-; AARCH64:      bti c
-; AARCH64-SAME: b $0
-; AARCH64-SAME: bti c
-; AARCH64-SAME: b $1
-
-; AARCH64: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none"
+; AARCH64-LABEL: define hidden void @f.cfi() !type !1 {
+; AARCH64-NEXT:    ret void
+;
+;
+; AARCH64-LABEL: define internal void @g.cfi() !type !1 {
+; AARCH64-NEXT:    ret void
+;
+;
+; AARCH64-LABEL: define i1 @foo
+; AARCH64-SAME: (ptr [[P:%.*]]) {
+; AARCH64-NEXT:    [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; AARCH64-NEXT:    [[TMP2:%.*]] = sub i64 [[TMP1]], ptrtoint (ptr @.cfi.jumptable to i64)
+; AARCH64-NEXT:    [[TMP3:%.*]] = lshr i64 [[TMP2]], 3
+; AARCH64-NEXT:    [[TMP4:%.*]] = shl i64 [[TMP2]], 61
+; AARCH64-NEXT:    [[TMP5:%.*]] = or i64 [[TMP3]], [[TMP4]]
+; AARCH64-NEXT:    [[TMP6:%.*]] = icmp ule i64 [[TMP5]], 1
+; AARCH64-NEXT:    ret i1 [[TMP6]]
+;
+;
+; AARCH64: Function Attrs: naked noinline
+; AARCH64-LABEL: define private void @.cfi.jumptable
+; AARCH64-SAME: () #[[ATTR1:[0-9]+]] align 8 {
+; AARCH64-NEXT:  entry:
+; AARCH64-NEXT:    call void asm sideeffect "bti c\0Ab $0\0Abti c\0Ab $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
+; AARCH64-NEXT:    unreachable
+;
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
new file mode 100644
index 000000000000000..4c88f4acc12f164
--- /dev/null
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
@@ -0,0 +1,160 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
+; RUN: opt < %s -passes='lowertypetests,default<O3>' -S | FileCheck %s
+
+; This IR is based of the following C++
+; which was compiled with:
+; clang -cc1 -fexceptions -fcxx-exceptions \
+; -std=c++11 -internal-isystem llvm-project/build/lib/clang/17/include \
+; -nostdsysteminc -triple x86_64-unknown-linux -fsanitize=cfi-icall \
+; -fsanitize-cfi-cross-dso -fsanitize-trap=cfi-icall -Oz -S -emit-llvm
+; int (*catch_ptr)(int);
+; int nothrow_e (int num) noexcept {
+;   if (num) return 1;
+;   return 0;
+; }
+; int call_catch(int num) {
+;   catch_ptr = &nothrow_e;
+;   return catch_ptr(num);
+; }
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux"
+
+ at catch_ptr = local_unnamed_addr global ptr null, align 8
+ at llvm.used = appending global [1 x ptr] [ptr @__cfi_check_fail], section "llvm.metadata"
+
+; Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none)
+define dso_local noundef i32 @_Z9nothrow_ei(i32 noundef %num) #0 !type !4 !type !5 !type !6 {
+entry:
+  %tobool.not = icmp ne i32 %num, 0
+  %. = zext i1 %tobool.not to i32
+  ret i32 %.
+}
+
+; Function Attrs: minsize mustprogress nounwind optsize
+define dso_local noundef i32 @_Z10call_catchi(i32 noundef %num) local_unnamed_addr #1 !type !4 !type !5 !type !6 {
+entry:
+  store ptr @_Z9nothrow_ei, ptr @catch_ptr, align 8, !tbaa !7
+  %0 = tail call i1 @llvm.type.test(ptr nonnull @_Z9nothrow_ei, metadata !"_ZTSFiiE"), !nosanitize !11
+  br i1 %0, label %cfi.cont, label %cfi.slowpath, !prof !12, !nosanitize !11
+
+cfi.slowpath:                                     ; preds = %entry
+  tail call void @__cfi_slowpath(i64 5174074510188755522, ptr nonnull @_Z9nothrow_ei) #5, !nosanitize !11
+  br label %cfi.cont, !nosanitize !11
+
+cfi.cont:                                         ; preds = %cfi.slowpath, %entry
+  %tobool.not.i = icmp ne i32 %num, 0
+  %..i = zext i1 %tobool.not.i to i32
+  ret i32 %..i
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare i1 @llvm.type.test(ptr, metadata) #2
+
+declare void @__cfi_slowpath(i64, ptr) local_unnamed_addr
+
+; Function Attrs: minsize optsize
+define weak_odr hidden void @__cfi_check_fail(ptr noundef %0, ptr noundef %1) #3 {
+entry:
+  %.not = icmp eq ptr %0, null, !nosanitize !11
+  br i1 %.not, label %trap, label %cont, !nosanitize !11
+
+trap:                                             ; preds = %cont, %entry
+  tail call void @llvm.ubsantrap(i8 2) #6, !nosanitize !11
+  unreachable, !nosanitize !11
+
+cont:                                             ; preds = %entry
+  %2 = load i8, ptr %0, align 4, !nosanitize !11
+  %switch = icmp ult i8 %2, 5
+  br i1 %switch, label %trap, label %cont6
+
+cont6:                                            ; preds = %cont
+  ret void, !nosanitize !11
+}
+
+; Function Attrs: cold noreturn nounwind
+declare void @llvm.ubsantrap(i8 immarg) #4
+
+define weak void @__cfi_check(i64 %0, ptr %1, ptr %2) local_unnamed_addr {
+entry:
+  tail call void @llvm.trap()
+  unreachable
+}
+
+; Function Attrs: cold noreturn nounwind
+declare void @llvm.trap() #4
+
+attributes #0 = { minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { minsize mustprogress nounwind optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #3 = { minsize optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #4 = { cold noreturn nounwind }
+attributes #5 = { nounwind }
+attributes #6 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.ident = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 4, !"Cross-DSO CFI", i32 1}
+!2 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
+!3 = !{!"clang version 17.0.2"}
+!4 = !{i64 0, !"_ZTSFiiE"}
+!5 = !{i64 0, !"_ZTSFiiE.generalized"}
+!6 = !{i64 0, i64 5174074510188755522}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"any pointer", !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C++ TBAA"}
+!11 = !{}
+!12 = !{!"branch_weights", i32 1048575, i32 1}
+; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none)
+; CHECK-LABEL: define dso_local noundef i32 @_Z9nothrow_ei
+; CHECK-SAME: (i32 noundef [[NUM:%.*]]) #[[ATTR0:[0-9]+]] !type !4 !type !5 !type !6 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp ne i32 [[NUM]], 0
+; CHECK-NEXT:    [[DOT:%.*]] = zext i1 [[TOBOOL_NOT]] to i32
+; CHECK-NEXT:    ret i32 [[DOT]]
+;
+;
+; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none)
+; CHECK-LABEL: define dso_local noundef i32 @_Z10call_catchi
+; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !type !4 !type !5 !type !6 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store ptr @_Z9nothrow_ei.cfi_jt, ptr @catch_ptr, align 8, !tbaa [[TBAA7:![0-9]+]]
+; CHECK-NEXT:    [[TOBOOL_NOT_I:%.*]] = icmp ne i32 [[NUM]], 0
+; CHECK-NEXT:    [[DOT_I:%.*]] = zext i1 [[TOBOOL_NOT_I]] to i32
+; CHECK-NEXT:    ret i32 [[DOT_I]]
+;
+;
+; CHECK: Function Attrs: minsize optsize
+; CHECK-LABEL: define weak_odr hidden void @__cfi_check_fail
+; CHECK-SAME: (ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], null, !nosanitize !11
+; CHECK-NEXT:    br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !nosanitize !11
+; CHECK:       trap:
+; CHECK-NEXT:    tail call void @llvm.ubsantrap(i8 2) #[[ATTR5:[0-9]+]], !nosanitize !11
+; CHECK-NEXT:    unreachable, !nosanitize !11
+; CHECK:       cont:
+; CHECK-NEXT:    [[TMP2:%.*]] = load i8, ptr [[TMP0]], align 4, !nosanitize !11
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[TMP2]], 5
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRAP]], label [[CONT6:%.*]]
+; CHECK:       cont6:
+; CHECK-NEXT:    ret void, !nosanitize !11
+;
+;
+; CHECK-LABEL: define weak void @__cfi_check
+; CHECK-SAME: (i64 [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    tail call void @llvm.trap()
+; CHECK-NEXT:    unreachable
+;
+;
+; CHECK: Function Attrs: naked nocf_check noinline nounwind
+; CHECK-LABEL: define internal void @_Z9nothrow_ei.cfi_jt
+; CHECK-SAME: () #[[ATTR4:[0-9]+]] align 8 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z9nothrow_ei) #[[ATTR6:[0-9]+]]
+; CHECK-NEXT:    unreachable
+;
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-unwind-direct-call.ll b/llvm/test/Transforms/LowerTypeTests/cfi-unwind-direct-call.ll
new file mode 100644
index 000000000000000..c560940835279a2
--- /dev/null
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-unwind-direct-call.ll
@@ -0,0 +1,228 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
+; RUN: opt < %s -passes='lowertypetests,default<O3>' -S | FileCheck %s
+
+; This IR is based of the following C++
+; which was compiled with:
+; clang -cc1 -fexceptions -fcxx-exceptions \
+; -std=c++11 -internal-isystem llvm-project/build/lib/clang/17/include \
+; -nostdsysteminc -triple x86_64-unknown-linux -fsanitize=cfi-icall \
+; -fsanitize-cfi-cross-dso -fsanitize-trap=cfi-icall -Oz -S -emit-llvm
+; void (*catch_ptr)(int);
+; void throw_e (int num) {
+;   if (num) throw 20;
+; }
+; void call_catch(int num) {
+;   catch_ptr = &throw_e;
+;   try{
+;     catch_ptr(num);
+;   } catch (int i) {
+;   }
+; }
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux"
+
+ at catch_ptr = local_unnamed_addr global ptr null, align 8
+ at _ZTIi = external constant ptr
+ at llvm.used = appending global [1 x ptr] [ptr @__cfi_check_fail], section "llvm.metadata"
+
+; Function Attrs: minsize mustprogress optsize
+define dso_local void @_Z7throw_ei(i32 noundef %num) #0 !type !4 !type !5 !type !6 {
+entry:
+  %tobool.not = icmp eq i32 %num, 0
+  br i1 %tobool.not, label %if.end, label %if.then
+
+if.then:                                          ; preds = %entry
+  %exception = tail call ptr @__cxa_allocate_exception(i64 4) #5
+  store i32 20, ptr %exception, align 16, !tbaa !7
+  tail call void @__cxa_throw(ptr nonnull %exception, ptr nonnull @_ZTIi, ptr null) #6
+  unreachable
+
+if.end:                                           ; preds = %entry
+  ret void
+}
+
+declare ptr @__cxa_allocate_exception(i64) local_unnamed_addr
+
+declare void @__cxa_throw(ptr, ptr, ptr) local_unnamed_addr
+
+; Function Attrs: minsize mustprogress optsize
+define dso_local void @_Z10call_catchi(i32 noundef %num) local_unnamed_addr #0 personality ptr @__gxx_personality_v0 !type !4 !type !5 !type !6 {
+entry:
+  store ptr @_Z7throw_ei, ptr @catch_ptr, align 8, !tbaa !11
+  %0 = tail call i1 @llvm.type.test(ptr nonnull @_Z7throw_ei, metadata !"_ZTSFviE"), !nosanitize !13
+  br i1 %0, label %cfi.cont, label %cfi.slowpath, !prof !14, !nosanitize !13
+
+cfi.slowpath:                                     ; preds = %entry
+  tail call void @__cfi_slowpath(i64 -8738933900360652027, ptr nonnull @_Z7throw_ei) #5, !nosanitize !13
+  br label %cfi.cont, !nosanitize !13
+
+cfi.cont:                                         ; preds = %cfi.slowpath, %entry
+  invoke void @_Z7throw_ei(i32 noundef %num) #7
+  to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %cfi.cont
+  %1 = landingpad { ptr, i32 }
+  catch ptr @_ZTIi
+  %2 = extractvalue { ptr, i32 } %1, 1
+  %3 = tail call i32 @llvm.eh.typeid.for(ptr nonnull @_ZTIi) #5
+  %matches = icmp eq i32 %2, %3
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  %4 = extractvalue { ptr, i32 } %1, 0
+  %5 = tail call ptr @__cxa_begin_catch(ptr %4) #5
+  tail call void @__cxa_end_catch() #5
+  br label %try.cont
+
+try.cont:                                         ; preds = %cfi.cont, %catch
+  ret void
+
+eh.resume:                                        ; preds = %lpad
+  resume { ptr, i32 } %1
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare i1 @llvm.type.test(ptr, metadata) #1
+
+declare void @__cfi_slowpath(i64, ptr) local_unnamed_addr
+
+declare i32 @__gxx_personality_v0(...)
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare i32 @llvm.eh.typeid.for(ptr) #2
+
+declare ptr @__cxa_begin_catch(ptr) local_unnamed_addr
+
+declare void @__cxa_end_catch() local_unnamed_addr
+
+; Function Attrs: minsize optsize
+define weak_odr hidden void @__cfi_check_fail(ptr noundef %0, ptr noundef %1) #3 {
+entry:
+  %.not = icmp eq ptr %0, null, !nosanitize !13
+  br i1 %.not, label %trap, label %cont, !nosanitize !13
+
+trap:                                             ; preds = %cont, %entry
+  tail call void @llvm.ubsantrap(i8 2) #8, !nosanitize !13
+  unreachable, !nosanitize !13
+
+cont:                                             ; preds = %entry
+  %2 = load i8, ptr %0, align 4, !nosanitize !13
+  %switch = icmp ult i8 %2, 5
+  br i1 %switch, label %trap, label %cont6
+
+cont6:                                            ; preds = %cont
+  ret void, !nosanitize !13
+}
+
+; Function Attrs: cold noreturn nounwind
+declare void @llvm.ubsantrap(i8 immarg) #4
+
+define weak void @__cfi_check(i64 %0, ptr %1, ptr %2) local_unnamed_addr {
+entry:
+  tail call void @llvm.trap()
+  unreachable
+}
+
+; Function Attrs: cold noreturn nounwind
+declare void @llvm.trap() #4
+
+attributes #0 = { minsize mustprogress optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { minsize optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #4 = { cold noreturn nounwind }
+attributes #5 = { nounwind }
+attributes #6 = { noreturn }
+attributes #7 = { minsize optsize }
+attributes #8 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.ident = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 4, !"Cross-DSO CFI", i32 1}
+!2 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
+!3 = !{!"clang version 17.0.2"}
+!4 = !{i64 0, !"_ZTSFviE"}
+!5 = !{i64 0, !"_ZTSFviE.generalized"}
+!6 = !{i64 0, i64 -8738933900360652027}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"int", !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C++ TBAA"}
+!11 = !{!12, !12, i64 0}
+!12 = !{!"any pointer", !9, i64 0}
+!13 = !{}
+!14 = !{!"branch_weights", i32 1048575, i32 1}
+; CHECK: Function Attrs: minsize mustprogress optsize
+; CHECK-LABEL: define dso_local void @_Z7throw_ei
+; CHECK-SAME: (i32 noundef [[NUM:%.*]]) #[[ATTR0:[0-9]+]] !type !4 !type !5 !type !6 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i32 [[NUM]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[EXCEPTION:%.*]] = tail call ptr @__cxa_allocate_exception(i64 4) #[[ATTR5:[0-9]+]]
+; CHECK-NEXT:    store i32 20, ptr [[EXCEPTION]], align 16, !tbaa [[TBAA7:![0-9]+]]
+; CHECK-NEXT:    tail call void @__cxa_throw(ptr nonnull [[EXCEPTION]], ptr nonnull @_ZTIi, ptr null) #[[ATTR6:[0-9]+]]
+; CHECK-NEXT:    unreachable
+; CHECK:       if.end:
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK: Function Attrs: minsize mustprogress optsize
+; CHECK-LABEL: define dso_local void @_Z10call_catchi
+; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR0]] personality ptr @__gxx_personality_v0 !type !4 !type !5 !type !6 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    store ptr @_Z7throw_ei.cfi_jt, ptr @catch_ptr, align 8, !tbaa [[TBAA11:![0-9]+]]
+; CHECK-NEXT:    invoke void @_Z7throw_ei.cfi_jt() #[[ATTR7:[0-9]+]]
+; CHECK-NEXT:    to label [[TRY_CONT:%.*]] unwind label [[LPAD:%.*]]
+; CHECK:       lpad:
+; CHECK-NEXT:    [[TMP0:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:    catch ptr @_ZTIi
+; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = tail call i32 @llvm.eh.typeid.for(ptr nonnull @_ZTIi) #[[ATTR5]]
+; CHECK-NEXT:    [[MATCHES:%.*]] = icmp eq i32 [[TMP1]], [[TMP2]]
+; CHECK-NEXT:    br i1 [[MATCHES]], label [[CATCH:%.*]], label [[EH_RESUME:%.*]]
+; CHECK:       catch:
+; CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0
+; CHECK-NEXT:    [[TMP4:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP3]]) #[[ATTR5]]
+; CHECK-NEXT:    tail call void @__cxa_end_catch() #[[ATTR5]]
+; CHECK-NEXT:    br label [[TRY_CONT]]
+; CHECK:       try.cont:
+; CHECK-NEXT:    ret void
+; CHECK:       eh.resume:
+; CHECK-NEXT:    resume { ptr, i32 } [[TMP0]]
+;
+;
+; CHECK: Function Attrs: minsize optsize
+; CHECK-LABEL: define weak_odr hidden void @__cfi_check_fail
+; CHECK-SAME: (ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], null, !nosanitize !13
+; CHECK-NEXT:    br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !nosanitize !13
+; CHECK:       trap:
+; CHECK-NEXT:    tail call void @llvm.ubsantrap(i8 2) #[[ATTR8:[0-9]+]], !nosanitize !13
+; CHECK-NEXT:    unreachable, !nosanitize !13
+; CHECK:       cont:
+; CHECK-NEXT:    [[TMP2:%.*]] = load i8, ptr [[TMP0]], align 4, !nosanitize !13
+; CHECK-NEXT:    [[SWITCH:%.*]] = icmp ult i8 [[TMP2]], 5
+; CHECK-NEXT:    br i1 [[SWITCH]], label [[TRAP]], label [[CONT6:%.*]]
+; CHECK:       cont6:
+; CHECK-NEXT:    ret void, !nosanitize !13
+;
+;
+; CHECK-LABEL: define weak void @__cfi_check
+; CHECK-SAME: (i64 [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    tail call void @llvm.trap()
+; CHECK-NEXT:    unreachable
+;
+;
+; CHECK: Function Attrs: naked nocf_check noinline
+; CHECK-LABEL: define internal void @_Z7throw_ei.cfi_jt
+; CHECK-SAME: () #[[ATTR4:[0-9]+]] align 8 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z7throw_ei) #[[ATTR5]]
+; CHECK-NEXT:    unreachable
+;
diff --git a/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll b/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
index 3406e70dd02b235..c482a20f9513a3c 100644
--- a/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
+++ b/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
@@ -45,5 +45,5 @@ define void @addrtaken() {
 ; CHECK-NEXT:  unreachable
 ; CHECK-NEXT: }
 
-; CHECK-DAG: attributes #[[AA]] = { naked nounwind "target-features"="-thumb-mode" }
-; CHECK-DAG: attributes #[[AT]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
+; CHECK-DAG: attributes #[[AA]] = { naked noinline "target-features"="-thumb-mode" }
+; CHECK-DAG: attributes #[[AT]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
diff --git a/llvm/test/Transforms/LowerTypeTests/function-thumb-bti.ll b/llvm/test/Transforms/LowerTypeTests/function-thumb-bti.ll
index 01bf81d0abd4e8e..c1308931c78164f 100644
--- a/llvm/test/Transforms/LowerTypeTests/function-thumb-bti.ll
+++ b/llvm/test/Transforms/LowerTypeTests/function-thumb-bti.ll
@@ -43,5 +43,5 @@ define i1 @foo(ptr %p) {
 ; BTI:   call void asm sideeffect "bti\0Ab.w $0\0Abti\0Ab.w $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
 ; NOBTI: call void asm sideeffect "b.w $0\0Ab.w $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
 
-; BTI: attributes [[ATTRS]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode,+pacbti" }
-; NOBTI: attributes [[ATTRS]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
+; BTI: attributes [[ATTRS]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode,+pacbti" }
+; NOBTI: attributes [[ATTRS]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
diff --git a/llvm/test/Transforms/LowerTypeTests/function.ll b/llvm/test/Transforms/LowerTypeTests/function.ll
index 5ba69e236e41355..a858aace834d6b3 100644
--- a/llvm/test/Transforms/LowerTypeTests/function.ll
+++ b/llvm/test/Transforms/LowerTypeTests/function.ll
@@ -51,7 +51,7 @@ define internal void @g() !type !0 {
 
 !0 = !{i32 0, !"typeid1"}
 
-declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) nounwind readnone
+declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) noinline readnone
 
 define i1 @foo(ptr %p) {
   ; NATIVE: sub i64 {{.*}}, ptrtoint (ptr @[[JT]] to i64)
@@ -109,13 +109,13 @@ define i1 @foo(ptr %p) {
 
 ; NATIVE-SAME: "s,s"(ptr @f.cfi, ptr @g.cfi)
 
-; X86-LINUX: attributes #[[ATTR]] = { naked nocf_check nounwind }
-; X86-WIN32: attributes #[[ATTR]] = { nocf_check nounwind }
-; ARM: attributes #[[ATTR]] = { naked nounwind
-; THUMB: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
-; THUMBV6M: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode" }
-; RISCV: attributes #[[ATTR]] = { naked nounwind "target-features"="-c,-relax" }
-; LOONGARCH64: attributes #[[ATTR]] = { naked nounwind }
+; X86-LINUX: attributes #[[ATTR]] = { naked nocf_check noinline }
+; X86-WIN32: attributes #[[ATTR]] = { nocf_check noinline }
+; ARM: attributes #[[ATTR]] = { naked noinline
+; THUMB: attributes #[[ATTR]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
+; THUMBV6M: attributes #[[ATTR]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode" }
+; RISCV: attributes #[[ATTR]] = { naked noinline "target-features"="-c,-relax" }
+; LOONGARCH64: attributes #[[ATTR]] = { naked noinline }
 
 ; WASM32: ![[I0]] = !{i64 1}
 ; WASM32: ![[I1]] = !{i64 2}
diff --git a/llvm/test/Transforms/LowerTypeTests/x86-jumptable.ll b/llvm/test/Transforms/LowerTypeTests/x86-jumptable.ll
index a88a45a65d59c28..f56d30be37959ff 100644
--- a/llvm/test/Transforms/LowerTypeTests/x86-jumptable.ll
+++ b/llvm/test/Transforms/LowerTypeTests/x86-jumptable.ll
@@ -28,4 +28,4 @@ define i1 @foo(ptr %p) {
 ; X86_32-NEXT:   call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0Aendbr32\0Ajmp ${1:c}@plt\0A.balign 16, 0xcc\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
 ; X86_64-NEXT:   call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0Aendbr64\0Ajmp ${1:c}@plt\0A.balign 16, 0xcc\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
 
-; X86_64: attributes #[[#ATTR]] = { naked nocf_check nounwind }
+; X86_64: attributes #[[#ATTR]] = { naked nocf_check noinline }



More information about the llvm-commits mailing list