Skip to content

Commit 25dddf5

Browse files
lxfindyln
authored andcommitted
[TSAN] Handle musttail call properly in EscapeEnumerator (and TSAN)
Call instructions with musttail tag must be optimized as a tailcall, otherwise could lead to incorrect program behavior. When TSAN is instrumenting functions, it broke the contract by adding a call to the tsan exit function inbetween the musttail call and return instruction, and also inserted exception handling code. This happend throguh EscapeEnumerator, which adds exception handling code and returns ret instructions as the place to insert instrumentation calls. This becomes especially problematic for coroutines, because coroutines rely on tail calls to do symmetric transfers properly. To fix this, this patch moves the location to insert instrumentation calls prior to the musttail call for ret instructions that are following musttail calls, and also does not handle exception for musttail calls. Differential Revision: https://reviews.llvm.org/D87620 (cherry picked from commit 7b4cc09) rdar://68166972
1 parent 8da9102 commit 25dddf5

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

llvm/lib/Transforms/Utils/EscapeEnumerator.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,27 @@ IRBuilder<> *EscapeEnumerator::Next() {
4141
if (!isa<ReturnInst>(TI) && !isa<ResumeInst>(TI))
4242
continue;
4343

44-
Builder.SetInsertPoint(TI);
44+
// If the ret instruction is followed by a musttaill call,
45+
// or a bitcast instruction and then a musttail call, we should return
46+
// the musttail call as the insertion point to not break the musttail
47+
// contract.
48+
auto AdjustMustTailCall = [&](Instruction *I) -> Instruction * {
49+
auto *RI = dyn_cast<ReturnInst>(I);
50+
if (!RI || !RI->getPrevNode())
51+
return I;
52+
auto *CI = dyn_cast<CallInst>(RI->getPrevNode());
53+
if (CI && CI->isMustTailCall())
54+
return CI;
55+
auto *BI = dyn_cast<BitCastInst>(RI->getPrevNode());
56+
if (!BI || !BI->getPrevNode())
57+
return I;
58+
CI = dyn_cast<CallInst>(BI->getPrevNode());
59+
if (CI && CI->isMustTailCall())
60+
return CI;
61+
return I;
62+
};
63+
64+
Builder.SetInsertPoint(AdjustMustTailCall(TI));
4565
return &Builder;
4666
}
4767

@@ -54,11 +74,12 @@ IRBuilder<> *EscapeEnumerator::Next() {
5474
return nullptr;
5575

5676
// Find all 'call' instructions that may throw.
77+
// We cannot tranform calls with musttail tag.
5778
SmallVector<Instruction *, 16> Calls;
5879
for (BasicBlock &BB : F)
5980
for (Instruction &II : BB)
6081
if (CallInst *CI = dyn_cast<CallInst>(&II))
61-
if (!CI->doesNotThrow())
82+
if (!CI->doesNotThrow() && !CI->isMustTailCall())
6283
Calls.push_back(CI);
6384

6485
if (Calls.empty())
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; To test that __tsan_func_exit always happen before musttaill call and no exception handling code.
2+
; RUN: opt < %s -tsan -S | FileCheck %s
3+
4+
define internal i32 @preallocated_musttail(i32* preallocated(i32) %p) sanitize_thread {
5+
%rv = load i32, i32* %p
6+
ret i32 %rv
7+
}
8+
9+
define i32 @call_preallocated_musttail(i32* preallocated(i32) %a) sanitize_thread {
10+
%r = musttail call i32 @preallocated_musttail(i32* preallocated(i32) %a)
11+
ret i32 %r
12+
}
13+
14+
; CHECK-LABEL: define i32 @call_preallocated_musttail(i32* preallocated(i32) %a)
15+
; CHECK: call void @__tsan_func_exit()
16+
; CHECK-NEXT: %r = musttail call i32 @preallocated_musttail(i32* preallocated(i32) %a)
17+
; CHECK-NEXT: ret i32 %r
18+
19+
20+
define i32 @call_preallocated_musttail_cast(i32* preallocated(i32) %a) sanitize_thread {
21+
%r = musttail call i32 @preallocated_musttail(i32* preallocated(i32) %a)
22+
%t = bitcast i32 %r to i32
23+
ret i32 %t
24+
}
25+
26+
; CHECK-LABEL: define i32 @call_preallocated_musttail_cast(i32* preallocated(i32) %a)
27+
; CHECK: call void @__tsan_func_exit()
28+
; CHECK-NEXT: %r = musttail call i32 @preallocated_musttail(i32* preallocated(i32) %a)
29+
; CHECK-NEXT: %t = bitcast i32 %r to i32
30+
; CHECK-NEXT: ret i32 %t

0 commit comments

Comments
 (0)