LLVM 24.0.0git
NVPTXTargetTransformInfo.cpp
Go to the documentation of this file.
1//===-- NVPTXTargetTransformInfo.cpp - NVPTX specific TTI -----------------===//
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
10#include "NVVMProperties.h"
11#include "llvm/ADT/ArrayRef.h"
12#include "llvm/ADT/DenseMap.h"
13#include "llvm/ADT/STLExtras.h"
20#include "llvm/IR/Constants.h"
22#include "llvm/IR/Intrinsics.h"
23#include "llvm/IR/IntrinsicsNVPTX.h"
24#include "llvm/IR/Value.h"
29#include <optional>
30#include <utility>
31using namespace llvm;
32
33#define DEBUG_TYPE "NVPTXtti"
34
35// Whether the given intrinsic reads threadIdx.x/y/z.
36static bool readsThreadIndex(const IntrinsicInst *II) {
37 switch (II->getIntrinsicID()) {
38 default: return false;
39 case Intrinsic::nvvm_read_ptx_sreg_tid_x:
40 case Intrinsic::nvvm_read_ptx_sreg_tid_y:
41 case Intrinsic::nvvm_read_ptx_sreg_tid_z:
42 return true;
43 }
44}
45
46static bool readsLaneId(const IntrinsicInst *II) {
47 return II->getIntrinsicID() == Intrinsic::nvvm_read_ptx_sreg_laneid;
48}
49
50bool NVPTXTTIImpl::isSourceOfDivergence(const Value *V) const {
51 // Without inter-procedural analysis, we conservatively assume that arguments
52 // to __device__ functions are divergent.
53 if (const Argument *Arg = dyn_cast<Argument>(V))
54 return !isKernelFunction(*Arg->getParent());
55
56 if (const Instruction *I = dyn_cast<Instruction>(V)) {
57 // Without pointer analysis, we conservatively assume values loaded from
58 // generic or local address space are divergent.
59 if (const LoadInst *LI = dyn_cast<LoadInst>(I)) {
60 unsigned AS = LI->getPointerAddressSpace();
61 return AS == ADDRESS_SPACE_GENERIC || AS == ADDRESS_SPACE_LOCAL;
62 }
63 // Atomic instructions may cause divergence. Atomic instructions are
64 // executed sequentially across all threads in a warp. Therefore, an earlier
65 // executed thread may see different memory inputs than a later executed
66 // thread. For example, suppose *a = 0 initially.
67 //
68 // atom.global.add.s32 d, [a], 1
69 //
70 // returns 0 for the first thread that enters the critical region, and 1 for
71 // the second thread.
72 if (I->isAtomic())
73 return true;
74 if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
75 // Instructions that read threadIdx are obviously divergent.
77 return true;
78 }
79 // Conservatively consider the return value of function calls as divergent.
80 // We could analyze callees with bodies more precisely using
81 // inter-procedural analysis.
82 if (isa<CallInst>(I))
83 return true;
84 }
85
86 return false;
87}
88
89// Convert NVVM intrinsics to target-generic LLVM code where possible.
92 // Each NVVM intrinsic we can simplify can be replaced with one of:
93 //
94 // * an LLVM intrinsic,
95 // * an LLVM cast operation,
96 // * an LLVM binary operation, or
97 // * ad-hoc LLVM IR for the particular operation.
98
99 // Some transformations are only valid when the module's
100 // flush-denormals-to-zero (ftz) setting is true/false, whereas other
101 // transformations are valid regardless of the module's ftz setting.
102 enum FtzRequirementTy {
103 FTZ_Any, // Any ftz setting is ok.
104 FTZ_MustBeOn, // Transformation is valid only if ftz is on.
105 FTZ_MustBeOff, // Transformation is valid only if ftz is off.
106 };
107 // Classes of NVVM intrinsics that can't be replaced one-to-one with a
108 // target-generic intrinsic, cast op, or binary op but that we can nonetheless
109 // simplify.
110 enum SpecialCase {
111 SPC_Reciprocal,
112 SCP_FunnelShiftClamp,
113 };
114
115 // SimplifyAction is a poor-man's variant (plus an additional flag) that
116 // represents how to replace an NVVM intrinsic with target-generic LLVM IR.
117 struct SimplifyAction {
118 // Invariant: At most one of these Optionals has a value.
119 std::optional<Intrinsic::ID> IID;
120 std::optional<Instruction::CastOps> CastOp;
121 std::optional<Instruction::BinaryOps> BinaryOp;
122 std::optional<SpecialCase> Special;
123
124 FtzRequirementTy FtzRequirement = FTZ_Any;
125 // Denormal handling is guarded by different attributes depending on the
126 // type (denormal-fp-math vs denormal-fp-math-f32), take note of halfs.
127 bool IsHalfTy = false;
128
129 SimplifyAction() = default;
130
131 SimplifyAction(Intrinsic::ID IID, FtzRequirementTy FtzReq,
132 bool IsHalfTy = false)
133 : IID(IID), FtzRequirement(FtzReq), IsHalfTy(IsHalfTy) {}
134
135 // Cast operations don't have anything to do with FTZ, so we skip that
136 // argument.
137 SimplifyAction(Instruction::CastOps CastOp) : CastOp(CastOp) {}
138
139 SimplifyAction(Instruction::BinaryOps BinaryOp, FtzRequirementTy FtzReq)
140 : BinaryOp(BinaryOp), FtzRequirement(FtzReq) {}
141
142 SimplifyAction(SpecialCase Special, FtzRequirementTy FtzReq)
143 : Special(Special), FtzRequirement(FtzReq) {}
144 };
145
146 // Try to generate a SimplifyAction describing how to replace our
147 // IntrinsicInstr with target-generic LLVM IR.
148 const SimplifyAction Action = [II]() -> SimplifyAction {
149 switch (II->getIntrinsicID()) {
150 // NVVM intrinsics that map directly to LLVM intrinsics.
151 case Intrinsic::nvvm_ceil_d:
152 return {Intrinsic::ceil, FTZ_Any};
153 case Intrinsic::nvvm_ceil_f:
154 return {Intrinsic::ceil, FTZ_MustBeOff};
155 case Intrinsic::nvvm_ceil_ftz_f:
156 return {Intrinsic::ceil, FTZ_MustBeOn};
157 case Intrinsic::nvvm_floor_d:
158 return {Intrinsic::floor, FTZ_Any};
159 case Intrinsic::nvvm_floor_f:
160 return {Intrinsic::floor, FTZ_MustBeOff};
161 case Intrinsic::nvvm_floor_ftz_f:
162 return {Intrinsic::floor, FTZ_MustBeOn};
163 case Intrinsic::nvvm_fma_rn_d:
164 return {Intrinsic::fma, FTZ_Any};
165 case Intrinsic::nvvm_fma_rn_f:
166 return {Intrinsic::fma, FTZ_MustBeOff};
167 case Intrinsic::nvvm_fma_rn_ftz_f:
168 return {Intrinsic::fma, FTZ_MustBeOn};
169 case Intrinsic::nvvm_fma_rn_f16:
170 return {Intrinsic::fma, FTZ_MustBeOff, true};
171 case Intrinsic::nvvm_fma_rn_ftz_f16:
172 return {Intrinsic::fma, FTZ_MustBeOn, true};
173 case Intrinsic::nvvm_fma_rn_f16x2:
174 return {Intrinsic::fma, FTZ_MustBeOff, true};
175 case Intrinsic::nvvm_fma_rn_ftz_f16x2:
176 return {Intrinsic::fma, FTZ_MustBeOn, true};
177 case Intrinsic::nvvm_fma_rn_bf16:
178 return {Intrinsic::fma, FTZ_MustBeOff, true};
179 case Intrinsic::nvvm_fma_rn_bf16x2:
180 return {Intrinsic::fma, FTZ_MustBeOff, true};
181 case Intrinsic::nvvm_fmax_d:
182 return {Intrinsic::maximumnum, FTZ_Any};
183 case Intrinsic::nvvm_fmax_f:
184 return {Intrinsic::maximumnum, FTZ_MustBeOff};
185 case Intrinsic::nvvm_fmax_ftz_f:
186 return {Intrinsic::maximumnum, FTZ_MustBeOn};
187 case Intrinsic::nvvm_fmax_nan_f:
188 return {Intrinsic::maximum, FTZ_MustBeOff};
189 case Intrinsic::nvvm_fmax_ftz_nan_f:
190 return {Intrinsic::maximum, FTZ_MustBeOn};
191 case Intrinsic::nvvm_fmax_f16:
192 return {Intrinsic::maximumnum, FTZ_MustBeOff, true};
193 case Intrinsic::nvvm_fmax_ftz_f16:
194 return {Intrinsic::maximumnum, FTZ_MustBeOn, true};
195 case Intrinsic::nvvm_fmax_f16x2:
196 return {Intrinsic::maximumnum, FTZ_MustBeOff, true};
197 case Intrinsic::nvvm_fmax_ftz_f16x2:
198 return {Intrinsic::maximumnum, FTZ_MustBeOn, true};
199 case Intrinsic::nvvm_fmax_nan_f16:
200 return {Intrinsic::maximum, FTZ_MustBeOff, true};
201 case Intrinsic::nvvm_fmax_ftz_nan_f16:
202 return {Intrinsic::maximum, FTZ_MustBeOn, true};
203 case Intrinsic::nvvm_fmax_nan_f16x2:
204 return {Intrinsic::maximum, FTZ_MustBeOff, true};
205 case Intrinsic::nvvm_fmax_ftz_nan_f16x2:
206 return {Intrinsic::maximum, FTZ_MustBeOn, true};
207 case Intrinsic::nvvm_fmin_d:
208 return {Intrinsic::minimumnum, FTZ_Any};
209 case Intrinsic::nvvm_fmin_f:
210 return {Intrinsic::minimumnum, FTZ_MustBeOff};
211 case Intrinsic::nvvm_fmin_ftz_f:
212 return {Intrinsic::minimumnum, FTZ_MustBeOn};
213 case Intrinsic::nvvm_fmin_nan_f:
214 return {Intrinsic::minimum, FTZ_MustBeOff};
215 case Intrinsic::nvvm_fmin_ftz_nan_f:
216 return {Intrinsic::minimum, FTZ_MustBeOn};
217 case Intrinsic::nvvm_fmin_f16:
218 return {Intrinsic::minimumnum, FTZ_MustBeOff, true};
219 case Intrinsic::nvvm_fmin_ftz_f16:
220 return {Intrinsic::minimumnum, FTZ_MustBeOn, true};
221 case Intrinsic::nvvm_fmin_f16x2:
222 return {Intrinsic::minimumnum, FTZ_MustBeOff, true};
223 case Intrinsic::nvvm_fmin_ftz_f16x2:
224 return {Intrinsic::minimumnum, FTZ_MustBeOn, true};
225 case Intrinsic::nvvm_fmin_nan_f16:
226 return {Intrinsic::minimum, FTZ_MustBeOff, true};
227 case Intrinsic::nvvm_fmin_ftz_nan_f16:
228 return {Intrinsic::minimum, FTZ_MustBeOn, true};
229 case Intrinsic::nvvm_fmin_nan_f16x2:
230 return {Intrinsic::minimum, FTZ_MustBeOff, true};
231 case Intrinsic::nvvm_fmin_ftz_nan_f16x2:
232 return {Intrinsic::minimum, FTZ_MustBeOn, true};
233 case Intrinsic::nvvm_sqrt_rn_d:
234 return {Intrinsic::sqrt, FTZ_Any};
235 case Intrinsic::nvvm_sqrt_f:
236 // nvvm_sqrt_f is a special case. For most intrinsics, foo_ftz_f is the
237 // ftz version, and foo_f is the non-ftz version. But nvvm_sqrt_f adopts
238 // the ftz-ness of the surrounding code. sqrt_rn_f and sqrt_rn_ftz_f are
239 // the versions with explicit ftz-ness.
240 return {Intrinsic::sqrt, FTZ_Any};
241 case Intrinsic::nvvm_trunc_d:
242 return {Intrinsic::trunc, FTZ_Any};
243 case Intrinsic::nvvm_trunc_f:
244 return {Intrinsic::trunc, FTZ_MustBeOff};
245 case Intrinsic::nvvm_trunc_ftz_f:
246 return {Intrinsic::trunc, FTZ_MustBeOn};
247
248 // NVVM intrinsics that map to LLVM cast operations.
249 // Note - we cannot map intrinsics like nvvm_d2ll_rz to LLVM's
250 // FPToSI, as NaN to int conversion with FPToSI is considered UB and is
251 // eliminated. NVVM conversion intrinsics are translated to PTX cvt
252 // instructions which define the outcome for NaN rather than leaving as UB.
253 // Therefore, translate NVVM intrinsics to sitofp/uitofp, but not to
254 // fptosi/fptoui.
255 case Intrinsic::nvvm_i2d_rn:
256 case Intrinsic::nvvm_i2f_rn:
257 case Intrinsic::nvvm_ll2d_rn:
258 case Intrinsic::nvvm_ll2f_rn:
259 return {Instruction::SIToFP};
260 case Intrinsic::nvvm_ui2d_rn:
261 case Intrinsic::nvvm_ui2f_rn:
262 case Intrinsic::nvvm_ull2d_rn:
263 case Intrinsic::nvvm_ull2f_rn:
264 return {Instruction::UIToFP};
265
266 // NVVM intrinsics that map to LLVM binary ops.
267 case Intrinsic::nvvm_div_rn_d:
268 return {Instruction::FDiv, FTZ_Any};
269
270 // The remainder of cases are NVVM intrinsics that map to LLVM idioms, but
271 // need special handling.
272 //
273 // We seem to be missing intrinsics for rcp.approx.{ftz.}f32, which is just
274 // as well.
275 case Intrinsic::nvvm_rcp_rn_d:
276 return {SPC_Reciprocal, FTZ_Any};
277
278 case Intrinsic::nvvm_fshl_clamp:
279 case Intrinsic::nvvm_fshr_clamp:
280 return {SCP_FunnelShiftClamp, FTZ_Any};
281
282 // We do not currently simplify intrinsics that give an approximate
283 // answer. These include:
284 //
285 // - nvvm_cos_approx_{f,ftz_f}
286 // - nvvm_ex2_approx(_ftz)
287 // - nvvm_lg2_approx_{d,f,ftz_f}
288 // - nvvm_sin_approx_{f,ftz_f}
289 // - nvvm_sqrt_approx_{f,ftz_f}
290 // - nvvm_rsqrt_approx_{d,f,ftz_f}
291 // - nvvm_div_approx_{ftz_d,ftz_f,f}
292 // - nvvm_rcp_approx_ftz_d
293 //
294 // Ideally we'd encode them as e.g. "fast call @llvm.cos", where "fast"
295 // means that fastmath is enabled in the intrinsic. Unfortunately only
296 // binary operators (currently) have a fastmath bit in SelectionDAG, so
297 // this information gets lost and we can't select on it.
298 //
299 // TODO: div and rcp are lowered to a binary op, so these we could in
300 // theory lower them to "fast fdiv".
301
302 default:
303 return {};
304 }
305 }();
306
307 // If Action.FtzRequirementTy is not satisfied by the module's ftz state, we
308 // can bail out now. (Notice that in the case that IID is not an NVVM
309 // intrinsic, we don't have to look up any module metadata, as
310 // FtzRequirementTy will be FTZ_Any.)
311 if (Action.FtzRequirement != FTZ_Any) {
312 // FIXME: Broken for f64
313 DenormalMode Mode = II->getFunction()->getDenormalMode(
314 Action.IsHalfTy ? APFloat::IEEEhalf() : APFloat::IEEEsingle());
315 bool FtzEnabled = Mode.Output == DenormalMode::PreserveSign;
316
317 if (FtzEnabled != (Action.FtzRequirement == FTZ_MustBeOn))
318 return nullptr;
319 }
320
321 // Simplify to target-generic intrinsic.
322 if (Action.IID) {
323 SmallVector<Value *, 4> Args(II->args());
324 // All the target-generic intrinsics currently of interest to us have one
325 // type argument, equal to that of the nvvm intrinsic's argument.
326 Type *Tys[] = {II->getArgOperand(0)->getType()};
327 return CallInst::Create(
328 Intrinsic::getOrInsertDeclaration(II->getModule(), *Action.IID, Tys),
329 Args);
330 }
331
332 // Simplify to target-generic binary op.
333 if (Action.BinaryOp)
334 return BinaryOperator::Create(*Action.BinaryOp, II->getArgOperand(0),
335 II->getArgOperand(1), II->getName());
336
337 // Simplify to target-generic cast op.
338 if (Action.CastOp)
339 return CastInst::Create(*Action.CastOp, II->getArgOperand(0), II->getType(),
340 II->getName());
341
342 // All that's left are the special cases.
343 if (!Action.Special)
344 return nullptr;
345
346 switch (*Action.Special) {
347 case SPC_Reciprocal:
348 // Simplify reciprocal.
350 Instruction::FDiv, ConstantFP::get(II->getArgOperand(0)->getType(), 1),
351 II->getArgOperand(0), II->getName());
352
353 case SCP_FunnelShiftClamp: {
354 // Canonicalize a clamping funnel shift to the generic llvm funnel shift
355 // when possible, as this is easier for llvm to optimize further.
356 if (const auto *ShiftConst = dyn_cast<ConstantInt>(II->getArgOperand(2))) {
357 const bool IsLeft = II->getIntrinsicID() == Intrinsic::nvvm_fshl_clamp;
358 if (ShiftConst->getZExtValue() >= II->getType()->getIntegerBitWidth())
359 return IC.replaceInstUsesWith(*II, II->getArgOperand(IsLeft ? 1 : 0));
360
361 const unsigned FshIID = IsLeft ? Intrinsic::fshl : Intrinsic::fshr;
363 II->getModule(), FshIID, II->getType()),
364 SmallVector<Value *, 3>(II->args()));
365 }
366 return nullptr;
367 }
368 }
369 llvm_unreachable("All SpecialCase enumerators should be handled in switch.");
370}
371
372// Returns whether a pointer in AS is in the given specific address space, or
373// nullopt when this cannot be determined at compile time.
374static std::optional<bool> isInAddressSpace(unsigned AS, unsigned SpecificAS) {
377 return std::nullopt; // Got to check at run-time.
379 SpecificAS == NVPTXAS::ADDRESS_SPACE_SHARED)
380 return std::nullopt;
381 return AS == SpecificAS ||
384}
385
386// Returns true/false when we know the answer, nullopt otherwise.
387static std::optional<bool> evaluateIsSpace(Intrinsic::ID IID, unsigned AS) {
388 switch (IID) {
389 case Intrinsic::nvvm_isspacep_global:
391 case Intrinsic::nvvm_isspacep_local:
393 case Intrinsic::nvvm_isspacep_shared:
395 case Intrinsic::nvvm_isspacep_shared_cluster:
397 case Intrinsic::nvvm_isspacep_const:
399 default:
400 llvm_unreachable("Unexpected intrinsic");
401 }
402}
403
404// Returns an instruction pointer (may be nullptr if we do not know the answer).
405// Returns nullopt if `II` is not one of the `isspacep` intrinsics.
406//
407// TODO: If InferAddressSpaces were run early enough in the pipeline this could
408// be removed in favor of the constant folding that occurs there through
409// rewriteIntrinsicWithAddressSpace
410static std::optional<Instruction *>
412
413 switch (auto IID = II.getIntrinsicID()) {
414 case Intrinsic::nvvm_isspacep_global:
415 case Intrinsic::nvvm_isspacep_local:
416 case Intrinsic::nvvm_isspacep_shared:
417 case Intrinsic::nvvm_isspacep_shared_cluster:
418 case Intrinsic::nvvm_isspacep_const: {
419 Value *Op0 = II.getArgOperand(0);
420 unsigned AS = Op0->getType()->getPointerAddressSpace();
421 // Peek through ASC to generic AS.
422 // TODO: we could dig deeper through both ASCs and GEPs.
424 if (auto *ASCO = dyn_cast<AddrSpaceCastOperator>(Op0))
425 AS = ASCO->getOperand(0)->getType()->getPointerAddressSpace();
426
427 if (std::optional<bool> Answer = evaluateIsSpace(IID, AS))
428 return IC.replaceInstUsesWith(II,
429 ConstantInt::get(II.getType(), *Answer));
430 return nullptr; // Don't know the answer, got to check at run time.
431 }
432 default:
433 return std::nullopt;
434 }
435}
436
438 Intrinsic::ID AbsIID;
439 switch (II.getIntrinsicID()) {
440 case Intrinsic::nvvm_redux_sync_fmin:
441 case Intrinsic::nvvm_redux_sync_fmin_abs:
442 AbsIID = Intrinsic::nvvm_redux_sync_fmin_abs;
443 break;
444 case Intrinsic::nvvm_redux_sync_fmax:
445 case Intrinsic::nvvm_redux_sync_fmax_abs:
446 AbsIID = Intrinsic::nvvm_redux_sync_fmax_abs;
447 break;
448 case Intrinsic::nvvm_redux_sync_fmin_NaN:
449 case Intrinsic::nvvm_redux_sync_fmin_abs_NaN:
450 AbsIID = Intrinsic::nvvm_redux_sync_fmin_abs_NaN;
451 break;
452 case Intrinsic::nvvm_redux_sync_fmax_NaN:
453 case Intrinsic::nvvm_redux_sync_fmax_abs_NaN:
454 AbsIID = Intrinsic::nvvm_redux_sync_fmax_abs_NaN;
455 break;
456 default:
457 return nullptr;
458 }
459
460 auto *Abs = dyn_cast<IntrinsicInst>(II.getArgOperand(0));
461 if (!Abs || Abs->getIntrinsicID() != Intrinsic::fabs)
462 return nullptr;
463
464 II.setCalledFunction(
465 Intrinsic::getOrInsertDeclaration(II.getModule(), AbsIID));
466 // These attributes described the absolute value, not its source.
467 II.removeParamAttr(0, Attribute::NoFPClass);
468 II.removeParamAttr(0, Attribute::Returned);
469 return IC.replaceOperand(II, 0, Abs->getArgOperand(0));
470}
471
472std::optional<Instruction *>
474 if (std::optional<Instruction *> I = handleSpaceCheckIntrinsics(IC, II))
475 return *I;
477 return I;
478 if (Instruction *I = foldAbsIntoRedux(IC, II))
479 return I;
480 return std::nullopt;
481}
482
487 if (const auto *CI = dyn_cast<CallInst>(U))
488 if (const auto *IA = dyn_cast<InlineAsm>(CI->getCalledOperand())) {
489 // Without this implementation getCallCost() would return the number
490 // of arguments+1 as the cost. Because the cost-model assumes it is a call
491 // since it is classified as a call in the IR. A better cost model would
492 // be to return the number of asm instructions embedded in the asm
493 // string.
494 StringRef AsmStr = IA->getAsmString();
495 const unsigned InstCount =
496 count_if(split(AsmStr, ';'), [](StringRef AsmInst) {
497 // Trim off scopes denoted by '{' and '}' as these can be ignored
498 AsmInst = AsmInst.trim().ltrim("{} \t\n\v\f\r");
499 // This is pretty coarse but does a reasonably good job of
500 // identifying things that look like instructions, possibly with a
501 // predicate ("@").
502 return !AsmInst.empty() &&
503 (AsmInst[0] == '@' || isAlpha(AsmInst[0]) ||
504 AsmInst.contains(".pragma"));
505 });
506 return InstCount * TargetTransformInfo::TCC_Basic;
507 }
508
510}
511
513 unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind,
515 ArrayRef<const Value *> Args, const Instruction *CtxI) const {
516 // Legalize the type.
517 std::pair<InstructionCost, MVT> LT = getTypeLegalizationCost(Ty);
518
519 int ISD = TLI->InstructionOpcodeToISD(Opcode);
520
521 switch (ISD) {
522 default:
523 return BaseT::getArithmeticInstrCost(Opcode, Ty, CostKind, Op1Info,
524 Op2Info);
525 case ISD::ADD:
526 case ISD::MUL:
527 case ISD::XOR:
528 case ISD::OR:
529 case ISD::AND:
530 // The machine code (SASS) simulates an i64 with two i32. Therefore, we
531 // estimate that arithmetic operations on i64 are twice as expensive as
532 // those on types that can fit into one machine register.
533 if (LT.second.SimpleTy == MVT::i64)
534 return 2 * LT.first;
535 // Delegate other cases to the basic TTI.
536 return BaseT::getArithmeticInstrCost(Opcode, Ty, CostKind, Op1Info,
537 Op2Info);
538 }
539}
540
543 OptimizationRemarkEmitter *ORE) const {
544 BaseT::getUnrollingPreferences(L, SE, UP, ORE);
545
546 // Enable partial unrolling and runtime unrolling, but reduce the
547 // threshold. This partially unrolls small loops which are often
548 // unrolled by the PTX to SASS compiler and unrolling earlier can be
549 // beneficial.
550 UP.Partial = UP.Runtime = true;
551 UP.PartialThreshold = UP.Threshold / 4;
552}
553
558
559namespace {
560enum class InvalidAddressSpaceAction { LeaveUnchanged, Trap };
561
562// Describes how to specialize GenericID's PointerOperand from generic memory to
563// one of AddressSpaces, using SpecificID (which may be the same intrinsic).
564// Trap only when InvalidAction permits it and the pointer is provably outside
565// every listed address space; otherwise leave the intrinsic unchanged.
566struct IntrinsicAddressSpaceInfo {
567 Intrinsic::ID GenericID;
568 Intrinsic::ID SpecificID;
569 SmallVector<unsigned, 2> AddressSpaces;
570 InvalidAddressSpaceAction InvalidAction;
571 unsigned PointerOperand;
572
573 IntrinsicAddressSpaceInfo(
574 Intrinsic::ID GenericID, Intrinsic::ID SpecificID,
576 InvalidAddressSpaceAction InvalidAction = InvalidAddressSpaceAction::Trap,
577 unsigned PointerOperand = 0)
578 : GenericID(GenericID), SpecificID(SpecificID),
579 AddressSpaces(AddressSpaces), InvalidAction(InvalidAction),
580 PointerOperand(PointerOperand) {}
581
582 IntrinsicAddressSpaceInfo(
583 Intrinsic::ID IID,
584 ArrayRef<unsigned> AddressSpaces = {NVPTXAS::ADDRESS_SPACE_SHARED},
585 InvalidAddressSpaceAction InvalidAction = InvalidAddressSpaceAction::Trap,
586 unsigned PointerOperand = 0)
587 : IntrinsicAddressSpaceInfo(IID, IID, AddressSpaces, InvalidAction,
588 PointerOperand) {}
589};
590} // namespace
591
592static const IntrinsicAddressSpaceInfo *
594 static const auto Infos = [] {
595 const IntrinsicAddressSpaceInfo Entries[] = {
596 {Intrinsic::nvvm_mbarrier_init},
597 {Intrinsic::nvvm_mbarrier_inval, Intrinsic::nvvm_mbarrier_inval_shared},
598 {Intrinsic::nvvm_mbarrier_check_layout},
599 {Intrinsic::nvvm_mbarrier_arrive,
600 Intrinsic::nvvm_mbarrier_arrive_shared},
601 {Intrinsic::nvvm_mbarrier_arrive_noComplete,
602 Intrinsic::nvvm_mbarrier_arrive_noComplete_shared},
603 {Intrinsic::nvvm_mbarrier_arrive_drop,
604 Intrinsic::nvvm_mbarrier_arrive_drop_shared},
605 {Intrinsic::nvvm_mbarrier_arrive_drop_noComplete,
606 Intrinsic::nvvm_mbarrier_arrive_drop_noComplete_shared},
607 {Intrinsic::nvvm_mbarrier_test_wait,
608 Intrinsic::nvvm_mbarrier_test_wait_shared},
609 {Intrinsic::nvvm_cp_async_mbarrier_arrive,
610 Intrinsic::nvvm_cp_async_mbarrier_arrive_shared},
611 {Intrinsic::nvvm_cp_async_mbarrier_arrive_noinc,
612 Intrinsic::nvvm_cp_async_mbarrier_arrive_noinc_shared},
613 // Other address spaces cannot be specialized but do not imply undefined
614 // behavior for prefetch.tensormap.
615 {Intrinsic::nvvm_prefetch_tensormap,
617 InvalidAddressSpaceAction::LeaveUnchanged},
618 };
620 llvm::from_range, llvm::map_range(Entries, [](const auto &Info) {
621 return std::pair{Info.GenericID, Info};
622 }));
623 }();
624 auto It = Infos.find(IID);
625 return It == Infos.end() ? nullptr : &It->second;
626}
627
629 Intrinsic::ID IID) const {
630 if (const auto *Info = getIntrinsicAddressSpaceInfo(IID)) {
631 OpIndexes.push_back(Info->PointerOperand);
632 return true;
633 }
634 switch (IID) {
635 case Intrinsic::nvvm_isspacep_const:
636 case Intrinsic::nvvm_isspacep_global:
637 case Intrinsic::nvvm_isspacep_local:
638 case Intrinsic::nvvm_isspacep_shared:
639 case Intrinsic::nvvm_isspacep_shared_cluster: {
640 OpIndexes.push_back(0);
641 return true;
642 }
643 }
644 return false;
645}
646
648 Value *OldV,
649 Value *NewV) const {
650 const Intrinsic::ID IID = II->getIntrinsicID();
651 if (const auto *Info = getIntrinsicAddressSpaceInfo(IID)) {
652 if (II->getArgOperand(Info->PointerOperand) != OldV)
653 return nullptr;
654
655 unsigned NewAS = NewV->getType()->getPointerAddressSpace();
656 if (!llvm::is_contained(Info->AddressSpaces, NewAS)) {
657 // A missing rewrite is not necessarily invalid. Trap only when the
658 // address is provably outside every allowed space and the entry permits
659 // it.
660 if (Info->InvalidAction == InvalidAddressSpaceAction::LeaveUnchanged ||
661 !llvm::all_of(Info->AddressSpaces, [NewAS](unsigned AS) {
662 return isInAddressSpace(NewAS, AS) == false;
663 }))
664 return nullptr;
665 // InferAddressSpaces preserves the CFG. Emit a trap here and leave
666 // truncating the block at the noreturn call to SimplifyCFG.
667 Value *Trap = IRBuilder<>(II).CreateIntrinsic(Intrinsic::trap, {});
668 return II->getType()->isVoidTy() ? Trap : PoisonValue::get(II->getType());
669 }
670
671 SmallVector<Type *, 8> ArgTypes(II->getFunctionType()->params());
672 ArgTypes[Info->PointerOperand] = NewV->getType();
673 FunctionType *FT = FunctionType::get(II->getType(), ArgTypes, false);
674 SmallVector<Type *, 4> OverloadTypes;
675 if (!Intrinsic::isSignatureValid(Info->SpecificID, FT, OverloadTypes))
676 return nullptr;
678 II->getModule(), Info->SpecificID, OverloadTypes);
679 II->setArgOperand(Info->PointerOperand, NewV);
680 II->setCalledFunction(Decl);
681 return II;
682 }
683 switch (IID) {
684 case Intrinsic::nvvm_isspacep_const:
685 case Intrinsic::nvvm_isspacep_global:
686 case Intrinsic::nvvm_isspacep_local:
687 case Intrinsic::nvvm_isspacep_shared:
688 case Intrinsic::nvvm_isspacep_shared_cluster: {
689 const unsigned NewAS = NewV->getType()->getPointerAddressSpace();
690 if (const auto R = evaluateIsSpace(IID, NewAS))
691 return ConstantInt::get(II->getType(), *R);
692 return nullptr;
693 }
694 }
695 return nullptr;
696}
697
699 unsigned AddrSpace,
700 TTI::MaskKind MaskKind) const {
701 if (MaskKind != TTI::MaskKind::ConstantMask)
702 return false;
703
704 // We currently only support this feature for 256-bit vectors, so the
705 // alignment must be at least 32
706 if (Alignment < 32)
707 return false;
708
709 if (!ST->has256BitVectorLoadStore(AddrSpace))
710 return false;
711
712 auto *VTy = dyn_cast<FixedVectorType>(DataTy);
713 if (!VTy)
714 return false;
715
716 auto *ElemTy = VTy->getScalarType();
717 return (ElemTy->getScalarSizeInBits() == 32 && VTy->getNumElements() == 8) ||
718 (ElemTy->getScalarSizeInBits() == 64 && VTy->getNumElements() == 4);
719}
720
722 unsigned /*AddrSpace*/,
723 TTI::MaskKind MaskKind) const {
724 if (MaskKind != TTI::MaskKind::ConstantMask)
725 return false;
726
727 if (Alignment < DL.getTypeStoreSize(DataTy))
728 return false;
729
730 // We do not support sub-byte element type masked loads.
731 auto *VTy = dyn_cast<FixedVectorType>(DataTy);
732 if (!VTy)
733 return false;
734 return VTy->getElementType()->getScalarSizeInBits() >= 8;
735}
736
737unsigned NVPTXTTIImpl::getLoadStoreVecRegBitWidth(unsigned AddrSpace) const {
738 // 256 bit loads/stores are currently only supported for global address space
739 if (ST->has256BitVectorLoadStore(AddrSpace))
740 return 256;
741 return 128;
742}
743
744unsigned NVPTXTTIImpl::getAssumedAddrSpace(const Value *V) const {
745 if (isa<AllocaInst>(V))
746 return ADDRESS_SPACE_LOCAL;
747
748 if (const Argument *Arg = dyn_cast<Argument>(V)) {
749 if (isKernelFunction(*Arg->getParent())) {
750 const NVPTXTargetMachine &TM =
751 static_cast<const NVPTXTargetMachine &>(getTLI()->getTargetMachine());
752 if (TM.getDrvInterface() == NVPTX::CUDA && !Arg->hasByValAttr())
754 } else {
755 // We assume that all device parameters that are passed byval will be
756 // placed in the local AS. Very simple cases will be updated after ISel to
757 // use the device param space where possible.
758 if (Arg->hasByValAttr())
759 return ADDRESS_SPACE_LOCAL;
760 }
761 }
762
763 return -1;
764}
765
767 const Function &F,
768 SmallVectorImpl<std::pair<StringRef, int64_t>> &LB) const {
769 if (const auto Val = getMaxClusterRank(F))
770 LB.push_back({"maxclusterrank", *Val});
771
772 const auto MaxNTID = getMaxNTID(F);
773 if (MaxNTID.size() > 0)
774 LB.push_back({"maxntidx", MaxNTID[0]});
775 if (MaxNTID.size() > 1)
776 LB.push_back({"maxntidy", MaxNTID[1]});
777 if (MaxNTID.size() > 2)
778 LB.push_back({"maxntidz", MaxNTID[2]});
779}
780
781// Global addresses can only be materialized if they are in generic global or
782// constant space.
784 if (const auto *GV = dyn_cast<GlobalValue>(C)) {
785 const unsigned AS = GV->getAddressSpace();
786 return AS == NVPTXAS::ADDRESS_SPACE_GENERIC ||
789 }
790 return true;
791}
792
794 if (isSourceOfDivergence(V))
796
798}
This file provides a helper that implements much of the TTI interface in terms of the target-independ...
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static cl::opt< OutputCostKind > CostKind("cost-kind", cl::desc("Target cost kind"), cl::init(OutputCostKind::RecipThroughput), cl::values(clEnumValN(OutputCostKind::RecipThroughput, "throughput", "Reciprocal throughput"), clEnumValN(OutputCostKind::Latency, "latency", "Instruction latency"), clEnumValN(OutputCostKind::CodeSize, "code-size", "Code size"), clEnumValN(OutputCostKind::SizeAndLatency, "size-latency", "Code size and latency"), clEnumValN(OutputCostKind::All, "all", "Print all cost kinds")))
This file defines the DenseMap class.
This file provides the interface for the instcombine pass implementation.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
NVPTX address space definition.
static std::optional< Instruction * > handleSpaceCheckIntrinsics(InstCombiner &IC, IntrinsicInst &II)
static Instruction * convertNvvmIntrinsicToLlvm(InstCombiner &IC, IntrinsicInst *II)
static bool readsLaneId(const IntrinsicInst *II)
static std::optional< bool > evaluateIsSpace(Intrinsic::ID IID, unsigned AS)
static bool readsThreadIndex(const IntrinsicInst *II)
static std::optional< bool > isInAddressSpace(unsigned AS, unsigned SpecificAS)
static Instruction * foldAbsIntoRedux(InstCombiner &IC, IntrinsicInst &II)
static const IntrinsicAddressSpaceInfo * getIntrinsicAddressSpaceInfo(Intrinsic::ID IID)
This file a TargetTransformInfoImplBase conforming object specific to the NVPTX target machine.
uint64_t IntrinsicInst * II
static cl::opt< RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode > Mode("regalloc-enable-advisor", cl::Hidden, cl::init(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default), cl::desc("Enable regalloc advisor mode"), cl::values(clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default, "default", "Default"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Release, "release", "precompiled"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Development, "development", "for training")))
SI Fold Operands
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
This file describes how to lower LLVM code to machine code.
This pass exposes codegen information to IR-level passes.
static const fltSemantics & IEEEsingle()
Definition APFloat.h:304
static const fltSemantics & IEEEhalf()
Definition APFloat.h:302
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
InstructionCost getArithmeticInstrCost(unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind, TTI::OperandValueInfo Opd1Info={TTI::OK_AnyValue, TTI::OP_None}, TTI::OperandValueInfo Opd2Info={TTI::OK_AnyValue, TTI::OP_None}, ArrayRef< const Value * > Args={}, const Instruction *CtxI=nullptr) const override
void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP, OptimizationRemarkEmitter *ORE) const override
void getPeelingPreferences(Loop *L, ScalarEvolution &SE, TTI::PeelingPreferences &PP) const override
std::pair< InstructionCost, MVT > getTypeLegalizationCost(Type *Ty) const
static LLVM_ABI BinaryOperator * Create(BinaryOps Op, Value *S1, Value *S2, const Twine &Name=Twine(), InsertPosition InsertBefore=nullptr)
Construct a binary instruction, given the opcode and the two operands.
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
static LLVM_ABI CastInst * Create(Instruction::CastOps, Value *S, Type *Ty, const Twine &Name="", InsertPosition InsertBefore=nullptr)
Provides a way to construct any of the CastInst subclasses using an opcode instead of the subclass's ...
This is an important base class in LLVM.
Definition Constant.h:43
Class to represent function types.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
The core instruction combiner logic.
Instruction * replaceInstUsesWith(Instruction &I, Value *V)
A combiner-aware RAUW-like routine.
Instruction * replaceOperand(Instruction &I, unsigned OpNum, Value *V)
Replace operand of instruction and add old operand to the worklist.
A wrapper class for inspecting calls to intrinsic functions.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
bool isLegalMaskedStore(Type *DataType, Align Alignment, unsigned AddrSpace, TTI::MaskKind MaskKind) const override
Value * rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV, Value *NewV) const override
InstructionCost getArithmeticInstrCost(unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind, TTI::OperandValueInfo Op1Info={TTI::OK_AnyValue, TTI::OP_None}, TTI::OperandValueInfo Op2Info={TTI::OK_AnyValue, TTI::OP_None}, ArrayRef< const Value * > Args={}, const Instruction *CtxI=nullptr) const override
InstructionCost getInstructionCost(const User *U, ArrayRef< const Value * > Operands, TTI::TargetCostKind CostKind) const override
bool shouldBuildLookupTablesForConstant(Constant *C) const override
unsigned getLoadStoreVecRegBitWidth(unsigned AddrSpace) const override
std::optional< Instruction * > instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const override
ValueUniformity getValueUniformity(const Value *V) const override
void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP, OptimizationRemarkEmitter *ORE) const override
void getPeelingPreferences(Loop *L, ScalarEvolution &SE, TTI::PeelingPreferences &PP) const override
bool collectFlatAddressOperands(SmallVectorImpl< int > &OpIndexes, Intrinsic::ID IID) const override
unsigned getAssumedAddrSpace(const Value *V) const override
void collectKernelLaunchBounds(const Function &F, SmallVectorImpl< std::pair< StringRef, int64_t > > &LB) const override
bool isLegalMaskedLoad(Type *DataType, Align Alignment, unsigned AddrSpace, TTI::MaskKind MaskKind) const override
NVPTX::DrvInterface getDrvInterface() const
The optimization diagnostic interface.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
The main scalar evolution driver.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
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
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
StringRef ltrim(char Char) const
Return string with consecutive Char characters starting from the the left removed.
Definition StringRef.h:826
bool contains(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition StringRef.h:446
StringRef trim(char Char) const
Return string with consecutive Char characters starting from the left and right removed.
Definition StringRef.h:850
virtual InstructionCost getInstructionCost(const User *U, ArrayRef< const Value * > Operands, TTI::TargetCostKind CostKind) const
MaskKind
Some targets only support masked load/store with a constant mask.
TargetCostKind
The kind of cost model.
@ TCC_Basic
The cost of a typical 'add' instruction.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
ISD namespace - This namespace contains an enum which represents all of the SelectionDAG node types a...
Definition ISDOpcodes.h:26
@ ADD
Simple integer binary arithmetic operators.
Definition ISDOpcodes.h:266
@ AND
Bitwise operators - logical and, logical or, logical xor.
Definition ISDOpcodes.h:757
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
LLVM_ABI bool isSignatureValid(Intrinsic::ID ID, FunctionType *FT, SmallVectorImpl< Type * > &OverloadTys, raw_ostream &OS=nulls())
Returns true if FT is a valid function type for intrinsic ID.
This is an optimization pass for GlobalISel generic memory operations.
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:1755
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ BinaryOp
One of the operands is a binary op.
constexpr from_range_t from_range
auto map_range(ContainerTy &&C, FuncTy F)
Return a range that applies F to the elements of C.
Definition STLExtras.h:366
bool isAlpha(char C)
Checks if character C is a valid letter as classified by "C" locale.
SmallVector< unsigned, 3 > getMaxNTID(const Function &)
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
std::optional< unsigned > getMaxClusterRank(const Function &)
iterator_range< SplittingIterator > split(StringRef Str, StringRef Separator)
Split the specified string over a separator and return a range-compatible iterable over its partition...
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 count_if(R &&Range, UnaryPredicate P)
Wrapper function around std::count_if to count the number of times an element satisfying a given pred...
Definition STLExtras.h:2035
bool isKernelFunction(const Function &F)
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1963
ValueUniformity
Enum describing how values behave with respect to uniformity and divergence, to answer the question: ...
Definition Uniformity.h:18
@ NeverUniform
The result value can never be assumed to be uniform.
Definition Uniformity.h:26
@ Default
The result value is uniform if and only if all operands are uniform.
Definition Uniformity.h:20
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
Represent subnormal handling kind for floating point instruction inputs and outputs.
@ PreserveSign
The sign of a flushed-to-zero number is preserved in the sign of 0.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
bool Runtime
Allow runtime unrolling (unrolling of loops to expand the size of the loop body even when the number ...
bool Partial
Allow partial unrolling (unrolling of loops to expand the size of the loop body, not only to eliminat...