blob: 747f6c2e1bb3bc87a51c0cf5702a0be7b9d629a8 [file] [edit]
; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s | FileCheck %s
; RUN: llc -mtriple=x86_64-unknown-windows-msvc -o - %s \
; RUN: -x86-wineh-unwindv2-unwind-codes-threshold=8 | FileCheck %s \
; RUN: -check-prefixes=ALLOWLESS,CHECK
define dso_local void @no_epilog() local_unnamed_addr {
entry:
ret void
}
; CHECK-LABEL: no_epilog:
; CHECK-NOT: .seh_
; CHECK: retq
define dso_local void @stack_alloc_no_pushes() local_unnamed_addr {
entry:
call void @a()
ret void
}
; CHECK-LABEL: stack_alloc_no_pushes:
; CHECK: .seh_unwindversion 2
; CHECK-NOT: .seh_pushreg
; CHECK: .seh_stackalloc
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
define dso_local i32 @stack_alloc_and_pushes(i32 %x) local_unnamed_addr {
entry:
%call = tail call i32 @c(i32 %x)
%call1 = tail call i32 @c(i32 %x)
%add = add nsw i32 %call1, %call
%call2 = tail call i32 @c(i32 %x)
%call3 = tail call i32 @c(i32 %call2)
%add4 = add nsw i32 %add, %call3
ret i32 %add4
}
; CHECK-LABEL: stack_alloc_and_pushes:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rsi
; CHECK: .seh_pushreg %rdi
; CHECK: .seh_pushreg %rbx
; CHECK: .seh_stackalloc
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: popq %rbx
; CHECK-NEXT: popq %rdi
; CHECK-NEXT: popq %rsi
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
define dso_local i32 @tail_call(i32 %x) local_unnamed_addr {
entry:
%call = tail call i32 @c(i32 %x)
%call1 = tail call i32 @c(i32 %call)
ret i32 %call1
}
; CHECK-LABEL: tail_call:
; CHECK: .seh_unwindversion 2
; CHECK-NOT: .seh_pushreg
; CHECK: .seh_stackalloc
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: jmp
define dso_local i32 @multiple_epilogs(i32 %x) local_unnamed_addr {
entry:
%call = tail call i32 @c(i32 noundef %x)
%cmp = icmp sgt i32 %call, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
%call1 = tail call i32 @c(i32 noundef %call)
ret i32 %call1
if.else:
%call2 = tail call i32 @b()
ret i32 %call2
}
; CHECK-LABEL: multiple_epilogs:
; CHECK: .seh_unwindversion 2
; CHECK-NOT: .seh_pushreg
; CHECK: .seh_stackalloc
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: jmp
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: jmp
define dso_local i32 @mismatched_terminators() local_unnamed_addr {
entry:
%call = tail call i32 @b()
%cmp = icmp sgt i32 %call, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
%call1 = tail call i32 @b()
ret i32 %call1
if.else:
ret i32 %call
}
; CHECK-LABEL: mismatched_terminators:
; CHECK: .seh_unwindversion 2
; CHECK-NOT: .seh_pushreg
; CHECK: .seh_stackalloc
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: jmp
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: addq
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: ret
define dso_local void @dynamic_stack_alloc(i32 %x) local_unnamed_addr {
entry:
%y = alloca i32, i32 %x
ret void
}
; CHECK-LABEL: dynamic_stack_alloc:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_setframe %rbp, 0
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: movq %rbp, %rsp
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK-NEXT: .seh_endproc
define dso_local void @large_aligned_alloc() align 16 {
%1 = alloca [128 x i8], align 64
ret void
}
; CHECK-LABEL: large_aligned_alloc:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_stackalloc 176
; CHECK: .seh_setframe %rbp, 128
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: leaq 48(%rbp), %rsp
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK-NEXT: .seh_endproc
define dso_local void @set_frame_only() local_unnamed_addr {
tail call i64 @llvm.x86.flags.read.u64()
ret void
}
; CHECK-LABEL: set_frame_only:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_setframe %rbp, 0
; CHECK: .seh_endprologue
; CHECK-NOT: .seh_endproc
; CHECK: .seh_startepilogue
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK-NEXT: .seh_endproc
attributes #1 = { noreturn }
define dso_local void @no_return_func() local_unnamed_addr #1 {
entry:
call void @d()
unreachable
}
; CHECK-LABEL: no_return_func:
; CHECK-NOT: .seh_unwindversion 2
; CHECK: .seh_stackalloc
; CHECK-NEXT: .seh_endprologue
; CHECK-NOT: .seh_startepilogue
; CHECK-NOT: .seh_unwindv2start
; CHECK: int3
; CHECK-NEXT: .seh_endproc
define dso_local i32 @has_funclet(i32 %x) local_unnamed_addr personality ptr @__C_specific_handler {
entry:
%call = invoke i32 @c(i32 %x)
to label %call.block.1 unwind label %cleanup
call.block.1:
%call1 = invoke i32 @c(i32 %x)
to label %call.block.2 unwind label %cleanup
call.block.2:
%add = add nsw i32 %call1, %call
%call2 = invoke i32 @c(i32 %x)
to label %call.block.3 unwind label %cleanup
call.block.3:
%call3 = invoke i32 @c(i32 %call2)
to label %call.block.4 unwind label %cleanup
call.block.4:
%add4 = add nsw i32 %add, %call3
ret i32 %add4
cleanup:
%cleanup_token = cleanuppad within none []
call fastcc void @is_funclet(i32 %x) #18 [ "funclet"(token %cleanup_token) ]
cleanupret from %cleanup_token unwind to caller
}
define internal fastcc void @is_funclet(i32 %x) local_unnamed_addr {
entry:
%y = alloca i32, i32 %x
ret void
}
; CHECK-LABEL: has_funclet:
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_pushreg %rsi
; CHECK: .seh_pushreg %rdi
; CHECK: .seh_stackalloc 48
; CHECK: .seh_setframe %rbp, 48
; CHECK: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK: .seh_unwindv2start
; CHECK-NEXT: popq %rdi
; CHECK-NEXT: popq %rsi
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; ALLOWLESS-NEXT: .seh_splitchained
; ALLOWLESS-NEXT: .seh_endprologue
; CHECK-NEXT: .seh_handlerdata
; CHECK: .text
; CHECK-NEXT: .seh_endproc
; CHECK-LABEL: "?dtor$5@?0?has_funclet@4HA":
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_pushreg %rsi
; CHECK: .seh_pushreg %rdi
; CHECK: .seh_stackalloc 32
; CHECK: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK: .seh_unwindv2start
; CHECK-NEXT: popq %rdi
; CHECK-NEXT: popq %rsi
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK: .seh_handlerdata
; CHECK-NEXT: .text
; CHECK-NEXT: .seh_endproc
; CHECK-LABEL: is_funclet:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_setframe %rbp, 0
; CHECK: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK: .seh_unwindv2start
; CHECK: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK-NEXT: .seh_endproc
define dso_local void @set_frame_and_alloc(i32 %x) local_unnamed_addr {
%y = alloca i32, i32 %x
%z = alloca ptr, align 8
ret void
}
; CHECK-LABEL: set_frame_and_alloc:
; CHECK: .seh_unwindversion 2
; CHECK: .seh_pushreg %rbp
; CHECK: .seh_stackalloc 16
; CHECK: .seh_setframe %rbp, 16
; CHECK: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: movq %rbp, %rsp
; CHECK-NEXT: .seh_unwindv2start
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: retq
; CHECK-NEXT: .seh_endproc
declare i64 @llvm.x86.flags.read.u64()
declare void @a() local_unnamed_addr
declare i32 @b() local_unnamed_addr
declare i32 @c(i32) local_unnamed_addr
declare void @d() local_unnamed_addr #1
declare dso_local i32 @__C_specific_handler(...)
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"winx64-eh-unwindv2", i32 2}