================ @@ -0,0 +1,343 @@ +; 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 +} + +; Clang only uses byval for arguments of 65 bytes or larger, but we test with a +; 20 byte struct to keep the tests more readable. This size was chosen to still +; make sure that it will be split between registers and the stack, to test all +; of the interesting code paths in the backend. +%twenty_bytes = type { [5 x i32] } +declare void @large_callee(%twenty_bytes* byval(%twenty_bytes) align 4) + +; Functions with byval parameters can be tail-called, because the value is +; actually passed in registers and the stack in the same way for the caller and +; callee. Within @large_caller the first 16 bytes of the argument are spilled +; to the local stack frame, but for the tail-call they are passed in r0-r3, so +; it's safe to de-allocate that memory before the call. +define void @large_caller(%twenty_bytes* byval(%twenty_bytes) align 4 %a) { +; CHECK-LABEL: large_caller: +; CHECK: @ %bb.0: @ %entry +; CHECK-NEXT: .pad #16 +; CHECK-NEXT: sub sp, sp, #16 +; CHECK-NEXT: stm sp!, {r0, r1, r2, r3} ---------------- ostannard wrote:
Done https://github.com/llvm/llvm-project/pull/109943 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits