diff options
author | Momchil Velikov <momchil.velikov@arm.com> | 2021-08-05 15:37:19 +0100 |
---|---|---|
committer | Momchil Velikov <momchil.velikov@arm.com> | 2021-08-05 15:54:42 +0100 |
commit | f171149e0d541ca7da7af5fe59bd6d9a77267d24 (patch) | |
tree | f00edd1db0d8e33e7c11895228b389faacfd025a /llvm/lib/Transforms/Utils/SimplifyCFG.cpp | |
parent | fc545c52cdfe1593967598ac9c3645095d5405c6 (diff) | |
download | llvm-f171149e0d541ca7da7af5fe59bd6d9a77267d24.zip llvm-f171149e0d541ca7da7af5fe59bd6d9a77267d24.tar.gz llvm-f171149e0d541ca7da7af5fe59bd6d9a77267d24.tar.bz2 |
[SimpifyCFG] Speculate a store preceded by a local non-escaping load
In SimplifyCFG we may simplify the CFG by speculatively executing
certain stores, when they are preceded by a store to the same
location. This patch allows such speculation also when the stores are
similarly preceded by a load.
In order for this transformation to be correct we need to ensure that
the memory location is writable and the store in the new location does
not introduce a data race.
Local objects (created by an `alloca` instruction) are always
writable, so once we are past a read from a location it is valid to
also write to that same location.
Seeing just a load does not guarantee absence of a data race (unlike
if we see a store) - the load may still be part of a race, just not
causing undefined behaviour
(cf. https://llvm.org/docs/Atomics.html#optimization-outside-atomic).
In the original program, a data race might have been prevented by the
condition, but once we move the store outside the condition, we must
be sure a data race wasn't possible anyway, no matter what the
condition evaluates to.
One way to be sure that a local object is never concurrently
read/written is check that its address never escapes the function.
Hence this transformation is restricted to local, non-escaping
objects.
Reviewed By: nikic, lebedev.ri
Differential Revision: https://reviews.llvm.org/D107281
Diffstat (limited to 'llvm/lib/Transforms/Utils/SimplifyCFG.cpp')
-rw-r--r-- | llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 0e39284..a72af2b 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -25,6 +25,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/GuardUtils.h" @@ -2250,6 +2251,23 @@ static Value *isSafeToSpeculateStore(Instruction *I, BasicBlock *BrBB, return SI->getValueOperand(); return nullptr; // Unknown store. } + + if (auto *LI = dyn_cast<LoadInst>(&CurI)) { + if (LI->getPointerOperand() == StorePtr && LI->getType() == StoreTy && + LI->isSimple()) { + // Local objects (created by an `alloca` instruction) are always + // writable, so once we are past a read from a location it is valid to + // also write to that same location. + // If the address of the local object never escapes the function, that + // means it's never concurrently read or written, hence moving the store + // from under the condition will not introduce a data race. + auto *AI = dyn_cast<AllocaInst>(getUnderlyingObject(StorePtr)); + if (AI && !PointerMayBeCaptured(AI, false, true)) + // Found a previous load, return it. + return LI; + } + // The load didn't work out, but we may still find a store. + } } return nullptr; |