commit 5cafc16c0e5af59f95f685025b487b306c10bd01 Author: Georg Koppen gk@torproject.org Date: Fri Dec 6 13:43:24 2019 +0000
Bug 32053: Fix LLVM reproducibility issues
This patch contains two backports for reproducibility issues found and fixed in LLVM's optimization code.
The patch from bug 42574 might not be necessary in our context as I only witnessed problems when compiling Stylo with an older Rust (1.32.0) and then omptimizing with LLVM 8. However, it's hard to exclude any mismatching builds we encountered so far from being affected by that issue, so I think it is safer to backport that patch as well.
Bug 43909 is an issue we and Mozilla definitely hit and very likely responsible for at least the vast majority of different builds we got over the course of the last weeks and months. Many thanks to Alex Crichton for the invaluable help in tracking this issue down. --- projects/clang/42574.patch | 236 ++++++++++++++++++++++++++++++++++++++++++ projects/clang/43909.patch | 252 +++++++++++++++++++++++++++++++++++++++++++++ projects/clang/build | 4 + projects/clang/config | 2 + projects/rust/42574.patch | 236 ++++++++++++++++++++++++++++++++++++++++++ projects/rust/43909.patch | 252 +++++++++++++++++++++++++++++++++++++++++++++ projects/rust/build | 7 ++ projects/rust/config | 2 + 8 files changed, 991 insertions(+)
diff --git a/projects/clang/42574.patch b/projects/clang/42574.patch new file mode 100644 index 0000000..285d254 --- /dev/null +++ b/projects/clang/42574.patch @@ -0,0 +1,236 @@ +From 3757213db371dcea53cae357cf9c56d1b0604f98 Mon Sep 17 00:00:00 2001 +From: Alina Sbirlea asbirlea@google.com +Date: Fri, 12 Jul 2019 22:30:30 +0000 +Subject: [PATCH] [MemorySSA] Use SetVector to avoid nondeterminism. + +Summary: +Use a SetVector for DeadBlockSet. +Resolves PR42574. + +Reviewers: george.burgess.iv, uabelho, dblaikie + +Subscribers: jlebar, Prazek, mgrang, llvm-commits + +Tags: #llvm + +Differential Revision: https://reviews.llvm.org/D64601 + +llvm-svn: 365970 + +diff --git a/llvm/include/llvm/Analysis/MemorySSAUpdater.h b/llvm/include/llvm/Analysis/MemorySSAUpdater.h +index 169d5bd9fa8..276620bd445 100644 +--- a/llvm/include/llvm/Analysis/MemorySSAUpdater.h ++++ b/llvm/include/llvm/Analysis/MemorySSAUpdater.h +@@ -32,6 +32,7 @@ + #ifndef LLVM_ANALYSIS_MEMORYSSAUPDATER_H + #define LLVM_ANALYSIS_MEMORYSSAUPDATER_H + ++#include "llvm/ADT/SetVector.h" + #include "llvm/ADT/SmallPtrSet.h" + #include "llvm/ADT/SmallSet.h" + #include "llvm/ADT/SmallVector.h" +@@ -239,7 +240,7 @@ public: + /// Deleted blocks still have successor info, but their predecessor edges and + /// Phi nodes may already be updated. Instructions in DeadBlocks should be + /// deleted after this call. +- void removeBlocks(const SmallPtrSetImpl<BasicBlock *> &DeadBlocks); ++ void removeBlocks(const SmallSetVector<BasicBlock *, 8> &DeadBlocks); + + /// Get handle on MemorySSA. + MemorySSA* getMemorySSA() const { return MSSA; } +diff --git a/llvm/lib/Analysis/MemorySSAUpdater.cpp b/llvm/lib/Analysis/MemorySSAUpdater.cpp +index 6c817d20368..a6c7142a697 100644 +--- a/llvm/lib/Analysis/MemorySSAUpdater.cpp ++++ b/llvm/lib/Analysis/MemorySSAUpdater.cpp +@@ -1101,7 +1101,7 @@ void MemorySSAUpdater::removeMemoryAccess(MemoryAccess *MA) { + } + + void MemorySSAUpdater::removeBlocks( +- const SmallPtrSetImpl<BasicBlock *> &DeadBlocks) { ++ const SmallSetVector<BasicBlock *, 8> &DeadBlocks) { + // First delete all uses of BB in MemoryPhis. + for (BasicBlock *BB : DeadBlocks) { + Instruction *TI = BB->getTerminator(); +diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp +index 2e5927f9a06..f464df26a02 100644 +--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp ++++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp +@@ -388,8 +388,8 @@ private: + void deleteDeadLoopBlocks() { + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); + if (MSSAU) { +- SmallPtrSet<BasicBlock *, 8> DeadLoopBlocksSet(DeadLoopBlocks.begin(), +- DeadLoopBlocks.end()); ++ SmallSetVector<BasicBlock *, 8> DeadLoopBlocksSet(DeadLoopBlocks.begin(), ++ DeadLoopBlocks.end()); + MSSAU->removeBlocks(DeadLoopBlocksSet); + } + for (auto *BB : DeadLoopBlocks) { +diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp +index 5a67178cef3..814cf814989 100644 +--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp ++++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp +@@ -1436,8 +1436,8 @@ deleteDeadClonedBlocks(Loop &L, ArrayRef<BasicBlock *> ExitBlocks, + + // Remove all MemorySSA in the dead blocks + if (MSSAU) { +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet(DeadBlocks.begin(), +- DeadBlocks.end()); ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet(DeadBlocks.begin(), ++ DeadBlocks.end()); + MSSAU->removeBlocks(DeadBlockSet); + } + +@@ -1455,7 +1455,7 @@ static void deleteDeadBlocksFromLoop(Loop &L, + MemorySSAUpdater *MSSAU) { + // Find all the dead blocks tied to this loop, and remove them from their + // successors. +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet; ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet; + + // Start with loop/exit blocks and get a transitive closure of reachable dead + // blocks. +diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp +index 499e611acb5..83dabcd7952 100644 +--- a/llvm/lib/Transforms/Utils/Local.cpp ++++ b/llvm/lib/Transforms/Utils/Local.cpp +@@ -2211,7 +2211,7 @@ bool llvm::removeUnreachableBlocks(Function &F, LazyValueInfo *LVI, + assert(Reachable.size() < F.size()); + NumRemoved += F.size()-Reachable.size(); + +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet; ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet; + for (Function::iterator I = ++F.begin(), E = F.end(); I != E; ++I) { + auto *BB = &*I; + if (Reachable.count(BB)) +diff --git a/llvm/test/Analysis/MemorySSA/nondeterminism.ll b/llvm/test/Analysis/MemorySSA/nondeterminism.ll +new file mode 100644 +index 00000000000..0bb3df30b58 +--- /dev/null ++++ b/llvm/test/Analysis/MemorySSA/nondeterminism.ll +@@ -0,0 +1,122 @@ ++; RUN: opt -simplifycfg -enable-mssa-loop-dependency -S --preserve-ll-uselistorder %s | FileCheck %s ++; REQUIRES: x86-registered-target ++; CHECK-LABEL: @n ++; CHECK: uselistorder i16 0, { 3, 2, 4, 1, 5, 0, 6 } ++ ++; Note: test was added in an effort to ensure determinism when updating memoryssa. See PR42574. ++; If the uselistorder check becomes no longer relevant, the test can be disabled or removed. ++ ++%rec9 = type { i16, i32, i32 } ++ ++@a = global [1 x [1 x %rec9]] zeroinitializer ++ ++define i16 @n() { ++ br label %..split_crit_edge ++ ++..split_crit_edge: ; preds = %0 ++ br label %.split ++ ++bb4.us4: ; preds = %bb2.split.us32, %bb6.us28 ++ %i.4.01.us5 = phi i16 [ %_tmp49.us30, %bb6.us28 ] ++ br label %g.exit4.us21 ++ ++bb1.i.us14: ; preds = %bb4.us4 ++ br label %g.exit4.us21 ++ ++g.exit4.us21: ; preds = %bb1.i.us14, %g.exit4.critedge.us9 ++ %i.4.02.us22 = phi i16 [ %i.4.01.us5, %bb4.us4 ], [ %i.4.01.us5, %bb1.i.us14 ] ++ br label %bb6.us28 ++ ++bb5.us26: ; preds = %g.exit4.us21 ++ br label %bb6.us28 ++ ++bb6.us28: ; preds = %bb5.us26, %g.exit4.us21 ++ %i.4.03.us29 = phi i16 [ %i.4.02.us22, %bb5.us26 ], [ %i.4.02.us22, %g.exit4.us21 ] ++ %_tmp49.us30 = add nuw nsw i16 %i.4.03.us29, 1 ++ br label %bb4.us4 ++ ++bb4.us.us: ; preds = %bb2.split.us.us, %bb6.us.us ++ %i.4.01.us.us = phi i16 [ %_tmp49.us.us, %bb6.us.us ] ++ br label %bb1.i.us.us ++ ++bb1.i.us.us: ; preds = %bb4.us.us ++ br label %g.exit4.us.us ++ ++g.exit4.us.us: ; preds = %bb1.i.us.us, %g.exit4.critedge.us.us ++ %i.4.02.us.us = phi i16 [ %i.4.01.us.us, %bb1.i.us.us ] ++ br label %bb5.us.us ++ ++bb5.us.us: ; preds = %g.exit4.us.us ++ br label %bb6.us.us ++ ++bb6.us.us: ; preds = %bb5.us.us, %g.exit4.us.us ++ %i.4.03.us.us = phi i16 [ %i.4.02.us.us, %bb5.us.us ] ++ %_tmp49.us.us = add nuw nsw i16 %i.4.03.us.us, 1 ++ br label %bb4.us.us ++ ++ ++.split: ; preds = %..split_crit_edge ++ br label %bb2 ++ ++bb2: ; preds = %.split, %bb7 ++ %h.3.0 = phi i16 [ undef, %.split ], [ %_tmp53, %bb7 ] ++ br label %bb2.bb2.split_crit_edge ++ ++bb2.bb2.split_crit_edge: ; preds = %bb2 ++ br label %bb2.split ++ ++bb2.split.us: ; preds = %bb2 ++ br label %bb4.us ++ ++bb4.us: ; preds = %bb6.us, %bb2.split.us ++ %i.4.01.us = phi i16 [ 0, %bb2.split.us ] ++ br label %bb1.i.us ++ ++g.exit4.critedge.us: ; preds = %bb4.us ++ br label %g.exit4.us ++ ++bb1.i.us: ; preds = %bb4.us ++ br label %g.exit4.us ++ ++g.exit4.us: ; preds = %bb1.i.us, %g.exit4.critedge.us ++ %i.4.02.us = phi i16 [ %i.4.01.us, %g.exit4.critedge.us ], [ %i.4.01.us, %bb1.i.us ] ++ br label %bb5.us ++ ++bb5.us: ; preds = %g.exit4.us ++ br label %bb7 ++ ++bb2.split: ; preds = %bb2.bb2.split_crit_edge ++ br label %bb4 ++ ++bb4: ; preds = %bb2.split, %bb6 ++ %i.4.01 = phi i16 [ 0, %bb2.split ] ++ %_tmp16 = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.01, i32 0 ++ %_tmp17 = load i16, i16* %_tmp16, align 1 ++ br label %g.exit4.critedge ++ ++bb1.i: ; preds = %bb4 ++ br label %g.exit4 ++ ++g.exit4.critedge: ; preds = %bb4 ++ %_tmp28.c = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.01, i32 1 ++ %_tmp29.c = load i32, i32* %_tmp28.c, align 1 ++ %_tmp30.c = trunc i32 %_tmp29.c to i16 ++ br label %g.exit4 ++ ++g.exit4: ; preds = %g.exit4.critedge, %bb1.i ++ %i.4.02 = phi i16 [ %i.4.01, %g.exit4.critedge ], [ %i.4.01, %bb1.i ] ++ %_tmp41 = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.02, i32 2 ++ br label %bb6 ++ ++bb5: ; preds = %g.exit4 ++ br label %bb6 ++ ++bb6: ; preds = %bb5, %g.exit4 ++ %i.4.03 = phi i16 [ %i.4.02, %bb5 ], [ %i.4.02, %g.exit4 ] ++ %_tmp49 = add nuw nsw i16 %i.4.03, 1 ++ br label %bb7 ++ ++bb7: ; preds = %bb7.us-lcssa.us, %bb7.us-lcssa ++ %_tmp53 = add nsw i16 %h.3.0, 1 ++ br label %bb2 ++} +-- +2.24.0 + diff --git a/projects/clang/43909.patch b/projects/clang/43909.patch new file mode 100644 index 0000000..78d2a75 --- /dev/null +++ b/projects/clang/43909.patch @@ -0,0 +1,252 @@ +From c95310f2d4fd3c88241c3b5d6dbf6251d34a3256 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] 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 110c085d3f3..aa6862cb588 100644 +--- a/llvm/lib/Analysis/LazyValueInfo.cpp ++++ b/llvm/lib/Analysis/LazyValueInfo.cpp +@@ -133,12 +133,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; +@@ -152,89 +149,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. +@@ -248,23 +219,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() { +@@ -274,18 +240,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, +@@ -303,10 +258,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 +@@ -320,10 +276,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) { +@@ -333,11 +289,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 + diff --git a/projects/clang/build b/projects/clang/build index c1afa6d..825b21e 100644 --- a/projects/clang/build +++ b/projects/clang/build @@ -20,6 +20,10 @@ tar -xf $rootdir/[% c('input_files_by_name/libcxxabi') %] tar -xf $rootdir/[% c('input_files_by_name/lld') %] tar -xf $rootdir/[% c('input_files_by_name/compiler-rt') %] mv llvm-* llvm +# LLVM has reproducibility issues when optimizing bitcode, which we need to +# patch. See: #32053 for more details. +patch -p1 < $rootdir/42574.patch +patch -p1 < $rootdir/43909.patch mv cfe-* llvm/tools/clang mv libcxx-* llvm/projects/libcxx mv libcxxabi-* llvm/projects/libcxxabi diff --git a/projects/clang/config b/projects/clang/config index 67798f8..cf4dcb0 100644 --- a/projects/clang/config +++ b/projects/clang/config @@ -45,3 +45,5 @@ input_files: enable: '[% c("var/windows") %]' - filename: timestamp.patch enable: '[% c("var/windows") %]' + - filename: 42574.patch + - filename: 43909.patch diff --git a/projects/rust/42574.patch b/projects/rust/42574.patch new file mode 100644 index 0000000..285d254 --- /dev/null +++ b/projects/rust/42574.patch @@ -0,0 +1,236 @@ +From 3757213db371dcea53cae357cf9c56d1b0604f98 Mon Sep 17 00:00:00 2001 +From: Alina Sbirlea asbirlea@google.com +Date: Fri, 12 Jul 2019 22:30:30 +0000 +Subject: [PATCH] [MemorySSA] Use SetVector to avoid nondeterminism. + +Summary: +Use a SetVector for DeadBlockSet. +Resolves PR42574. + +Reviewers: george.burgess.iv, uabelho, dblaikie + +Subscribers: jlebar, Prazek, mgrang, llvm-commits + +Tags: #llvm + +Differential Revision: https://reviews.llvm.org/D64601 + +llvm-svn: 365970 + +diff --git a/llvm/include/llvm/Analysis/MemorySSAUpdater.h b/llvm/include/llvm/Analysis/MemorySSAUpdater.h +index 169d5bd9fa8..276620bd445 100644 +--- a/llvm/include/llvm/Analysis/MemorySSAUpdater.h ++++ b/llvm/include/llvm/Analysis/MemorySSAUpdater.h +@@ -32,6 +32,7 @@ + #ifndef LLVM_ANALYSIS_MEMORYSSAUPDATER_H + #define LLVM_ANALYSIS_MEMORYSSAUPDATER_H + ++#include "llvm/ADT/SetVector.h" + #include "llvm/ADT/SmallPtrSet.h" + #include "llvm/ADT/SmallSet.h" + #include "llvm/ADT/SmallVector.h" +@@ -239,7 +240,7 @@ public: + /// Deleted blocks still have successor info, but their predecessor edges and + /// Phi nodes may already be updated. Instructions in DeadBlocks should be + /// deleted after this call. +- void removeBlocks(const SmallPtrSetImpl<BasicBlock *> &DeadBlocks); ++ void removeBlocks(const SmallSetVector<BasicBlock *, 8> &DeadBlocks); + + /// Get handle on MemorySSA. + MemorySSA* getMemorySSA() const { return MSSA; } +diff --git a/llvm/lib/Analysis/MemorySSAUpdater.cpp b/llvm/lib/Analysis/MemorySSAUpdater.cpp +index 6c817d20368..a6c7142a697 100644 +--- a/llvm/lib/Analysis/MemorySSAUpdater.cpp ++++ b/llvm/lib/Analysis/MemorySSAUpdater.cpp +@@ -1101,7 +1101,7 @@ void MemorySSAUpdater::removeMemoryAccess(MemoryAccess *MA) { + } + + void MemorySSAUpdater::removeBlocks( +- const SmallPtrSetImpl<BasicBlock *> &DeadBlocks) { ++ const SmallSetVector<BasicBlock *, 8> &DeadBlocks) { + // First delete all uses of BB in MemoryPhis. + for (BasicBlock *BB : DeadBlocks) { + Instruction *TI = BB->getTerminator(); +diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp +index 2e5927f9a06..f464df26a02 100644 +--- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp ++++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp +@@ -388,8 +388,8 @@ private: + void deleteDeadLoopBlocks() { + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); + if (MSSAU) { +- SmallPtrSet<BasicBlock *, 8> DeadLoopBlocksSet(DeadLoopBlocks.begin(), +- DeadLoopBlocks.end()); ++ SmallSetVector<BasicBlock *, 8> DeadLoopBlocksSet(DeadLoopBlocks.begin(), ++ DeadLoopBlocks.end()); + MSSAU->removeBlocks(DeadLoopBlocksSet); + } + for (auto *BB : DeadLoopBlocks) { +diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp +index 5a67178cef3..814cf814989 100644 +--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp ++++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp +@@ -1436,8 +1436,8 @@ deleteDeadClonedBlocks(Loop &L, ArrayRef<BasicBlock *> ExitBlocks, + + // Remove all MemorySSA in the dead blocks + if (MSSAU) { +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet(DeadBlocks.begin(), +- DeadBlocks.end()); ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet(DeadBlocks.begin(), ++ DeadBlocks.end()); + MSSAU->removeBlocks(DeadBlockSet); + } + +@@ -1455,7 +1455,7 @@ static void deleteDeadBlocksFromLoop(Loop &L, + MemorySSAUpdater *MSSAU) { + // Find all the dead blocks tied to this loop, and remove them from their + // successors. +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet; ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet; + + // Start with loop/exit blocks and get a transitive closure of reachable dead + // blocks. +diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp +index 499e611acb5..83dabcd7952 100644 +--- a/llvm/lib/Transforms/Utils/Local.cpp ++++ b/llvm/lib/Transforms/Utils/Local.cpp +@@ -2211,7 +2211,7 @@ bool llvm::removeUnreachableBlocks(Function &F, LazyValueInfo *LVI, + assert(Reachable.size() < F.size()); + NumRemoved += F.size()-Reachable.size(); + +- SmallPtrSet<BasicBlock *, 16> DeadBlockSet; ++ SmallSetVector<BasicBlock *, 8> DeadBlockSet; + for (Function::iterator I = ++F.begin(), E = F.end(); I != E; ++I) { + auto *BB = &*I; + if (Reachable.count(BB)) +diff --git a/llvm/test/Analysis/MemorySSA/nondeterminism.ll b/llvm/test/Analysis/MemorySSA/nondeterminism.ll +new file mode 100644 +index 00000000000..0bb3df30b58 +--- /dev/null ++++ b/llvm/test/Analysis/MemorySSA/nondeterminism.ll +@@ -0,0 +1,122 @@ ++; RUN: opt -simplifycfg -enable-mssa-loop-dependency -S --preserve-ll-uselistorder %s | FileCheck %s ++; REQUIRES: x86-registered-target ++; CHECK-LABEL: @n ++; CHECK: uselistorder i16 0, { 3, 2, 4, 1, 5, 0, 6 } ++ ++; Note: test was added in an effort to ensure determinism when updating memoryssa. See PR42574. ++; If the uselistorder check becomes no longer relevant, the test can be disabled or removed. ++ ++%rec9 = type { i16, i32, i32 } ++ ++@a = global [1 x [1 x %rec9]] zeroinitializer ++ ++define i16 @n() { ++ br label %..split_crit_edge ++ ++..split_crit_edge: ; preds = %0 ++ br label %.split ++ ++bb4.us4: ; preds = %bb2.split.us32, %bb6.us28 ++ %i.4.01.us5 = phi i16 [ %_tmp49.us30, %bb6.us28 ] ++ br label %g.exit4.us21 ++ ++bb1.i.us14: ; preds = %bb4.us4 ++ br label %g.exit4.us21 ++ ++g.exit4.us21: ; preds = %bb1.i.us14, %g.exit4.critedge.us9 ++ %i.4.02.us22 = phi i16 [ %i.4.01.us5, %bb4.us4 ], [ %i.4.01.us5, %bb1.i.us14 ] ++ br label %bb6.us28 ++ ++bb5.us26: ; preds = %g.exit4.us21 ++ br label %bb6.us28 ++ ++bb6.us28: ; preds = %bb5.us26, %g.exit4.us21 ++ %i.4.03.us29 = phi i16 [ %i.4.02.us22, %bb5.us26 ], [ %i.4.02.us22, %g.exit4.us21 ] ++ %_tmp49.us30 = add nuw nsw i16 %i.4.03.us29, 1 ++ br label %bb4.us4 ++ ++bb4.us.us: ; preds = %bb2.split.us.us, %bb6.us.us ++ %i.4.01.us.us = phi i16 [ %_tmp49.us.us, %bb6.us.us ] ++ br label %bb1.i.us.us ++ ++bb1.i.us.us: ; preds = %bb4.us.us ++ br label %g.exit4.us.us ++ ++g.exit4.us.us: ; preds = %bb1.i.us.us, %g.exit4.critedge.us.us ++ %i.4.02.us.us = phi i16 [ %i.4.01.us.us, %bb1.i.us.us ] ++ br label %bb5.us.us ++ ++bb5.us.us: ; preds = %g.exit4.us.us ++ br label %bb6.us.us ++ ++bb6.us.us: ; preds = %bb5.us.us, %g.exit4.us.us ++ %i.4.03.us.us = phi i16 [ %i.4.02.us.us, %bb5.us.us ] ++ %_tmp49.us.us = add nuw nsw i16 %i.4.03.us.us, 1 ++ br label %bb4.us.us ++ ++ ++.split: ; preds = %..split_crit_edge ++ br label %bb2 ++ ++bb2: ; preds = %.split, %bb7 ++ %h.3.0 = phi i16 [ undef, %.split ], [ %_tmp53, %bb7 ] ++ br label %bb2.bb2.split_crit_edge ++ ++bb2.bb2.split_crit_edge: ; preds = %bb2 ++ br label %bb2.split ++ ++bb2.split.us: ; preds = %bb2 ++ br label %bb4.us ++ ++bb4.us: ; preds = %bb6.us, %bb2.split.us ++ %i.4.01.us = phi i16 [ 0, %bb2.split.us ] ++ br label %bb1.i.us ++ ++g.exit4.critedge.us: ; preds = %bb4.us ++ br label %g.exit4.us ++ ++bb1.i.us: ; preds = %bb4.us ++ br label %g.exit4.us ++ ++g.exit4.us: ; preds = %bb1.i.us, %g.exit4.critedge.us ++ %i.4.02.us = phi i16 [ %i.4.01.us, %g.exit4.critedge.us ], [ %i.4.01.us, %bb1.i.us ] ++ br label %bb5.us ++ ++bb5.us: ; preds = %g.exit4.us ++ br label %bb7 ++ ++bb2.split: ; preds = %bb2.bb2.split_crit_edge ++ br label %bb4 ++ ++bb4: ; preds = %bb2.split, %bb6 ++ %i.4.01 = phi i16 [ 0, %bb2.split ] ++ %_tmp16 = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.01, i32 0 ++ %_tmp17 = load i16, i16* %_tmp16, align 1 ++ br label %g.exit4.critedge ++ ++bb1.i: ; preds = %bb4 ++ br label %g.exit4 ++ ++g.exit4.critedge: ; preds = %bb4 ++ %_tmp28.c = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.01, i32 1 ++ %_tmp29.c = load i32, i32* %_tmp28.c, align 1 ++ %_tmp30.c = trunc i32 %_tmp29.c to i16 ++ br label %g.exit4 ++ ++g.exit4: ; preds = %g.exit4.critedge, %bb1.i ++ %i.4.02 = phi i16 [ %i.4.01, %g.exit4.critedge ], [ %i.4.01, %bb1.i ] ++ %_tmp41 = getelementptr [1 x [1 x %rec9]], [1 x [1 x %rec9]]* @a, i16 0, i16 %h.3.0, i16 %i.4.02, i32 2 ++ br label %bb6 ++ ++bb5: ; preds = %g.exit4 ++ br label %bb6 ++ ++bb6: ; preds = %bb5, %g.exit4 ++ %i.4.03 = phi i16 [ %i.4.02, %bb5 ], [ %i.4.02, %g.exit4 ] ++ %_tmp49 = add nuw nsw i16 %i.4.03, 1 ++ br label %bb7 ++ ++bb7: ; preds = %bb7.us-lcssa.us, %bb7.us-lcssa ++ %_tmp53 = add nsw i16 %h.3.0, 1 ++ br label %bb2 ++} +-- +2.24.0 + diff --git a/projects/rust/43909.patch b/projects/rust/43909.patch new file mode 100644 index 0000000..78d2a75 --- /dev/null +++ b/projects/rust/43909.patch @@ -0,0 +1,252 @@ +From c95310f2d4fd3c88241c3b5d6dbf6251d34a3256 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] 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 110c085d3f3..aa6862cb588 100644 +--- a/llvm/lib/Analysis/LazyValueInfo.cpp ++++ b/llvm/lib/Analysis/LazyValueInfo.cpp +@@ -133,12 +133,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; +@@ -152,89 +149,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. +@@ -248,23 +219,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() { +@@ -274,18 +240,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, +@@ -303,10 +258,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 +@@ -320,10 +276,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) { +@@ -333,11 +289,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 + diff --git a/projects/rust/build b/projects/rust/build index 9a6b54a..8b45e3f 100644 --- a/projects/rust/build +++ b/projects/rust/build @@ -51,6 +51,13 @@ mkdir /var/tmp/build tar -C /var/tmp/build -xf [% c('input_files_by_name/rust') %] cd /var/tmp/build/rustc-[% c('version') %]-src
+# LLVM has reproducibility issues when optimizing bitcode, which we need to +# patch. See: #32053 for more details. +cd src/llvm-project +patch -p1 < $rootdir/42574.patch +patch -p1 < $rootdir/43909.patch +cd ../../ + [% IF c("var/windows-i686") %] # Cross-compiling for Windows 32bit is currently not possible without any # patches. The reason for that is libstd expecting DWARF unwinding while most diff --git a/projects/rust/config b/projects/rust/config index 03d7be0..848ccd0 100644 --- a/projects/rust/config +++ b/projects/rust/config @@ -106,3 +106,5 @@ input_files: gpg_keyring: rust.gpg - filename: unwind.patch enable: '[% c("var/windows-i686") %]' + - filename: 42574.patch + - filename: 43909.patch
tbb-commits@lists.torproject.org