| ;; Check that this produces the expected assembly output | |
| ; RUN: llc -mtriple=thumbv7-windows -o - %s -verify-machineinstrs | FileCheck %s | |
| ;; Also try to write an object file, which verifies that the SEH opcodes | |
| ;; match the actual prologue/epilogue length. | |
| ; RUN: llc -mtriple=thumbv7-windows -filetype=obj -o %t.obj %s -verify-machineinstrs | |
| ; CHECK-LABEL: clobberR4Frame: | |
| ; CHECK-NEXT: .seh_proc clobberR4Frame | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push.w {r4, r7, r11, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r4, r7, r11, lr} | |
| ; CHECK-NEXT: add.w r11, sp, #8 | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK-NEXT: bl other | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: pop.w {r4, r7, r11, pc} | |
| ; CHECK-NEXT: .seh_save_regs_w {r4, r7, r11, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @clobberR4Frame() uwtable "frame-pointer"="all" { | |
| entry: | |
| call arm_aapcs_vfpcc void @other() | |
| call void asm sideeffect "", "~{r4}"() | |
| ret void | |
| } | |
| ; CHECK-LABEL: clobberR4NoFrame: | |
| ; CHECK-NEXT: .seh_proc clobberR4NoFrame | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push {r4, lr} | |
| ; CHECK-NEXT: .seh_save_regs {r4, lr} | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK-NEXT: bl other | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: pop {r4, pc} | |
| ; CHECK-NEXT: .seh_save_regs {r4, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @clobberR4NoFrame() uwtable "frame-pointer"="none" { | |
| entry: | |
| call arm_aapcs_vfpcc void @other() | |
| call void asm sideeffect "", "~{r4}"() | |
| ret void | |
| } | |
| ; CHECK-LABEL: clobberR4Tail: | |
| ; CHECK-NEXT: .seh_proc clobberR4Tail | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push {r4, lr} | |
| ; CHECK-NEXT: .seh_save_regs {r4, lr} | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: pop.w {r4, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r4, lr} | |
| ; CHECK-NEXT: b.w other | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @clobberR4Tail() uwtable "frame-pointer"="none" { | |
| entry: | |
| call void asm sideeffect "", "~{r4}"() | |
| tail call arm_aapcs_vfpcc void @other() | |
| ret void | |
| } | |
| ; CHECK-LABEL: clobberD8D10: | |
| ; CHECK-NEXT: .seh_proc clobberD8D10 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: vpush {d8, d9, d10} | |
| ; CHECK-NEXT: .seh_save_fregs {d8-d10} | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: vpop {d8, d9, d10} | |
| ; CHECK-NEXT: .seh_save_fregs {d8-d10} | |
| ; CHECK-NEXT: b.w other | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @clobberD8D10() uwtable "frame-pointer"="none" { | |
| entry: | |
| call void asm sideeffect "", "~{d8},~{d9},~{d10}"() | |
| tail call arm_aapcs_vfpcc void @other() | |
| ret void | |
| } | |
| declare arm_aapcs_vfpcc void @other() | |
| ; CHECK-LABEL: vararg: | |
| ; CHECK-NEXT: .seh_proc vararg | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: sub sp, #12 | |
| ; CHECK-NEXT: .seh_stackalloc 12 | |
| ; CHECK-NEXT: push.w {r11, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: sub sp, #4 | |
| ; CHECK-NEXT: .seh_stackalloc 4 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add sp, #4 | |
| ; CHECK-NEXT: .seh_stackalloc 4 | |
| ; CHECK-NEXT: pop.w {r11, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: add sp, #12 | |
| ; CHECK-NEXT: .seh_stackalloc 12 | |
| ; CHECK-NEXT: bx lr | |
| ; CHECK-NEXT: .seh_nop | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @vararg(i32 noundef %a, ...) uwtable "frame-pointer"="none" { | |
| entry: | |
| %ap = alloca ptr, align 4 | |
| call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %ap) | |
| call void @llvm.va_start(ptr nonnull %ap) | |
| %0 = load ptr, ptr %ap | |
| call arm_aapcs_vfpcc void @useva(ptr noundef %0) | |
| call void @llvm.va_end(ptr nonnull %ap) | |
| call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %ap) | |
| ret void | |
| } | |
| declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) | |
| declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) | |
| declare void @llvm.va_start(ptr) | |
| declare void @llvm.va_end(ptr) | |
| declare arm_aapcs_vfpcc void @useva(ptr noundef) | |
| ; CHECK-LABEL: onlystack: | |
| ; CHECK-NEXT: .seh_proc onlystack | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: sub sp, #4 | |
| ; CHECK-NEXT: .seh_stackalloc 4 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add sp, #4 | |
| ; CHECK-NEXT: .seh_stackalloc 4 | |
| ; CHECK-NEXT: bx lr | |
| ; CHECK-NEXT: .seh_nop | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define dso_local arm_aapcs_vfpcc void @onlystack() uwtable "frame-pointer"="none" { | |
| entry: | |
| %buf = alloca [4 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %buf) | |
| call void asm sideeffect "", "r"(ptr nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %buf) | |
| ret void | |
| } | |
| ; CHECK-LABEL: func50: | |
| ; CHECK-NEXT: .seh_proc func50 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push.w {r11, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: sub sp, #56 | |
| ; CHECK-NEXT: .seh_stackalloc 56 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add sp, #56 | |
| ; CHECK-NEXT: .seh_stackalloc 56 | |
| ; CHECK-NEXT: pop.w {r11, pc} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @func50() { | |
| entry: | |
| %buf = alloca [50 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 50, ptr nonnull %buf) | |
| call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 50, ptr nonnull %buf) | |
| ret void | |
| } | |
| ; CHECK-LABEL: func4000: | |
| ; CHECK-NEXT: .seh_proc func4000 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push.w {r11, lr} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: sub.w sp, sp, #4000 | |
| ; CHECK-NEXT: .seh_stackalloc_w 4000 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add.w sp, sp, #4000 | |
| ; CHECK-NEXT: .seh_stackalloc_w 4000 | |
| ; CHECK-NEXT: pop.w {r11, pc} | |
| ; CHECK-NEXT: .seh_save_regs_w {r11, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @func4000() { | |
| entry: | |
| %buf = alloca [4000 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 4000, ptr nonnull %buf) | |
| call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 4000, ptr nonnull %buf) | |
| ret void | |
| } | |
| ; CHECK-LABEL: func5000: | |
| ; CHECK-NEXT: .seh_proc func5000 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push {r4, r5, r6, lr} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: movw r4, #1250 | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: bl __chkstk | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: sub.w sp, sp, r4 | |
| ; CHECK-NEXT: .seh_stackalloc_w 5000 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add.w sp, sp, #4992 | |
| ; CHECK-NEXT: .seh_stackalloc_w 4992 | |
| ; CHECK-NEXT: add sp, #8 | |
| ; CHECK-NEXT: .seh_stackalloc 8 | |
| ; CHECK-NEXT: pop {r4, r5, r6, pc} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @func5000() { | |
| entry: | |
| %buf = alloca [5000 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 5000, ptr nonnull %buf) | |
| call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 5000, ptr nonnull %buf) | |
| ret void | |
| } | |
| ; CHECK-LABEL: func262144: | |
| ; CHECK-NEXT: .seh_proc func262144 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push {r4, r5, r6, lr} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: movs r4, #0 | |
| ; CHECK-NEXT: .seh_nop | |
| ; CHECK-NEXT: movt r4, #1 | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: bl __chkstk | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: sub.w sp, sp, r4 | |
| ; CHECK-NEXT: .seh_stackalloc_w 262144 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add.w sp, sp, #262144 | |
| ; CHECK-NEXT: .seh_stackalloc_w 262144 | |
| ; CHECK-NEXT: pop {r4, r5, r6, pc} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @func262144() { | |
| entry: | |
| %buf = alloca [262144 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 262144, ptr nonnull %buf) | |
| call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 262144, ptr nonnull %buf) | |
| ret void | |
| } | |
| ; CHECK-LABEL: func270000: | |
| ; CHECK-NEXT: .seh_proc func270000 | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: push {r4, r5, r6, lr} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: movw r4, #1964 | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: movt r4, #1 | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: bl __chkstk | |
| ; CHECK-NEXT: .seh_nop_w | |
| ; CHECK-NEXT: sub.w sp, sp, r4 | |
| ; CHECK-NEXT: .seh_stackalloc_w 270000 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK: .seh_startepilogue | |
| ; CHECK-NEXT: add.w sp, sp, #268288 | |
| ; CHECK-NEXT: .seh_stackalloc_w 268288 | |
| ; CHECK-NEXT: add.w sp, sp, #1712 | |
| ; CHECK-NEXT: .seh_stackalloc_w 1712 | |
| ; CHECK-NEXT: pop {r4, r5, r6, pc} | |
| ; CHECK-NEXT: .seh_save_regs {r4-r6, lr} | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc void @func270000() { | |
| entry: | |
| %buf = alloca [270000 x i8], align 1 | |
| call void @llvm.lifetime.start.p0(i64 270000, ptr nonnull %buf) | |
| call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf) | |
| call void @llvm.lifetime.end.p0(i64 270000, ptr nonnull %buf) | |
| ret void | |
| } | |
| declare arm_aapcs_vfpcc void @useptr(ptr noundef) | |
| ; CHECK-LABEL: func_fp: | |
| ; CHECK-NEXT: .seh_proc func_fp | |
| ; CHECK-NEXT: @ %bb.0: @ %entry | |
| ; CHECK-NEXT: str r11, [sp, #-4]! | |
| ; CHECK-NEXT: .seh_save_regs_w {r11} | |
| ; CHECK-NEXT: mov r11, sp | |
| ; CHECK-NEXT: .seh_save_sp r11 | |
| ; CHECK-NEXT: .seh_endprologue | |
| ; CHECK-NEXT: mov r0, r11 | |
| ; CHECK-NEXT: .seh_startepilogue | |
| ; CHECK-NEXT: ldr r11, [sp], #4 | |
| ; CHECK-NEXT: .seh_save_regs_w {r11} | |
| ; CHECK-NEXT: bx lr | |
| ; CHECK-NEXT: .seh_nop | |
| ; CHECK-NEXT: .seh_endepilogue | |
| ; CHECK-NEXT: .seh_endproc | |
| define arm_aapcs_vfpcc i32 @func_fp() { | |
| entry: | |
| %0 = tail call ptr @llvm.frameaddress.p0(i32 0) | |
| %1 = ptrtoint ptr %0 to i32 | |
| ret i32 %1 | |
| } | |
| declare ptr @llvm.frameaddress.p0(i32 immarg) |