| ; RUN: llc -mtriple=hexagon < %s | FileCheck %s | |
| ;; Small frame (< probe size): no probing loop, normal allocframe. | |
| ; CHECK-LABEL: small_frame: | |
| ; CHECK: allocframe(r29,#128):raw | |
| ; CHECK-NOT: cmp.gtu | |
| ; CHECK: dealloc_return | |
| define void @small_frame() #0 { | |
| entry: | |
| %a = alloca [128 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Large frame (> probe size): probing loop emitted. | |
| ; CHECK-LABEL: large_frame: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-8192) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @large_frame() #0 { | |
| entry: | |
| %a = alloca [8192 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Exact multiple of probe size: probing loop still emitted. | |
| ; CHECK-LABEL: exact_multiple: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-12288) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @exact_multiple() #0 { | |
| entry: | |
| %a = alloca [12288 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; No frame pointer path with no call: probing works without allocframe. | |
| ; CHECK-LABEL: no_fp_large: | |
| ; CHECK-NOT: allocframe | |
| ; CHECK: r28 = add(r29,#-8192) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @no_fp_large() #1 { | |
| entry: | |
| %a = alloca [8192 x i8], align 1 | |
| store volatile i8 0, ptr %a | |
| ret void | |
| } | |
| ;; Custom probe size of 512 bytes. | |
| ; CHECK-LABEL: custom_probe_size: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-8192) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-512) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @custom_probe_size() #2 { | |
| entry: | |
| %a = alloca [8192 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; No probe attribute: normal codegen, no probing. | |
| ; CHECK-LABEL: no_probe: | |
| ; CHECK: allocframe | |
| ; CHECK-NOT: cmp.gtu | |
| ; CHECK: dealloc_return | |
| define void @no_probe() { | |
| entry: | |
| %a = alloca [8192 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Frame >= ALLOCFRAME_MAX (16384): allocframe(#0) + probed alloc. | |
| ; CHECK-LABEL: very_large_frame: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-20480) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @very_large_frame() #0 { | |
| entry: | |
| %a = alloca [20480 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Frame == probe size exactly: no probing loop, normal allocframe. | |
| ; CHECK-LABEL: exact_probe_size: | |
| ; CHECK: allocframe(r29,#4096):raw | |
| ; CHECK-NOT: cmp.gtu | |
| ; CHECK: dealloc_return | |
| define void @exact_probe_size() #0 { | |
| entry: | |
| %a = alloca [4096 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Large frame requiring constant-extended immediate (> 32767). | |
| ; CHECK-LABEL: const_extd_frame: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,##-65536) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| define void @const_extd_frame() #0 { | |
| entry: | |
| %a = alloca [65536 x i8], align 1 | |
| call void @use(ptr %a) | |
| ret void | |
| } | |
| ;; Callee-saved register spills coexist with probing. | |
| ; CHECK-LABEL: callee_saved_regs: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-8216) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| ; CHECK: memd(r29+##{{[0-9]+}}) = r{{[0-9]+}}:{{[0-9]+}} | |
| ; CHECK: dealloc_return | |
| define void @callee_saved_regs(ptr %p) #0 { | |
| entry: | |
| %a = alloca [8192 x i8], align 1 | |
| call void @use(ptr %a) | |
| call void asm sideeffect "", "~{r16},~{r17},~{r18},~{r19}"() | |
| call void @use(ptr %p) | |
| ret void | |
| } | |
| ;; VLA (dynamic alloca): probe loop emitted for runtime-sized allocation. | |
| ; CHECK-LABEL: vla_basic: | |
| ; CHECK: allocframe | |
| ; CHECK: r[[TGT:[0-9]+]] = sub(r29,r{{[0-9]+}}) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r[[TGT]]) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r[[TGT]] | |
| define void @vla_basic(i32 %n) #0 { | |
| entry: | |
| %sz = zext i32 %n to i64 | |
| %buf = alloca i8, i64 %sz, align 1 | |
| call void @use(ptr %buf) | |
| ret void | |
| } | |
| ;; VLA without probe attribute: plain sub(r29,Rs), no probe loop. | |
| ; CHECK-LABEL: vla_no_probe: | |
| ; CHECK: sub(r29,r | |
| ; CHECK-NOT: cmp.gtu | |
| ; CHECK: dealloc_return | |
| define void @vla_no_probe(i32 %n) { | |
| entry: | |
| %sz = zext i32 %n to i64 | |
| %buf = alloca i8, i64 %sz, align 1 | |
| call void @use(ptr %buf) | |
| ret void | |
| } | |
| ;; Static large frame + VLA: two separate probe loops are emitted. | |
| ; CHECK-LABEL: vla_and_static: | |
| ; CHECK: allocframe(r29,#0):raw | |
| ; CHECK: r28 = add(r29,#-8200) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r28) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r28 | |
| ; CHECK: r[[TGT2:[0-9]+]] = sub(r29,r{{[0-9]+}}) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r[[TGT2]]) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r[[TGT2]] | |
| define void @vla_and_static(i32 %n) #0 { | |
| entry: | |
| %static = alloca [8192 x i8], align 1 | |
| %sz = zext i32 %n to i64 | |
| %dyn = alloca i8, i64 %sz, align 1 | |
| call void @use2(ptr %static, ptr %dyn) | |
| ret void | |
| } | |
| ;; VLA with alignment > 8: target pointer is aligned before probing. | |
| ; CHECK-LABEL: vla_align16: | |
| ; CHECK: allocframe | |
| ; CHECK: r[[TGT3:[0-9]+]] = sub(r29,r{{[0-9]+}}) | |
| ; CHECK: r[[TGT3]] = and(r[[TGT3]],#-16) | |
| ; CHECK: .LBB{{[0-9]+}}_{{[0-9]+}}: | |
| ; CHECK: { | |
| ; CHECK-NEXT: r29 = add(r29,#-4096) | |
| ; CHECK-NEXT: } | |
| ; CHECK-NEXT: { | |
| ; CHECK-NEXT: p0 = cmp.gtu(r29,r[[TGT3]]) | |
| ; CHECK-NEXT: if (p0.new) jump:t .LBB | |
| ; CHECK-NEXT: memw(r29+#0) = #0 | |
| ; CHECK-NEXT: } | |
| ; CHECK: r29 = r[[TGT3]] | |
| define void @vla_align16(i32 %n) #0 { | |
| entry: | |
| %sz = zext i32 %n to i64 | |
| %buf = alloca i8, i64 %sz, align 16 | |
| call void @use(ptr %buf) | |
| ret void | |
| } | |
| declare void @use(ptr) | |
| declare void @use2(ptr, ptr) | |
| attributes #0 = { "probe-stack"="inline-asm" } | |
| attributes #1 = { nounwind "frame-pointer"="none" "probe-stack"="inline-asm" } | |
| attributes #2 = { "probe-stack"="inline-asm" "stack-probe-size"="512" } |