Skip to content

Commit a9ea224

Browse files
committed
Fix IP2State tables
style: revert one change Adjust tests adjust another test fix llvm/test/CodeGen/XCore/exception.ll
1 parent 65eaed7 commit a9ea224

30 files changed

+1229
-231
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHa -O2 /GS- \
2+
// RUN: -Xclang=-import-call-optimization \
3+
// RUN: /clang:-S /clang:-o- %s 2>&1 \
4+
// RUN: | FileCheck %s
5+
6+
#ifdef __clang__
7+
#define NO_TAIL __attribute((disable_tail_calls))
8+
#else
9+
#define NO_TAIL
10+
#endif
11+
12+
void might_throw();
13+
void other_func(int x);
14+
15+
void does_not_throw() noexcept(true);
16+
17+
extern "C" void __declspec(dllimport) some_dll_import();
18+
19+
class HasDtor {
20+
int x;
21+
char foo[40];
22+
23+
public:
24+
explicit HasDtor(int x);
25+
~HasDtor();
26+
};
27+
28+
class BadError {
29+
public:
30+
int errorCode;
31+
};
32+
33+
void normal_has_regions() {
34+
// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ"
35+
// CHECK: .seh_endprologue
36+
37+
// <-- state -1 (none)
38+
{
39+
HasDtor hd{42};
40+
41+
// <-- state goes from -1 to 0
42+
// because state changes, we expect the HasDtor::HasDtor() call to have a NOP
43+
// CHECK: call "??0HasDtor@@QEAA@H@Z"
44+
// CHECK-NEXT: nop
45+
46+
might_throw();
47+
// CHECK: call "?might_throw@@YAXXZ"
48+
// CHECK-NEXT: nop
49+
50+
// <-- state goes from 0 to -1 because we're about to call HasDtor::~HasDtor()
51+
// CHECK: call "??1HasDtor@@QEAA@XZ"
52+
// <-- state -1
53+
}
54+
55+
// <-- state -1
56+
other_func(10);
57+
// CHECK: call "?other_func@@YAXH@Z"
58+
// CHECK-NEXT: nop
59+
// CHECK: .seh_startepilogue
60+
61+
// <-- state -1
62+
}
63+
64+
// This tests a tail call to a destructor.
65+
void case_dtor_arg_empty_body(HasDtor x)
66+
{
67+
// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z"
68+
// CHECK: jmp "??1HasDtor@@QEAA@XZ"
69+
}
70+
71+
int case_dtor_arg_empty_with_ret(HasDtor x)
72+
{
73+
// CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z"
74+
// CHECK: .seh_endprologue
75+
76+
// CHECK: call "??1HasDtor@@QEAA@XZ"
77+
// CHECK-NOT: nop
78+
79+
// The call to HasDtor::~HasDtor() should NOT have a NOP because the
80+
// following "mov eax, 100" instruction is in the same EH state.
81+
82+
return 100;
83+
84+
// CHECK: mov eax, 100
85+
// CHECK: .seh_startepilogue
86+
// CHECK: .seh_endepilogue
87+
// CHECK: .seh_endproc
88+
}
89+
90+
int case_noexcept_dtor(HasDtor x) noexcept(true)
91+
{
92+
// CHECK: .def "?case_noexcept_dtor@@YAHVHasDtor@@@Z"
93+
// CHECK: call "??1HasDtor@@QEAA@XZ"
94+
// CHECK-NEXT: mov eax, 100
95+
// CHECK: .seh_startepilogue
96+
return 100;
97+
}
98+
99+
void case_except_simple_call() NO_TAIL
100+
{
101+
does_not_throw();
102+
}
103+
104+
void case_noexcept_simple_call() noexcept(true) NO_TAIL
105+
{
106+
does_not_throw();
107+
}
108+
109+
// This tests that the destructor is called right before SEH_BeginEpilogue,
110+
// but in a function that has a return value.
111+
int case_dtor_arg_calls_no_throw(HasDtor x)
112+
{
113+
does_not_throw(); // no NOP expected
114+
return 100;
115+
}
116+
117+
// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within
118+
// a non-null EH state (state -1) and is at the end of an MBB, then we expect
119+
// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which
120+
// is the desired result.
121+
void case_dtor_runs_after_join(int x) {
122+
// CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z"
123+
// CHECK: .seh_endprologue
124+
125+
// <-- EH state -1
126+
127+
// ctor call does not need a NOP, because it has real instructions after it
128+
HasDtor hd{42};
129+
// CHECK: call "??0HasDtor@@QEAA@H@Z"
130+
// CHECK-NEXT: nop
131+
// CHECK: test
132+
133+
// <-- EH state transition from -1 0
134+
if (x) {
135+
might_throw(); // <-- NOP expected (at end of BB w/ EH_LABEL)
136+
// CHECK: call "?might_throw@@YAXXZ"
137+
// CHECK-NEXT: nop
138+
} else {
139+
other_func(10); // <-- NOP expected (at end of BB w/ EH_LABEL)
140+
// CHECK: call "?other_func@@YAXH@Z"
141+
// CHECK-NEXT: nop
142+
}
143+
does_not_throw();
144+
// <-- EH state transition 0 to -1
145+
// ~HasDtor() runs
146+
147+
// CHECK: .seh_endproc
148+
149+
// CHECK: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z":
150+
// CHECK-NEXT: .long [[func_begin:.Lfunc_begin([0-9]+)@IMGREL]]
151+
// CHECK-NEXT: .long -1
152+
// CHECK-NEXT: .long [[tmp1:.Ltmp([0-9]+)]]@IMGREL
153+
// CHECK-NEXT: .long 0
154+
// CHECK-NEXT: .long [[tmp2:.Ltmp([0-9]+)]]@IMGREL
155+
// CHECK-NEXT: .long -1
156+
}
157+
158+
159+
// Check the behavior of NOP padding around tail calls.
160+
// We do not expect to insert NOPs around tail calls.
161+
// However, the first call (to other_func()) does get a NOP
162+
// because it comes before .seh_startepilogue.
163+
void case_tail_call_no_eh() {
164+
// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ"
165+
// CHECK: .seh_endprologue
166+
167+
// ordinary call
168+
other_func(10);
169+
// CHECK: call "?other_func@@YAXH@Z"
170+
// CHECK-NEXT: nop
171+
172+
// tail call; no NOP padding after JMP
173+
does_not_throw();
174+
175+
// CHECK: .seh_startepilogue
176+
// CHECK: .seh_endepilogue
177+
// CHECK: jmp "?does_not_throw@@YAXXZ"
178+
// CHECK-NOT: nop
179+
// CHECK: .seh_endproc
180+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// RUN: %clang_cl -c --target=x86_64-windows-msvc /EHs-c- -O2 /GS- \
2+
// RUN: -Xclang=-import-call-optimization \
3+
// RUN: /clang:-S /clang:-o- %s 2>&1 \
4+
// RUN: | FileCheck %s
5+
6+
#ifdef __clang__
7+
#define NO_TAIL __attribute((disable_tail_calls))
8+
#else
9+
#define NO_TAIL
10+
#endif
11+
12+
void might_throw();
13+
void other_func(int x);
14+
15+
void does_not_throw() noexcept(true);
16+
17+
extern "C" void __declspec(dllimport) some_dll_import();
18+
19+
class HasDtor {
20+
int x;
21+
char foo[40];
22+
23+
public:
24+
explicit HasDtor(int x);
25+
~HasDtor();
26+
};
27+
28+
void normal_has_regions() {
29+
{
30+
HasDtor hd{42};
31+
32+
// because state changes, we expect the HasDtor::HasDtor() call to have a NOP
33+
might_throw();
34+
}
35+
36+
other_func(10);
37+
}
38+
// CHECK-LABEL: .def "?normal_has_regions@@YAXXZ"
39+
// CHECK: .seh_endprologue
40+
// CHECK: call "??0HasDtor@@QEAA@H@Z"
41+
// CHECK-NEXT: call "?might_throw@@YAXXZ"
42+
// CHECK-NEXT: mov
43+
// CHECK: call "??1HasDtor@@QEAA@XZ"
44+
// CHECK-NEXT: mov ecx, 10
45+
// CHECK-NEXT: call "?other_func@@YAXH@Z"
46+
// CHECK-NEXT: nop
47+
// CHECK-NEXT: .seh_startepilogue
48+
// CHECK-NOT: "$ip2state$?normal_has_regions@@YAXXZ"
49+
50+
// This tests a tail call to a destructor.
51+
void case_dtor_arg_empty_body(HasDtor x)
52+
{
53+
}
54+
// CHECK-LABEL: .def "?case_dtor_arg_empty_body@@YAXVHasDtor@@@Z"
55+
// CHECK: jmp "??1HasDtor@@QEAA@XZ"
56+
57+
int case_dtor_arg_empty_with_ret(HasDtor x)
58+
{
59+
// The call to HasDtor::~HasDtor() should NOT have a NOP because the
60+
// following "mov eax, 100" instruction is in the same EH state.
61+
return 100;
62+
}
63+
// CHECK-LABEL: .def "?case_dtor_arg_empty_with_ret@@YAHVHasDtor@@@Z"
64+
// CHECK: .seh_endprologue
65+
// CHECK: call "??1HasDtor@@QEAA@XZ"
66+
// CHECK-NOT: nop
67+
// CHECK: mov eax, 100
68+
// CHECK: .seh_startepilogue
69+
// CHECK: .seh_endepilogue
70+
// CHECK: .seh_endproc
71+
72+
void case_except_simple_call() NO_TAIL
73+
{
74+
does_not_throw();
75+
}
76+
77+
// This tests that the destructor is called right before SEH_BeginEpilogue,
78+
// but in a function that has a return value.
79+
int case_dtor_arg_calls_no_throw(HasDtor x)
80+
{
81+
does_not_throw(); // no NOP expected
82+
return 100;
83+
}
84+
85+
// Check the behavior of CALLs that are at the end of MBBs. If a CALL is within
86+
// a non-null EH state (state -1) and is at the end of an MBB, then we expect
87+
// to find an EH_LABEL after the CALL. This causes us to insert a NOP, which
88+
// is the desired result.
89+
void case_dtor_runs_after_join(int x) {
90+
91+
// ctor call does not need a NOP, because it has real instructions after it
92+
HasDtor hd{42};
93+
94+
if (x) {
95+
might_throw();
96+
} else {
97+
other_func(10);
98+
}
99+
does_not_throw();
100+
// ~HasDtor() runs
101+
}
102+
103+
// CHECK-LABEL: .def "?case_dtor_runs_after_join@@YAXH@Z"
104+
// CHECK: .seh_endprologue
105+
// CHECK: call "??0HasDtor@@QEAA@H@Z"
106+
// CHECK-NEXT: test
107+
// CHECK: call "?might_throw@@YAXXZ"
108+
// CHECK-NEXT: jmp
109+
// CHECK: call "?other_func@@YAXH@Z"
110+
// CHECK-NEXT: .LBB
111+
// CHECK: call "?does_not_throw@@YAXXZ"
112+
// CHECK-NEXT: lea
113+
// CHECK-NEXT: call "??1HasDtor@@QEAA@XZ"
114+
// CHECK-NEXT: nop
115+
// CHECK-NEXT: .seh_startepilogue
116+
// CHECK-NOT: "$ip2state$?case_dtor_runs_after_join@@YAXH@Z":
117+
118+
119+
// Check the behavior of NOP padding around tail calls.
120+
// We do not expect to insert NOPs around tail calls.
121+
// However, the first call (to other_func()) does get a NOP
122+
// because it comes before .seh_startepilogue.
123+
void case_tail_call_no_eh() {
124+
// ordinary call
125+
other_func(10);
126+
127+
// tail call; no NOP padding after JMP
128+
does_not_throw();
129+
}
130+
131+
// CHECK-LABEL: .def "?case_tail_call_no_eh@@YAXXZ"
132+
// CHECK: .seh_endprologue
133+
// CHECK: call "?other_func@@YAXH@Z"
134+
// CHECK-NEXT: nop
135+
// CHECK-NEXT: .seh_startepilogue
136+
// CHECK: .seh_endepilogue
137+
// CHECK: jmp "?does_not_throw@@YAXXZ"
138+
// CHECK-NOT: nop
139+
// CHECK: .seh_endproc

0 commit comments

Comments
 (0)