| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 |
| ; RUN: llc -mtriple=armv7a-none-eabi %s -o - | FileCheck %s |
| |
| declare i32 @many_args_callee(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5) |
| |
| define i32 @many_args_tail(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5) { |
| ; CHECK-LABEL: many_args_tail: |
| ; CHECK: @ %bb.0: |
| ; CHECK-NEXT: mov r0, #5 |
| ; CHECK-NEXT: mov r1, #2 |
| ; CHECK-NEXT: str r0, [sp] |
| ; CHECK-NEXT: mov r0, #6 |
| ; CHECK-NEXT: str r0, [sp, #4] |
| ; CHECK-NEXT: mov r0, #1 |
| ; CHECK-NEXT: mov r2, #3 |
| ; CHECK-NEXT: mov r3, #4 |
| ; CHECK-NEXT: b many_args_callee |
| %ret = tail call i32 @many_args_callee(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) |
| ret i32 %ret |
| } |
| |
| define i32 @many_args_musttail(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5) { |
| ; CHECK-LABEL: many_args_musttail: |
| ; CHECK: @ %bb.0: |
| ; CHECK-NEXT: mov r0, #5 |
| ; CHECK-NEXT: mov r1, #2 |
| ; CHECK-NEXT: str r0, [sp] |
| ; CHECK-NEXT: mov r0, #6 |
| ; CHECK-NEXT: str r0, [sp, #4] |
| ; CHECK-NEXT: mov r0, #1 |
| ; CHECK-NEXT: mov r2, #3 |
| ; CHECK-NEXT: mov r3, #4 |
| ; CHECK-NEXT: b many_args_callee |
| %ret = musttail call i32 @many_args_callee(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) |
| ret i32 %ret |
| } |
| |
| ; This function has more arguments than it's tail-callee. This isn't valid for |
| ; the musttail attribute, but can still be tail-called as a non-guaranteed |
| ; optimisation, because the outgoing arguments to @many_args_callee fit in the |
| ; stack space allocated by the caller of @more_args_tail. |
| define i32 @more_args_tail(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i32 %6) { |
| ; CHECK-LABEL: more_args_tail: |
| ; CHECK: @ %bb.0: |
| ; CHECK-NEXT: mov r0, #5 |
| ; CHECK-NEXT: mov r1, #2 |
| ; CHECK-NEXT: str r0, [sp] |
| ; CHECK-NEXT: mov r0, #6 |
| ; CHECK-NEXT: str r0, [sp, #4] |
| ; CHECK-NEXT: mov r0, #1 |
| ; CHECK-NEXT: mov r2, #3 |
| ; CHECK-NEXT: mov r3, #4 |
| ; CHECK-NEXT: b many_args_callee |
| %ret = tail call i32 @many_args_callee(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) |
| ret i32 %ret |
| } |
| |
| ; Again, this isn't valid for musttail, but can be tail-called in practice |
| ; because the stack size if the same. |
| define i32 @different_args_tail(i64 %0, i64 %1, i64 %2) { |
| ; CHECK-LABEL: different_args_tail: |
| ; CHECK: @ %bb.0: |
| ; CHECK-NEXT: mov r0, #5 |
| ; CHECK-NEXT: mov r1, #2 |
| ; CHECK-NEXT: str r0, [sp] |
| ; CHECK-NEXT: mov r0, #6 |
| ; CHECK-NEXT: str r0, [sp, #4] |
| ; CHECK-NEXT: mov r0, #1 |
| ; CHECK-NEXT: mov r2, #3 |
| ; CHECK-NEXT: mov r3, #4 |
| ; CHECK-NEXT: b many_args_callee |
| %ret = tail call i32 @many_args_callee(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) |
| ret i32 %ret |
| } |
| |
| ; Here, the caller requires less stack space for it's arguments than the |
| ; callee, so it would not ba valid to do a tail-call. |
| define i32 @fewer_args_tail(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4) { |
| ; CHECK-LABEL: fewer_args_tail: |
| ; CHECK: @ %bb.0: |
| ; CHECK-NEXT: .save {r11, lr} |
| ; CHECK-NEXT: push {r11, lr} |
| ; CHECK-NEXT: .pad #8 |
| ; CHECK-NEXT: sub sp, sp, #8 |
| ; CHECK-NEXT: mov r1, #6 |
| ; CHECK-NEXT: mov r0, #5 |
| ; CHECK-NEXT: strd r0, r1, [sp] |
| ; CHECK-NEXT: mov r0, #1 |
| ; CHECK-NEXT: mov r1, #2 |
| ; CHECK-NEXT: mov r2, #3 |
| ; CHECK-NEXT: mov r3, #4 |
| ; CHECK-NEXT: bl many_args_callee |
| ; CHECK-NEXT: add sp, sp, #8 |
| ; CHECK-NEXT: pop {r11, pc} |
| %ret = tail call i32 @many_args_callee(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6) |
| ret i32 %ret |
| } |
| |
| declare void @sret_callee(ptr sret({ double, double }) align 8) |
| |
| ; Functions which return by sret can be tail-called because the incoming sret |
| ; pointer gets passed through to the callee. |
| define void @sret_caller_tail(ptr sret({ double, double }) align 8 %result) { |
| ; CHECK-LABEL: sret_caller_tail: |
| ; CHECK: @ %bb.0: @ %entry |
| ; CHECK-NEXT: b sret_callee |
| entry: |
| tail call void @sret_callee(ptr sret({ double, double }) align 8 %result) |
| ret void |
| } |
| |
| define void @sret_caller_musttail(ptr sret({ double, double }) align 8 %result) { |
| ; CHECK-LABEL: sret_caller_musttail: |
| ; CHECK: @ %bb.0: @ %entry |
| ; CHECK-NEXT: b sret_callee |
| entry: |
| musttail call void @sret_callee(ptr sret({ double, double }) align 8 %result) |
| ret void |
| } |