LLVM 24.0.0git
X86FenceNonTemporalStores.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10///
11/// This pass performs forward dataflow analysis to insert `llvm.x86.sse.sfence`
12/// intrinsics between non-temporal stores and subsequent potential
13/// synchronization points (such as atomic operations, function calls, exception
14/// unwinding, and function returns), guaranteeing that weakly-ordered
15/// non-temporal stores are serialized before synchronization, while still
16/// allowing the performance benefits of non-temporal stores in a loop.
17///
18/// Non-temporal store instructions in LLVM IR (!nontemporal metadata) are
19/// documented as optimization hints. However, on X86, regular stores follow the
20/// TSO (Total Store Order) memory model, whereas non-temporal stores (MOVNT*)
21/// write to weakly-ordered write-combining (WC) memory buffers that bypass
22/// cache hierarchies.
23///
24/// Without explicit memory fences, write-combining stores are not ordered with
25/// respect to other WC stores or normal WB stores. As a result, cross-thread
26/// synchronization that follows a non-temporal store may allow other threads to
27/// observe out-of-order writes, unless an SFENCE is executed.
28///
29//===----------------------------------------------------------------------===//
30
31#include "X86.h"
32#include "X86Subtarget.h"
33#include "X86TargetMachine.h"
34#include "llvm/ADT/DenseMap.h"
35#include "llvm/ADT/STLExtras.h"
39#include "llvm/IR/BasicBlock.h"
40#include "llvm/IR/CFG.h"
41#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/IntrinsicsX86.h"
46#include "llvm/IR/PassManager.h"
47#include "llvm/Pass.h"
48#include "llvm/Support/Debug.h"
49
50using namespace llvm;
51
52#define DEBUG_TYPE "x86-fence-nontemporal-stores"
53
54static bool isNonTemporalStore(const Instruction &I) {
55 return I.hasMetadata(LLVMContext::MD_nontemporal) && I.mayWriteToMemory();
56}
57
58/// Returns true if I is a hardware fence that serializes write-combining
59/// stores:
60/// - SFENCE (llvm.x86.sse.sfence) flushes and serializes WC stores.
61/// - MFENCE (llvm.x86.sse2.mfence) serializes all memory operations.
62///
63/// Note: Atomic fences (such as `fence seq_cst` or `fence release`) do NOT
64/// necessarily serialize write-combining stores.
65static bool isNonTemporalFence(const Instruction &I) {
66 if (const auto *CB = dyn_cast<CallBase>(&I)) {
67 if (auto Intrin = CB->getIntrinsicID()) {
68 return Intrin == Intrinsic::x86_sse_sfence ||
69 Intrin == Intrinsic::x86_sse2_mfence;
70 }
71 }
72 return false;
73}
74
75/// Returns true if I is a potential cross-thread synchronization point, or
76/// which might return to a caller (either via return or throwing).
77///
78/// InvokeInst and CleanupRetInst are handled as a special case, because
79/// I.mayThrow() may return false, yet return true for their CatchSwitchInst
80/// unwind destination, which cannot have non-PHI instructions preceeding it.
81static bool isSyncOrReturnPoint(const Instruction &I) {
83 I.maySynchronize() || I.mayThrow();
84}
85
87 for (const BasicBlock &BB : F) {
88 for (const Instruction &I : BB) {
90 return true;
91 }
92 }
93 return false;
94}
95
96static bool runImpl(Function &F, const X86Subtarget *ST) {
97 // If the target lacks SSE1, non-temporal stores cannot use MOVNT instructions
98 // (they lower to regular TSO stores), and SFENCE is not supported by
99 // hardware.
100 if (ST && !ST->hasSSE1())
101 return false;
102
103 // Fast path: if the function contains no non-temporal stores, do nothing.
105 return false;
106
107 // Compute the "dirty" (currently outstanding unfenced non-temporal stores)
108 // state at entry of each basic block. Note that this is conservative: if a
109 // block has multiple predecessors, we will insert a sfence if _any_ of the
110 // predecessors had a nontemporal store, instead of splitting the critical
111 // edge.
112 //
113 // The Flags bitfield keeps track of the state:
114 // - If the block contains no state-altering instructions, Passthrough = true,
115 // and we'll then propagate DirtyOut based on DirtyIn
116 // - If the last state-altering instruction is an NT store, DirtyOut = true
117 // - If the last state-altering instruction is a fence or sync point (before
118 // which a fence would be added), DirtyOut = false.
119 enum BlockFlags {
120 Passthrough = 1,
121 DirtyIn = 2,
122 DirtyOut = 4,
123 };
125 // Forward dataflow analysis to compute DirtyIn for all basic blocks.
127
128 for (const BasicBlock &BB : F) {
129 int Flag = Passthrough;
130 for (const Instruction &I : BB) {
131 if (isNonTemporalStore(I)) {
132 Flag = DirtyOut;
133 } else if (isNonTemporalFence(I) || isSyncOrReturnPoint(I)) {
134 Flag = 0;
135 }
136 }
137 Flags[&BB] = Flag;
138
139 if ((Flag & DirtyOut)) {
140 for (const BasicBlock *Succ : successors(&BB)) {
141 Worklist.insert(Succ);
142 }
143 }
144 }
145
146 // Iterate the worklist and update DirtyIn/DirtyOut as required, adding
147 // successors to worklist whenever DirtyOut is modified.
148 while (!Worklist.empty()) {
149 const BasicBlock *BB = Worklist.pop_back_val();
150
151 bool NewDirtyIn = false;
152 for (const BasicBlock *Pred : predecessors(BB)) {
153 if (Flags[Pred] & DirtyOut) {
154 NewDirtyIn = true;
155 break;
156 }
157 }
158 if (NewDirtyIn && !(Flags[BB] & DirtyIn)) {
159 Flags[BB] |= DirtyIn;
160 if (Flags[BB] & Passthrough) {
161 Flags[BB] |= DirtyOut;
162 for (const BasicBlock *Succ : successors(BB))
163 Worklist.insert(Succ);
164 }
165 }
166 }
167
168 // Finally, walk each block and insert sfence before any sync point reached in
169 // a dirty state.
170 bool Changed = false;
171 IRBuilder<> Builder(*F.getParent());
172
173 for (BasicBlock &BB : F) {
174 bool CurrentDirty = Flags[&BB] & DirtyIn;
176 if (isNonTemporalStore(I)) {
177 CurrentDirty = true;
178 } else if (isNonTemporalFence(I)) {
179 CurrentDirty = false;
180 } else if (isSyncOrReturnPoint(I)) {
181 if (CurrentDirty) {
182 Builder.SetInsertPoint(&I);
183 Builder.SetCurrentDebugLocation(I.getDebugLoc());
184 Builder.CreateIntrinsic(Intrinsic::x86_sse_sfence, {});
185 CurrentDirty = false;
186 Changed = true;
187 }
188 }
189 }
190 }
191
192 return Changed;
193}
194
195namespace {
196
197class X86FenceNonTemporalStoresLegacy : public FunctionPass {
198public:
199 static char ID;
200
201 X86FenceNonTemporalStoresLegacy() : FunctionPass(ID) {}
202
203 bool runOnFunction(Function &F) override {
204 auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
205 const X86Subtarget *ST =
206 TPC ? &TPC->getTM<X86TargetMachine>().getSubtarget<X86Subtarget>(F)
207 : nullptr;
208 return runImpl(F, ST);
209 }
210
211 void getAnalysisUsage(AnalysisUsage &AU) const override {
212 AU.setPreservesCFG();
213 }
214
215 StringRef getPassName() const override {
216 return "X86 Fence Non-Temporal Stores";
217 }
218};
219
220} // end anonymous namespace
221
222char X86FenceNonTemporalStoresLegacy::ID = 0;
223
224INITIALIZE_PASS(X86FenceNonTemporalStoresLegacy, DEBUG_TYPE,
225 "X86 Fence Non-Temporal Stores", false, false)
226
228 return new X86FenceNonTemporalStoresLegacy();
229}
230
233 const X86Subtarget *ST = TM ? TM->getSubtargetImpl(F) : nullptr;
234 if (!runImpl(F, ST))
235 return PreservedAnalyses::all();
236
239 return PA;
240}
static bool runImpl(MachineFunction &MF)
Definition CFIFixup.cpp:304
This file defines the DenseMap class.
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
FunctionAnalysisManager FAM
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallPtrSet class.
This file defines the SmallVector class.
Target-Independent Code Generator Pass Configuration Options pass.
static bool runImpl(Function &F, const X86Subtarget *ST)
static bool isNonTemporalFence(const Instruction &I)
Returns true if I is a hardware fence that serializes write-combining stores:
static bool isNonTemporalStore(const Instruction &I)
static bool containsNonTemporalStores(Function &F)
static bool isSyncOrReturnPoint(const Instruction &I)
Returns true if I is a potential cross-thread synchronization point, or which might return to a calle...
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
LLVM Basic Block Representation.
Definition BasicBlock.h:62
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2909
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:157
value_type pop_back_val()
Definition SetVector.h:285
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
FunctionPass * createX86FenceNonTemporalStoresLegacyPass()
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
auto predecessors(const MachineBasicBlock *BB)
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.