| # RUN: llc -mtriple=aarch64-none-linux-gnu -run-pass=prologepilog %s -o - | FileCheck %s |
| # |
| # Test allocation and deallocation of SVE objects on the stack, |
| # as well as using a combination of scalable and non-scalable |
| # offsets to access the SVE on the stack. |
| # |
| # SVE objects are allocated below the (scalar) callee saves, |
| # and above spills/locals and the alignment gap, e.g. |
| # |
| # +-------------+ |
| # | stack arg | |
| # +-------------+ <- SP before call |
| # | Callee Saves| |
| # | Frame record| (if available) |
| # |-------------| <- FP (if available) |
| # | SVE area | |
| # +-------------+ |
| # |/////////////| alignment gap. |
| # | : | |
| # | Stack objs | |
| # | : | |
| # +-------------+ <- SP after call and frame-setup |
| # |
| --- | |
| |
| define void @test_allocate_sve() nounwind { entry: unreachable } |
| define void @test_allocate_sve_gpr_callee_saves() nounwind { entry: unreachable } |
| define void @test_allocate_sve_gpr_realigned() nounwind { entry: unreachable } |
| define void @test_address_sve() nounwind { entry: unreachable } |
| define void @test_address_sve_fp() nounwind { entry: unreachable } |
| define void @test_stack_arg_sve() nounwind { entry: unreachable } |
| define void @test_address_sve_out_of_range() nounwind { entry: unreachable } |
| |
| ... |
| # +----------+ |
| # |scratchreg| // x29 is used as scratch reg. |
| # +----------+ |
| # | %fixed- | // scalable SVE object of n * 18 bytes, aligned to 16 bytes, |
| # | stack.0 | // to be materialized with 2*ADDVL (<=> 2 * n * 16bytes) |
| # +----------+ |
| # | %stack.0 | // not scalable |
| # +----------+ <- SP |
| |
| # CHECK-LABEL: name: test_allocate_sve |
| # CHECK: stackSize: 32 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STRXpre killed $[[SCRATCH:[a-z0-9]+]], $sp, -16 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -2 |
| # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 |
| |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 2 |
| # CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 16, 0 |
| # CHECK-NEXT: $sp, $[[SCRATCH]] = frame-destroy LDRXpost $sp, 16 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_allocate_sve |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 18, alignment: 2, offset: -18 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 8 } |
| body: | |
| bb.0.entry: |
| RET_ReallyLR |
| --- |
| ... |
| # +----------+ |
| # | x20, x21 | // callee saves |
| # |scratchreg| // x29 is used as scratch reg. |
| # +----------+ |
| # | %fixed- | // scalable objects |
| # | stack.0 | |
| # +----------+ |
| # | %stack.0 | // not scalable |
| # +----------+ <- SP |
| |
| # CHECK-LABEL: name: test_allocate_sve_gpr_callee_saves |
| # CHECK: stackSize: 48 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STRXpre killed $[[SCRATCH:[a-z0-9]+]], $sp, -32 |
| # CHECK-NEXT: frame-setup STPXi killed $x21, killed $x20, $sp, 2 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -2 |
| # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 |
| # CHECK-NEXT: $x20 = IMPLICIT_DEF |
| # CHECK-NEXT: $x21 = IMPLICIT_DEF |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 2 |
| # CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 16, 0 |
| # CHECK-NEXT: $x21, $x20 = frame-destroy LDPXi $sp, 2 |
| # CHECK-NEXT: $sp, $[[SCRATCH]] = frame-destroy LDRXpost $sp, 32 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_allocate_sve_gpr_callee_saves |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 18, alignment: 2, offset: -18 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 8 } |
| body: | |
| bb.0.entry: |
| $x20 = IMPLICIT_DEF |
| $x21 = IMPLICIT_DEF |
| RET_ReallyLR |
| --- |
| ... |
| # +----------+ |
| # | lr, fp | // frame record |
| # +----------+ <- FP |
| # | %fixed- | // scalable objects |
| # | stack.0 | |
| # +----------+ |
| # |//////////| // alignment gap |
| # | %stack.0 | // not scalable |
| # +----------+ <- SP |
| # CHECK-LABEL: name: test_allocate_sve_gpr_realigned |
| # CHECK: stackSize: 32 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 |
| # CHECK-NEXT: $fp = frame-setup ADDXri $sp, 0, 0 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -2 |
| # CHECK-NEXT: $[[TMP:x[0-9]+]] = frame-setup SUBXri $sp, 16, 0 |
| # CHECK-NEXT: $sp = ANDXri killed $[[TMP]] |
| # CHECK-NEXT: $sp = frame-destroy ADDXri $fp, 0, 0 |
| # CHECK-NEXT: $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_allocate_sve_gpr_realigned |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 18, alignment: 2, offset: -18 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 32 } |
| body: | |
| bb.0.entry: |
| RET_ReallyLR |
| --- |
| ... |
| # +----------+ |
| # | x20, x21 | // callee saves |
| # +----------+ |
| # | %stack.0 | // scalable @ SP + 16b + 32 scalable bytes |
| # | %stack.1 | // scalable @ SP + 16b + 16 scalable bytes |
| # | %stack.2 | // scalable @ SP + 16b + 14 scalable bytes |
| # +----------+ |
| # | %stack.0 | // not scalable |
| # +----------+ <- SP |
| |
| # CHECK-LABEL: name: test_address_sve |
| # CHECK: stackSize: 32 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STRXpre killed $[[SCRATCH:[a-z0-9]+]], $sp, -16 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -3 |
| # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 |
| |
| # CHECK-NEXT: $[[TMP:x[0-9]+]] = ADDXri $sp, 16 |
| # CHECK-NEXT: STR_ZXI $z0, killed $[[TMP]], 2 |
| # CHECK-NEXT: $[[TMP:x[0-9]+]] = ADDXri $sp, 16 |
| # CHECK-NEXT: STR_ZXI $z1, killed $[[TMP]], 1 |
| # CHECK-NEXT: $[[TMP:x[0-9]+]] = ADDXri $sp, 16 |
| # CHECK-NEXT: STR_PXI $p0, killed $[[TMP]], 7 |
| |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 3 |
| # CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 16, 0 |
| # CHECK-NEXT: $sp, $[[SCRATCH]] = frame-destroy LDRXpost $sp, 16 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_address_sve |
| frameInfo: |
| maxAlignment: 16 |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 16, alignment: 8, offset: -16 } |
| - { id: 1, stack-id: sve-vec, size: 16, alignment: 8, offset: -32 } |
| - { id: 2, stack-id: sve-vec, size: 2, alignment: 2, offset: -34 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 8 } |
| body: | |
| bb.0.entry: |
| liveins: $z0, $z1, $p0 |
| |
| STR_ZXI $z0, %fixed-stack.0, 0 |
| STR_ZXI $z1, %fixed-stack.1, 0 |
| STR_PXI $p0, %fixed-stack.2, 0 |
| |
| RET_ReallyLR |
| --- |
| ... |
| # +-----------+ |
| # | x20, x21 | // callee saves |
| # | lr, fp | // frame record |
| # +-----------+ <- FP |
| # | %fstack.0 | // scalable @ FP - 16 scalable bytes |
| # | %fstack.1 | // scalable @ FP - 32 scalable bytes |
| # | %fstack.2 | // scalable @ FP - 34 scalable bytes |
| # +-----------+ |
| # | %stack.0 | // not scalable |
| # +-----------+ <- SP |
| |
| # CHECK-LABEL: name: test_address_sve_fp |
| # CHECK: stackSize: 32 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 |
| # CHECK-NEXT: $fp = frame-setup ADDXri $sp, 0, 0 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -3 |
| # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 |
| |
| # CHECK-NEXT: STR_ZXI $z0, $fp, -1 |
| # CHECK-NEXT: STR_ZXI $z1, $fp, -2 |
| # CHECK-NEXT: STR_PXI $p0, $fp, -17 |
| |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 3 |
| # CHECK: $sp = frame-destroy ADDXri $sp, 16, 0 |
| # CHECK-NEXT: $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_address_sve_fp |
| frameInfo: |
| maxAlignment: 16 |
| isFrameAddressTaken: true |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 16, alignment: 8, offset: -16 } |
| - { id: 1, stack-id: sve-vec, size: 16, alignment: 8, offset: -32 } |
| - { id: 2, stack-id: sve-vec, size: 2, alignment: 2, offset: -34 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 8 } |
| body: | |
| bb.0.entry: |
| liveins: $z0, $z1, $p0 |
| |
| STR_ZXI $z0, %fixed-stack.0, 0 |
| STR_ZXI $z1, %fixed-stack.1, 0 |
| STR_PXI $p0, %fixed-stack.2, 0 |
| |
| RET_ReallyLR |
| --- |
| ... |
| # +-----------+ |
| # | %fstack.1 | // stack arg @ SP + 16 scalable bytes + 32 bytes. |
| # +-----------+ |
| # |callee save| // register saved as scratch reg. |
| # +-----------+ |
| # | %fstack.1 | // vector of 16 scalable bytes |
| # +---------- + |
| # | %stack.0 | // not scalable, 16 bytes |
| # +-----------+ <- SP |
| # CHECK-LABEL: name: test_stack_arg_sve |
| # CHECK: stackSize: 32 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STRXpre killed $[[SCRATCH:[a-z0-9]+]], $sp, -16 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -1 |
| # CHECK-NEXT: $sp = frame-setup SUBXri $sp, 16, 0 |
| |
| # CHECK: $[[TMP:x[0-9]+]] = ADDVL_XXI $sp, 1 |
| # CHECK-NEXT: $x0 = LDRXui killed $[[TMP]], 4 |
| |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 1 |
| # CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 16, 0 |
| # CHECK-NEXT: $sp, $[[SCRATCH]] = frame-destroy LDRXpost $sp, 16 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_stack_arg_sve |
| fixedStack: |
| - { id: 0, stack-id: default, size: 16, alignment: 16, offset: 0 } |
| - { id: 1, stack-id: sve-vec, size: 16, alignment: 16, offset: -16 } |
| stack: |
| - { id: 0, stack-id: default, size: 16, alignment: 16 } |
| body: | |
| bb.0.entry: |
| liveins: $x0 |
| |
| $x0 = LDRXui %fixed-stack.0, 0 |
| RET_ReallyLR |
| --- |
| ... |
| # Test that the address to access an SVE data vector at an offset that |
| # does not fit its immediate, is correctly materialized. |
| # +----------+ |
| # |calleesave| // register saved as scratch reg. |
| # +----------+ |
| # | %stack.0 | // one SVE data object @ SP + 256 scalable bytes. |
| # |::::::::::| |
| # |: :| |
| # |:%stack.1:| // Large object |
| # |: :| |
| # |::::::::::| |
| # +----------+ <- SP |
| # CHECK-LABEL: name: test_address_sve_out_of_range |
| # CHECK: stackSize: 16 |
| |
| # CHECK: bb.0.entry: |
| # CHECK-NEXT: $sp = frame-setup STRXpre killed $[[SCRATCH:[a-z0-9]+]], $sp, -16 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -32 |
| # CHECK-NEXT: $sp = frame-setup ADDVL_XXI $sp, -1 |
| |
| # CHECK-NEXT: $[[TMP2:x[0-9]+]] = ADDVL_XXI $sp, 1 |
| # CHECK-NEXT: STR_ZXI $z0, killed $[[TMP2]], 255 |
| |
| # CHECK-NEXT: $[[TMP2:x[0-9]+]] = ADDPL_XXI $sp, 1 |
| # CHECK-NEXT: STR_PXI $p0, killed $[[TMP2]], 255 |
| |
| # CHECK: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 31 |
| # CHECK-NEXT: $sp = frame-destroy ADDVL_XXI $sp, 9 |
| # CHECK-NEXT: $sp, $[[SCRATCH]] = frame-destroy LDRXpost $sp, 16 |
| # CHECK-NEXT: RET_ReallyLR |
| name: test_address_sve_out_of_range |
| frameInfo: |
| maxAlignment: 16 |
| fixedStack: |
| - { id: 0, stack-id: sve-vec, size: 16, alignment: 16, offset: -16 } |
| - { id: 1, stack-id: sve-vec, size: 3584, alignment: 16, offset: -3600 } |
| - { id: 2, stack-id: sve-vec, size: 512, alignment: 16, offset: -4112 } |
| |
| body: | |
| bb.0.entry: |
| liveins: $z0, $p0 |
| |
| STR_ZXI $z0, %fixed-stack.0, 0 |
| STR_PXI $p0, %fixed-stack.1, 0 |
| |
| RET_ReallyLR |
| --- |