зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1724522 - Remove unused clang-linux64.json. r=firefox-build-system-reviewers,mhentges
Differential Revision: https://phabricator.services.mozilla.com/D122400
This commit is contained in:
Родитель
91f7c1ff11
Коммит
8f94edfa1f
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"stages": "4",
|
||||
"pgo" : true,
|
||||
"build_libcxx": true,
|
||||
"build_wasm": true,
|
||||
"build_type": "Release",
|
||||
"assertions": false,
|
||||
"cc": "{MOZ_FETCHES_DIR}/gcc/bin/gcc",
|
||||
"cxx": "{MOZ_FETCHES_DIR}/gcc/bin/g++",
|
||||
"as": "{MOZ_FETCHES_DIR}/gcc/bin/gcc",
|
||||
"wasi-sysroot": "{MOZ_FETCHES_DIR}/wasi-sysroot",
|
||||
"patches": [
|
||||
"static-llvm-symbolizer.patch",
|
||||
"find_symbolizer_linux.patch",
|
||||
"rG7e18aeba5062.patch",
|
||||
"llvmorg-11-init-4265-g2dcbdba8540.patch",
|
||||
"android-mangling-error.patch",
|
||||
"unpoison-thread-stacks.patch",
|
||||
"downgrade-mangling-error.patch",
|
||||
"tsan-hang-be41a98ac222.patch",
|
||||
"llvmorg-11-init-15486-gfc937806efd-dont-jump-to-landing-pads.patch",
|
||||
"loosen-msvc-detection.patch"
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
From d1c09fb47e2778538c5b1f918724d31d05497883 Mon Sep 17 00:00:00 2001
|
||||
From: Arthur Eubanks <aeubanks@google.com>
|
||||
Date: Wed, 13 May 2020 16:33:09 -0700
|
||||
Subject: [PATCH] Don't jump to landing pads in Control Flow Optimizer
|
||||
|
||||
Summary: Likely fixes https://bugs.llvm.org/show_bug.cgi?id=45858.
|
||||
|
||||
Subscribers: hiraditya, llvm-commits
|
||||
|
||||
Tags: #llvm
|
||||
|
||||
Differential Revision: https://reviews.llvm.org/D80047
|
||||
---
|
||||
llvm/lib/CodeGen/BranchFolding.cpp | 18 ++++++------
|
||||
llvm/test/CodeGen/X86/branchfolding-ehpad.mir | 28 +++++++++++++++++++
|
||||
2 files changed, 38 insertions(+), 8 deletions(-)
|
||||
create mode 100644 llvm/test/CodeGen/X86/branchfolding-ehpad.mir
|
||||
|
||||
diff --git a/llvm/lib/CodeGen/BranchFolding.cpp b/llvm/lib/CodeGen/BranchFolding.cpp
|
||||
index fb54b5d6c8d..4a822b58446 100644
|
||||
--- a/llvm/lib/CodeGen/BranchFolding.cpp
|
||||
+++ b/llvm/lib/CodeGen/BranchFolding.cpp
|
||||
@@ -991,10 +991,10 @@ bool BranchFolder::TryTailMergeBlocks(MachineBasicBlock *SuccBB,
|
||||
continue;
|
||||
}
|
||||
|
||||
- // If one of the blocks is the entire common tail (and not the entry
|
||||
- // block, which we can't jump to), we can treat all blocks with this same
|
||||
- // tail at once. Use PredBB if that is one of the possibilities, as that
|
||||
- // will not introduce any extra branches.
|
||||
+ // If one of the blocks is the entire common tail (and is not the entry
|
||||
+ // block/an EH pad, which we can't jump to), we can treat all blocks with
|
||||
+ // this same tail at once. Use PredBB if that is one of the possibilities,
|
||||
+ // as that will not introduce any extra branches.
|
||||
MachineBasicBlock *EntryBB =
|
||||
&MergePotentials.front().getBlock()->getParent()->front();
|
||||
unsigned commonTailIndex = SameTails.size();
|
||||
@@ -1002,19 +1002,21 @@ bool BranchFolder::TryTailMergeBlocks(MachineBasicBlock *SuccBB,
|
||||
// into the other.
|
||||
if (SameTails.size() == 2 &&
|
||||
SameTails[0].getBlock()->isLayoutSuccessor(SameTails[1].getBlock()) &&
|
||||
- SameTails[1].tailIsWholeBlock())
|
||||
+ SameTails[1].tailIsWholeBlock() && !SameTails[1].getBlock()->isEHPad())
|
||||
commonTailIndex = 1;
|
||||
else if (SameTails.size() == 2 &&
|
||||
SameTails[1].getBlock()->isLayoutSuccessor(
|
||||
- SameTails[0].getBlock()) &&
|
||||
- SameTails[0].tailIsWholeBlock())
|
||||
+ SameTails[0].getBlock()) &&
|
||||
+ SameTails[0].tailIsWholeBlock() &&
|
||||
+ !SameTails[0].getBlock()->isEHPad())
|
||||
commonTailIndex = 0;
|
||||
else {
|
||||
// Otherwise just pick one, favoring the fall-through predecessor if
|
||||
// there is one.
|
||||
for (unsigned i = 0, e = SameTails.size(); i != e; ++i) {
|
||||
MachineBasicBlock *MBB = SameTails[i].getBlock();
|
||||
- if (MBB == EntryBB && SameTails[i].tailIsWholeBlock())
|
||||
+ if ((MBB == EntryBB || MBB->isEHPad()) &&
|
||||
+ SameTails[i].tailIsWholeBlock())
|
||||
continue;
|
||||
if (MBB == PredBB) {
|
||||
commonTailIndex = i;
|
||||
diff --git a/llvm/test/CodeGen/X86/branchfolding-ehpad.mir b/llvm/test/CodeGen/X86/branchfolding-ehpad.mir
|
||||
new file mode 100644
|
||||
index 00000000000..d445cd20680
|
||||
--- /dev/null
|
||||
+++ b/llvm/test/CodeGen/X86/branchfolding-ehpad.mir
|
||||
@@ -0,0 +1,28 @@
|
||||
+# RUN: llc -mtriple=x86_64-windows-msvc -verify-machineinstrs -run-pass branch-folder -o - %s | FileCheck %s
|
||||
+
|
||||
+# Check that branch-folder does not create a fallthrough to a landing pad.
|
||||
+# Also make sure that the landing pad still can be tail merged.
|
||||
+---
|
||||
+name: foo
|
||||
+body: |
|
||||
+ ; CHECK-LABEL: name: foo
|
||||
+ bb.0:
|
||||
+ successors: %bb.1, %bb.3
|
||||
+ bb.1:
|
||||
+ JCC_1 %bb.4, 5, implicit killed $eflags
|
||||
+ bb.2:
|
||||
+ MOV8mi $r13, 1, $noreg, 0, $noreg, 0
|
||||
+ JMP_1 %bb.5
|
||||
+ ; CHECK: bb.2:
|
||||
+ ; CHECK-NOT: successors: {{.*}}bb.3
|
||||
+ ; CHECK: bb.3 (landing-pad):
|
||||
+ ; CHECK-NOT: MOV8mi
|
||||
+ bb.3(landing-pad):
|
||||
+ MOV8mi $r13, 1, $noreg, 0, $noreg, 0
|
||||
+ JMP_1 %bb.5
|
||||
+ ; CHECK: bb.4:
|
||||
+ bb.4:
|
||||
+ MOV8mi $r13, 2, $noreg, 0, $noreg, 0
|
||||
+ bb.5:
|
||||
+ RET 0
|
||||
+...
|
||||
--
|
||||
2.24.1.windows.2
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
|
||||
index 9a184c79798..733decfe52c 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
|
||||
@@ -1021,7 +1021,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
|
||||
SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
|
||||
- int tid = ThreadTid(thr, pc, (uptr)th);
|
||||
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
|
||||
ThreadIgnoreBegin(thr, pc);
|
||||
int res = BLOCK_REAL(pthread_join)(th, ret);
|
||||
ThreadIgnoreEnd(thr, pc);
|
||||
@@ -1034,8 +1034,8 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
|
||||
DEFINE_REAL_PTHREAD_FUNCTIONS
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
|
||||
- SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
|
||||
- int tid = ThreadTid(thr, pc, (uptr)th);
|
||||
+ SCOPED_INTERCEPTOR_RAW(pthread_detach, th);
|
||||
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
|
||||
int res = REAL(pthread_detach)(th);
|
||||
if (res == 0) {
|
||||
ThreadDetach(thr, pc, tid);
|
||||
@@ -1055,8 +1055,8 @@ TSAN_INTERCEPTOR(void, pthread_exit, void *retval) {
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
|
||||
- SCOPED_TSAN_INTERCEPTOR(pthread_tryjoin_np, th, ret);
|
||||
- int tid = ThreadTid(thr, pc, (uptr)th);
|
||||
+ SCOPED_INTERCEPTOR_RAW(pthread_tryjoin_np, th, ret);
|
||||
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
|
||||
ThreadIgnoreBegin(thr, pc);
|
||||
int res = REAL(pthread_tryjoin_np)(th, ret);
|
||||
ThreadIgnoreEnd(thr, pc);
|
||||
@@ -1069,8 +1069,8 @@ TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
|
||||
|
||||
TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
|
||||
const struct timespec *abstime) {
|
||||
- SCOPED_TSAN_INTERCEPTOR(pthread_timedjoin_np, th, ret, abstime);
|
||||
- int tid = ThreadTid(thr, pc, (uptr)th);
|
||||
+ SCOPED_INTERCEPTOR_RAW(pthread_timedjoin_np, th, ret, abstime);
|
||||
+ int tid = ThreadConsumeTid(thr, pc, (uptr)th);
|
||||
ThreadIgnoreBegin(thr, pc);
|
||||
int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
|
||||
ThreadIgnoreEnd(thr, pc);
|
||||
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
|
||||
index 3a8231bda9a..30e144fbd00 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
|
||||
@@ -772,7 +772,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
|
||||
void ThreadStart(ThreadState *thr, int tid, tid_t os_id,
|
||||
ThreadType thread_type);
|
||||
void ThreadFinish(ThreadState *thr);
|
||||
-int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
|
||||
+int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid);
|
||||
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
|
||||
void ThreadDetach(ThreadState *thr, uptr pc, int tid);
|
||||
void ThreadFinalize(ThreadState *thr);
|
||||
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
|
||||
index fd95cfed4f5..13e457bd770 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
|
||||
@@ -285,19 +285,34 @@ void ThreadFinish(ThreadState *thr) {
|
||||
ctx->thread_registry->FinishThread(thr->tid);
|
||||
}
|
||||
|
||||
-static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
|
||||
- uptr uid = (uptr)arg;
|
||||
- if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
|
||||
+struct ConsumeThreadContext {
|
||||
+ uptr uid;
|
||||
+ ThreadContextBase* tctx;
|
||||
+};
|
||||
+
|
||||
+static bool ConsumeThreadByUid(ThreadContextBase *tctx, void *arg) {
|
||||
+ ConsumeThreadContext *findCtx = (ConsumeThreadContext*)arg;
|
||||
+ if (tctx->user_id == findCtx->uid && tctx->status != ThreadStatusInvalid) {
|
||||
+ if (findCtx->tctx) {
|
||||
+ // Ensure that user_id is unique. If it's not the case we are screwed.
|
||||
+ // Something went wrong before, but now there is no way to recover.
|
||||
+ // Returning a wrong thread is not an option, it may lead to very hard
|
||||
+ // to debug false positives (e.g. if we join a wrong thread).
|
||||
+ Report("ThreadSanitizer: dup thread with used id 0x%zx\n", findCtx->uid);
|
||||
+ Die();
|
||||
+ }
|
||||
+ findCtx->tctx = tctx;
|
||||
tctx->user_id = 0;
|
||||
- return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
-int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
|
||||
- int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
|
||||
- DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
|
||||
- return res;
|
||||
+int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) {
|
||||
+ ConsumeThreadContext findCtx = {uid, nullptr};
|
||||
+ ctx->thread_registry->FindThread(ConsumeThreadByUid, &findCtx);
|
||||
+ int tid = findCtx.tctx ? findCtx.tctx->tid : ThreadRegistry::kUnknownTid;
|
||||
+ DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, tid);
|
||||
+ return tid;
|
||||
}
|
||||
|
||||
void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
|
|
@ -1,255 +0,0 @@
|
|||
From 779a169144581438d9e24b8b46a86704f6335e35 Mon Sep 17 00:00:00 2001
|
||||
From: Nikita Popov <nikita.ppv@gmail.com>
|
||||
Date: Sat, 16 Nov 2019 16:22:18 +0100
|
||||
Subject: [PATCH] [LVI] Restructure caching
|
||||
|
||||
Variant on D70103. The caching is switched to always use a BB to
|
||||
cache entry map, which then contains per-value caches. A separate
|
||||
set contains value handles with a deletion callback. This allows us
|
||||
to properly invalidate overdefined values.
|
||||
|
||||
A possible alternative would be to always cache by value first and
|
||||
have per-BB maps/sets in the each cache entry. In that case we could
|
||||
use a ValueMap and would avoid the separate value handle set. I went
|
||||
with the BB indexing at the top level to make it easier to integrate
|
||||
D69914, but possibly that's not the right choice.
|
||||
|
||||
Differential Revision: https://reviews.llvm.org/D70376
|
||||
---
|
||||
llvm/lib/Analysis/LazyValueInfo.cpp | 143 +++++++++-------------------
|
||||
1 file changed, 47 insertions(+), 96 deletions(-)
|
||||
|
||||
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
index 542ff709d47..eb51744aec3 100644
|
||||
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
@@ -132,12 +132,9 @@ namespace {
|
||||
/// A callback value handle updates the cache when values are erased.
|
||||
class LazyValueInfoCache;
|
||||
struct LVIValueHandle final : public CallbackVH {
|
||||
- // Needs to access getValPtr(), which is protected.
|
||||
- friend struct DenseMapInfo<LVIValueHandle>;
|
||||
-
|
||||
LazyValueInfoCache *Parent;
|
||||
|
||||
- LVIValueHandle(Value *V, LazyValueInfoCache *P)
|
||||
+ LVIValueHandle(Value *V, LazyValueInfoCache *P = nullptr)
|
||||
: CallbackVH(V), Parent(P) { }
|
||||
|
||||
void deleted() override;
|
||||
@@ -151,89 +148,63 @@ namespace {
|
||||
/// This is the cache kept by LazyValueInfo which
|
||||
/// maintains information about queries across the clients' queries.
|
||||
class LazyValueInfoCache {
|
||||
- /// This is all of the cached block information for exactly one Value*.
|
||||
- /// The entries are sorted by the BasicBlock* of the
|
||||
- /// entries, allowing us to do a lookup with a binary search.
|
||||
- /// Over-defined lattice values are recorded in OverDefinedCache to reduce
|
||||
- /// memory overhead.
|
||||
- struct ValueCacheEntryTy {
|
||||
- ValueCacheEntryTy(Value *V, LazyValueInfoCache *P) : Handle(V, P) {}
|
||||
- LVIValueHandle Handle;
|
||||
- SmallDenseMap<PoisoningVH<BasicBlock>, ValueLatticeElement, 4> BlockVals;
|
||||
+ /// This is all of the cached information for one basic block. It contains
|
||||
+ /// the per-value lattice elements, as well as a separate set for
|
||||
+ /// overdefined values to reduce memory usage.
|
||||
+ struct BlockCacheEntryTy {
|
||||
+ SmallDenseMap<AssertingVH<Value>, ValueLatticeElement, 4> LatticeElements;
|
||||
+ SmallDenseSet<AssertingVH<Value>, 4> OverDefined;
|
||||
};
|
||||
|
||||
- /// This tracks, on a per-block basis, the set of values that are
|
||||
- /// over-defined at the end of that block.
|
||||
- typedef DenseMap<PoisoningVH<BasicBlock>, SmallPtrSet<Value *, 4>>
|
||||
- OverDefinedCacheTy;
|
||||
- /// Keep track of all blocks that we have ever seen, so we
|
||||
- /// don't spend time removing unused blocks from our caches.
|
||||
- DenseSet<PoisoningVH<BasicBlock> > SeenBlocks;
|
||||
-
|
||||
- /// This is all of the cached information for all values,
|
||||
- /// mapped from Value* to key information.
|
||||
- DenseMap<Value *, std::unique_ptr<ValueCacheEntryTy>> ValueCache;
|
||||
- OverDefinedCacheTy OverDefinedCache;
|
||||
-
|
||||
+ /// Cached information per basic block.
|
||||
+ DenseMap<PoisoningVH<BasicBlock>, BlockCacheEntryTy> BlockCache;
|
||||
+ /// Set of value handles used to erase values from the cache on deletion.
|
||||
+ DenseSet<LVIValueHandle, DenseMapInfo<Value *>> ValueHandles;
|
||||
|
||||
public:
|
||||
void insertResult(Value *Val, BasicBlock *BB,
|
||||
const ValueLatticeElement &Result) {
|
||||
- SeenBlocks.insert(BB);
|
||||
-
|
||||
+ auto &CacheEntry = BlockCache.try_emplace(BB).first->second;
|
||||
// Insert over-defined values into their own cache to reduce memory
|
||||
// overhead.
|
||||
if (Result.isOverdefined())
|
||||
- OverDefinedCache[BB].insert(Val);
|
||||
- else {
|
||||
- auto It = ValueCache.find_as(Val);
|
||||
- if (It == ValueCache.end()) {
|
||||
- ValueCache[Val] = make_unique<ValueCacheEntryTy>(Val, this);
|
||||
- It = ValueCache.find_as(Val);
|
||||
- assert(It != ValueCache.end() && "Val was just added to the map!");
|
||||
- }
|
||||
- It->second->BlockVals[BB] = Result;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- bool isOverdefined(Value *V, BasicBlock *BB) const {
|
||||
- auto ODI = OverDefinedCache.find(BB);
|
||||
-
|
||||
- if (ODI == OverDefinedCache.end())
|
||||
- return false;
|
||||
+ CacheEntry.OverDefined.insert(Val);
|
||||
+ else
|
||||
+ CacheEntry.LatticeElements.insert({ Val, Result });
|
||||
|
||||
- return ODI->second.count(V);
|
||||
+ auto HandleIt = ValueHandles.find_as(Val);
|
||||
+ if (HandleIt == ValueHandles.end())
|
||||
+ ValueHandles.insert({ Val, this });
|
||||
}
|
||||
|
||||
bool hasCachedValueInfo(Value *V, BasicBlock *BB) const {
|
||||
- if (isOverdefined(V, BB))
|
||||
- return true;
|
||||
-
|
||||
- auto I = ValueCache.find_as(V);
|
||||
- if (I == ValueCache.end())
|
||||
+ auto It = BlockCache.find(BB);
|
||||
+ if (It == BlockCache.end())
|
||||
return false;
|
||||
|
||||
- return I->second->BlockVals.count(BB);
|
||||
+ return It->second.OverDefined.count(V) ||
|
||||
+ It->second.LatticeElements.count(V);
|
||||
}
|
||||
|
||||
ValueLatticeElement getCachedValueInfo(Value *V, BasicBlock *BB) const {
|
||||
- if (isOverdefined(V, BB))
|
||||
+ auto It = BlockCache.find(BB);
|
||||
+ if (It == BlockCache.end())
|
||||
+ return ValueLatticeElement();
|
||||
+
|
||||
+ if (It->second.OverDefined.count(V))
|
||||
return ValueLatticeElement::getOverdefined();
|
||||
|
||||
- auto I = ValueCache.find_as(V);
|
||||
- if (I == ValueCache.end())
|
||||
+ auto LatticeIt = It->second.LatticeElements.find(V);
|
||||
+ if (LatticeIt == It->second.LatticeElements.end())
|
||||
return ValueLatticeElement();
|
||||
- auto BBI = I->second->BlockVals.find(BB);
|
||||
- if (BBI == I->second->BlockVals.end())
|
||||
- return ValueLatticeElement();
|
||||
- return BBI->second;
|
||||
+
|
||||
+ return LatticeIt->second;
|
||||
}
|
||||
|
||||
/// clear - Empty the cache.
|
||||
void clear() {
|
||||
- SeenBlocks.clear();
|
||||
- ValueCache.clear();
|
||||
- OverDefinedCache.clear();
|
||||
+ BlockCache.clear();
|
||||
+ ValueHandles.clear();
|
||||
}
|
||||
|
||||
/// Inform the cache that a given value has been deleted.
|
||||
@@ -247,23 +218,18 @@ namespace {
|
||||
/// OldSucc might have (unless also overdefined in NewSucc). This just
|
||||
/// flushes elements from the cache and does not add any.
|
||||
void threadEdgeImpl(BasicBlock *OldSucc,BasicBlock *NewSucc);
|
||||
-
|
||||
- friend struct LVIValueHandle;
|
||||
};
|
||||
}
|
||||
|
||||
void LazyValueInfoCache::eraseValue(Value *V) {
|
||||
- for (auto I = OverDefinedCache.begin(), E = OverDefinedCache.end(); I != E;) {
|
||||
- // Copy and increment the iterator immediately so we can erase behind
|
||||
- // ourselves.
|
||||
- auto Iter = I++;
|
||||
- SmallPtrSetImpl<Value *> &ValueSet = Iter->second;
|
||||
- ValueSet.erase(V);
|
||||
- if (ValueSet.empty())
|
||||
- OverDefinedCache.erase(Iter);
|
||||
+ for (auto &Pair : BlockCache) {
|
||||
+ Pair.second.LatticeElements.erase(V);
|
||||
+ Pair.second.OverDefined.erase(V);
|
||||
}
|
||||
|
||||
- ValueCache.erase(V);
|
||||
+ auto HandleIt = ValueHandles.find_as(V);
|
||||
+ if (HandleIt != ValueHandles.end())
|
||||
+ ValueHandles.erase(HandleIt);
|
||||
}
|
||||
|
||||
void LVIValueHandle::deleted() {
|
||||
@@ -273,18 +239,7 @@ void LVIValueHandle::deleted() {
|
||||
}
|
||||
|
||||
void LazyValueInfoCache::eraseBlock(BasicBlock *BB) {
|
||||
- // Shortcut if we have never seen this block.
|
||||
- DenseSet<PoisoningVH<BasicBlock> >::iterator I = SeenBlocks.find(BB);
|
||||
- if (I == SeenBlocks.end())
|
||||
- return;
|
||||
- SeenBlocks.erase(I);
|
||||
-
|
||||
- auto ODI = OverDefinedCache.find(BB);
|
||||
- if (ODI != OverDefinedCache.end())
|
||||
- OverDefinedCache.erase(ODI);
|
||||
-
|
||||
- for (auto &I : ValueCache)
|
||||
- I.second->BlockVals.erase(BB);
|
||||
+ BlockCache.erase(BB);
|
||||
}
|
||||
|
||||
void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
|
||||
@@ -302,10 +257,11 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
|
||||
std::vector<BasicBlock*> worklist;
|
||||
worklist.push_back(OldSucc);
|
||||
|
||||
- auto I = OverDefinedCache.find(OldSucc);
|
||||
- if (I == OverDefinedCache.end())
|
||||
+ auto I = BlockCache.find(OldSucc);
|
||||
+ if (I == BlockCache.end() || I->second.OverDefined.empty())
|
||||
return; // Nothing to process here.
|
||||
- SmallVector<Value *, 4> ValsToClear(I->second.begin(), I->second.end());
|
||||
+ SmallVector<Value *, 4> ValsToClear(I->second.OverDefined.begin(),
|
||||
+ I->second.OverDefined.end());
|
||||
|
||||
// Use a worklist to perform a depth-first search of OldSucc's successors.
|
||||
// NOTE: We do not need a visited list since any blocks we have already
|
||||
@@ -319,10 +275,10 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
|
||||
if (ToUpdate == NewSucc) continue;
|
||||
|
||||
// If a value was marked overdefined in OldSucc, and is here too...
|
||||
- auto OI = OverDefinedCache.find(ToUpdate);
|
||||
- if (OI == OverDefinedCache.end())
|
||||
+ auto OI = BlockCache.find(ToUpdate);
|
||||
+ if (OI == BlockCache.end() || OI->second.OverDefined.empty())
|
||||
continue;
|
||||
- SmallPtrSetImpl<Value *> &ValueSet = OI->second;
|
||||
+ auto &ValueSet = OI->second.OverDefined;
|
||||
|
||||
bool changed = false;
|
||||
for (Value *V : ValsToClear) {
|
||||
@@ -332,11 +288,6 @@ void LazyValueInfoCache::threadEdgeImpl(BasicBlock *OldSucc,
|
||||
// If we removed anything, then we potentially need to update
|
||||
// blocks successors too.
|
||||
changed = true;
|
||||
-
|
||||
- if (ValueSet.empty()) {
|
||||
- OverDefinedCache.erase(OI);
|
||||
- break;
|
||||
- }
|
||||
}
|
||||
|
||||
if (!changed) continue;
|
||||
--
|
||||
2.24.0
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
From be41a98ac222f33ed5558d86e1cede67249e99b5 Mon Sep 17 00:00:00 2001
|
||||
From: Dmitry Vyukov <dvyukov@google.com>
|
||||
Date: Sat, 21 Mar 2020 13:34:50 +0100
|
||||
Subject: [PATCH] tsan: fix deadlock with pthread_atfork callbacks
|
||||
|
||||
This fixes the bug reported at:
|
||||
https://groups.google.com/forum/#!topic/thread-sanitizer/e_zB9gYqFHM
|
||||
|
||||
A pthread_atfork callback triggers a data race
|
||||
and we deadlock on the report_mtx. Ignore memory access
|
||||
in the pthread_atfork callbacks to prevent the deadlock.
|
||||
---
|
||||
compiler-rt/lib/tsan/rtl/tsan_rtl.cc | 9 ++++
|
||||
.../test/tsan/pthread_atfork_deadlock2.c | 49 +++++++++++++++++++
|
||||
2 files changed, 58 insertions(+)
|
||||
create mode 100644 compiler-rt/test/tsan/pthread_atfork_deadlock2.c
|
||||
|
||||
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl.ccc
|
||||
index fe469faad2a2..13c9b770f50a 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc
|
||||
@@ -495,14 +495,23 @@ int Finalize(ThreadState *thr) {
|
||||
void ForkBefore(ThreadState *thr, uptr pc) {
|
||||
ctx->thread_registry->Lock();
|
||||
ctx->report_mtx.Lock();
|
||||
+ // Ignore memory accesses in the pthread_atfork callbacks.
|
||||
+ // If any of them triggers a data race we will deadlock
|
||||
+ // on the report_mtx.
|
||||
+ // We could ignore interceptors and sync operations as well,
|
||||
+ // but so far it's unclear if it will do more good or harm.
|
||||
+ // Unnecessarily ignoring things can lead to false positives later.
|
||||
+ ThreadIgnoreBegin(thr, pc);
|
||||
}
|
||||
|
||||
void ForkParentAfter(ThreadState *thr, uptr pc) {
|
||||
+ ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore.
|
||||
ctx->report_mtx.Unlock();
|
||||
ctx->thread_registry->Unlock();
|
||||
}
|
||||
|
||||
void ForkChildAfter(ThreadState *thr, uptr pc) {
|
||||
+ ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore.
|
||||
ctx->report_mtx.Unlock();
|
||||
ctx->thread_registry->Unlock();
|
||||
|
||||
diff --git a/compiler-rt/test/tsan/pthread_atfork_deadlock2.c b/compiler-rt/test/tsan/pthread_atfork_deadlock2.c
|
||||
new file mode 100644
|
||||
index 000000000000..700507c1e637
|
||||
--- /dev/null
|
||||
+++ b/compiler-rt/test/tsan/pthread_atfork_deadlock2.c
|
||||
@@ -0,0 +1,49 @@
|
||||
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
|
||||
+// Regression test for
|
||||
+// https://groups.google.com/d/msg/thread-sanitizer/e_zB9gYqFHM/DmAiTsrLAwAJ
|
||||
+// pthread_atfork() callback triggers a data race and we deadlocked
|
||||
+// on the report_mtx as we lock it around fork.
|
||||
+#include "test.h"
|
||||
+#include <sys/types.h>
|
||||
+#include <sys/wait.h>
|
||||
+#include <errno.h>
|
||||
+
|
||||
+int glob = 0;
|
||||
+
|
||||
+void *worker(void *unused) {
|
||||
+ glob++;
|
||||
+ barrier_wait(&barrier);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+void atfork() {
|
||||
+ glob++;
|
||||
+}
|
||||
+
|
||||
+int main() {
|
||||
+ barrier_init(&barrier, 2);
|
||||
+ pthread_atfork(atfork, NULL, NULL);
|
||||
+ pthread_t t;
|
||||
+ pthread_create(&t, NULL, worker, NULL);
|
||||
+ barrier_wait(&barrier);
|
||||
+ pid_t pid = fork();
|
||||
+ if (pid < 0) {
|
||||
+ fprintf(stderr, "fork failed: %d\n", errno);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (pid == 0) {
|
||||
+ fprintf(stderr, "CHILD\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ if (pid != waitpid(pid, NULL, 0)) {
|
||||
+ fprintf(stderr, "waitpid failed: %d\n", errno);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ pthread_join(t, NULL);
|
||||
+ fprintf(stderr, "PARENT\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+// CHECK-NOT: ThreadSanitizer: data race
|
||||
+// CHECK: CHILD
|
||||
+// CHECK: PARENT
|
|
@ -1,62 +0,0 @@
|
|||
[winasan] Unpoison the stack in NtTerminateThread
|
||||
|
||||
In long-running builds we've seen some ASan complaints during thread creation
|
||||
that we suspect are due to leftover poisoning from previous threads whose stacks
|
||||
occupied that memory. This patch adds a hook that unpoisons the stack just
|
||||
before the NtTerminateThread syscall.
|
||||
|
||||
Differential Revision: https://reviews.llvm.org/D52091
|
||||
|
||||
** Update for clang 9 ** : After some backouts, this patch eventually landed
|
||||
upstream in a different form, as the TLS handler `asan_thread_exit`, but that
|
||||
variant causes failures in our test suite, so revert the TLS handler in favor of
|
||||
the interceptor approach from the first patch.
|
||||
|
||||
--- a/compiler-rt/lib/asan/asan_win.cc
|
||||
+++ b/compiler-rt/lib/asan/asan_win.cc
|
||||
@@ -154,6 +154,14 @@
|
||||
thr_flags, tid);
|
||||
}
|
||||
|
||||
+INTERCEPTOR_WINAPI(void, NtTerminateThread, void *rcx) {
|
||||
+ // Unpoison the terminating thread's stack because the memory may be re-used.
|
||||
+ NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
|
||||
+ uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
|
||||
+ __asan_unpoison_memory_region(tib->StackLimit, stackSize);
|
||||
+ return REAL(NtTerminateThread(rcx));
|
||||
+}
|
||||
+
|
||||
// }}}
|
||||
|
||||
namespace __asan {
|
||||
@@ -168,7 +176,9 @@
|
||||
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
|
||||
-
|
||||
+ CHECK(::__interception::OverrideFunction("NtTerminateThread",
|
||||
+ (uptr)WRAP(NtTerminateThread),
|
||||
+ (uptr *)&REAL(NtTerminateThread)));
|
||||
#ifdef _WIN64
|
||||
ASAN_INTERCEPT_FUNC(__C_specific_handler);
|
||||
#else
|
||||
@@ -380,19 +390,6 @@
|
||||
void *, unsigned long, void *) = asan_thread_init;
|
||||
#endif
|
||||
|
||||
-static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
|
||||
- if (reason == DLL_THREAD_DETACH) {
|
||||
- // Unpoison the thread's stack because the memory may be re-used.
|
||||
- NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
|
||||
- uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
|
||||
- __asan_unpoison_memory_region(tib->StackLimit, stackSize);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#pragma section(".CRT$XLY", long, read) // NOLINT
|
||||
-__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
|
||||
- void *, unsigned long, void *) = asan_thread_exit;
|
||||
-
|
||||
WIN_FORCE_LINK(__asan_dso_reg_hook)
|
||||
|
||||
// }}}
|
Загрузка…
Ссылка в новой задаче