From bea52edcc6ecb1c8537f04dbe21c7cf130667027 Mon Sep 17 00:00:00 2001 From: Oskar Wirga <10386631+oskarwirga@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:27:54 -0700 Subject: [PATCH] [CFI] Fix Direct Call Issues in CFI Dispatch Table This commit addresses two issues related to the use of Control Flow Integrity (CFI) dispatch table entries as direct calls. Issue with Inlining: When a dispatch table entry contains only a single function pointer, it can be replaced with a direct call to the jump table. However, if this function is inlined, the unreachable instruction that follows can disrupt the control flow of the containing function. This is illustrated in the following code snippet: ```llvm ; 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 } ``` Issue with Unwinding: When unwinding, a direct call of the jump table can cause all exception handling code of the containing function to be dropped. This occurs even if the indirectly dispatched function throws an exception which then results in unhandled exception crashes. --- 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 5fcb518cf37c13..733f290b1bc93a 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -1467,9 +1467,19 @@ void LowerTypeTestsModule::createJumpTable( SmallVector 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(GTM->getGlobal()) + ->hasFnAttribute(llvm::Attribute::NoUnwind)) { + areAllEntriesNounwind = false; + } createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs, cast(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 139df60a7b916f..3464a748778b66 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 00000000000000..4c88f4acc12f16 --- /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' -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 = ¬hrow_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" + +@catch_ptr = local_unnamed_addr global ptr null, align 8 +@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 00000000000000..c560940835279a --- /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' -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" + +@catch_ptr = local_unnamed_addr global ptr null, align 8 +@_ZTIi = external constant ptr +@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 3406e70dd02b23..c482a20f9513a3 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 01bf81d0abd4e8..c1308931c78164 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 5ba69e236e4135..a858aace834d6b 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 a88a45a65d59c2..f56d30be37959f 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 }