Enable tail calls by default for (micro)MIPS(64). microMIPS is slightly more tricky than doing it for MIPS(R6) or microMIPSR6. microMIPS has two instruction encodings: 16bit and 32bit along with some restrictions on the size of the instruction that can fill the delay slot. For safe tail calls for microMIPS, the delay slot filler attempts to find a correct size instruction for the delay slot of TAILCALL pseudos. Reviewers: dsanders, vkalintris Subscribers: jfb, dsanders, sdardis, llvm-commits Differential Revision: https://reviews.llvm.org/D21138 llvm-svn: 277708
231 lines
7.7 KiB
LLVM
231 lines
7.7 KiB
LLVM
; Test the 'call' instruction and the tailcall variant.
|
|
|
|
; RUN: llc -march=mips -mcpu=mips32 -relocation-model=pic < %s | FileCheck %s -check-prefixes=ALL,O32,NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r2 -relocation-model=pic < %s | FileCheck %s -check-prefixes=ALL,O32,NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r3 -relocation-model=pic < %s | FileCheck %s -check-prefixes=ALL,O32,NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r5 -relocation-model=pic < %s | FileCheck %s -check-prefixes=ALL,O32,NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r6 -relocation-model=pic -disable-mips-delay-filler < %s | FileCheck %s -check-prefixes=ALL,O32,R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r6 -relocation-model=pic -mattr=+fp64,+nooddspreg -disable-mips-delay-filler < %s | FileCheck %s -check-prefixes=ALL,O32,R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips4 < %s | FileCheck %s -check-prefixes=ALL,N64,NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64 < %s | FileCheck %s -check-prefixes=ALL,N64,NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r2 < %s | FileCheck %s -check-prefixes=ALL,N64,NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r3 < %s | FileCheck %s -check-prefixes=ALL,N64,NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r5 < %s | FileCheck %s -check-prefixes=ALL,N64,NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r6 -disable-mips-delay-filler < %s | FileCheck %s -check-prefixes=ALL,N64,R6C
|
|
; RUN: llc -march=mips -mcpu=mips32 -relocation-model=pic < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r2 -relocation-model=pic < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r3 -relocation-model=pic < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r5 -relocation-model=pic < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r6 -relocation-model=pic -disable-mips-delay-filler < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=R6C
|
|
; RUN: llc -march=mips -mcpu=mips32r6 -relocation-model=pic -mattr=+fp64,+nooddspreg -disable-mips-delay-filler < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 -check-prefix=R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips4 < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64 < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r2 < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r3 < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r5 < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=NOT-R6C
|
|
; RUN: llc -march=mips64 -mcpu=mips64r6 -disable-mips-delay-filler < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 -check-prefix=R6C
|
|
|
|
declare void @extern_void_void()
|
|
declare i32 @extern_i32_void()
|
|
declare float @extern_float_void()
|
|
|
|
define i32 @call_void_void() {
|
|
; ALL-LABEL: call_void_void:
|
|
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_void_void)($gp)
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_void_void)($gp)
|
|
|
|
; NOT-R6C: jalr $[[TGT]]
|
|
; R6C: jalrc $[[TGT]]
|
|
|
|
call void @extern_void_void()
|
|
; R6C: jrc $ra
|
|
ret i32 0
|
|
}
|
|
|
|
define i32 @call_i32_void() {
|
|
; ALL-LABEL: call_i32_void:
|
|
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp)
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp)
|
|
|
|
; NOT-R6C: jalr $[[TGT]]
|
|
; R6C: jalrc $[[TGT]]
|
|
|
|
%1 = call i32 @extern_i32_void()
|
|
%2 = add i32 %1, 1
|
|
; R6C: jrc $ra
|
|
ret i32 %2
|
|
}
|
|
|
|
define float @call_float_void() {
|
|
; ALL-LABEL: call_float_void:
|
|
|
|
; FIXME: Not sure why we don't use $gp directly on such a simple test. We should
|
|
; look into it at some point.
|
|
; O32: addu $[[GP:[0-9]+]], ${{[0-9]+}}, $25
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_float_void)($[[GP]])
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_float_void)($gp)
|
|
|
|
; NOT-R6C: jalr $[[TGT]]
|
|
; R6C: jalrc $[[TGT]]
|
|
|
|
|
|
%1 = call float @extern_float_void()
|
|
%2 = fadd float %1, 1.0
|
|
; R6C: jrc $ra
|
|
ret float %2
|
|
}
|
|
|
|
define void @musttail_call_void_void() {
|
|
; ALL-LABEL: musttail_call_void_void:
|
|
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_void_void)($gp)
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_void_void)($gp)
|
|
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
musttail call void @extern_void_void()
|
|
ret void
|
|
}
|
|
|
|
define i32 @musttail_call_i32_void() {
|
|
; ALL-LABEL: musttail_call_i32_void:
|
|
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp)
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp)
|
|
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
%1 = musttail call i32 @extern_i32_void()
|
|
ret i32 %1
|
|
}
|
|
|
|
define float @musttail_call_float_void() {
|
|
; ALL-LABEL: musttail_call_float_void:
|
|
|
|
; O32: lw $[[TGT:[0-9]+]], %call16(extern_float_void)($gp)
|
|
|
|
; N64: ld $[[TGT:[0-9]+]], %call16(extern_float_void)($gp)
|
|
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
%1 = musttail call float @extern_float_void()
|
|
ret float %1
|
|
}
|
|
|
|
define i32 @indirect_call_void_void(void ()* %addr) {
|
|
; ALL-LABEL: indirect_call_void_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jalr $25
|
|
; R6C: jalrc $25
|
|
|
|
call void %addr()
|
|
; R6C: jrc $ra
|
|
ret i32 0
|
|
}
|
|
|
|
define i32 @indirect_call_i32_void(i32 ()* %addr) {
|
|
; ALL-LABEL: indirect_call_i32_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jalr $25
|
|
; R6C: jalrc $25
|
|
|
|
|
|
%1 = call i32 %addr()
|
|
%2 = add i32 %1, 1
|
|
; R6C: jrc $ra
|
|
ret i32 %2
|
|
}
|
|
|
|
define float @indirect_call_float_void(float ()* %addr) {
|
|
; ALL-LABEL: indirect_call_float_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jalr $25
|
|
; R6C: jalrc $25
|
|
|
|
|
|
%1 = call float %addr()
|
|
%2 = fadd float %1, 1.0
|
|
; R6C: jrc $ra
|
|
ret float %2
|
|
}
|
|
|
|
; We can't use 'musttail' here because the verifier is too conservative and
|
|
; prohibits any prototype difference.
|
|
define void @tail_indirect_call_void_void(void ()* %addr) {
|
|
; ALL-LABEL: tail_indirect_call_void_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
tail call void %addr()
|
|
ret void
|
|
}
|
|
|
|
define i32 @tail_indirect_call_i32_void(i32 ()* %addr) {
|
|
; ALL-LABEL: tail_indirect_call_i32_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
%1 = tail call i32 %addr()
|
|
ret i32 %1
|
|
}
|
|
|
|
define float @tail_indirect_call_float_void(float ()* %addr) {
|
|
; ALL-LABEL: tail_indirect_call_float_void:
|
|
|
|
; ALL: move $25, $4
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
%1 = tail call float %addr()
|
|
ret float %1
|
|
}
|
|
|
|
; Check that passing undef as a double value doesn't cause machine code errors
|
|
; for FP64.
|
|
declare hidden void @undef_double(i32 %this, double %volume) unnamed_addr align 2
|
|
|
|
define hidden void @thunk_undef_double(i32 %this, double %volume) unnamed_addr align 2 {
|
|
; ALL-LABEL: thunk_undef_double:
|
|
; O32: # implicit-def: %A2
|
|
; O32: # implicit-def: %A3
|
|
; NOT-R6C: jr $[[TGT]]
|
|
; R6C: jrc $[[TGT]]
|
|
|
|
tail call void @undef_double(i32 undef, double undef) #8
|
|
ret void
|
|
}
|
|
|
|
; Check that immediate addresses do not use jal.
|
|
define i32 @jal_only_allows_symbols() {
|
|
; ALL-LABEL: jal_only_allows_symbols:
|
|
|
|
; ALL-NOT: {{jal }}
|
|
; ALL: addiu $[[TGT:[0-9]+]], $zero, 1234
|
|
; ALL-NOT: {{jal }}
|
|
; NOT-R6C: jalr $[[TGT]]
|
|
; R6C: jalrc $[[TGT]]
|
|
; ALL-NOT: {{jal }}
|
|
|
|
call void () inttoptr (i32 1234 to void ()*)()
|
|
; R6C: jrc $ra
|
|
ret i32 0
|
|
}
|
|
|