LLVM 24.0.0git
NVPTXPromoteParamAlign.cpp
Go to the documentation of this file.
1//===-- NVPTXPromoteParamAlign.cpp - Promote .param alignment ------------===//
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// Increase the .param-space alignment of NVPTX arguments and return values so
10// their loads and stores can be vectorized. On every defined function:
11//
12// 1. Give each byval param an explicit ABI `align` for its pointee type
13// (capped at the PTX max). byval already implies this, but the alignment
14// is otherwise invisible to IR alignment analyses.
15// 2. For a local function whose every use is a type-compatible direct call,
16// we control all call sites and raise aggregate/byval param and return
17// alignment to at least 16 (for 128-bit vectorization). This is recorded
18// as `stackalign` and mirrored onto the calls.
19// 3. Propagate the result onto byval loads at a known offset, since `align`
20// and `stackalign` aren't both picked up by IR alignment analyses.
21//
22// (2) runs before (1) so byval `align` still matches between caller and callee
23// while stackalign is mirrored onto the calls.
24//
25//===----------------------------------------------------------------------===//
26
27#include "NVPTX.h"
28#include "NVPTXUtilities.h"
29#include "llvm/ADT/Sequence.h"
30#include "llvm/IR/Attributes.h"
31#include "llvm/IR/Function.h"
33#include "llvm/IR/Module.h"
35#include "llvm/Pass.h"
36#include "llvm/Support/Debug.h"
37#include <optional>
38#include <queue>
39
40#define DEBUG_TYPE "nvptx-promote-param-align"
41
42using namespace llvm;
43
44namespace {
45class NVPTXPromoteParamAlignLegacyPass : public ModulePass {
46 bool runOnModule(Module &M) override;
47
48public:
49 static char ID;
50 NVPTXPromoteParamAlignLegacyPass() : ModulePass(ID) {}
51 StringRef getPassName() const override {
52 return "Promote alignment of parameters and return values (NVPTX)";
53 }
54};
55} // namespace
56
57char NVPTXPromoteParamAlignLegacyPass::ID = 0;
58
59INITIALIZE_PASS(NVPTXPromoteParamAlignLegacyPass, "nvptx-promote-param-align",
60 "Promote alignment of parameters and return values (NVPTX)",
61 false, false)
62
63// Return true if the attributes that determine an NVPTX .param slot's layout
64// match.
65static bool layoutAttrsMatch(AttributeSet CalleeAttrs, AttributeSet CallAttrs) {
66 if (CalleeAttrs.getByValType() != CallAttrs.getByValType() ||
67 CalleeAttrs.getStackAlignment() != CallAttrs.getStackAlignment())
68 return false;
69
70 // `align` only affects the layout for byval parameters.
71 return !CalleeAttrs.getByValType() ||
72 CalleeAttrs.getAlignment() == CallAttrs.getAlignment();
73}
74
75static bool callSiteMatchesCalleeABI(const CallBase &CB, const Function &F) {
76 const AttributeList CalleeAttrs = F.getAttributes();
77 const AttributeList CallAttrs = CB.getAttributes();
78
79 if (!layoutAttrsMatch(CalleeAttrs.getRetAttrs(), CallAttrs.getRetAttrs()))
80 return false;
81
82 return all_of(seq(F.arg_size()), [&](size_t I) {
83 return layoutAttrsMatch(CalleeAttrs.getParamAttrs(I),
84 CallAttrs.getParamAttrs(I));
85 });
86}
87
88// Promotable if the function is local and every use is an ABI-compatible direct
89// call, so we control every call site and can raise alignment on both sides.
91 if (F.isDeclaration() || !F.hasLocalLinkage())
92 return false;
93
94 if (F.hasAddressTaken(/*Users=*/nullptr, /*IgnoreCallbackUses=*/false,
95 /*IgnoreAssumeLikeCalls=*/true,
96 /*IgnoreLLVMUsed=*/true))
97 return false;
98
99 return all_of(F.users(), [&](const User *U) {
100 const auto *CB = dyn_cast<CallBase>(U);
101 if (!CB || CB->getCalledOperand() != &F)
102 return true;
103 return CB->getFunctionType() == F.getFunctionType() &&
104 callSiteMatchesCalleeABI(*CB, F);
105 });
106}
107
108// Raise the alignment of every load reachable from a byval pointer at a known
109// constant offset. Must stay in sync with the param load/store in LowerCall.
110static bool propagateAlignmentToLoads(Value *Val, Align NewAlign,
111 const DataLayout &DL) {
112 struct Load {
113 LoadInst *Inst;
115 };
116
117 struct LoadContext {
118 Value *InitialVal;
120 };
121
122 SmallVector<Load> Loads;
123 std::queue<LoadContext> Worklist;
124 Worklist.push({Val, 0});
125
126 while (!Worklist.empty()) {
127 LoadContext Ctx = Worklist.front();
128 Worklist.pop();
129
130 for (User *CurUser : Ctx.InitialVal->users()) {
131 if (auto *I = dyn_cast<LoadInst>(CurUser))
132 Loads.push_back({I, Ctx.Offset});
133 else if (isa<BitCastInst>(CurUser) || isa<AddrSpaceCastInst>(CurUser))
134 Worklist.push({cast<Instruction>(CurUser), Ctx.Offset});
135 else if (auto *I = dyn_cast<GetElementPtrInst>(CurUser)) {
136 APInt OffsetAccumulated =
137 APInt::getZero(DL.getIndexTypeSizeInBits(I->getType()));
138
139 if (!I->accumulateConstantOffset(DL, OffsetAccumulated))
140 continue;
141
142 uint64_t OffsetLimit = -1;
143 uint64_t Offset = OffsetAccumulated.getLimitedValue(OffsetLimit);
144 assert(Offset != OffsetLimit && "Expect Offset less than UINT64_MAX");
145
146 Worklist.push({I, Ctx.Offset + Offset});
147 }
148 }
149 }
150
151 bool Changed = false;
152 for (Load &CurLoad : Loads) {
153 Align NewLoadAlign = commonAlignment(NewAlign, CurLoad.Offset);
154 if (NewLoadAlign > CurLoad.Inst->getAlign()) {
155 CurLoad.Inst->setAlignment(NewLoadAlign);
156 Changed = true;
157 }
158 }
159 return Changed;
160}
161
162// Bump an alignment up to at least 16 (for 128-bit vectorization), or nullopt
163// if it's already large enough.
165 const Align PromotedAlign = std::max(CurrentAlign, Align(16));
166 if (PromotedAlign > CurrentAlign)
167 return PromotedAlign;
168 return std::nullopt;
169}
170
173 return false;
174
175 LLVMContext &Ctx = F.getContext();
176 const DataLayout &DL = F.getDataLayout();
177
178 // Promoted (arg index, new alignment) pairs, to mirror onto call sites.
180 MaybeAlign PromotedRet;
181
182 // Promote aggregate and byval parameters.
183 for (Argument &Arg : F.args()) {
184 const bool IsByVal = Arg.hasByValAttr();
185 Type *ArgTy = IsByVal ? Arg.getParamByValType() : Arg.getType();
186 if (ArgTy->isEmptyTy() || (!IsByVal && !shouldPassAsArray(ArgTy)))
187 continue;
188
189 // An explicit stackalign already wins at emission time, nothing to promote.
190 if (Arg.getParamStackAlign())
191 continue;
192 const unsigned ArgNo = Arg.getArgNo();
193
194 // `align` only applies to byval (pointer) args, not by-value aggregates.
195 Align CurrentAlign = getPTXParamTypeAlign(ArgTy, DL);
196 if (IsByVal)
197 CurrentAlign = std::max(CurrentAlign, Arg.getParamAlign().valueOrOne());
198 const MaybeAlign PromotedAlign = getPromotedParamAlign(CurrentAlign);
199 if (!PromotedAlign)
200 continue;
201
202 LLVM_DEBUG(dbgs() << "Promoting alignment of " << Arg << " to "
203 << PromotedAlign->value() << '\n');
204 Arg.addAttr(Attribute::getWithStackAlignment(Ctx, *PromotedAlign));
205 PromotedParams.emplace_back(ArgNo, *PromotedAlign);
206 }
207
208 // Promote an aggregate return value.
209 Type *RetTy = F.getReturnType();
210 if (shouldPassAsArray(RetTy) && !RetTy->isEmptyTy() &&
211 !F.getAttributes().getRetStackAlignment()) {
212 const MaybeAlign PromotedAlign =
214 if (PromotedAlign) {
215 F.addRetAttr(Attribute::getWithStackAlignment(Ctx, *PromotedAlign));
216 PromotedRet = *PromotedAlign;
217 }
218 }
219
220 if (PromotedParams.empty() && !PromotedRet)
221 return false;
222
223 // Mirror the promotion onto every direct call site so both sides agree on the
224 // .param layout. canPromoteParamAlign already verified they're
225 // ABI-compatible.
226 for (User *U : F.users()) {
227 auto *CB = dyn_cast<CallBase>(U);
228 if (!CB || CB->getCalledOperand() != &F)
229 continue;
230
231 for (const auto &[ArgNo, PromotedAlign] : PromotedParams)
232 CB->addParamAttr(ArgNo,
233 Attribute::getWithStackAlignment(Ctx, PromotedAlign));
234 if (PromotedRet)
235 CB->addRetAttr(Attribute::getWithStackAlignment(Ctx, *PromotedRet));
236
238 "mirroring must preserve call-site/callee ABI compatibility");
239 }
240
241 return true;
242}
243
244// Spell out each byval parameter's ABI alignment as an explicit `align` (step 1
245// above). Runs after promoteParamAlign, which needs byval `align` to still
246// match between callers and callees.
248 if (F.isDeclaration())
249 return false;
250
251 LLVMContext &Ctx = F.getContext();
252 const DataLayout &DL = F.getDataLayout();
253 bool Changed = false;
254 for (Argument &Arg : F.args()) {
255 if (!Arg.hasByValAttr())
256 continue;
257 Type *ETy = Arg.getParamByValType();
258 if (ETy->isEmptyTy())
259 continue;
260 const Align ABIAlign = getPTXParamTypeAlign(ETy, DL);
261 if (Arg.getParamAlign().valueOrOne() >= ABIAlign)
262 continue;
263 Arg.removeAttr(Attribute::Alignment);
264 Arg.addAttr(Attribute::getWithAlignment(Ctx, ABIAlign));
265 Changed = true;
266 }
267 return Changed;
268}
269
270// Propagate each byval parameter's .param alignment onto its constant-offset
271// loads (step 3 above). Runs after promoteParamAlign so the promoted
272// `stackalign` is included, and on every function so kernels and external
273// functions benefit too.
275 if (F.isDeclaration())
276 return false;
277
278 const DataLayout &DL = F.getDataLayout();
279 bool Changed = false;
280 for (Argument &Arg : F.args()) {
281 if (!Arg.hasByValAttr())
282 continue;
283 Type *ETy = Arg.getParamByValType();
284 if (ETy->isEmptyTy())
285 continue;
286 const unsigned ParamIdx = Arg.getArgNo() + AttributeList::FirstArgIndex;
287 const Align ParamAlign = getDeviceByValParamAlign(&F, ETy, ParamIdx, DL);
288 Changed |= propagateAlignmentToLoads(&Arg, ParamAlign, DL);
289 }
290 return Changed;
291}
292
294 bool Changed = false;
295 for (Function &F : M) {
296 // Order matters (see the file header): promote, normalize `align`, then
297 // propagate to loads.
301 }
302 return Changed;
303}
304
305bool NVPTXPromoteParamAlignLegacyPass::runOnModule(Module &M) {
306 return promoteParamAlignModule(M);
307}
308
310 return new NVPTXPromoteParamAlignLegacyPass();
311}
312
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
This file contains the simple types necessary to represent the attributes associated with functions a...
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
static bool propagateByValParamLoadAlign(Function &F)
static bool promoteParamAlignModule(Module &M)
static bool canPromoteParamAlign(Function &F)
AttributeSet CallAttrs
static bool setByValParamABIAlign(Function &F)
static bool callSiteMatchesCalleeABI(const CallBase &CB, const Function &F)
static bool promoteParamAlign(Function &F)
static MaybeAlign getPromotedParamAlign(Align CurrentAlign)
static bool propagateAlignmentToLoads(Value *Val, Align NewAlign, const DataLayout &DL)
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
Provides some synthesis utilities to produce sequences of values.
#define LLVM_DEBUG(...)
Definition Debug.h:119
Class for arbitrary precision integers.
Definition APInt.h:78
uint64_t getLimitedValue(uint64_t Limit=UINT64_MAX) const
If this value is smaller than the specified limit, return it, otherwise return the limit value.
Definition APInt.h:476
static APInt getZero(unsigned numBits)
Get the '0' value for the specified bit-width.
Definition APInt.h:201
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:407
static LLVM_ABI Attribute getWithStackAlignment(LLVMContext &Context, Align Alignment)
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
AttributeList getAttributes() const
Return the attributes for this call.
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM_ABI bool isEmptyTy() const
Return true if this type is empty, that is, it has no elements or all of its elements are empty.
Definition Type.cpp:180
LLVM Value Representation.
Definition Value.h:75
iterator_range< user_iterator > users()
Definition Value.h:426
Changed
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:573
Align getDeviceByValParamAlign(const Function *F, Type *ArgTy, unsigned AttrIdx, const DataLayout &DL)
The .param-space alignment for a byval parameter or call argument: the (possibly promoted) parameter ...
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Align getPTXParamTypeAlign(Type *ArgTy, const DataLayout &DL)
ABI alignment of ArgTy in .param space, capped at the PTX maximum of 128.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool shouldPassAsArray(Type *Ty)
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
ModulePass * createNVPTXPromoteParamAlignPass()
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
Align commonAlignment(Align A, uint64_t Offset)
Returns the alignment that satisfies both alignments.
Definition Alignment.h:201
auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
Definition Sequence.h:305
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:106
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)