diff options
Diffstat (limited to 'clang/test/CodeGenCXX')
-rw-r--r-- | clang/test/CodeGenCXX/builtin-amdgcn-fence.cpp | 4 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/delete.cpp | 32 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp | 209 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp | 140 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp | 242 |
5 files changed, 618 insertions, 9 deletions
diff --git a/clang/test/CodeGenCXX/builtin-amdgcn-fence.cpp b/clang/test/CodeGenCXX/builtin-amdgcn-fence.cpp index 3af5a21..1e977dd 100644 --- a/clang/test/CodeGenCXX/builtin-amdgcn-fence.cpp +++ b/clang/test/CodeGenCXX/builtin-amdgcn-fence.cpp @@ -105,7 +105,7 @@ void test_mixed() { __builtin_amdgcn_fence( __ATOMIC_SEQ_CST, "workgroup", "local", "local", "global", "local", "local"); } //. -// CHECK: [[META3]] = !{!"amdgpu-as", !"local"} -// CHECK: [[META4]] = !{!"amdgpu-as", !"global"} +// CHECK: [[META3]] = !{!"amdgpu-synchronize-as", !"local"} +// CHECK: [[META4]] = !{!"amdgpu-synchronize-as", !"global"} // CHECK: [[META5]] = !{[[META4]], [[META3]]} //. diff --git a/clang/test/CodeGenCXX/delete.cpp b/clang/test/CodeGenCXX/delete.cpp index d5b0dc6..21b9f8c 100644 --- a/clang/test/CodeGenCXX/delete.cpp +++ b/clang/test/CodeGenCXX/delete.cpp @@ -76,27 +76,45 @@ namespace test1 { ~A(); }; - // CHECK-LABEL: define{{.*}} void @_ZN5test14testEPA10_A20_NS_1AE( - void test(A (*arr)[10][20]) { + // CHECK-LABEL: define{{.*}} void @_ZN5test11fEPA10_A20_NS_1AE( + void f(A (*arr)[10][20]) { delete [] arr; // CHECK: icmp eq ptr [[PTR:%.*]], null // CHECK-NEXT: br i1 - // CHECK: [[BEGIN:%.*]] = getelementptr inbounds [10 x [20 x [[A:%.*]]]], ptr [[PTR]], i32 0, i32 0, i32 0 - // CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[BEGIN]], i64 -8 + // CHECK: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 -8 // CHECK-NEXT: [[COUNT:%.*]] = load i64, ptr [[ALLOC]] - // CHECK: [[END:%.*]] = getelementptr inbounds [[A]], ptr [[BEGIN]], i64 [[COUNT]] - // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[BEGIN]], [[END]] + // CHECK: [[END:%.*]] = getelementptr inbounds [[A:%.*]], ptr [[PTR]], i64 [[COUNT]] + // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[PTR]], [[END]] // CHECK-NEXT: br i1 [[ISEMPTY]], // CHECK: [[PAST:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]], ptr [[PAST]], i64 -1 // CHECK-NEXT: call void @_ZN5test11AD1Ev(ptr {{[^,]*}} [[CUR]]) - // CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]] + // CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[PTR]] // CHECK-NEXT: br i1 [[ISDONE]] // CHECK: [[MUL:%.*]] = mul i64 4, [[COUNT]] // CHECK-NEXT: [[SIZE:%.*]] = add i64 [[MUL]], 8 // CHECK-NEXT: call void @_ZdaPvm(ptr noundef [[ALLOC]], i64 noundef [[SIZE]]) } + + // CHECK-LABEL: define{{.*}} void @_ZN5test11gEPA_NS_1AE( + void g(A (*arr)[]) { + delete [] arr; + // CHECK: icmp eq ptr [[PTR:%.*]], null + // CHECK-NEXT: br i1 + + // CHECK: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 -8 + // CHECK-NEXT: [[COUNT:%.*]] = load i64, ptr [[ALLOC]] + // CHECK: [[END:%.*]] = getelementptr inbounds [[A:%.*]], ptr [[PTR]], i64 [[COUNT]] + // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[PTR]], [[END]] + // CHECK-NEXT: br i1 [[ISEMPTY]], + // CHECK: [[PAST:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]], ptr [[PAST]], i64 -1 + // CHECK-NEXT: call void @_ZN5test11AD1Ev(ptr {{[^,]*}} [[CUR]]) + // CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[PTR]] + // CHECK-NEXT: br i1 [[ISDONE]] + // CHECK: call void @_ZdaPv(ptr noundef [[ALLOC]]) + } } namespace test2 { diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp new file mode 100644 index 0000000..b831737 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-async.cpp @@ -0,0 +1,209 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHa -O2 /GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: /clang:-S /clang:-o- -- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +class BadError { +public: + int errorCode; +}; + +void normal_has_regions() { + // CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" + // CHECK: .seh_endprologue + + // <-- state -1 (none) + { + HasDtor hd{42}; + + // <-- state goes from -1 to 0 + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + + might_throw(); + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + + // <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor() + // CHECK: call "??1HasDtor@@QEAA@XZ" + // <-- state -1 + } + + // <-- state -1 + other_func(10); + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + // CHECK: .seh_startepilogue + + // <-- state -1 +} + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" + // CHECK: jmp "??1HasDtor@@QEAA@XZ" +} + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" + // CHECK: .seh_endprologue + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NOT: nop + + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + + return 100; + + // CHECK: mov eax, 100 + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: .seh_endproc +} + +int case_noexcept_dtor(HasDtor x) noexcept(true) +{ + // CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z" + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: mov eax, 100 + // CHECK: .seh_startepilogue + return 100; +} + +void case_except_simple_call() NO_TAIL +{ + does_not_throw(); +} +// CHECK-LABEL: .def "?case_except_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK: .seh_endproc + +void case_noexcept_simple_call() noexcept(true) NO_TAIL +{ + does_not_throw(); +} +// CHECK-LABEL: .def "?case_noexcept_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK: .seh_endepilogue +// CHECK-NEXT: ret +// CHECK-NEXT: .seh_endproc + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. Loading the return value +// counts as a real instruction, so there is no need for a NOP after the +// dtor call. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} +// CHECK-LABEL: .def "?case_dtor_arg_calls_no_throw@@YAHVHasDtor@@@Z" +// CHECK: .seh_endprologue +// CHECK: "?does_not_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK: "??1HasDtor@@QEAA@XZ" +// CHECK-NEXT: mov eax, 100 +// CHECK: .seh_startepilogue +// CHECK: .seh_endproc + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + // CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" + // CHECK: .seh_endprologue + + // <-- EH state -1 + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + // CHECK: test + + // <-- EH state transition from -1 0 + if (x) { + might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + } else { + other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + } + does_not_throw(); + // <-- EH state transition 0 to -1 + // ~HasDtor() runs + + // CHECK: .seh_endproc + + // CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + // CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]] + // CHECK-NEXT: .long -1 + // CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long 0 + // CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long -1 +} + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh(bool b) { + // tail call; no NOP padding after JMP + if (b) { + does_not_throw(); + // <-- no NOP here + return; + } + + other_func(20); + // <-- NOP does get inserted here +} +// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAX_N@Z" +// CHECK: test +// CHECK-NEXT: je .LBB +// CHECK: jmp "?does_not_throw@@YAXXZ" +// CHECK-SAME: TAILCALL +// CHECK-NEXT: .LBB +// CHECK-NEXT: mov ecx, 20 +// CHECK-NEXT: jmp "?other_func@@YAXH@Z" +// CHECK-SAME: TAILCALL +// CHECK-NEXT: # -- End function diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp new file mode 100644 index 0000000..744f863 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-disabled.cpp @@ -0,0 +1,140 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cl -c --target=x86_64-windows-msvc -EHs-c- -O2 -GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: -clang:-S -clang:-o- -- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +void normal_has_regions() { + { + HasDtor hd{42}; + + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + might_throw(); + } + + other_func(10); +} +// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: call "?might_throw@@YAXXZ" +// CHECK-NEXT: mov +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK-NEXT: mov ecx, 10 +// CHECK-NEXT: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK-NOT: "$ip2state$?normal_has_regions@@YAXXZ" + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" +// CHECK: jmp "??1HasDtor@@QEAA@XZ" + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + return 100; +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" +// CHECK: .seh_endprologue +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK-NOT: nop +// CHECK: mov eax, 100 +// CHECK: .seh_startepilogue +// CHECK: .seh_endepilogue +// CHECK: .seh_endproc + +void case_except_simple_call() NO_TAIL +{ + does_not_throw(); +} + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + + if (x) { + might_throw(); + } else { + other_func(10); + } + does_not_throw(); + // ~HasDtor() runs +} + +// CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: test +// CHECK: call "?might_throw@@YAXXZ" +// CHECK-NEXT: jmp +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: .LBB +// CHECK: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: lea +// CHECK-NEXT: call "??1HasDtor@@QEAA@XZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK-NOT: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh() { + // ordinary call + other_func(10); + + // tail call; no NOP padding after JMP + does_not_throw(); +} + +// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue +// CHECK: .seh_endepilogue +// CHECK: jmp "?does_not_throw@@YAXXZ" +// CHECK-NOT: nop +// CHECK: .seh_endproc diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp new file mode 100644 index 0000000..0b7b406 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-ip2state.cpp @@ -0,0 +1,242 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 -EHsc -GS- \ +// RUN: -Xclang=-import-call-optimization \ +// RUN: -clang:-S -clang:-o- -- %s 2>&1 \ +// RUN: | FileCheck %s + +#ifdef __clang__ +#define NO_TAIL __attribute((disable_tail_calls)) +#else +#define NO_TAIL +#endif + +void might_throw(); +void other_func(int x); + +void does_not_throw() noexcept(true); + +extern "C" void __declspec(dllimport) some_dll_import(); + +class HasDtor { + int x; + char foo[40]; + +public: + explicit HasDtor(int x); + ~HasDtor(); +}; + +class BadError { +public: + int errorCode; +}; + +// Verify that when NOP padding for IP2State is active *and* Import Call +// Optimization is active that we see both forms of NOP padding. +void case_calls_dll_import() NO_TAIL { + some_dll_import(); +} +// CHECK-LABEL: .def "?case_calls_dll_import@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: .Limpcall{{[0-9]+}}: +// CHECK-NEXT: rex64 +// CHECK-NEXT: call __imp_some_dll_import +// CHECK-NEXT: nop dword ptr {{\[.*\]}} +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + +void normal_has_regions() { + + // <-- state -1 (none) + { + HasDtor hd{42}; + + // <-- state goes from -1 to 0 + // because state changes, we expect the HasDtor::HasDtor() call to have a NOP + + might_throw(); + + // <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor() + // <-- state -1 + } + + // <-- state -1 + other_func(10); + + // <-- state -1 +} +// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK: call "??0HasDtor@@QEAA@H@Z" +// CHECK-NEXT: nop +// CHECK: call "?might_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK: call "??1HasDtor@@QEAA@XZ" +// CHECK: call "?other_func@@YAXH@Z" +// CHECK-NEXT: nop +// CHECK: .seh_startepilogue + +// This tests a tail call to a destructor. +void case_dtor_arg_empty_body(HasDtor x) +{ +} +// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z" +// CHECK: jmp "??1HasDtor@@QEAA@XZ" + +int case_dtor_arg_empty_with_ret(HasDtor x) +{ + // CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z" + // CHECK: .seh_endprologue + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NOT: nop + + // The call to HasDtor::~HasDtor() should NOT have a NOP because the + // following "mov eax, 100" instruction is in the same EH state. + + return 100; + + // CHECK: mov eax, 100 + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: .seh_endproc +} + +int case_noexcept_dtor(HasDtor x) noexcept(true) +{ + // CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z" + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: mov eax, 100 + // CHECK-NEXT: .seh_startepilogue + return 100; +} + +// Simple call of a function that can throw +void case_except_simple_call() NO_TAIL +{ + might_throw(); +} +// CHECK-LABEL: .def "?case_except_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?might_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + +// Simple call of a function that cannot throw, in a noexcept context. +void case_noexcept_simple_call() noexcept(true) NO_TAIL +{ + does_not_throw(); +} +// CHECK-LABEL: .def "?case_noexcept_simple_call@@YAXXZ" +// CHECK: .seh_endprologue +// CHECK-NEXT: call "?does_not_throw@@YAXXZ" +// CHECK-NEXT: nop +// CHECK-NEXT: .seh_startepilogue + + +// This tests that the destructor is called right before SEH_BeginEpilogue, +// but in a function that has a return value. +int case_dtor_arg_calls_no_throw(HasDtor x) +{ + does_not_throw(); // no NOP expected + return 100; +} + +// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within +// a non-null EH state (state -1) and is at the end of an MBB, then we expect +// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which +// is the desired result. +void case_dtor_runs_after_join(int x) { + // CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z" + // CHECK: .seh_endprologue + + // <-- EH state -1 + + // ctor call does not need a NOP, because it has real instructions after it + HasDtor hd{42}; + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: test + + // <-- EH state transition from -1 0 + if (x) { + might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?might_throw@@YAXXZ" + // CHECK-NEXT: nop + } else { + other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL) + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + } + does_not_throw(); + // <-- EH state transition 0 to -1 + // ~HasDtor() runs + + // CHECK: .seh_endproc + + // CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z": + // CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]] + // CHECK-NEXT: .long -1 + // CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long 0 + // CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL + // CHECK-NEXT: .long -1 +} + + +// Check the behavior of NOP padding around tail calls. +// We do not expect to insert NOPs around tail calls. +// However, the first call (to other_func()) does get a NOP +// because it comes before .seh_startepilogue. +void case_tail_call_no_eh() { + // CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ" + // CHECK: .seh_endprologue + + // ordinary call + other_func(10); + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + + // tail call; no NOP padding after JMP + does_not_throw(); + + // CHECK: .seh_startepilogue + // CHECK: .seh_endepilogue + // CHECK: jmp "?does_not_throw@@YAXXZ" + // CHECK-NOT: nop + // CHECK: .seh_endproc +} + + +// Check the behavior of a try/catch +int case_try_catch() { + // CHECK-LABEL: .def "?case_try_catch@@YAHXZ" + // CHECK: .seh_endprologue + + // Because of the EH_LABELs, the ctor and other_func() get NOPs. + + int result = 0; + try { + // CHECK: call "??0HasDtor@@QEAA@H@Z" + // CHECK-NEXT: nop + HasDtor hd{20}; + + // CHECK: call "?other_func@@YAXH@Z" + // CHECK-NEXT: nop + other_func(10); + + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK: mov + } catch (BadError& e) { + result = 1; + } + return result; + + // CHECK: .seh_endproc + + // CHECK: .def "?dtor$4@?0??case_try_catch@@YAHXZ@4HA" + // CHECK: .seh_endprologue + // CHECK: call "??1HasDtor@@QEAA@XZ" + // CHECK-NEXT: nop + // CHECK: .seh_startepilogue + // CHECK: .seh_endproc +} |