================
@@ -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`, 
to make this push work?

https://github.com/llvm/llvm-project/pull/208076
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to