Fix llc to not reuse spill slots in functions that invoke setjmp()

We need to ensure that StackSlotColoring.cpp does not reuse stack
spill slots in functions that call "returns_twice" functions such as
setjmp(), otherwise this can lead to miscompiled code, because a stack
slot would be clobbered when it's still live.

This was already handled correctly for functions that call setjmp()
(though this wasn't covered by a test), but not for functions that
invoke setjmp().

We fix this by changing callsFunctionThatReturnsTwice() to check for
invoke instructions.

This fixes PR18244.

llvm-svn: 199180
This commit is contained in:
Mark Seaborn 2014-01-14 04:20:01 +00:00
parent a29175d7ca
commit 76b70ff14a
2 changed files with 143 additions and 4 deletions

View File

@ -736,10 +736,8 @@ bool Function::isDefTriviallyDead() const {
bool Function::callsFunctionThatReturnsTwice() const {
for (const_inst_iterator
I = inst_begin(this), E = inst_end(this); I != E; ++I) {
const CallInst* callInst = dyn_cast<CallInst>(&*I);
if (!callInst)
continue;
if (callInst->canReturnTwice())
ImmutableCallSite CS(&*I);
if (CS && CS.hasFnAttr(Attribute::ReturnsTwice))
return true;
}

View File

@ -0,0 +1,141 @@
; RUN: llc < %s -mtriple=i386-linux | FileCheck %s -check-prefix=X86-32
; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s -check-prefix=X86-64
declare i32 @get_val()
declare void @use_val(i32)
declare i1 @setjmp()
declare void @longjmp()
declare void @personality()
; Test that llc avoids reusing spill slots in functions that call
; setjmp(), whether they use "call" or "invoke" for calling setjmp()
; (PR18244).
define void @setjmp_caller() {
; X86-32-LABEL: setjmp_caller:
; X86-64-LABEL: setjmp_caller:
; This code keeps enough variables live across the setjmp() call that
; they don't all fit in registers and the compiler will allocate a
; spill slot.
%a1 = call i32 @get_val()
%a2 = call i32 @get_val()
%a3 = call i32 @get_val()
%a4 = call i32 @get_val()
%a5 = call i32 @get_val()
%a6 = call i32 @get_val()
%a7 = call i32 @get_val()
%a8 = call i32 @get_val()
; X86-32: movl %eax, [[SPILL_SLOT:[0-9]+]](%esp)
; X86-32: calll get_val
; X86-64: movl %eax, [[SPILL_SLOT:[0-9]+]](%rsp)
; X86-64: callq get_val
%setjmp_result = call i1 @setjmp() returns_twice
br i1 %setjmp_result, label %second, label %first
; X86-32: calll setjmp
; X86-64: callq setjmp
; Again, keep enough variables live that they need spill slots. Since
; this function calls a returns_twice function (setjmp()), the
; compiler should not reuse the spill slots. longjmp() can return to
; where the first spill slots were still live.
first:
%b1 = call i32 @get_val()
%b2 = call i32 @get_val()
%b3 = call i32 @get_val()
%b4 = call i32 @get_val()
%b5 = call i32 @get_val()
%b6 = call i32 @get_val()
%b7 = call i32 @get_val()
%b8 = call i32 @get_val()
call void @use_val(i32 %b1)
call void @use_val(i32 %b2)
call void @use_val(i32 %b3)
call void @use_val(i32 %b4)
call void @use_val(i32 %b5)
call void @use_val(i32 %b6)
call void @use_val(i32 %b7)
call void @use_val(i32 %b8)
call void @longjmp()
unreachable
; X86-32-NOT: movl {{.*}}, [[SPILL_SLOT]](%esp)
; X86-64-NOT: movl {{.*}}, [[SPILL_SLOT]](%rsp)
second:
call void @use_val(i32 %a1)
call void @use_val(i32 %a2)
call void @use_val(i32 %a3)
call void @use_val(i32 %a4)
call void @use_val(i32 %a5)
call void @use_val(i32 %a6)
call void @use_val(i32 %a7)
call void @use_val(i32 %a8)
ret void
}
; This is the same as above, but using "invoke" rather than "call" to
; call setjmp().
define void @setjmp_invoker() {
; X86-32-LABEL: setjmp_invoker:
; X86-64-LABEL: setjmp_invoker:
%a1 = call i32 @get_val()
%a2 = call i32 @get_val()
%a3 = call i32 @get_val()
%a4 = call i32 @get_val()
%a5 = call i32 @get_val()
%a6 = call i32 @get_val()
%a7 = call i32 @get_val()
%a8 = call i32 @get_val()
; X86-32: movl %eax, [[SPILL_SLOT:[0-9]+]](%esp)
; X86-32: calll get_val
; X86-64: movl %eax, [[SPILL_SLOT:[0-9]+]](%rsp)
; X86-64: callq get_val
%setjmp_result = invoke i1 @setjmp() returns_twice
to label %cont unwind label %lpad
; X86-32: calll setjmp
; X86-64: callq setjmp
cont:
br i1 %setjmp_result, label %second, label %first
lpad:
%lp = landingpad { i8*, i32 } personality void ()* @personality cleanup
unreachable
first:
%b1 = call i32 @get_val()
%b2 = call i32 @get_val()
%b3 = call i32 @get_val()
%b4 = call i32 @get_val()
%b5 = call i32 @get_val()
%b6 = call i32 @get_val()
%b7 = call i32 @get_val()
%b8 = call i32 @get_val()
call void @use_val(i32 %b1)
call void @use_val(i32 %b2)
call void @use_val(i32 %b3)
call void @use_val(i32 %b4)
call void @use_val(i32 %b5)
call void @use_val(i32 %b6)
call void @use_val(i32 %b7)
call void @use_val(i32 %b8)
call void @longjmp()
unreachable
; X86-32-NOT: movl {{.*}}, [[SPILL_SLOT]](%esp)
; X86-64-NOT: movl {{.*}}, [[SPILL_SLOT]](%rsp)
second:
call void @use_val(i32 %a1)
call void @use_val(i32 %a2)
call void @use_val(i32 %a3)
call void @use_val(i32 %a4)
call void @use_val(i32 %a5)
call void @use_val(i32 %a6)
call void @use_val(i32 %a7)
call void @use_val(i32 %a8)
ret void
}