Skip to content

Commit

Permalink
Fix function pointer tail calls in armv8-M.base
Browse files Browse the repository at this point in the history
Summary:
The compiler fails with the following error message:

fatal error: error in backend: ran out of registers during
register allocation

Tail call optimization for Armv8-M.base fails to meet all the required
constraints when handling calls to function pointers where the
arguments take up r0-r3. This is because the pointer to the
function to be called can only be stored in r0-r3, but these are
all occupied by arguments. This patch makes sure that tail call
optimization does not try to handle this type of calls.

Reviewers: chill, MatzeB, olista01, rengolin, efriedma

Reviewed By: olista01, efriedma

Subscribers: efriedma, aemerson, javed.absar, llvm-commits, kristof.beyls

Differential Revision: https://reviews.llvm.org/D40706

llvm-svn: 319664
  • Loading branch information
pbarrio committed Dec 4, 2017
1 parent f2fdc18 commit 2b43858
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
7 changes: 7 additions & 0 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Expand Up @@ -2282,6 +2282,13 @@ ARMTargetLowering::IsEligibleForTailCallOptimization(SDValue Callee,

assert(Subtarget->supportsTailCall());

// Tail calls to function pointers cannot be optimized for Thumb1 if the args
// to the call take up r0-r3. The reason is that there are no legal registers
// left to hold the pointer to the function to be called.
if (Subtarget->isThumb1Only() && Outs.size() >= 4 &&
!isa<GlobalAddressSDNode>(Callee.getNode()))
return false;

// Look for obvious safe cases to perform tail call optimization that do not
// require ABI changes. This is what gcc calls sibcall.

Expand Down
58 changes: 58 additions & 0 deletions llvm/test/CodeGen/ARM/v8m-tail-call.ll
Expand Up @@ -45,3 +45,61 @@ define hidden i32 @f2(i32, i32, i32, i32, i32) {
; CHECK-NEXT: add sp, #4
; CHECK-NEXT: b h2
}

; Make sure that tail calls to function pointers that require r0-r3 for argument
; passing do not break the compiler.
@fnptr = global i32 (i32, i32, i32, i32)* null
define i32 @test3() {
; CHECK-LABEL: test3:
; CHECK: blx {{r[0-9]+}}
%1 = load i32 (i32, i32, i32, i32)*, i32 (i32, i32, i32, i32)** @fnptr
%2 = tail call i32 %1(i32 1, i32 2, i32 3, i32 4)
ret i32 %2
}

@fnptr2 = global i32 (i32, i32, i64)* null
define i32 @test4() {
; CHECK-LABEL: test4:
; CHECK: blx {{r[0-9]+}}
%1 = load i32 (i32, i32, i64)*, i32 (i32, i32, i64)** @fnptr2
%2 = tail call i32 %1(i32 1, i32 2, i64 3)
ret i32 %2
}

; Check that tail calls to function pointers where not all of r0-r3 are used for
; parameter passing are tail-call optimized.
; test5: params in r0, r1. r2 & r3 are free.
@fnptr3 = global i32 (i32, i32)* null
define i32 @test5() {
; CHECK-LABEL: test5:
; CHECK: ldr [[REG:r[0-9]+]]
; CHECK: bx [[REG]]
; CHECK-NOT: blx [[REG]]
%1 = load i32 (i32, i32)*, i32 (i32, i32)** @fnptr3
%2 = tail call i32 %1(i32 1, i32 2)
ret i32 %2
}

; test6: params in r0 and r2-r3. r1 is free.
@fnptr4 = global i32 (i32, i64)* null
define i32 @test6() {
; CHECK-LABEL: test6:
; CHECK: ldr [[REG:r[0-9]+]]
; CHECK: bx [[REG]]
; CHECK-NOT: blx [[REG]]
%1 = load i32 (i32, i64)*, i32 (i32, i64)** @fnptr4
%2 = tail call i32 %1(i32 1, i64 2)
ret i32 %2
}

; Check that tail calls to functions other than function pointers are
; tail-call optimized.
define i32 @test7() {
; CHECK-LABEL: test7:
; CHECK: b bar
; CHECK-NOT: bl bar
%tail = tail call i32 @bar(i32 1, i32 2, i32 3, i32 4)
ret i32 %tail
}

declare i32 @bar(i32, i32, i32, i32)

0 comments on commit 2b43858

Please sign in to comment.