зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1625884 - add `clang-10` to `linux64` `clang-tools`. r=nalexander,dmajor
Patches that are applied on top on `clang-10` repo are based on the original patches from our trunk and have been rebased on top of `clang-10`. Please see as an example: `find_symbolizer_linux.patch` copied to `find_symbolizer_linux_clang_10.patch`. Differential Revision: https://phabricator.services.mozilla.com/D70063 --HG-- rename : build/build-clang/clang-linux64.json => build/build-clang/clang-10-linux64.json rename : build/build-clang/find_symbolizer_linux.patch => build/build-clang/find_symbolizer_linux_clang_10.patch rename : build/build-clang/llvmorg-11-init-4265-g2dcbdba8540.patch => build/build-clang/llvmorg-11-init-4265-g2dcbdba8540_clang_10.patch rename : build/build-clang/rG7e18aeba5062.patch => build/build-clang/rG7e18aeba5062_clang_10.patch rename : build/build-clang/rename_gcov_flush.patch => build/build-clang/rename_gcov_flush_clang_10.patch rename : build/build-clang/tsan-hang-be41a98ac222.patch => build/build-clang/tsan-hang-be41a98ac222_clang_10.patch rename : build/build-clang/unpoison-thread-stacks.patch => build/build-clang/unpoison-thread-stacks_clang_10.patch extra : moz-landing-system : lando
This commit is contained in:
Родитель
20a905ded8
Коммит
06e7a3c818
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"stages": "4",
|
||||
"pgo" : true,
|
||||
"build_libcxx": true,
|
||||
"build_wasm": true,
|
||||
"build_type": "Release",
|
||||
"assertions": false,
|
||||
"python_path": "/usr/bin/python2.7",
|
||||
"gcc_dir": "{MOZ_FETCHES_DIR}/gcc",
|
||||
"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_clang_10.patch",
|
||||
"rename_gcov_flush_clang_10.patch",
|
||||
"critical_section_on_gcov_flush-rG02ce9d8ef5a8.patch",
|
||||
"rG7e18aeba5062_clang_10.patch",
|
||||
"llvmorg-11-init-4265-g2dcbdba8540_clang_10.patch",
|
||||
"android-mangling-error.patch",
|
||||
"unpoison-thread-stacks_clang_10.patch",
|
||||
"downgrade-mangling-error.patch",
|
||||
"tsan-hang-be41a98ac222_clang_10.patch",
|
||||
"loosen-msvc-detection.patch"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
We currently need this patch because ASan only searches PATH to find the
|
||||
llvm-symbolizer binary to symbolize ASan traces. On testing machines, this
|
||||
can be installed in PATH easily. However, for e.g. the ASan Nightly Project,
|
||||
where we ship an ASan build, including llvm-symbolizer, to the user, we
|
||||
cannot expect llvm-symbolizer to be on PATH. Instead, we should try to look
|
||||
it up next to the binary. This patch implements the functionality for Linux
|
||||
only until there is similar functionality provided upstream.
|
||||
|
||||
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
|
||||
index 79930d79425..cfb4f90c0d5 100644
|
||||
--- a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
|
||||
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_file.h"
|
||||
|
||||
+#if SANITIZER_LINUX
|
||||
+#include "sanitizer_posix.h"
|
||||
+#endif
|
||||
+
|
||||
namespace __sanitizer {
|
||||
|
||||
void CatastrophicErrorWrite(const char *buffer, uptr length) {
|
||||
@@ -194,6 +198,34 @@ char *FindPathToBinary(const char *name) {
|
||||
if (*end == '\0') break;
|
||||
beg = end + 1;
|
||||
}
|
||||
+
|
||||
+#if SANITIZER_LINUX
|
||||
+ // If we cannot find the requested binary in PATH, we should try to locate
|
||||
+ // it next to the binary, in case it is shipped with the build itself
|
||||
+ // (e.g. llvm-symbolizer shipped with sanitizer build to symbolize on client.
|
||||
+ if (internal_readlink("/proc/self/exe", buffer.data(), kMaxPathLength) < 0)
|
||||
+ return nullptr;
|
||||
+
|
||||
+ uptr buf_len = internal_strlen(buffer.data());
|
||||
+
|
||||
+ /* Avoid using dirname() here */
|
||||
+ while (buf_len > 0) {
|
||||
+ if (buffer[buf_len - 1] == '/')
|
||||
+ break;
|
||||
+ buf_len--;
|
||||
+ }
|
||||
+
|
||||
+ if (!buf_len)
|
||||
+ return nullptr;
|
||||
+
|
||||
+ if (buf_len + name_len + 1 <= kMaxPathLength) {
|
||||
+ internal_memcpy(&buffer[buf_len], name, name_len);
|
||||
+ buffer[buf_len + name_len] = '\0';
|
||||
+ if (FileExists(buffer.data()))
|
||||
+ return internal_strdup(buffer.data());
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
|
||||
index 8aea1e4ec05..a623f4fe589 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
|
||||
@@ -1016,7 +1016,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);
|
||||
@@ -1029,8 +1029,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);
|
||||
@@ -1050,8 +1050,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);
|
||||
@@ -1064,8 +1064,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 c38fc43a9f8..20f7a99157a 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
|
||||
@@ -775,7 +775,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.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
|
||||
index 0ac1ee99c47..f7068f0d331 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
|
||||
@@ -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) {
|
|
@ -0,0 +1,249 @@
|
|||
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
|
||||
|
||||
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
index bad2de9e5f5..33406a75d80 100644
|
||||
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
|
||||
@@ -136,12 +136,10 @@ 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;
|
||||
@@ -155,89 +153,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] = std::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())
|
||||
- return ValueLatticeElement();
|
||||
- auto BBI = I->second->BlockVals.find(BB);
|
||||
- if (BBI == I->second->BlockVals.end())
|
||||
+ auto LatticeIt = It->second.LatticeElements.find(V);
|
||||
+ if (LatticeIt == It->second.LatticeElements.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.
|
||||
@@ -251,23 +223,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() {
|
||||
@@ -277,18 +244,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,
|
||||
@@ -306,10 +262,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
|
||||
@@ -323,10 +280,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) {
|
||||
@@ -336,11 +293,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;
|
|
@ -0,0 +1,42 @@
|
|||
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
|
||||
index 220bc8f9835..4f7ce485777 100644
|
||||
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
|
||||
@@ -1143,7 +1143,7 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
|
||||
// runtime's functionality.
|
||||
if (hasExportSymbolDirective(Args)) {
|
||||
if (ForGCOV) {
|
||||
- addExportedSymbol(CmdArgs, "___gcov_flush");
|
||||
+ addExportedSymbol(CmdArgs, "___custom_llvm_gcov_flush");
|
||||
addExportedSymbol(CmdArgs, "_flush_fn_list");
|
||||
addExportedSymbol(CmdArgs, "_writeout_fn_list");
|
||||
} else {
|
||||
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
|
||||
index 498c05900bf..b7257db10e7 100644
|
||||
--- a/compiler-rt/lib/profile/GCDAProfiling.c
|
||||
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
|
||||
@@ -619,7 +619,7 @@ void llvm_register_flush_function(fn_ptr fn) {
|
||||
fn_list_insert(&flush_fn_list, fn);
|
||||
}
|
||||
|
||||
-void __gcov_flush() {
|
||||
+void __custom_llvm_gcov_flush() {
|
||||
struct fn_node* curr = flush_fn_list.head;
|
||||
|
||||
while (curr) {
|
||||
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 00000000000..e69de29bb2d
|
||||
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
|
||||
index bf3e4ed3e31..37bdcfaeab8 100644
|
||||
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
|
||||
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
|
||||
@@ -656,7 +656,7 @@ void GCOVProfiler::AddFlushBeforeForkAndExec() {
|
||||
for (auto I : ForkAndExecs) {
|
||||
IRBuilder<> Builder(I);
|
||||
FunctionType *FTy = FunctionType::get(Builder.getVoidTy(), {}, false);
|
||||
- FunctionCallee GCOVFlush = M->getOrInsertFunction("__gcov_flush", FTy);
|
||||
+ FunctionCallee GCOVFlush = M->getOrInsertFunction("__custom_llvm_gcov_flush", FTy);
|
||||
Builder.CreateCall(GCOVFlush);
|
||||
I->getParent()->splitBasicBlock(I);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
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.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
|
||||
index 3f3c0cce119..5e324a0a5fd 100644
|
||||
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
|
||||
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
|
||||
@@ -494,14 +494,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 00000000000..700507c1e63
|
||||
--- /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
|
|
@ -0,0 +1,64 @@
|
|||
[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.
|
||||
|
||||
diff --git a/compiler-rt/lib/asan/asan_win.cpp b/compiler-rt/lib/asan/asan_win.cpp
|
||||
index 417892aaedd..5fe86db44f4 100644
|
||||
--- a/compiler-rt/lib/asan/asan_win.cpp
|
||||
+++ b/compiler-rt/lib/asan/asan_win.cpp
|
||||
@@ -154,6 +154,14 @@ INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security,
|
||||
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 @@ void InitializePlatformInterceptors() {
|
||||
|
||||
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 @@ __declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
|
||||
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)
|
||||
-__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
|
||||
- void *, unsigned long, void *) = asan_thread_exit;
|
||||
-
|
||||
WIN_FORCE_LINK(__asan_dso_reg_hook)
|
||||
|
||||
// }}}
|
|
@ -423,6 +423,13 @@ clang-9:
|
|||
repo: https://github.com/llvm/llvm-project
|
||||
revision: c1a0a213378a458fbea1a5c77b315c7dce08fd05
|
||||
|
||||
clang-10:
|
||||
description: clang 10.0.0 source code
|
||||
fetch:
|
||||
type: git
|
||||
repo: https://github.com/llvm/llvm-project
|
||||
revision: d32170dbd5b0d54436537b6b75beaf44324e0c28
|
||||
|
||||
wasi-sdk:
|
||||
description: wasi-sdk source code
|
||||
fetch:
|
||||
|
|
|
@ -228,6 +228,27 @@ linux64-clang-9-win-cross:
|
|||
- artifact: clang.tar.bz2
|
||||
extract: false
|
||||
|
||||
|
||||
linux64-clang-10:
|
||||
description: "Clang 10 toolchain build"
|
||||
treeherder:
|
||||
symbol: TL(clang10)
|
||||
run:
|
||||
using: toolchain-script
|
||||
script: build-clang.sh
|
||||
arguments:
|
||||
- 'build/build-clang/clang-10-linux64.json'
|
||||
resources:
|
||||
- 'build/build-clang/clang-10-linux64.json'
|
||||
toolchain-artifact: public/build/clang.tar.xz
|
||||
fetches:
|
||||
fetch:
|
||||
- clang-10
|
||||
toolchain:
|
||||
- linux64-binutils
|
||||
- linux64-gcc-7
|
||||
- wasi-sysroot
|
||||
|
||||
macosx64-clang:
|
||||
description: "Clang toolchain build"
|
||||
treeherder:
|
||||
|
|
Загрузка…
Ссылка в новой задаче