aboutsummaryrefslogtreecommitdiff
path: root/test/CodeGen/RISCV/tail-calls.ll
blob: 2279e8c377920571b01149cffe4000a7ca493f91 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
; RUN: llc -mtriple riscv32-unknown-linux-gnu -o - %s | FileCheck %s
; RUN: llc -mtriple riscv32-unknown-elf       -o - %s | FileCheck %s

; Perform tail call optimization for global address.
declare i32 @callee_tail(i32 %i)
define i32 @caller_tail(i32 %i) {
; CHECK-LABEL: caller_tail
; CHECK: tail callee_tail
entry:
  %r = tail call i32 @callee_tail(i32 %i)
  ret i32 %r
}

; Perform tail call optimization for external symbol.
@dest = global [2 x i8] zeroinitializer
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
define void @caller_extern(i8* %src) optsize {
entry:
; CHECK: caller_extern
; CHECK-NOT: call memcpy
; CHECK: tail memcpy
  tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @dest, i32 0, i32 0), i8* %src, i32 7, i1 false)
  ret void
}

; Perform indirect tail call optimization (for function pointer call).
declare void @callee_indirect1()
declare void @callee_indirect2()
define void @caller_indirect_tail(i32 %a) {
; CHECK-LABEL: caller_indirect_tail
; CHECK-NOT: call callee_indirect1
; CHECK-NOT: call callee_indirect2
; CHECK-NOT: tail callee_indirect1
; CHECK-NOT: tail callee_indirect2

; CHECK: lui a0, %hi(callee_indirect2)
; CHECK-NEXT: addi a5, a0, %lo(callee_indirect2)
; CHECK-NEXT: jr a5

; CHECK: lui a0, %hi(callee_indirect1)
; CHECK-NEXT: addi a5, a0, %lo(callee_indirect1)
; CHECK-NEXT: jr a5
entry:
  %tobool = icmp eq i32 %a, 0
  %callee = select i1 %tobool, void ()* @callee_indirect1, void ()* @callee_indirect2
  tail call void %callee()
  ret void
}

; Do not tail call optimize functions with varargs.
declare i32 @callee_varargs(i32, ...)
define void @caller_varargs(i32 %a, i32 %b) {
; CHECK-LABEL: caller_varargs
; CHECK-NOT: tail callee_varargs
; CHECK: call callee_varargs
entry:
  %call = tail call i32 (i32, ...) @callee_varargs(i32 %a, i32 %b, i32 %b, i32 %a)
  ret void
}

; Do not tail call optimize if stack is used to pass parameters.
declare i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n)
define i32 @caller_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n) {
; CHECK-LABEL: caller_args
; CHECK-NOT: tail callee_args
; CHECK: call callee_args
entry:
  %r = tail call i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n)
  ret i32 %r
}

; Do not tail call optimize if parameters need to be passed indirectly.
declare i32 @callee_indirect_args(fp128 %a)
define void @caller_indirect_args() {
; CHECK-LABEL: caller_indirect_args
; CHECK-NOT: tail callee_indirect_args
; CHECK: call callee_indirect_args
entry:
  %call = tail call i32 @callee_indirect_args(fp128 0xL00000000000000003FFF000000000000)
  ret void
}

; Externally-defined functions with weak linkage should not be tail-called.
; The behaviour of branch instructions in this situation (as used for tail
; calls) is implementation-defined, so we cannot rely on the linker replacing
; the tail call with a return.
declare extern_weak void @callee_weak()
define void @caller_weak() {
; CHECK-LABEL: caller_weak
; CHECK-NOT: tail callee_weak
; CHECK: call callee_weak
entry:
  tail call void @callee_weak()
  ret void
}

; Exception-handling functions need a special set of instructions to indicate a
; return to the hardware. Tail-calling another function would probably break
; this.
declare void @callee_irq()
define void @caller_irq() #0 {
; CHECK-LABEL: caller_irq
; CHECK-NOT: tail callee_irq
; CHECK: call callee_irq
entry:
  tail call void @callee_irq()
  ret void
}
attributes #0 = { "interrupt" }

; Byval parameters hand the function a pointer directly into the stack area
; we want to reuse during a tail call. Do not tail call optimize functions with
; byval parameters.
declare i32 @callee_byval(i32** byval %a)
define i32 @caller_byval() {
; CHECK-LABEL: caller_byval
; CHECK-NOT: tail callee_byval
; CHECK: call callee_byval
entry:
  %a = alloca i32*
  %r = tail call i32 @callee_byval(i32** byval %a)
  ret i32 %r
}

; Do not tail call optimize if callee uses structret semantics.
%struct.A = type { i32 }
@a = global %struct.A zeroinitializer

declare void @callee_struct(%struct.A* sret %a)
define void @caller_nostruct() {
; CHECK-LABEL: caller_nostruct
; CHECK-NOT: tail callee_struct
; CHECK: call callee_struct
entry:
  tail call void @callee_struct(%struct.A* sret @a)
  ret void
}

; Do not tail call optimize if caller uses structret semantics.
declare void @callee_nostruct()
define void @caller_struct(%struct.A* sret %a) {
; CHECK-LABEL: caller_struct
; CHECK-NOT: tail callee_nostruct
; CHECK: call callee_nostruct
entry:
  tail call void @callee_nostruct()
  ret void
}