-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WinEH] Apply funclet operand bundles to nounwind intrinsics that low…
…er to function calls in the course of IR transforms WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs. * Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand. * PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers. * LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites. Reviewed By: theraven Differential Revision: https://reviews.llvm.org/D128190
- Loading branch information
1 parent
22ff434
commit 463592e
Showing
8 changed files
with
245 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck %s | ||
|
||
// WinEH requires funclet tokens on nounwind intrinsics if they can lower to | ||
// regular function calls in the course of IR transformations. | ||
// | ||
// This is the case for ObjC ARC runtime intrinsics. Test that clang emits the | ||
// funclet tokens for llvm.objc.retain and llvm.objc.storeStrong and that they | ||
// refer to their catchpad's SSA value. | ||
|
||
@class Ety; | ||
void opaque(void); | ||
void test_catch_with_objc_intrinsic(void) { | ||
@try { | ||
opaque(); | ||
} @catch (Ety *ex) { | ||
// Destroy ex when leaving catchpad. This emits calls to intrinsic functions | ||
// llvm.objc.retain and llvm.objc.storeStrong | ||
} | ||
} | ||
|
||
// CHECK-LABEL: define{{.*}} void {{.*}}test_catch_with_objc_intrinsic | ||
// ... | ||
// CHECK: catch.dispatch: | ||
// CHECK-NEXT: [[CATCHSWITCH:%[0-9]+]] = catchswitch within none | ||
// ... | ||
// CHECK: catch: | ||
// CHECK-NEXT: [[CATCHPAD:%[0-9]+]] = catchpad within [[CATCHSWITCH]] | ||
// CHECK: {{%[0-9]+}} = call {{.*}} @llvm.objc.retain{{.*}} [ "funclet"(token [[CATCHPAD]]) ] | ||
// CHECK: call {{.*}} @llvm.objc.storeStrong{{.*}} [ "funclet"(token [[CATCHPAD]]) ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s | ||
|
||
; WinEH requires funclet tokens on nounwind intrinsics if they can lower to | ||
; regular function calls in the course of IR transformations. | ||
; | ||
; Test that the code generator will emit the function call and not consider it | ||
; an "implausible instruciton". In the past this silently truncated code on | ||
; exception paths and caused crashes at runtime. | ||
; | ||
; Reduced IR generated from ObjC++ source: | ||
; | ||
; @class Ety; | ||
; void opaque(void); | ||
; void test_catch_with_objc_intrinsic(void) { | ||
; @try { | ||
; opaque(); | ||
; } @catch (Ety *ex) { | ||
; // Destroy ex when leaving catchpad. This would emit calls to two | ||
; // intrinsic functions: llvm.objc.retain and llvm.objc.storeStrong | ||
; } | ||
; } | ||
; | ||
; llvm.objc.retain and llvm.objc.storeStrong both lower into regular function | ||
; calls before ISel. We only need one of them to trigger funclet truncation | ||
; during codegen: | ||
|
||
define void @test_catch_with_objc_intrinsic() personality ptr @__CxxFrameHandler3 { | ||
entry: | ||
%exn.slot = alloca ptr, align 8 | ||
%ex2 = alloca ptr, align 8 | ||
invoke void @opaque() to label %invoke.cont unwind label %catch.dispatch | ||
|
||
catch.dispatch: | ||
%0 = catchswitch within none [label %catch] unwind to caller | ||
|
||
invoke.cont: | ||
unreachable | ||
|
||
catch: | ||
%1 = catchpad within %0 [ptr null, i32 64, ptr %exn.slot] | ||
call void @llvm.objc.storeStrong(ptr %ex2, ptr null) [ "funclet"(token %1) ] | ||
catchret from %1 to label %catchret.dest | ||
|
||
catchret.dest: | ||
ret void | ||
} | ||
|
||
declare void @opaque() | ||
declare void @llvm.objc.storeStrong(ptr, ptr) #0 | ||
declare i32 @__CxxFrameHandler3(...) | ||
|
||
attributes #0 = { nounwind } | ||
|
||
; EH catchpad with SEH prologue: | ||
; CHECK: # %catch | ||
; CHECK: pushq %rbp | ||
; CHECK: .seh_pushreg %rbp | ||
; ... | ||
; CHECK: .seh_endprologue | ||
; | ||
; At this point the code used to be truncated (and sometimes terminated with an | ||
; int3 opcode): | ||
; CHECK-NOT: int3 | ||
; | ||
; Instead, the call to objc_storeStrong should be emitted: | ||
; CHECK: leaq -24(%rbp), %rcx | ||
; CHECK: xorl %edx, %edx | ||
; CHECK: callq objc_storeStrong | ||
; ... | ||
; | ||
; This is the end of the funclet: | ||
; CHECK: popq %rbp | ||
; CHECK: retq # CATCHRET | ||
; ... | ||
; CHECK: .seh_handlerdata | ||
; ... | ||
; CHECK: .seh_endproc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
; RUN: opt -S -always-inline -mtriple=x86_64-windows-msvc < %s | FileCheck %s | ||
|
||
; WinEH requires funclet tokens on nounwind intrinsics if they can lower to | ||
; regular function calls in the course of IR transformations. | ||
; | ||
; Test that the inliner propagates funclet tokens to such intrinsics when | ||
; inlining into EH funclets, i.e.: llvm.objc.storeStrong inherits the "funclet" | ||
; token from inlined_fn. | ||
|
||
define void @inlined_fn(ptr %ex) #1 { | ||
entry: | ||
call void @llvm.objc.storeStrong(ptr %ex, ptr null) | ||
ret void | ||
} | ||
|
||
define void @test_catch_with_inline() personality ptr @__CxxFrameHandler3 { | ||
entry: | ||
%exn.slot = alloca ptr, align 8 | ||
%ex = alloca ptr, align 8 | ||
invoke void @opaque() to label %invoke.cont unwind label %catch.dispatch | ||
|
||
catch.dispatch: | ||
%0 = catchswitch within none [label %catch] unwind to caller | ||
|
||
invoke.cont: | ||
unreachable | ||
|
||
catch: | ||
%1 = catchpad within %0 [ptr null, i32 64, ptr %exn.slot] | ||
call void @inlined_fn(ptr %ex) [ "funclet"(token %1) ] | ||
catchret from %1 to label %catchret.dest | ||
|
||
catchret.dest: | ||
ret void | ||
} | ||
|
||
declare void @opaque() | ||
declare void @llvm.objc.storeStrong(ptr, ptr) #0 | ||
declare i32 @__CxxFrameHandler3(...) | ||
|
||
attributes #0 = { nounwind } | ||
attributes #1 = { alwaysinline } | ||
|
||
; After inlining, llvm.objc.storeStrong inherited the "funclet" token: | ||
; | ||
; CHECK-LABEL: define void @test_catch_with_inline() | ||
; ... | ||
; CHECK: catch: | ||
; CHECK-NEXT: %1 = catchpad within %0 [ptr null, i32 64, ptr %exn.slot] | ||
; CHECK-NEXT: call void @llvm.objc.storeStrong(ptr %ex, ptr null) [ "funclet"(token %1) ] | ||
; CHECK-NEXT: catchret from %1 to label %catchret.dest |