1 ; Test the 'call' instruction and the tailcall variant. 2 3 ; FIXME: We should remove the need for -enable-mips-tail-calls 4 ; RUN: llc -march=mips -mcpu=mips32 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 5 ; RUN: llc -march=mips -mcpu=mips32r2 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 6 ; RUN: llc -march=mips -mcpu=mips32r3 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 7 ; RUN: llc -march=mips -mcpu=mips32r5 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 8 ; RUN: llc -march=mips -mcpu=mips32r6 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 9 ; RUN: llc -march=mips -mcpu=mips32r6 -mattr=+fp64,+nooddspreg -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=O32 10 ; RUN: llc -march=mips64 -mcpu=mips4 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 11 ; RUN: llc -march=mips64 -mcpu=mips64 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 12 ; RUN: llc -march=mips64 -mcpu=mips64r2 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 13 ; RUN: llc -march=mips64 -mcpu=mips64r3 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 14 ; RUN: llc -march=mips64 -mcpu=mips64r5 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 15 ; RUN: llc -march=mips64 -mcpu=mips64r6 -enable-mips-tail-calls < %s | FileCheck %s -check-prefix=ALL -check-prefix=N64 16 17 declare void @extern_void_void() 18 declare i32 @extern_i32_void() 19 declare float @extern_float_void() 20 21 define i32 @call_void_void() { 22 ; ALL-LABEL: call_void_void: 23 24 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_void_void)($gp) 25 26 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_void_void)($gp) 27 28 ; ALL: jalr $[[TGT]] 29 30 call void @extern_void_void() 31 ret i32 0 32 } 33 34 define i32 @call_i32_void() { 35 ; ALL-LABEL: call_i32_void: 36 37 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp) 38 39 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp) 40 41 ; ALL: jalr $[[TGT]] 42 43 %1 = call i32 @extern_i32_void() 44 %2 = add i32 %1, 1 45 ret i32 %2 46 } 47 48 define float @call_float_void() { 49 ; ALL-LABEL: call_float_void: 50 51 ; FIXME: Not sure why we don't use $gp directly on such a simple test. We should 52 ; look into it at some point. 53 ; O32: addu $[[GP:[0-9]+]], ${{[0-9]+}}, $25 54 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_float_void)($[[GP]]) 55 56 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_float_void)($gp) 57 58 ; ALL: jalr $[[TGT]] 59 60 ; O32: move $gp, $[[GP]] 61 62 %1 = call float @extern_float_void() 63 %2 = fadd float %1, 1.0 64 ret float %2 65 } 66 67 define void @musttail_call_void_void() { 68 ; ALL-LABEL: musttail_call_void_void: 69 70 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_void_void)($gp) 71 72 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_void_void)($gp) 73 74 ; NOT-R6: jr $[[TGT]] 75 ; R6: r6.jr $[[TGT]] 76 77 musttail call void @extern_void_void() 78 ret void 79 } 80 81 define i32 @musttail_call_i32_void() { 82 ; ALL-LABEL: musttail_call_i32_void: 83 84 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp) 85 86 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_i32_void)($gp) 87 88 ; NOT-R6: jr $[[TGT]] 89 ; R6: r6.jr $[[TGT]] 90 91 %1 = musttail call i32 @extern_i32_void() 92 ret i32 %1 93 } 94 95 define float @musttail_call_float_void() { 96 ; ALL-LABEL: musttail_call_float_void: 97 98 ; O32: lw $[[TGT:[0-9]+]], %call16(extern_float_void)($gp) 99 100 ; N64: ld $[[TGT:[0-9]+]], %call16(extern_float_void)($gp) 101 102 ; NOT-R6: jr $[[TGT]] 103 ; R6: r6.jr $[[TGT]] 104 105 %1 = musttail call float @extern_float_void() 106 ret float %1 107 } 108 109 define i32 @indirect_call_void_void(void ()* %addr) { 110 ; ALL-LABEL: indirect_call_void_void: 111 112 ; ALL: move $25, $4 113 ; ALL: jalr $25 114 115 call void %addr() 116 ret i32 0 117 } 118 119 define i32 @indirect_call_i32_void(i32 ()* %addr) { 120 ; ALL-LABEL: indirect_call_i32_void: 121 122 ; ALL: move $25, $4 123 ; ALL: jalr $25 124 125 %1 = call i32 %addr() 126 %2 = add i32 %1, 1 127 ret i32 %2 128 } 129 130 define float @indirect_call_float_void(float ()* %addr) { 131 ; ALL-LABEL: indirect_call_float_void: 132 133 ; ALL: move $25, $4 134 ; ALL: jalr $25 135 136 %1 = call float %addr() 137 %2 = fadd float %1, 1.0 138 ret float %2 139 } 140 141 ; We can't use 'musttail' here because the verifier is too conservative and 142 ; prohibits any prototype difference. 143 define void @tail_indirect_call_void_void(void ()* %addr) { 144 ; ALL-LABEL: tail_indirect_call_void_void: 145 146 ; ALL: move $25, $4 147 ; ALL: jr $25 148 149 tail call void %addr() 150 ret void 151 } 152 153 define i32 @tail_indirect_call_i32_void(i32 ()* %addr) { 154 ; ALL-LABEL: tail_indirect_call_i32_void: 155 156 ; ALL: move $25, $4 157 ; ALL: jr $25 158 159 %1 = tail call i32 %addr() 160 ret i32 %1 161 } 162 163 define float @tail_indirect_call_float_void(float ()* %addr) { 164 ; ALL-LABEL: tail_indirect_call_float_void: 165 166 ; ALL: move $25, $4 167 ; ALL: jr $25 168 169 %1 = tail call float %addr() 170 ret float %1 171 } 172 173 ; Check that passing undef as a double value doesn't cause machine code errors 174 ; for FP64. 175 declare hidden void @undef_double(i32 %this, double %volume) unnamed_addr align 2 176 177 define hidden void @thunk_undef_double(i32 %this, double %volume) unnamed_addr align 2 { 178 ; ALL-LABEL: thunk_undef_double: 179 ; O32: # implicit-def: %A2 180 ; O32: # implicit-def: %A3 181 ; ALL: jr $25 182 tail call void @undef_double(i32 undef, double undef) #8 183 ret void 184 } 185