210 lines · cpp
1// REQUIRES: x86-registered-target2 3// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHa -O2 /GS- \4// RUN: -Xclang=-import-call-optimization \5// RUN: /clang:-S /clang:-o- -- %s 2>&1 \6// RUN: | FileCheck %s7 8#ifdef __clang__9#define NO_TAIL __attribute((disable_tail_calls))10#else11#define NO_TAIL12#endif13 14void might_throw();15void other_func(int x);16 17void does_not_throw() noexcept(true);18 19extern "C" void __declspec(dllimport) some_dll_import();20 21class HasDtor {22 int x;23 char foo[40];24 25public:26 explicit HasDtor(int x);27 ~HasDtor();28};29 30class BadError {31public:32 int errorCode;33};34 35void normal_has_regions() {36 // CHECK-LABEL: .def "?normal_has_regions@@YAXXZ"37 // CHECK: .seh_endprologue38 39 // <-- state -1 (none)40 {41 HasDtor hd{42};42 43 // <-- state goes from -1 to 044 // because state changes, we expect the HasDtor::HasDtor() call to have a NOP45 // CHECK: call "??0HasDtor@@QEAA@H@Z"46 // CHECK-NEXT: nop47 48 might_throw();49 // CHECK: call "?might_throw@@YAXXZ"50 // CHECK-NEXT: nop51 52 // <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor()53 // CHECK: call "??1HasDtor@@QEAA@XZ"54 // <-- state -155 }56 57 // <-- state -158 other_func(10);59 // CHECK: call "?other_func@@YAXH@Z"60 // CHECK-NEXT: nop61 // CHECK: .seh_startepilogue62 63 // <-- state -164}65 66// This tests a tail call to a destructor.67void case_dtor_arg_empty_body(HasDtor x)68{69 // CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z"70 // CHECK: jmp "??1HasDtor@@QEAA@XZ"71}72 73int case_dtor_arg_empty_with_ret(HasDtor x)74{75 // CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z"76 // CHECK: .seh_endprologue77 78 // CHECK: call "??1HasDtor@@QEAA@XZ"79 // CHECK-NOT: nop80 81 // The call to HasDtor::~HasDtor() should NOT have a NOP because the82 // following "mov eax, 100" instruction is in the same EH state.83 84 return 100;85 86 // CHECK: mov eax, 10087 // CHECK: .seh_startepilogue88 // CHECK: .seh_endepilogue89 // CHECK: .seh_endproc90}91 92int case_noexcept_dtor(HasDtor x) noexcept(true)93{94 // CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z"95 // CHECK: call "??1HasDtor@@QEAA@XZ"96 // CHECK-NEXT: mov eax, 10097 // CHECK: .seh_startepilogue98 return 100;99}100 101void case_except_simple_call() NO_TAIL102{103 does_not_throw();104}105// CHECK-LABEL: .def "?case_except_simple_call@@YAXXZ"106// CHECK: .seh_endprologue107// CHECK-NEXT: call "?does_not_throw@@YAXXZ"108// CHECK-NEXT: nop109// CHECK-NEXT: .seh_startepilogue110// CHECK: .seh_endproc111 112void case_noexcept_simple_call() noexcept(true) NO_TAIL113{114 does_not_throw();115}116// CHECK-LABEL: .def "?case_noexcept_simple_call@@YAXXZ"117// CHECK: .seh_endprologue118// CHECK-NEXT: call "?does_not_throw@@YAXXZ"119// CHECK-NEXT: nop120// CHECK-NEXT: .seh_startepilogue121// CHECK: .seh_endepilogue122// CHECK-NEXT: ret123// CHECK-NEXT: .seh_endproc124 125// This tests that the destructor is called right before SEH_BeginEpilogue,126// but in a function that has a return value. Loading the return value127// counts as a real instruction, so there is no need for a NOP after the128// dtor call.129int case_dtor_arg_calls_no_throw(HasDtor x)130{131 does_not_throw(); // no NOP expected132 return 100;133}134// CHECK-LABEL: .def "?case_dtor_arg_calls_no_throw@@YAHVHasDtor@@@Z"135// CHECK: .seh_endprologue136// CHECK: "?does_not_throw@@YAXXZ"137// CHECK-NEXT: nop138// CHECK: "??1HasDtor@@QEAA@XZ"139// CHECK-NEXT: mov eax, 100140// CHECK: .seh_startepilogue141// CHECK: .seh_endproc142 143// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within144// a non-null EH state (state -1) and is at the end of an MBB, then we expect145// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which146// is the desired result.147void case_dtor_runs_after_join(int x) {148 // CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z"149 // CHECK: .seh_endprologue150 151 // <-- EH state -1152 153 // ctor call does not need a NOP, because it has real instructions after it154 HasDtor hd{42};155 // CHECK: call "??0HasDtor@@QEAA@H@Z"156 // CHECK-NEXT: nop157 // CHECK: test158 159 // <-- EH state transition from -1 0160 if (x) {161 might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL)162 // CHECK: call "?might_throw@@YAXXZ"163 // CHECK-NEXT: nop164 } else {165 other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL)166 // CHECK: call "?other_func@@YAXH@Z"167 // CHECK-NEXT: nop168 }169 does_not_throw();170 // <-- EH state transition 0 to -1171 // ~HasDtor() runs172 173 // CHECK: .seh_endproc174 175 // CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z":176 // CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]]177 // CHECK-NEXT: .long -1178 // CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL179 // CHECK-NEXT: .long 0180 // CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL181 // CHECK-NEXT: .long -1182}183 184 185// Check the behavior of NOP padding around tail calls.186// We do not expect to insert NOPs around tail calls.187// However, the first call (to other_func()) does get a NOP188// because it comes before .seh_startepilogue.189void case_tail_call_no_eh(bool b) {190 // tail call; no NOP padding after JMP191 if (b) {192 does_not_throw();193 // <-- no NOP here194 return;195 }196 197 other_func(20);198 // <-- NOP does get inserted here199}200// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAX_N@Z"201// CHECK: test202// CHECK-NEXT: je .LBB203// CHECK: jmp "?does_not_throw@@YAXXZ"204// CHECK-SAME: TAILCALL205// CHECK-NEXT: .LBB206// CHECK-NEXT: mov ecx, 20207// CHECK-NEXT: jmp "?other_func@@YAXH@Z"208// CHECK-SAME: TAILCALL209// CHECK-NEXT: # -- End function210