================
@@ -0,0 +1,265 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
UTC_ARGS: --version 6
+; RUN: llc -mtriple=thumbv6m-none-eabi -verify-machineinstrs < %s | FileCheck
%s
+
+; The -fstack-limit-variable=<var> prologue is emitted only for functions that
+; carry the "stack-limit-variable"="<var>" attribute and have a non-empty stack
+; frame. It loads <var>, computes the free space (sp - limit), compares it
+; against the frame size and traps with "svc #255" when it is too small.
+; Thumb1 has no movw/movt or wide encodings, so the sequence loads the limit's
+; address from the constant pool and does the arithmetic in two low registers
+; (r2/r3). Under AAPCS arguments are assigned r0, r1, r2, r3 in order, so
+; r2/r3 are the argument registers least likely to be live on entry. Each is
+; used directly if dead and preserved with push/pop only if it still carries a
+; live argument.
+
+declare void @sink(ptr)
+
+; A small frame: the required size fits in the 8-bit "cmp" immediate.
+define void @needs_check() #0 {
+; CHECK-LABEL: needs_check:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: mov r3, sp
+; CHECK-NEXT: ldr r2, .LCPI0_0
+; CHECK-NEXT: ldr r2, [r2]
+; CHECK-NEXT: sub r3, r3, r2
+; CHECK-NEXT: cmp r3, #72
+; CHECK-NEXT: bhs .LBB0_2
+; CHECK-NEXT: @ %bb.1:
+; CHECK-NEXT: svc #255
+; CHECK-NEXT: .LBB0_2: @ %entry
+; CHECK-NEXT: .save {r7, lr}
+; CHECK-NEXT: push {r7, lr}
+; CHECK-NEXT: .pad #64
+; CHECK-NEXT: sub sp, #64
+; CHECK-NEXT: mov r0, sp
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: add sp, #64
+; CHECK-NEXT: pop {r7, pc}
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: @ %bb.3:
+; CHECK-NEXT: .LCPI0_0:
+; CHECK-NEXT: .long __stack_boundary
+entry:
+ %buf = alloca [64 x i8], align 1
+ call void @sink(ptr %buf)
+ ret void
+}
+
+; A large frame: the required size is materialized from the constant pool and
+; compared register-to-register.
+define void @large_frame() #0 {
+; CHECK-LABEL: large_frame:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: mov r3, sp
+; CHECK-NEXT: ldr r2, .LCPI1_0
+; CHECK-NEXT: ldr r2, [r2]
+; CHECK-NEXT: sub r3, r3, r2
+; CHECK-NEXT: ldr r2, .LCPI1_1
+; CHECK-NEXT: cmp r3, r2
+; CHECK-NEXT: bhs .LBB1_2
+; CHECK-NEXT: @ %bb.1:
+; CHECK-NEXT: svc #255
+; CHECK-NEXT: .LBB1_2: @ %entry
+; CHECK-NEXT: .save {r4, r5, r6, lr}
+; CHECK-NEXT: push {r4, r5, r6, lr}
+; CHECK-NEXT: .pad #508
+; CHECK-NEXT: sub sp, #508
+; CHECK-NEXT: .pad #508
+; CHECK-NEXT: sub sp, #508
+; CHECK-NEXT: .pad #8
+; CHECK-NEXT: sub sp, #8
+; CHECK-NEXT: mov r0, sp
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: add sp, #508
+; CHECK-NEXT: add sp, #508
+; CHECK-NEXT: add sp, #8
+; CHECK-NEXT: pop {r4, r5, r6, pc}
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: @ %bb.3:
+; CHECK-NEXT: .LCPI1_0:
+; CHECK-NEXT: .long __stack_boundary
+; CHECK-NEXT: .LCPI1_1:
+; CHECK-NEXT: .long 1040 @ 0x410
+entry:
+ %buf = alloca [1024 x i8], align 1
+ call void @sink(ptr %buf)
+ ret void
+}
+
+; The variable name comes from the attribute.
+define void @custom_var() #1 {
+; CHECK-LABEL: custom_var:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: mov r3, sp
+; CHECK-NEXT: ldr r2, .LCPI2_0
+; CHECK-NEXT: ldr r2, [r2]
+; CHECK-NEXT: sub r3, r3, r2
+; CHECK-NEXT: cmp r3, #72
+; CHECK-NEXT: bhs .LBB2_2
+; CHECK-NEXT: @ %bb.1:
+; CHECK-NEXT: svc #255
+; CHECK-NEXT: .LBB2_2: @ %entry
+; CHECK-NEXT: .save {r7, lr}
+; CHECK-NEXT: push {r7, lr}
+; CHECK-NEXT: .pad #64
+; CHECK-NEXT: sub sp, #64
+; CHECK-NEXT: mov r0, sp
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: add sp, #64
+; CHECK-NEXT: pop {r7, pc}
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: @ %bb.3:
+; CHECK-NEXT: .LCPI2_0:
+; CHECK-NEXT: .long my_limit
+entry:
+ %buf = alloca [64 x i8], align 1
+ call void @sink(ptr %buf)
+ ret void
+}
+
+; The trap immediate is taken from "stack-limit-trap-number": svc #42.
+define void @custom_trap() #2 {
+; CHECK-LABEL: custom_trap:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: mov r3, sp
+; CHECK-NEXT: ldr r2, .LCPI3_0
+; CHECK-NEXT: ldr r2, [r2]
+; CHECK-NEXT: sub r3, r3, r2
+; CHECK-NEXT: cmp r3, #72
+; CHECK-NEXT: bhs .LBB3_2
+; CHECK-NEXT: @ %bb.1:
+; CHECK-NEXT: svc #42
+; CHECK-NEXT: .LBB3_2: @ %entry
+; CHECK-NEXT: .save {r7, lr}
+; CHECK-NEXT: push {r7, lr}
+; CHECK-NEXT: .pad #64
+; CHECK-NEXT: sub sp, #64
+; CHECK-NEXT: mov r0, sp
+; CHECK-NEXT: bl sink
+; CHECK-NEXT: add sp, #64
+; CHECK-NEXT: pop {r7, pc}
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: @ %bb.3:
+; CHECK-NEXT: .LCPI3_0:
+; CHECK-NEXT: .long __stack_boundary
+entry:
+ %buf = alloca [64 x i8], align 1
+ call void @sink(ptr %buf)
+ ret void
+}
+
+; All four argument registers are live on entry, so both scratch registers
+; (r2, r3) are preserved with push/pop around the check.
+define i32 @args_live(i32 %a, i32 %b, i32 %c, i32 %d) #0 {
+; CHECK-LABEL: args_live:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: push {r2, r3}
----------------
efriedma-quic wrote:
Is the user expected to reserve some amount of space beyond `__stack_boundary`?
https://github.com/llvm/llvm-project/pull/208076
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits