MLIR 24.0.0git
LowerGpuOpsToNVVMOps.cpp
Go to the documentation of this file.
1//===- LowerGpuOpsToNVVMOps.cpp - MLIR GPU to NVVM lowering passes --------===//
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// This file implements a pass to generate NVVMIR operations for higher-level
10// GPU operations.
11//
12//===----------------------------------------------------------------------===//
13
33#include "mlir/IR/SymbolTable.h"
37
41#include <optional>
42
43namespace mlir {
44#define GEN_PASS_DEF_CONVERTGPUOPSTONVVMOPS
45#include "mlir/Conversion/Passes.h.inc"
46} // namespace mlir
47
48using namespace mlir;
49
50namespace {
51
52/// Convert gpu dialect shfl mode enum to the equivalent nvvm one.
53static NVVM::ShflKind convertShflKind(gpu::ShuffleMode mode) {
54 switch (mode) {
55 case gpu::ShuffleMode::XOR:
56 return NVVM::ShflKind::bfly;
57 case gpu::ShuffleMode::UP:
58 return NVVM::ShflKind::up;
59 case gpu::ShuffleMode::DOWN:
60 return NVVM::ShflKind::down;
61 case gpu::ShuffleMode::IDX:
62 return NVVM::ShflKind::idx;
63 }
64 llvm_unreachable("unknown shuffle mode");
65}
66
67static std::optional<NVVM::ReductionKind>
68convertToNVVMReductionKind(gpu::AllReduceOperation mode) {
69 switch (mode) {
70 case gpu::AllReduceOperation::ADD:
71 return NVVM::ReductionKind::ADD;
72 case gpu::AllReduceOperation::MUL:
73 return std::nullopt;
74 case gpu::AllReduceOperation::MINSI:
75 return NVVM::ReductionKind::MIN;
76 case gpu::AllReduceOperation::MINUI:
77 return std::nullopt;
78 case gpu::AllReduceOperation::MINNUMF:
79 return NVVM::ReductionKind::MIN;
80 case gpu::AllReduceOperation::MAXSI:
81 return NVVM::ReductionKind::MAX;
82 case gpu::AllReduceOperation::MAXUI:
83 return std::nullopt;
84 case gpu::AllReduceOperation::MAXNUMF:
85 return NVVM::ReductionKind::MAX;
86 case gpu::AllReduceOperation::AND:
87 return NVVM::ReductionKind::AND;
88 case gpu::AllReduceOperation::OR:
89 return NVVM::ReductionKind::OR;
90 case gpu::AllReduceOperation::XOR:
91 return NVVM::ReductionKind::XOR;
92 case gpu::AllReduceOperation::MINIMUMF:
93 case gpu::AllReduceOperation::MAXIMUMF:
94 case gpu::AllReduceOperation::MINIMUMNUMF:
95 case gpu::AllReduceOperation::MAXIMUMNUMF:
96 return std::nullopt;
97 }
98 return std::nullopt;
99}
100
101static constexpr llvm::StringLiteral kNVVMNamedBarrierIdPrefix =
102 "__named_barrier_id";
103static constexpr int32_t kNVVMFirstNamedBarrierId = 1;
104static constexpr int32_t kNVVMLastNamedBarrierId = 15;
105static constexpr int32_t kNVVMWarpSize = 32;
106
107static FailureOr<StringAttr>
108createNVVMNamedBarrierIdGlobal(gpu::InitializeNamedBarrierOp op,
109 ConversionPatternRewriter &rewriter) {
110 auto funcOp = op->getParentOfType<FunctionOpInterface>();
111 if (!funcOp) {
112 op.emitOpError("must be inside a function-like op");
113 return failure();
114 }
115 Operation *symbolTableOp = funcOp->getParentWithTrait<OpTrait::SymbolTable>();
116 if (!symbolTableOp) {
117 op.emitOpError(
118 "enclosing function-like op must have a symbol-table parent");
119 return failure();
120 }
121
122 int32_t numNamedBarriers = 0;
123 for (auto globalOp :
124 symbolTableOp->getRegion(0).front().getOps<LLVM::GlobalOp>())
125 if (globalOp.getSymName().starts_with(kNVVMNamedBarrierIdPrefix))
126 ++numNamedBarriers;
127
128 int32_t barrierId = kNVVMFirstNamedBarrierId + numNamedBarriers;
129 if (barrierId > kNVVMLastNamedBarrierId) {
130 op.emitOpError("NVVM supports at most 15 named barriers per CTA");
131 return failure();
132 }
133
134 OpBuilder detachedBuilder(rewriter.getContext());
135 Type i32 = rewriter.getI32Type();
136 auto globalOp = LLVM::GlobalOp::create(
137 detachedBuilder, op.getLoc(), i32, /*isConstant=*/true,
138 LLVM::Linkage::Internal, kNVVMNamedBarrierIdPrefix,
139 rewriter.getI32IntegerAttr(barrierId), /*alignment=*/0,
140 /*addrSpace=*/0);
141 return SymbolTable(symbolTableOp).insert(globalOp);
142}
143
144/// This pass lowers gpu.subgroup_reduce op into to the nvvm.redux op. The op
145/// must be run by the entire subgroup, otherwise it is undefined behaviour.
146struct GPUSubgroupReduceOpLowering
147 : public ConvertOpToLLVMPattern<gpu::SubgroupReduceOp> {
148 using ConvertOpToLLVMPattern<gpu::SubgroupReduceOp>::ConvertOpToLLVMPattern;
149 LogicalResult
150
151 matchAndRewrite(gpu::SubgroupReduceOp op, OpAdaptor adaptor,
152 ConversionPatternRewriter &rewriter) const override {
153 if (op.getClusterSize())
154 return rewriter.notifyMatchFailure(
155 op, "lowering for clustered reduce not implemented");
156
157 if (!op.getUniform())
158 return rewriter.notifyMatchFailure(
159 op, "cannot be lowered to redux as the op must be run "
160 "uniformly (entire subgroup).");
161 if (!op.getValue().getType().isInteger(32))
162 return rewriter.notifyMatchFailure(op, "unsupported data type");
163
164 std::optional<NVVM::ReductionKind> mode =
165 convertToNVVMReductionKind(op.getOp());
166 if (!mode.has_value())
167 return rewriter.notifyMatchFailure(
168 op, "unsupported reduction mode for redux");
169
170 Location loc = op->getLoc();
171 auto int32Type = IntegerType::get(rewriter.getContext(), 32);
172 Value offset = LLVM::ConstantOp::create(rewriter, loc, int32Type, -1);
173
174 auto reduxOp = NVVM::ReduxOp::create(rewriter, loc, int32Type,
175 op.getValue(), mode.value(), offset);
176
177 rewriter.replaceOp(op, reduxOp->getResult(0));
178 return success();
179 }
180};
181
182struct GPUShuffleOpLowering : public ConvertOpToLLVMPattern<gpu::ShuffleOp> {
183 using ConvertOpToLLVMPattern<gpu::ShuffleOp>::ConvertOpToLLVMPattern;
184
185 /// Lowers a shuffle to the corresponding NVVM op.
186 ///
187 /// Convert the `width` argument into an activeMask (a bitmask which specifies
188 /// which threads participate in the shuffle) and a maskAndClamp (specifying
189 /// the highest lane which participates in the shuffle).
190 ///
191 /// %one = llvm.constant(1 : i32) : i32
192 /// %minus_one = llvm.constant(-1 : i32) : i32
193 /// %thirty_two = llvm.constant(32 : i32) : i32
194 /// %num_lanes = llvm.sub %thirty_two, %width : i32
195 /// %active_mask = llvm.lshr %minus_one, %num_lanes : i32
196 /// %mask_and_clamp = llvm.sub %width, %one : i32
197 /// %shfl = nvvm.shfl.sync.bfly %active_mask, %value, %offset,
198 /// %mask_and_clamp : !llvm<"{ float, i1 }">
199 /// %shfl_value = llvm.extractvalue %shfl[0] :
200 /// !llvm<"{ float, i1 }">
201 /// %shfl_pred = llvm.extractvalue %shfl[1] :
202 /// !llvm<"{ float, i1 }">
203 LogicalResult
204 matchAndRewrite(gpu::ShuffleOp op, OpAdaptor adaptor,
205 ConversionPatternRewriter &rewriter) const override {
206 Location loc = op->getLoc();
207
208 auto valueTy = adaptor.getValue().getType();
209 auto int32Type = IntegerType::get(rewriter.getContext(), 32);
210 auto predTy = IntegerType::get(rewriter.getContext(), 1);
211
212 Value one = LLVM::ConstantOp::create(rewriter, loc, int32Type, 1);
213 Value minusOne = LLVM::ConstantOp::create(rewriter, loc, int32Type, -1);
214 Value thirtyTwo = LLVM::ConstantOp::create(rewriter, loc, int32Type, 32);
215 Value numLeadInactiveLane = LLVM::SubOp::create(
216 rewriter, loc, int32Type, thirtyTwo, adaptor.getWidth());
217 // Bit mask of active lanes: `(-1) >> (32 - activeWidth)`.
218 Value activeMask = LLVM::LShrOp::create(rewriter, loc, int32Type, minusOne,
219 numLeadInactiveLane);
220 Value maskAndClamp;
221 if (op.getMode() == gpu::ShuffleMode::UP) {
222 // Clamp lane: `32 - activeWidth`
223 maskAndClamp = numLeadInactiveLane;
224 } else {
225 // Clamp lane: `activeWidth - 1`
226 maskAndClamp = LLVM::SubOp::create(rewriter, loc, int32Type,
227 adaptor.getWidth(), one);
228 }
229
230 bool predIsUsed = !op->getResult(1).use_empty();
231 UnitAttr returnValueAndIsValidAttr = nullptr;
232 Type resultTy = valueTy;
233 if (predIsUsed) {
234 returnValueAndIsValidAttr = rewriter.getUnitAttr();
235 resultTy = LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
236 {valueTy, predTy});
237 }
238 Value shfl = NVVM::ShflOp::create(
239 rewriter, loc, resultTy, activeMask, adaptor.getValue(),
240 adaptor.getOffset(), maskAndClamp, convertShflKind(op.getMode()),
241 returnValueAndIsValidAttr);
242 if (predIsUsed) {
243 Value shflValue = LLVM::ExtractValueOp::create(rewriter, loc, shfl, 0);
244 Value isActiveSrcLane =
245 LLVM::ExtractValueOp::create(rewriter, loc, shfl, 1);
246 rewriter.replaceOp(op, {shflValue, isActiveSrcLane});
247 } else {
248 rewriter.replaceOp(op, {shfl, nullptr});
249 }
250 return success();
251 }
252};
253
254struct GPULaneIdOpToNVVM : ConvertOpToLLVMPattern<gpu::LaneIdOp> {
255 using ConvertOpToLLVMPattern<gpu::LaneIdOp>::ConvertOpToLLVMPattern;
256
257 LogicalResult
258 matchAndRewrite(gpu::LaneIdOp op, gpu::LaneIdOp::Adaptor adaptor,
259 ConversionPatternRewriter &rewriter) const override {
260 auto loc = op->getLoc();
261 MLIRContext *context = rewriter.getContext();
262 LLVM::ConstantRangeAttr bounds = nullptr;
263 if (std::optional<APInt> upperBound = op.getUpperBound())
264 bounds = rewriter.getAttr<LLVM::ConstantRangeAttr>(
265 /*bitWidth=*/32, /*lower=*/0, upperBound->getZExtValue());
266 else
267 bounds = rewriter.getAttr<LLVM::ConstantRangeAttr>(
268 /*bitWidth=*/32, /*lower=*/0, /*upper=*/kWarpSize);
269 Value newOp =
270 NVVM::LaneIdOp::create(rewriter, loc, rewriter.getI32Type(), bounds);
271 // Truncate or extend the result depending on the index bitwidth specified
272 // by the LLVMTypeConverter options.
273 const unsigned indexBitwidth = getTypeConverter()->getIndexTypeBitwidth();
274 if (indexBitwidth > 32) {
275 newOp = LLVM::SExtOp::create(
276 rewriter, loc, IntegerType::get(context, indexBitwidth), newOp);
277 } else if (indexBitwidth < 32) {
278 newOp = LLVM::TruncOp::create(
279 rewriter, loc, IntegerType::get(context, indexBitwidth), newOp);
280 }
281 rewriter.replaceOp(op, {newOp});
282 return success();
283 }
284};
285
286struct GPUBallotOpToNVVM : public ConvertOpToLLVMPattern<gpu::BallotOp> {
287 using ConvertOpToLLVMPattern<gpu::BallotOp>::ConvertOpToLLVMPattern;
288
289 LogicalResult
290 matchAndRewrite(gpu::BallotOp op, gpu::BallotOp::Adaptor adaptor,
291 ConversionPatternRewriter &rewriter) const override {
292 Location loc = op->getLoc();
293 auto int32Type = IntegerType::get(rewriter.getContext(), 32);
294 auto intType = cast<IntegerType>(op.getType());
295 unsigned width = intType.getWidth();
296
297 // NVVM ballot natively returns i32. For i64 results, zero-extend since
298 // NVIDIA warps have exactly 32 threads, so upper 32 bits are always zero.
299 if (width != 32 && width != 64)
300 return rewriter.notifyMatchFailure(
301 op, "nvvm.vote.sync ballot only supports i32 and i64 result types");
302
303 // Use full mask (-1) so all 32 lanes participate in the ballot.
304 Value mask = LLVM::ConstantOp::create(rewriter, loc, int32Type,
305 rewriter.getI32IntegerAttr(-1));
306
307 auto voteKind = NVVM::VoteSyncKindAttr::get(rewriter.getContext(),
308 NVVM::VoteSyncKind::ballot);
309 Value result = NVVM::VoteSyncOp::create(rewriter, loc, int32Type, mask,
310 adaptor.getPredicate(), voteKind);
311
312 if (width == 64)
313 result = LLVM::ZExtOp::create(rewriter, loc, op.getType(), result);
314
315 rewriter.replaceOp(op, result);
316 return success();
317 }
318};
319
320/// Lowering of cf.assert into a conditional __assertfail.
321struct AssertOpToAssertfailLowering
322 : public ConvertOpToLLVMPattern<cf::AssertOp> {
323 using ConvertOpToLLVMPattern<cf::AssertOp>::ConvertOpToLLVMPattern;
324
325 LogicalResult
326 matchAndRewrite(cf::AssertOp assertOp, cf::AssertOpAdaptor adaptor,
327 ConversionPatternRewriter &rewriter) const override {
328 MLIRContext *ctx = rewriter.getContext();
329 Location loc = assertOp.getLoc();
330 Type i8Type = typeConverter->convertType(rewriter.getIntegerType(8));
331 Type i32Type = typeConverter->convertType(rewriter.getIntegerType(32));
332 Type i64Type = typeConverter->convertType(rewriter.getIntegerType(64));
333 Type ptrType = LLVM::LLVMPointerType::get(ctx);
334 Type voidType = LLVM::LLVMVoidType::get(ctx);
335
336 // Find or create __assertfail function declaration.
337 auto moduleOp = assertOp->getParentOfType<gpu::GPUModuleOp>();
338 auto assertfailType = LLVM::LLVMFunctionType::get(
339 voidType, {ptrType, ptrType, i32Type, ptrType, i64Type});
340 LLVM::LLVMFuncOp assertfailDecl = getOrDefineFunction(
341 moduleOp, loc, rewriter, "__assertfail", assertfailType);
342 assertfailDecl.setPassthroughAttr(
343 ArrayAttr::get(ctx, StringAttr::get(ctx, "noreturn")));
344
345 // Split blocks and insert conditional branch.
346 // ^before:
347 // ...
348 // cf.cond_br %condition, ^after, ^assert
349 // ^assert:
350 // cf.assert
351 // cf.br ^after
352 // ^after:
353 // ...
354 Block *beforeBlock = assertOp->getBlock();
355 Block *assertBlock =
356 rewriter.splitBlock(beforeBlock, assertOp->getIterator());
357 Block *afterBlock =
358 rewriter.splitBlock(assertBlock, ++assertOp->getIterator());
359 rewriter.setInsertionPointToEnd(beforeBlock);
360 cf::CondBranchOp::create(rewriter, loc, adaptor.getArg(), afterBlock,
361 assertBlock);
362 rewriter.setInsertionPointToEnd(assertBlock);
363 cf::BranchOp::create(rewriter, loc, afterBlock);
364
365 // Continue cf.assert lowering.
366 rewriter.setInsertionPoint(assertOp);
367
368 // Populate file name, file number and function name from the location of
369 // the AssertOp.
370 StringRef fileName = "(unknown)";
371 StringRef funcName = "(unknown)";
372 int32_t fileLine = 0;
373 while (auto callSiteLoc = dyn_cast<CallSiteLoc>(loc))
374 loc = callSiteLoc.getCallee();
375 if (auto fileLineColLoc = dyn_cast<FileLineColRange>(loc)) {
376 fileName = fileLineColLoc.getFilename().strref();
377 fileLine = fileLineColLoc.getStartLine();
378 } else if (auto nameLoc = dyn_cast<NameLoc>(loc)) {
379 funcName = nameLoc.getName().strref();
380 if (auto fileLineColLoc =
381 dyn_cast<FileLineColRange>(nameLoc.getChildLoc())) {
382 fileName = fileLineColLoc.getFilename().strref();
383 fileLine = fileLineColLoc.getStartLine();
384 }
385 }
386
387 // Create constants.
388 auto getGlobal = [&](LLVM::GlobalOp global) {
389 // Get a pointer to the format string's first element.
390 Value globalPtr = LLVM::AddressOfOp::create(
391 rewriter, loc, LLVM::LLVMPointerType::get(ctx, global.getAddrSpace()),
392 global.getSymNameAttr());
393 Value start =
394 LLVM::GEPOp::create(rewriter, loc, ptrType, global.getGlobalType(),
395 globalPtr, ArrayRef<LLVM::GEPArg>{0, 0});
396 return start;
397 };
398 Value assertMessage = getGlobal(getOrCreateStringConstant(
399 rewriter, loc, moduleOp, i8Type, "assert_message_", assertOp.getMsg()));
400 Value assertFile = getGlobal(getOrCreateStringConstant(
401 rewriter, loc, moduleOp, i8Type, "assert_file_", fileName));
402 Value assertFunc = getGlobal(getOrCreateStringConstant(
403 rewriter, loc, moduleOp, i8Type, "assert_func_", funcName));
404 Value assertLine =
405 LLVM::ConstantOp::create(rewriter, loc, i32Type, fileLine);
406 Value c1 = LLVM::ConstantOp::create(rewriter, loc, i64Type, 1);
407
408 // Insert function call to __assertfail.
409 SmallVector<Value> arguments{assertMessage, assertFile, assertLine,
410 assertFunc, c1};
411 rewriter.replaceOpWithNewOp<LLVM::CallOp>(assertOp, assertfailDecl,
412 arguments);
413 return success();
414 }
415};
416
417struct GPUBarrierOpToNVVMLowering final
418 : public ConvertOpToLLVMPattern<gpu::BarrierOp> {
420
421 LogicalResult
422 matchAndRewrite(gpu::BarrierOp op, gpu::BarrierOp::Adaptor adaptor,
423 ConversionPatternRewriter &rewriter) const override {
424 if (Value namedBarrier = adaptor.getNamedBarrier()) {
425 Location loc = op.getLoc();
426 Value barrierId =
427 LLVM::ExtractValueOp::create(rewriter, loc, namedBarrier, 0);
428 Value numberOfThreads =
429 LLVM::ExtractValueOp::create(rewriter, loc, namedBarrier, 1);
430 NVVM::BarrierOp::create(rewriter, loc, barrierId, numberOfThreads);
431 rewriter.eraseOp(op);
432 return success();
433 }
434
435 gpu::BarrierScope scope = op.getScope();
436 switch (scope) {
437 case gpu::BarrierScope::Workgroup:
438 rewriter.replaceOpWithNewOp<NVVM::BarrierOp>(op);
439 return success();
440 case gpu::BarrierScope::Subgroup: {
441 // Emit __syncwarp(0xFFFFFFFF) for full-warp sync.
442 Value mask =
443 LLVM::ConstantOp::create(rewriter, op.getLoc(), rewriter.getI32Type(),
444 rewriter.getI32IntegerAttr(0xFFFFFFFF));
445 rewriter.replaceOpWithNewOp<NVVM::SyncWarpOp>(op, mask);
446 return success();
447 }
448 default:
449 return rewriter.notifyMatchFailure(
450 op, "unsupported scope for NVVM barrier lowering");
451 }
452 }
453};
454
455struct GPUInitializeNamedBarrierOpToNVVMLowering final
456 : public ConvertOpToLLVMPattern<gpu::InitializeNamedBarrierOp> {
458
459 LogicalResult
460 matchAndRewrite(gpu::InitializeNamedBarrierOp op,
461 gpu::InitializeNamedBarrierOp::Adaptor adaptor,
462 ConversionPatternRewriter &rewriter) const override {
463 Location loc = op.getLoc();
464 MLIRContext *ctx = rewriter.getContext();
465 Type i32 = rewriter.getI32Type();
466 Type namedBarrierType =
467 getTypeConverter()->convertType(op.getResult().getType());
468 if (!namedBarrierType)
469 return rewriter.notifyMatchFailure(op, "failed to convert result type");
470
471 FailureOr<StringAttr> maybeGlobalName =
472 createNVVMNamedBarrierIdGlobal(op, rewriter);
473 if (failed(maybeGlobalName))
474 return failure();
475
476 auto addressOf = LLVM::AddressOfOp::create(
477 rewriter, loc, LLVM::LLVMPointerType::get(ctx), *maybeGlobalName);
478 Value barrierId =
479 LLVM::LoadOp::create(rewriter, loc, i32, addressOf.getResult());
480
481 Value warpSize = LLVM::ConstantOp::create(
482 rewriter, loc, i32, rewriter.getI32IntegerAttr(kNVVMWarpSize));
483 Value numberOfThreads =
484 LLVM::MulOp::create(rewriter, loc, adaptor.getMemberCount(), warpSize);
485
486 Value namedBarrier =
487 LLVM::PoisonOp::create(rewriter, loc, namedBarrierType);
488 DenseI64ArrayAttr barrierIdPos = rewriter.getDenseI64ArrayAttr({0});
489 DenseI64ArrayAttr numberOfThreadsPos = rewriter.getDenseI64ArrayAttr({1});
490 namedBarrier = LLVM::InsertValueOp::create(rewriter, loc, namedBarrier,
491 barrierId, barrierIdPos);
492 namedBarrier = LLVM::InsertValueOp::create(
493 rewriter, loc, namedBarrier, numberOfThreads, numberOfThreadsPos);
494 rewriter.replaceOp(op, namedBarrier);
495 return success();
496 }
497};
498
499/// A pass that replaces all occurrences of GPU device operations with their
500/// corresponding NVVM equivalent.
501///
502/// This pass only handles device code and is not meant to be run on GPU host
503/// code.
504struct LowerGpuOpsToNVVMOpsPass final
505 : public impl::ConvertGpuOpsToNVVMOpsBase<LowerGpuOpsToNVVMOpsPass> {
506 using Base::Base;
507
508 void getDependentDialects(DialectRegistry &registry) const override {
509 Base::getDependentDialects(registry);
511 }
512
513 void runOnOperation() override {
514 gpu::GPUModuleOp m = getOperation();
515
516 // Request C wrapper emission for externally visible functions only. A
517 // private function cannot be called from outside its module, so its C
518 // interface wrapper is unreachable and would only anchor the wrapped
519 // function against dead-code elimination.
520 for (auto func : m.getOps<func::FuncOp>()) {
521 if (func.isPrivate())
522 continue;
523 func->setDiscardableAttr(LLVM::LLVMDialect::getEmitCWrapperAttrName(),
524 UnitAttr::get(&getContext()));
525 }
526
527 // Customize the bitwidth used for the device side index computations.
528 LowerToLLVMOptions options(
529 m.getContext(),
530 DataLayout(cast<DataLayoutOpInterface>(m.getOperation())));
531 if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
532 options.overrideIndexBitwidth(indexBitwidth);
533 options.useBarePtrCallConv = useBarePtrCallConv;
534
535 // Apply in-dialect lowering. In-dialect lowering will replace
536 // ops which need to be lowered further, which is not supported by a
537 // single conversion pass.
538 {
539 RewritePatternSet patterns(m.getContext());
541 // Transform N-D vector.from_elements to 1-D vector.from_elements before
542 // conversion.
543 vector::populateVectorFromElementsUnrollPatterns(patterns);
544 if (failed(applyPatternsGreedily(m, std::move(patterns))))
545 return signalPassFailure();
546 }
547
548 LLVMTypeConverter converter(m.getContext(), options);
550 RewritePatternSet llvmPatterns(m.getContext());
551 LLVMConversionTarget target(getContext());
552
553 // Set higher benefit, so patterns will run before generic LLVM lowering.
554 populateGpuToNVVMConversionPatterns(converter, llvmPatterns,
555 /*benefit=*/10);
556
557 llvm::SmallDenseSet<StringRef> allowedDialectsSet(allowedDialects.begin(),
558 allowedDialects.end());
559 for (Dialect *dialect : getContext().getLoadedDialects()) {
560 // Skip math patterns as nvvm needs custom math lowering.
561 if (isa<math::MathDialect>(dialect))
562 continue;
563
564 bool allowed = allowedDialectsSet.contains(dialect->getNamespace());
565 // Empty `allowedDialectsSet` means all dialects are allowed.
566 if (!allowedDialectsSet.empty() && !allowed)
567 continue;
568
569 auto *iface = dyn_cast<ConvertToLLVMPatternInterface>(dialect);
570 if (!iface) {
571 // Error out if dialect was explicily specified but doesn't implement
572 // conversion interface.
573 if (allowed) {
574 m.emitError()
575 << "dialect does not implement ConvertToLLVMPatternInterface: "
576 << dialect->getNamespace();
577 return signalPassFailure();
578 }
579 continue;
580 }
581
582 iface->populateConvertToLLVMConversionPatterns(target, converter,
583 llvmPatterns);
584 }
585
586 populateGpuWMMAToNVVMConversionPatterns(converter, llvmPatterns);
587 if (this->hasRedux)
588 populateGpuSubgroupReduceOpLoweringPattern(converter, llvmPatterns);
590 ConversionConfig config;
591 config.allowPatternRollback = allowPatternRollback;
592 if (failed(
593 applyPartialConversion(m, target, std::move(llvmPatterns), config)))
594 signalPassFailure();
595 }
596};
597
598} // namespace
599
601 target.addIllegalOp<func::FuncOp>();
602 target.addIllegalOp<cf::AssertOp>();
603 target.addLegalDialect<::mlir::LLVM::LLVMDialect>();
604 target.addLegalDialect<::mlir::NVVM::NVVMDialect>();
605 target.addIllegalDialect<gpu::GPUDialect>();
606 target.addIllegalOp<LLVM::CopySignOp, LLVM::CosOp, LLVM::ExpOp, LLVM::Exp2Op,
607 LLVM::FAbsOp, LLVM::FCeilOp, LLVM::FFloorOp, LLVM::FRemOp,
608 LLVM::LogOp, LLVM::Log10Op, LLVM::Log2Op, LLVM::PowOp,
609 LLVM::RoundEvenOp, LLVM::RoundOp, LLVM::SinOp,
610 LLVM::SincosOp, LLVM::SqrtOp>();
611
612 // TODO: Remove once we support replacing non-root ops.
613 target.addLegalOp<gpu::YieldOp, gpu::GPUModuleOp>();
614}
615
618
619 converter.addConversion([&](gpu::NamedBarrierType type) -> Type {
620 Type i32 = IntegerType::get(type.getContext(), 32);
621 return LLVM::LLVMStructType::getLiteral(type.getContext(), {i32, i32});
622 });
623
624 // Lowering for MMAMatrixType.
625 converter.addConversion([&](gpu::MMAMatrixType type) -> Type {
626 return convertMMAToLLVMType(type);
627 });
628}
629
631 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
632 PatternBenefit benefit) {
633 patterns.add<GPUSubgroupReduceOpLowering>(converter, benefit);
634}
635
637 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
638 PatternBenefit benefit) {
641
642 patterns.add<GPUBarrierOpToNVVMLowering,
643 GPUInitializeNamedBarrierOpToNVVMLowering,
644 GPUPrintfOpToVPrintfLowering, AssertOpToAssertfailLowering>(
645 converter, benefit);
646 patterns.add<
647 gpu::index_lowering::OpLowering<gpu::ThreadIdOp, NVVM::ThreadIdXOp,
648 NVVM::ThreadIdYOp, NVVM::ThreadIdZOp>>(
649 converter, IndexKind::Block, IntrType::Id, benefit);
650 patterns.add<
651 gpu::index_lowering::OpLowering<gpu::BlockDimOp, NVVM::BlockDimXOp,
652 NVVM::BlockDimYOp, NVVM::BlockDimZOp>>(
653 converter, IndexKind::Block, IntrType::Dim, benefit);
654 patterns.add<
655 gpu::index_lowering::OpLowering<gpu::ClusterIdOp, NVVM::ClusterIdXOp,
656 NVVM::ClusterIdYOp, NVVM::ClusterIdZOp>>(
657 converter, IndexKind::Other, IntrType::Id, benefit);
659 gpu::ClusterDimOp, NVVM::ClusterDimXOp, NVVM::ClusterDimYOp,
660 NVVM::ClusterDimZOp>>(converter, IndexKind::Other, IntrType::Dim,
661 benefit);
663 gpu::ClusterBlockIdOp, NVVM::BlockInClusterIdXOp,
664 NVVM::BlockInClusterIdYOp, NVVM::BlockInClusterIdZOp>>(
665 converter, IndexKind::Cluster, IntrType::Id, benefit);
667 gpu::ClusterDimBlocksOp, NVVM::ClusterDimBlocksXOp,
668 NVVM::ClusterDimBlocksYOp, NVVM::ClusterDimBlocksZOp>>(
669 converter, IndexKind::Cluster, IntrType::Dim, benefit);
671 gpu::BlockIdOp, NVVM::BlockIdXOp, NVVM::BlockIdYOp, NVVM::BlockIdZOp>>(
672 converter, IndexKind::Grid, IntrType::Id, benefit);
674 gpu::GridDimOp, NVVM::GridDimXOp, NVVM::GridDimYOp, NVVM::GridDimZOp>>(
675 converter, IndexKind::Grid, IntrType::Dim, benefit);
676 patterns.add<GPULaneIdOpToNVVM, GPUBallotOpToNVVM, GPUShuffleOpLowering,
677 GPUReturnOpLowering>(converter, benefit);
678
680 converter, NVVM::kSharedMemoryAlignmentBit, benefit);
681
682 // Explicitly drop memory space when lowering private memory
683 // attributions since NVVM models it as `alloca`s in the default
684 // memory space and does not support `alloca`s with addrspace(5).
685 patterns.add<GPUFuncOpLowering>(
686 converter,
688 /*allocaAddrSpace=*/0,
689 /*workgroupAddrSpace=*/
690 static_cast<unsigned>(NVVM::NVVMMemorySpace::Shared),
691 StringAttr::get(&converter.getContext(),
692 NVVM::NVVMDialect::getKernelFuncAttrName()),
693 StringAttr::get(&converter.getContext(),
694 NVVM::NVVMDialect::getMaxntidAttrName()),
695 StringAttr::get(&converter.getContext(),
696 NVVM::NVVMDialect::getClusterDimAttrName())},
697 benefit);
698
699 populateLibDeviceConversionPatterns(converter, patterns, benefit);
700}
701
702//===----------------------------------------------------------------------===//
703// NVVMTargetAttr convert to LLVM attr interface
704//===----------------------------------------------------------------------===//
705
706namespace {
707struct NVVMTargetConvertToLLVMAttrInterface
708 : public ConvertToLLVMAttrInterface::ExternalModel<
709 NVVMTargetConvertToLLVMAttrInterface, NVVM::NVVMTargetAttr> {
710 /// Configure GPU to NVVM.
711 void populateConvertToLLVMConversionPatterns(
713 LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) const;
714};
715} // namespace
716
717void NVVMTargetConvertToLLVMAttrInterface::
718 populateConvertToLLVMConversionPatterns(Attribute attr,
720 LLVMTypeConverter &typeConverter,
721 RewritePatternSet &patterns) const {
723 configureGpuToNVVMTypeConverter(typeConverter);
724 populateGpuToNVVMConversionPatterns(typeConverter, patterns);
725}
726
728 registry.addExtension(+[](MLIRContext *ctx, NVVMDialect *dialect) {
729 NVVMTargetAttr::attachInterface<NVVMTargetConvertToLLVMAttrInterface>(*ctx);
730 });
731}
return success()
b getContext())
constexpr int kWarpSize
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
Definition Attributes.h:25
auto getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Definition Block.h:213
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
Conversion from types to the LLVM IR dialect.
MLIRContext & getContext() const
Returns the MLIR context.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
This class helps build Operations.
Definition Builders.h:210
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:738
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Definition Operation.h:273
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
Block & front()
Definition Region.h:65
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:25
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MMAMatrix represents a matrix held by a subgroup for matrix-matrix multiply accumulate operations.
Definition GPUDialect.h:145
constexpr int kSharedMemoryAlignmentBit
Definition NVVMDialect.h:49
void registerConvertGpuToNVVMInterface(DialectRegistry &registry)
Registers the ConvertToLLVMAttrInterface interface on the NVVM::NVVMTargetAttr attribute.
void populateCommonGPUTypeAndAttributeConversions(TypeConverter &typeConverter)
Remap common GPU memory spaces (Workgroup, Private, etc) to LLVM address spaces.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
void populateGpuRewritePatterns(RewritePatternSet &patterns)
Collect all patterns to rewrite ops within the GPU dialect.
Definition Passes.h:90
Type convertMMAToLLVMType(gpu::MMAMatrixType type)
Return the LLVMStructureType corresponding to the MMAMatrixType type.
LLVM::LLVMFuncOp getOrDefineFunction(Operation *moduleOp, Location loc, OpBuilder &b, StringRef name, LLVM::LLVMFunctionType type)
Note that these functions don't take a SymbolTable because GPU module lowerings can have name collisi...
void configureGpuToNVVMTypeConverter(LLVMTypeConverter &converter)
Configure the LLVM type convert to convert types and address spaces from the GPU dialect to NVVM.
void configureGpuToNVVMConversionLegality(ConversionTarget &target)
Configure target to convert from the GPU dialect to NVVM.
void registerConvertToLLVMDependentDialectLoading(DialectRegistry &registry)
Register the extension that will load dependent dialects for LLVM conversion.
void populateGpuSubgroupReduceOpLoweringPattern(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate GpuSubgroupReduce pattern to NVVM.
void populateGpuToNVVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of patterns to convert from the GPU dialect to NVVM.
LLVM::GlobalOp getOrCreateStringConstant(OpBuilder &b, Location loc, Operation *moduleOp, Type llvmI8, StringRef namePrefix, StringRef str, uint64_t alignment=0, unsigned addrSpace=0)
Create a global that contains the given string.
void populateLibDeviceConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate the given list with patterns that convert from Math to NVVM libdevice calls.
void populateGpuWMMAToNVVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of patterns to convert WMMA ops from GPU dialect to NVVM.
Lowering for gpu.dynamic.shared.memory to LLVM dialect.
Lowering of gpu.printf to a vprintf standard library.