xref: /llvm-project/llvm/test/CodeGen/X86/seh-safe-div-win32.ll (revision 2f448bf509432c1a19ec46ab8cbc7353c03c6280)
1; RUN: llc -mtriple i686-pc-windows-msvc < %s | FileCheck %s
2
3; This test case is also intended to be run manually as a complete functional
4; test. It should link, print something, and exit zero rather than crashing.
5; It is the hypothetical lowering of a C source program that looks like:
6;
7;   int safe_div(int *n, int *d) {
8;     int r;
9;     __try {
10;       __try {
11;         r = *n / *d;
12;       } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) {
13;         puts("EXCEPTION_ACCESS_VIOLATION");
14;         r = -1;
15;       }
16;     } __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) {
17;       puts("EXCEPTION_INT_DIVIDE_BY_ZERO");
18;       r = -2;
19;     }
20;     return r;
21;   }
22
23@str1 = internal constant [27 x i8] c"EXCEPTION_ACCESS_VIOLATION\00"
24@str2 = internal constant [29 x i8] c"EXCEPTION_INT_DIVIDE_BY_ZERO\00"
25
26define i32 @safe_div(ptr %n, ptr %d) personality ptr @_except_handler3 {
27entry:
28  %r = alloca i32, align 4
29  store i32 42, ptr %r
30  invoke void @try_body(ptr %r, ptr %n, ptr %d)
31          to label %__try.cont unwind label %lpad0
32
33lpad0:
34  %cs0 = catchswitch within none [label %handler0] unwind label %lpad1
35
36handler0:
37  %p0 = catchpad within %cs0 [ptr @safe_div_filt0]
38  call void @puts(ptr @str1) [ "funclet"(token %p0) ]
39  store i32 -1, ptr %r, align 4
40  catchret from %p0 to label %__try.cont
41
42lpad1:
43  %cs1 = catchswitch within none [label %handler1] unwind to caller
44
45handler1:
46  %p1 = catchpad within %cs1 [ptr @safe_div_filt1]
47  call void @puts(ptr @str2) [ "funclet"(token %p1) ]
48  store i32 -2, ptr %r, align 4
49  catchret from %p1 to label %__try.cont
50
51__try.cont:
52  %safe_ret = load i32, ptr %r, align 4
53  ret i32 %safe_ret
54}
55
56; Normal path code
57
58; CHECK: {{^}}_safe_div:
59; CHECK: movl $42, [[rloc:.*\(%ebp\)]]
60; CHECK: leal [[rloc]],
61; CHECK: calll _try_body
62; CHECK: [[cont_bb:LBB0_[0-9]+]]:
63; CHECK: movl [[rloc]], %eax
64; CHECK: retl
65
66; Landing pad code
67
68; CHECK: [[handler1:LBB0_[0-9]+]]: # %handler1
69; 	Restore SP
70; CHECK: movl {{.*}}(%ebp), %esp
71; CHECK: calll _puts
72; CHECK: jmp [[cont_bb]]
73
74; CHECK: [[handler0:LBB0_[0-9]+]]: # %handler0
75; 	Restore SP
76; CHECK: movl {{.*}}(%ebp), %esp
77; CHECK: calll _puts
78; CHECK: jmp [[cont_bb]]
79
80; CHECK: .section .xdata,"dr"
81; CHECK: L__ehtable$safe_div:
82; CHECK-NEXT: .long -1
83; CHECK-NEXT: .long _safe_div_filt1
84; CHECK-NEXT: .long [[handler1]]
85; CHECK-NEXT: .long 0
86; CHECK-NEXT: .long _safe_div_filt0
87; CHECK-NEXT: .long [[handler0]]
88
89define void @try_body(ptr %r, ptr %n, ptr %d) {
90entry:
91  %0 = load i32, ptr %n, align 4
92  %1 = load i32, ptr %d, align 4
93  %div = sdiv i32 %0, %1
94  store i32 %div, ptr %r, align 4
95  ret void
96}
97
98; The prototype of these filter functions is:
99; int filter(EXCEPTION_POINTERS *eh_ptrs, ptr rbp);
100
101; The definition of EXCEPTION_POINTERS is:
102;   typedef struct _EXCEPTION_POINTERS {
103;     EXCEPTION_RECORD *ExceptionRecord;
104;     CONTEXT          *ContextRecord;
105;   } EXCEPTION_POINTERS;
106
107; The definition of EXCEPTION_RECORD is:
108;   typedef struct _EXCEPTION_RECORD {
109;     DWORD ExceptionCode;
110;     ...
111;   } EXCEPTION_RECORD;
112
113define i32 @safe_div_filt0() {
114  %ebp = call ptr @llvm.frameaddress(i32 1)
115  %eh_ptrs.addr.i8 = getelementptr inbounds i8, ptr %ebp, i32 -20
116  %eh_ptrs = load ptr, ptr %eh_ptrs.addr.i8
117  %eh_rec = load ptr, ptr %eh_ptrs
118  %eh_code = load i32, ptr %eh_rec
119  ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005
120  %cmp = icmp eq i32 %eh_code, 3221225477
121  %filt.res = zext i1 %cmp to i32
122  ret i32 %filt.res
123}
124define i32 @safe_div_filt1() {
125  %ebp = call ptr @llvm.frameaddress(i32 1)
126  %eh_ptrs.addr.i8 = getelementptr inbounds i8, ptr %ebp, i32 -20
127  %eh_ptrs = load ptr, ptr %eh_ptrs.addr.i8
128  %eh_rec = load ptr, ptr %eh_ptrs
129  %eh_code = load i32, ptr %eh_rec
130  ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
131  %cmp = icmp eq i32 %eh_code, 3221225620
132  %filt.res = zext i1 %cmp to i32
133  ret i32 %filt.res
134}
135
136@str_result = internal constant [21 x i8] c"safe_div result: %d\0A\00"
137
138define i32 @main() {
139  %d.addr = alloca i32, align 4
140  %n.addr = alloca i32, align 4
141
142  store i32 10, ptr %n.addr, align 4
143  store i32 2, ptr %d.addr, align 4
144  %r1 = call i32 @safe_div(ptr %n.addr, ptr %d.addr)
145  call void (ptr, ...) @printf(ptr @str_result, i32 %r1)
146
147  store i32 10, ptr %n.addr, align 4
148  store i32 0, ptr %d.addr, align 4
149  %r2 = call i32 @safe_div(ptr %n.addr, ptr %d.addr)
150  call void (ptr, ...) @printf(ptr @str_result, i32 %r2)
151
152  %r3 = call i32 @safe_div(ptr %n.addr, ptr null)
153  call void (ptr, ...) @printf(ptr @str_result, i32 %r3)
154  ret i32 0
155}
156
157declare i32 @_except_handler3(...)
158declare i32 @llvm.eh.typeid.for(ptr) readnone nounwind
159declare void @puts(ptr)
160declare void @printf(ptr, ...)
161declare void @abort()
162declare ptr @llvm.frameaddress(i32)
163