aboutsummaryrefslogtreecommitdiff
path: root/test/CodeGen/RISCV/tail-calls.ll
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
commiteb11fae6d08f479c0799db45860a98af528fa6e7 (patch)
tree44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /test/CodeGen/RISCV/tail-calls.ll
parentb8a2042aa938069e862750553db0e4d82d25822c (diff)
downloadsrc-eb11fae6d08f479c0799db45860a98af528fa6e7.tar.gz
src-eb11fae6d08f479c0799db45860a98af528fa6e7.zip
Vendor import of llvm trunk r338150:vendor/llvm/llvm-trunk-r338150
Notes
Notes: svn path=/vendor/llvm/dist/; revision=336809 svn path=/vendor/llvm/llvm-trunk-r338150/; revision=336814; tag=vendor/llvm/llvm-trunk-r338150
Diffstat (limited to 'test/CodeGen/RISCV/tail-calls.ll')
-rw-r--r--test/CodeGen/RISCV/tail-calls.ll148
1 files changed, 148 insertions, 0 deletions
diff --git a/test/CodeGen/RISCV/tail-calls.ll b/test/CodeGen/RISCV/tail-calls.ll
new file mode 100644
index 000000000000..2279e8c37792
--- /dev/null
+++ b/test/CodeGen/RISCV/tail-calls.ll
@@ -0,0 +1,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
+}