MLIR 23.0.0git
GPUToSPIRV.cpp
Go to the documentation of this file.
1//===- GPUToSPIRV.cpp - GPU to SPIR-V Patterns ----------------------------===//
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 patterns to convert GPU dialect to SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
21#include "mlir/IR/Matchers.h"
23#include <optional>
24
25using namespace mlir;
26
27static constexpr const char kSPIRVModule[] = "__spv__";
28
29namespace {
30/// Pattern lowering GPU block/thread size/id to loading SPIR-V invocation
31/// builtin variables.
32template <typename SourceOp, spirv::BuiltIn builtin>
33class LaunchConfigConversion : public OpConversionPattern<SourceOp> {
34public:
35 using OpConversionPattern<SourceOp>::OpConversionPattern;
36
37 LogicalResult
38 matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor,
39 ConversionPatternRewriter &rewriter) const override;
40};
41
42/// Pattern lowering subgroup size/id to loading SPIR-V invocation
43/// builtin variables.
44template <typename SourceOp, spirv::BuiltIn builtin>
45class SingleDimLaunchConfigConversion : public OpConversionPattern<SourceOp> {
46public:
47 using OpConversionPattern<SourceOp>::OpConversionPattern;
48
49 LogicalResult
50 matchAndRewrite(SourceOp op, typename SourceOp::Adaptor adaptor,
51 ConversionPatternRewriter &rewriter) const override;
52};
53
54/// This is separate because in Vulkan workgroup size is exposed to shaders via
55/// a constant with WorkgroupSize decoration. So here we cannot generate a
56/// builtin variable; instead the information in the `spirv.entry_point_abi`
57/// attribute on the surrounding FuncOp is used to replace the gpu::BlockDimOp.
58class WorkGroupSizeConversion : public OpConversionPattern<gpu::BlockDimOp> {
59public:
60 WorkGroupSizeConversion(const TypeConverter &typeConverter,
61 MLIRContext *context)
62 : OpConversionPattern(typeConverter, context, /*benefit*/ 10) {}
63
64 LogicalResult
65 matchAndRewrite(gpu::BlockDimOp op, OpAdaptor adaptor,
66 ConversionPatternRewriter &rewriter) const override;
67};
68
69/// Pattern to convert a kernel function in GPU dialect within a spirv.module.
70class GPUFuncOpConversion final : public OpConversionPattern<gpu::GPUFuncOp> {
71public:
72 using Base::Base;
73
74 LogicalResult
75 matchAndRewrite(gpu::GPUFuncOp funcOp, OpAdaptor adaptor,
76 ConversionPatternRewriter &rewriter) const override;
77
78private:
79 SmallVector<int32_t, 3> workGroupSizeAsInt32;
80};
81
82/// Pattern to convert a gpu.module to a spirv.module.
83class GPUModuleConversion final : public OpConversionPattern<gpu::GPUModuleOp> {
84public:
85 using Base::Base;
86
87 LogicalResult
88 matchAndRewrite(gpu::GPUModuleOp moduleOp, OpAdaptor adaptor,
89 ConversionPatternRewriter &rewriter) const override;
90};
91
92/// Pattern to convert a gpu.return into a SPIR-V return.
93// TODO: This can go to DRR when GPU return has operands.
94class GPUReturnOpConversion final : public OpConversionPattern<gpu::ReturnOp> {
95public:
96 using Base::Base;
97
98 LogicalResult
99 matchAndRewrite(gpu::ReturnOp returnOp, OpAdaptor adaptor,
100 ConversionPatternRewriter &rewriter) const override;
101};
102
103/// Pattern to convert a gpu.barrier op into a spirv.ControlBarrier or
104/// spirv.MemoryNamedBarrier op.
105class GPUBarrierConversion final : public OpConversionPattern<gpu::BarrierOp> {
106public:
107 using Base::Base;
108
109 LogicalResult
110 matchAndRewrite(gpu::BarrierOp barrierOp, OpAdaptor adaptor,
111 ConversionPatternRewriter &rewriter) const override;
112};
113
114/// Pattern to convert a gpu.initialize_named_barrier into
115/// spirv.NamedBarrierInitialize.
116class GPUInitializeNamedBarrierConversion final
117 : public OpConversionPattern<gpu::InitializeNamedBarrierOp> {
118public:
119 using Base::Base;
120
121 LogicalResult
122 matchAndRewrite(gpu::InitializeNamedBarrierOp op, OpAdaptor adaptor,
123 ConversionPatternRewriter &rewriter) const override;
124};
125
126/// Pattern to convert a gpu.shuffle op into a spirv.GroupNonUniformShuffle op.
127class GPUShuffleConversion final : public OpConversionPattern<gpu::ShuffleOp> {
128public:
129 using Base::Base;
130
131 LogicalResult
132 matchAndRewrite(gpu::ShuffleOp shuffleOp, OpAdaptor adaptor,
133 ConversionPatternRewriter &rewriter) const override;
134};
135
136/// Pattern to convert a gpu.rotate op into a spirv.GroupNonUniformRotateKHROp.
137class GPURotateConversion final : public OpConversionPattern<gpu::RotateOp> {
138public:
139 using Base::Base;
140
141 LogicalResult
142 matchAndRewrite(gpu::RotateOp rotateOp, OpAdaptor adaptor,
143 ConversionPatternRewriter &rewriter) const override;
144};
145
146/// Pattern to convert a gpu.subgroup_broadcast op into a
147/// spirv.GroupNonUniformBroadcast op.
148class GPUSubgroupBroadcastConversion final
149 : public OpConversionPattern<gpu::SubgroupBroadcastOp> {
150public:
151 using Base::Base;
152
153 LogicalResult
154 matchAndRewrite(gpu::SubgroupBroadcastOp op, OpAdaptor adaptor,
155 ConversionPatternRewriter &rewriter) const override;
156};
157
158class GPUBallotConversion final : public OpConversionPattern<gpu::BallotOp> {
159public:
160 using Base::Base;
161
162 LogicalResult
163 matchAndRewrite(gpu::BallotOp ballotOp, OpAdaptor adaptor,
164 ConversionPatternRewriter &rewriter) const override;
165};
166
167class GPUPrintfConversion final : public OpConversionPattern<gpu::PrintfOp> {
168public:
169 using Base::Base;
170
171 LogicalResult
172 matchAndRewrite(gpu::PrintfOp gpuPrintfOp, OpAdaptor adaptor,
173 ConversionPatternRewriter &rewriter) const override;
174};
175
176} // namespace
177
178//===----------------------------------------------------------------------===//
179// Builtins.
180//===----------------------------------------------------------------------===//
181
182template <typename SourceOp, spirv::BuiltIn builtin>
183LogicalResult LaunchConfigConversion<SourceOp, builtin>::matchAndRewrite(
184 SourceOp op, typename SourceOp::Adaptor adaptor,
185 ConversionPatternRewriter &rewriter) const {
186 auto *typeConverter = this->template getTypeConverter<SPIRVTypeConverter>();
187 Type indexType = typeConverter->getIndexType();
188
189 // For Vulkan, these SPIR-V builtin variables are required to be a vector of
190 // type <3xi32> by the spec:
191 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/NumWorkgroups.html
192 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/WorkgroupId.html
193 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/WorkgroupSize.html
194 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/LocalInvocationId.html
195 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/LocalInvocationId.html
196 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/GlobalInvocationId.html
197 //
198 // For OpenCL, it depends on the Physical32/Physical64 addressing model:
199 // https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_built_in_variables
200 bool forShader =
201 typeConverter->getTargetEnv().allows(spirv::Capability::Shader);
202 Type builtinType = forShader ? rewriter.getIntegerType(32) : indexType;
203
204 Value vector =
205 spirv::getBuiltinVariableValue(op, builtin, builtinType, rewriter);
206 Value dim = spirv::CompositeExtractOp::create(
207 rewriter, op.getLoc(), builtinType, vector,
208 rewriter.getI32ArrayAttr({static_cast<int32_t>(op.getDimension())}));
209 if (forShader && builtinType != indexType)
210 dim = spirv::UConvertOp::create(rewriter, op.getLoc(), indexType, dim);
211 rewriter.replaceOp(op, dim);
212 return success();
213}
214
215template <typename SourceOp, spirv::BuiltIn builtin>
216LogicalResult
217SingleDimLaunchConfigConversion<SourceOp, builtin>::matchAndRewrite(
218 SourceOp op, typename SourceOp::Adaptor adaptor,
219 ConversionPatternRewriter &rewriter) const {
220 auto *typeConverter = this->template getTypeConverter<SPIRVTypeConverter>();
221 Type indexType = typeConverter->getIndexType();
222 Type i32Type = rewriter.getIntegerType(32);
223
224 // For Vulkan, these SPIR-V builtin variables are required to be a vector of
225 // type i32 by the spec:
226 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/NumSubgroups.html
227 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/SubgroupId.html
228 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/SubgroupSize.html
229 //
230 // For OpenCL, they are also required to be i32:
231 // https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_built_in_variables
232 Value builtinValue =
233 spirv::getBuiltinVariableValue(op, builtin, i32Type, rewriter);
234 if (i32Type != indexType)
235 builtinValue = spirv::UConvertOp::create(rewriter, op.getLoc(), indexType,
236 builtinValue);
237 rewriter.replaceOp(op, builtinValue);
238 return success();
239}
240
241LogicalResult WorkGroupSizeConversion::matchAndRewrite(
242 gpu::BlockDimOp op, OpAdaptor adaptor,
243 ConversionPatternRewriter &rewriter) const {
245 if (!workGroupSizeAttr)
246 return failure();
247
248 int val =
249 workGroupSizeAttr.asArrayRef()[static_cast<int32_t>(op.getDimension())];
250 auto convertedType =
251 getTypeConverter()->convertType(op.getResult().getType());
252 if (!convertedType)
253 return failure();
254 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(
255 op, convertedType, IntegerAttr::get(convertedType, val));
256 return success();
257}
258
259//===----------------------------------------------------------------------===//
260// GPUFuncOp
261//===----------------------------------------------------------------------===//
262
263// Legalizes a GPU function as an entry SPIR-V function.
264static spirv::FuncOp
265lowerAsEntryFunction(gpu::GPUFuncOp funcOp, const TypeConverter &typeConverter,
266 ConversionPatternRewriter &rewriter,
267 spirv::EntryPointABIAttr entryPointInfo,
269 auto fnType = funcOp.getFunctionType();
270 if (fnType.getNumResults()) {
271 funcOp.emitError("SPIR-V lowering only supports entry functions"
272 "with no return values right now");
273 return nullptr;
274 }
275 if (!argABIInfo.empty() && fnType.getNumInputs() != argABIInfo.size()) {
276 funcOp.emitError(
277 "lowering as entry functions requires ABI info for all arguments "
278 "or none of them");
279 return nullptr;
280 }
281 // Update the signature to valid SPIR-V types and add the ABI
282 // attributes. These will be "materialized" by using the
283 // LowerABIAttributesPass.
284 TypeConverter::SignatureConversion signatureConverter(fnType.getNumInputs());
285 {
286 for (const auto &argType :
287 enumerate(funcOp.getFunctionType().getInputs())) {
288 auto convertedType = typeConverter.convertType(argType.value());
289 if (!convertedType)
290 return nullptr;
291 signatureConverter.addInputs(argType.index(), convertedType);
292 }
293 }
294 auto newFuncOp = spirv::FuncOp::create(
295 rewriter, funcOp.getLoc(), funcOp.getName(),
296 rewriter.getFunctionType(signatureConverter.getConvertedTypes(), {}));
297 for (const auto &namedAttr : funcOp->getAttrs()) {
298 if (namedAttr.getName() == funcOp.getFunctionTypeAttrName() ||
299 namedAttr.getName() == SymbolTable::getSymbolAttrName())
300 continue;
301 newFuncOp->setAttr(namedAttr.getName(), namedAttr.getValue());
302 }
303
304 rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
305 newFuncOp.end());
306 if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), typeConverter,
307 &signatureConverter)))
308 return nullptr;
309 rewriter.eraseOp(funcOp);
310
311 // Set the attributes for argument and the function.
312 StringRef argABIAttrName = spirv::getInterfaceVarABIAttrName();
313 for (auto argIndex : llvm::seq<unsigned>(0, argABIInfo.size())) {
314 newFuncOp.setArgAttr(argIndex, argABIAttrName, argABIInfo[argIndex]);
315 }
316 newFuncOp->setAttr(spirv::getEntryPointABIAttrName(), entryPointInfo);
317
318 return newFuncOp;
319}
320
321/// Populates `argABI` with spirv.interface_var_abi attributes for lowering
322/// gpu.func to spirv.func if no arguments have the attributes set
323/// already. Returns failure if any argument has the ABI attribute set already.
324static LogicalResult
325getDefaultABIAttrs(const spirv::TargetEnv &targetEnv, gpu::GPUFuncOp funcOp,
327 if (!spirv::needsInterfaceVarABIAttrs(targetEnv))
328 return success();
329
330 for (auto argIndex : llvm::seq<unsigned>(0, funcOp.getNumArguments())) {
331 if (funcOp.getArgAttrOfType<spirv::InterfaceVarABIAttr>(
333 return failure();
334 // Vulkan's interface variable requirements needs scalars to be wrapped in a
335 // struct. The struct held in storage buffer.
336 std::optional<spirv::StorageClass> sc;
337 if (funcOp.getArgument(argIndex).getType().isIntOrIndexOrFloat())
338 sc = spirv::StorageClass::StorageBuffer;
339 argABI.push_back(
340 spirv::getInterfaceVarABIAttr(0, argIndex, sc, funcOp.getContext()));
341 }
342 return success();
343}
344
345LogicalResult GPUFuncOpConversion::matchAndRewrite(
346 gpu::GPUFuncOp funcOp, OpAdaptor adaptor,
347 ConversionPatternRewriter &rewriter) const {
348 if (!gpu::GPUDialect::isKernel(funcOp))
349 return failure();
350
351 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
352 SmallVector<spirv::InterfaceVarABIAttr, 4> argABI;
353 if (failed(
354 getDefaultABIAttrs(typeConverter->getTargetEnv(), funcOp, argABI))) {
355 argABI.clear();
356 for (auto argIndex : llvm::seq<unsigned>(0, funcOp.getNumArguments())) {
357 // If the ABI is already specified, use it.
358 auto abiAttr = funcOp.getArgAttrOfType<spirv::InterfaceVarABIAttr>(
360 if (!abiAttr) {
361 funcOp.emitRemark(
362 "match failure: missing 'spirv.interface_var_abi' attribute at "
363 "argument ")
364 << argIndex;
365 return failure();
366 }
367 argABI.push_back(abiAttr);
368 }
369 }
370
371 auto entryPointAttr = spirv::lookupEntryPointABI(funcOp);
372 if (!entryPointAttr) {
373 funcOp.emitRemark(
374 "match failure: missing 'spirv.entry_point_abi' attribute");
375 return failure();
376 }
377 spirv::FuncOp newFuncOp = lowerAsEntryFunction(
378 funcOp, *getTypeConverter(), rewriter, entryPointAttr, argABI);
379 if (!newFuncOp)
380 return failure();
381 newFuncOp->removeAttr(
382 rewriter.getStringAttr(gpu::GPUDialect::getKernelFuncAttrName()));
383 return success();
384}
385
386//===----------------------------------------------------------------------===//
387// ModuleOp with gpu.module.
388//===----------------------------------------------------------------------===//
389
390LogicalResult GPUModuleConversion::matchAndRewrite(
391 gpu::GPUModuleOp moduleOp, OpAdaptor adaptor,
392 ConversionPatternRewriter &rewriter) const {
393 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
394 const spirv::TargetEnv &targetEnv = typeConverter->getTargetEnv();
395 spirv::AddressingModel addressingModel = spirv::getAddressingModel(
396 targetEnv, typeConverter->getOptions().use64bitIndex);
397 FailureOr<spirv::MemoryModel> memoryModel = spirv::getMemoryModel(targetEnv);
398 if (failed(memoryModel))
399 return moduleOp.emitRemark(
400 "cannot deduce memory model from 'spirv.target_env'");
401
402 // Add a keyword to the module name to avoid symbolic conflict.
403 std::string spvModuleName = (kSPIRVModule + moduleOp.getName()).str();
404 auto spvModule = spirv::ModuleOp::create(
405 rewriter, moduleOp.getLoc(), addressingModel, *memoryModel, std::nullopt,
406 StringRef(spvModuleName));
407
408 // Move the region from the module op into the SPIR-V module.
409 Region &spvModuleRegion = spvModule.getRegion();
410 rewriter.inlineRegionBefore(moduleOp.getBodyRegion(), spvModuleRegion,
411 spvModuleRegion.begin());
412 // The spirv.module build method adds a block. Remove that.
413 rewriter.eraseBlock(&spvModuleRegion.back());
414
415 // Some of the patterns call `lookupTargetEnv` during conversion and they
416 // will fail if called after GPUModuleConversion and we don't preserve
417 // `TargetEnv` attribute.
418 // Copy TargetEnvAttr only if it is attached directly to the GPUModuleOp.
419 if (auto attr = moduleOp->getAttrOfType<spirv::TargetEnvAttr>(
421 spvModule->setAttr(spirv::getTargetEnvAttrName(), attr);
422 if (ArrayAttr targets = moduleOp.getTargetsAttr()) {
423 for (Attribute targetAttr : targets)
424 if (auto spirvTargetEnvAttr =
425 dyn_cast<spirv::TargetEnvAttr>(targetAttr)) {
426 spvModule->setAttr(spirv::getTargetEnvAttrName(), spirvTargetEnvAttr);
427 break;
428 }
429 }
430
431 rewriter.eraseOp(moduleOp);
432 return success();
433}
434
435//===----------------------------------------------------------------------===//
436// GPU return inside kernel functions to SPIR-V return.
437//===----------------------------------------------------------------------===//
438
439LogicalResult GPUReturnOpConversion::matchAndRewrite(
440 gpu::ReturnOp returnOp, OpAdaptor adaptor,
441 ConversionPatternRewriter &rewriter) const {
442 if (!adaptor.getOperands().empty())
443 return failure();
444
445 rewriter.replaceOpWithNewOp<spirv::ReturnOp>(returnOp);
446 return success();
447}
448
449//===----------------------------------------------------------------------===//
450// Barrier.
451//===----------------------------------------------------------------------===//
452
453/// Map gpu::BarrierScope to spirv::Scope.
454static FailureOr<spirv::Scope>
455mapGPUBarrierScopeToSPIRV(gpu::BarrierScope gpuScope) {
456 switch (gpuScope) {
457 case gpu::BarrierScope::Subgroup:
458 return spirv::Scope::Subgroup;
459 case gpu::BarrierScope::Workgroup:
460 return spirv::Scope::Workgroup;
461 case gpu::BarrierScope::Cluster:
462 return failure();
463 }
464 return failure();
465}
466
467LogicalResult GPUBarrierConversion::matchAndRewrite(
468 gpu::BarrierOp barrierOp, OpAdaptor adaptor,
469 ConversionPatternRewriter &rewriter) const {
470 MLIRContext *context = getContext();
471
472 // Map GPU scope to SPIR-V scope.
473 auto spirvScope = mapGPUBarrierScopeToSPIRV(barrierOp.getScope());
474 if (failed(spirvScope))
475 return rewriter.notifyMatchFailure(
476 barrierOp, "cluster scope is not supported in SPIR-V");
477
478 auto scopeAttr = spirv::ScopeAttr::get(context, *spirvScope);
479 auto memoryScopeAttr =
480 spirv::ScopeAttr::get(context, spirv::Scope::Workgroup);
481
482 // Require acquire and release memory semantics for workgroup memory.
483 auto memorySemantics = spirv::MemorySemanticsAttr::get(
484 context, spirv::MemorySemantics::WorkgroupMemory |
485 spirv::MemorySemantics::AcquireRelease);
486
487 if (adaptor.getNamedBarrier()) {
488 spirv::MemoryNamedBarrierOp::create(rewriter, barrierOp.getLoc(),
489 adaptor.getNamedBarrier(),
490 memoryScopeAttr, memorySemantics);
491 rewriter.eraseOp(barrierOp);
492 } else {
493 rewriter.replaceOpWithNewOp<spirv::ControlBarrierOp>(
494 barrierOp, scopeAttr, memoryScopeAttr, memorySemantics);
495 }
496 return success();
497}
498
499LogicalResult GPUInitializeNamedBarrierConversion::matchAndRewrite(
500 gpu::InitializeNamedBarrierOp op, OpAdaptor adaptor,
501 ConversionPatternRewriter &rewriter) const {
503 rewriter.replaceOpWithNewOp<spirv::NamedBarrierInitializeOp>(
504 op, nbType, adaptor.getMemberCount());
505 return success();
506}
507
508//===----------------------------------------------------------------------===//
509// Shuffle
510//===----------------------------------------------------------------------===//
511
512LogicalResult GPUShuffleConversion::matchAndRewrite(
513 gpu::ShuffleOp shuffleOp, OpAdaptor adaptor,
514 ConversionPatternRewriter &rewriter) const {
515 // Require the shuffle width to be the same as the target's subgroup size,
516 // given that for SPIR-V non-uniform subgroup ops, we cannot select
517 // participating invocations.
518 auto targetEnv = getTypeConverter<SPIRVTypeConverter>()->getTargetEnv();
519 unsigned subgroupSize =
520 targetEnv.getAttr().getResourceLimits().getSubgroupSize();
521 IntegerAttr widthAttr;
522 if (!matchPattern(shuffleOp.getWidth(), m_Constant(&widthAttr)) ||
523 widthAttr.getValue().getZExtValue() != subgroupSize)
524 return rewriter.notifyMatchFailure(
525 shuffleOp, "shuffle width and target subgroup size mismatch");
526
527 assert(!adaptor.getOffset().getType().isSignedInteger() &&
528 "shuffle offset must be a signless/unsigned integer");
529
530 Location loc = shuffleOp.getLoc();
531 auto scope = rewriter.getAttr<spirv::ScopeAttr>(spirv::Scope::Subgroup);
532 Value result;
533 Value validVal;
534
535 switch (shuffleOp.getMode()) {
536 case gpu::ShuffleMode::XOR: {
537 result = spirv::GroupNonUniformShuffleXorOp::create(
538 rewriter, loc, scope, adaptor.getValue(), adaptor.getOffset());
539 validVal = spirv::ConstantOp::getOne(rewriter.getI1Type(),
540 shuffleOp.getLoc(), rewriter);
541 break;
542 }
543 case gpu::ShuffleMode::IDX: {
544 result = spirv::GroupNonUniformShuffleOp::create(
545 rewriter, loc, scope, adaptor.getValue(), adaptor.getOffset());
546 validVal = spirv::ConstantOp::getOne(rewriter.getI1Type(),
547 shuffleOp.getLoc(), rewriter);
548 break;
549 }
550 case gpu::ShuffleMode::DOWN: {
551 result = spirv::GroupNonUniformShuffleDownOp::create(
552 rewriter, loc, scope, adaptor.getValue(), adaptor.getOffset());
553
554 Value laneId = gpu::LaneIdOp::create(rewriter, loc, widthAttr);
555 Value resultLaneId =
556 arith::AddIOp::create(rewriter, loc, laneId, adaptor.getOffset());
557 validVal = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ult,
558 resultLaneId, adaptor.getWidth());
559 break;
560 }
561 case gpu::ShuffleMode::UP: {
562 result = spirv::GroupNonUniformShuffleUpOp::create(
563 rewriter, loc, scope, adaptor.getValue(), adaptor.getOffset());
564
565 Value laneId = gpu::LaneIdOp::create(rewriter, loc, widthAttr);
566 Value resultLaneId =
567 arith::SubIOp::create(rewriter, loc, laneId, adaptor.getOffset());
568 auto i32Type = rewriter.getIntegerType(32);
569 validVal = arith::CmpIOp::create(
570 rewriter, loc, arith::CmpIPredicate::sge, resultLaneId,
571 arith::ConstantOp::create(rewriter, loc, i32Type,
572 rewriter.getIntegerAttr(i32Type, 0)));
573 break;
574 }
575 }
576
577 rewriter.replaceOp(shuffleOp, {result, validVal});
578 return success();
579}
580
581//===----------------------------------------------------------------------===//
582// Rotate
583//===----------------------------------------------------------------------===//
584
585LogicalResult GPURotateConversion::matchAndRewrite(
586 gpu::RotateOp rotateOp, OpAdaptor adaptor,
587 ConversionPatternRewriter &rewriter) const {
588 const spirv::TargetEnv &targetEnv =
589 getTypeConverter<SPIRVTypeConverter>()->getTargetEnv();
590 unsigned subgroupSize =
591 targetEnv.getAttr().getResourceLimits().getSubgroupSize();
592 unsigned width = rotateOp.getWidth();
593 if (width > subgroupSize)
594 return rewriter.notifyMatchFailure(
595 rotateOp, "rotate width is larger than target subgroup size");
596
597 Location loc = rotateOp.getLoc();
598 auto scope = rewriter.getAttr<spirv::ScopeAttr>(spirv::Scope::Subgroup);
599 Value offsetVal =
600 arith::ConstantOp::create(rewriter, loc, adaptor.getOffsetAttr());
601 Value widthVal =
602 arith::ConstantOp::create(rewriter, loc, adaptor.getWidthAttr());
603 Value rotateResult = spirv::GroupNonUniformRotateKHROp::create(
604 rewriter, loc, scope, adaptor.getValue(), offsetVal, widthVal);
605 Value validVal;
606 if (width == subgroupSize) {
607 validVal = spirv::ConstantOp::getOne(rewriter.getI1Type(), loc, rewriter);
608 } else {
609 IntegerAttr widthAttr = adaptor.getWidthAttr();
610 Value laneId = gpu::LaneIdOp::create(rewriter, loc, widthAttr);
611 validVal = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ult,
612 laneId, widthVal);
613 }
614
615 rewriter.replaceOp(rotateOp, {rotateResult, validVal});
616 return success();
617}
618
619//===----------------------------------------------------------------------===//
620// Subgroup broadcast
621//===----------------------------------------------------------------------===//
622
623LogicalResult GPUSubgroupBroadcastConversion::matchAndRewrite(
624 gpu::SubgroupBroadcastOp op, OpAdaptor adaptor,
625 ConversionPatternRewriter &rewriter) const {
626 Location loc = op.getLoc();
627 auto scope = rewriter.getAttr<spirv::ScopeAttr>(spirv::Scope::Subgroup);
628 Value result;
629
630 switch (op.getBroadcastType()) {
631 case gpu::BroadcastType::specific_lane:
632 result = spirv::GroupNonUniformBroadcastOp::create(
633 rewriter, loc, scope, adaptor.getSrc(), adaptor.getLane());
634 break;
635 case gpu::BroadcastType::first_active_lane:
636 result = spirv::GroupNonUniformBroadcastFirstOp::create(
637 rewriter, loc, scope, adaptor.getSrc());
638 break;
639 }
640
641 rewriter.replaceOp(op, result);
642 return success();
643}
644
645LogicalResult GPUBallotConversion::matchAndRewrite(
646 gpu::BallotOp ballotOp, OpAdaptor adaptor,
647 ConversionPatternRewriter &rewriter) const {
648 Location loc = ballotOp.getLoc();
649 auto scope = rewriter.getAttr<spirv::ScopeAttr>(spirv::Scope::Subgroup);
650 auto int32Type = rewriter.getI32Type();
651 auto vec4i32Type = VectorType::get({4}, int32Type);
652
653 // SPIR-V ballot returns vector<4xi32> to support subgroups up to 128 lanes.
654 Value ballot = spirv::GroupNonUniformBallotOp::create(
655 rewriter, loc, vec4i32Type, scope, adaptor.getPredicate());
656
657 auto intType = cast<IntegerType>(ballotOp.getType());
658 unsigned width = intType.getWidth();
659
660 if (width == 32) {
661 Value result =
662 spirv::CompositeExtractOp::create(rewriter, loc, ballot, {0});
663 rewriter.replaceOp(ballotOp, result);
664 } else if (width == 64) {
665 // Combine first two vector elements: low 32 bits + (high 32 bits << 32).
666 Value low = spirv::CompositeExtractOp::create(rewriter, loc, ballot, {0});
667 Value high = spirv::CompositeExtractOp::create(rewriter, loc, ballot, {1});
668
669 auto int64Type = rewriter.getI64Type();
670 Value lowExt = spirv::UConvertOp::create(rewriter, loc, int64Type, low);
671 Value highExt = spirv::UConvertOp::create(rewriter, loc, int64Type, high);
672
673 Value shift32 = spirv::ConstantOp::create(
674 rewriter, loc, int64Type, rewriter.getIntegerAttr(int64Type, 32));
675 Value highShifted =
676 spirv::ShiftLeftLogicalOp::create(rewriter, loc, highExt, shift32);
677
678 Value result =
679 spirv::BitwiseOrOp::create(rewriter, loc, lowExt, highShifted);
680 rewriter.replaceOp(ballotOp, result);
681 } else {
682 return rewriter.notifyMatchFailure(
683 ballotOp, "only i32 and i64 result types are supported for SPIR-V");
684 }
685
686 return success();
687}
688
689//===----------------------------------------------------------------------===//
690// Group ops
691//===----------------------------------------------------------------------===//
692
693template <typename UniformOp, typename NonUniformOp>
695 Value arg, bool isGroup, bool isUniform,
696 std::optional<uint32_t> clusterSize) {
697 Type type = arg.getType();
698 auto scope = mlir::spirv::ScopeAttr::get(builder.getContext(),
699 isGroup ? spirv::Scope::Workgroup
700 : spirv::Scope::Subgroup);
701 auto groupOp = spirv::GroupOperationAttr::get(
702 builder.getContext(), clusterSize.has_value()
703 ? spirv::GroupOperation::ClusteredReduce
704 : spirv::GroupOperation::Reduce);
705 if (isUniform) {
706 return UniformOp::create(builder, loc, type, scope, groupOp, arg)
707 .getResult();
708 }
709
710 Value clusterSizeValue;
711 if (clusterSize.has_value())
712 clusterSizeValue = spirv::ConstantOp::create(
713 builder, loc, builder.getI32Type(),
714 builder.getIntegerAttr(builder.getI32Type(), *clusterSize));
715
716 return NonUniformOp::create(builder, loc, type, scope, groupOp, arg,
717 clusterSizeValue)
718 .getResult();
719}
720
721static std::optional<Value>
723 gpu::AllReduceOperation opType, bool isGroup,
724 bool isUniform, std::optional<uint32_t> clusterSize) {
725 enum class ElemType { Float, Boolean, Integer };
726 using FuncT = Value (*)(OpBuilder &, Location, Value, bool, bool,
727 std::optional<uint32_t>);
728 struct OpHandler {
729 gpu::AllReduceOperation kind;
730 ElemType elemType;
731 FuncT func;
732 };
733
734 Type type = arg.getType();
735 ElemType elementType;
736 if (isa<FloatType>(type)) {
737 elementType = ElemType::Float;
738 } else if (auto intTy = dyn_cast<IntegerType>(type)) {
739 elementType = (intTy.getIntOrFloatBitWidth() == 1) ? ElemType::Boolean
740 : ElemType::Integer;
741 } else {
742 return std::nullopt;
743 }
744
745 // TODO(https://github.com/llvm/llvm-project/issues/73459): The SPIR-V spec
746 // does not specify how -0.0 / +0.0 and NaN values are handled in *FMin/*FMax
747 // reduction ops. We should account possible precision requirements in this
748 // conversion.
749
750 using ReduceType = gpu::AllReduceOperation;
751 const OpHandler handlers[] = {
752 {ReduceType::ADD, ElemType::Integer,
753 &createGroupReduceOpImpl<spirv::GroupIAddOp,
754 spirv::GroupNonUniformIAddOp>},
755 {ReduceType::ADD, ElemType::Float,
756 &createGroupReduceOpImpl<spirv::GroupFAddOp,
757 spirv::GroupNonUniformFAddOp>},
758 {ReduceType::MUL, ElemType::Integer,
759 &createGroupReduceOpImpl<spirv::GroupIMulKHROp,
760 spirv::GroupNonUniformIMulOp>},
761 {ReduceType::MUL, ElemType::Float,
762 &createGroupReduceOpImpl<spirv::GroupFMulKHROp,
763 spirv::GroupNonUniformFMulOp>},
764 {ReduceType::MINUI, ElemType::Integer,
765 &createGroupReduceOpImpl<spirv::GroupUMinOp,
766 spirv::GroupNonUniformUMinOp>},
767 {ReduceType::MINSI, ElemType::Integer,
768 &createGroupReduceOpImpl<spirv::GroupSMinOp,
769 spirv::GroupNonUniformSMinOp>},
770 {ReduceType::MINNUMF, ElemType::Float,
771 &createGroupReduceOpImpl<spirv::GroupFMinOp,
772 spirv::GroupNonUniformFMinOp>},
773 {ReduceType::MAXUI, ElemType::Integer,
774 &createGroupReduceOpImpl<spirv::GroupUMaxOp,
775 spirv::GroupNonUniformUMaxOp>},
776 {ReduceType::MAXSI, ElemType::Integer,
777 &createGroupReduceOpImpl<spirv::GroupSMaxOp,
778 spirv::GroupNonUniformSMaxOp>},
779 {ReduceType::MAXNUMF, ElemType::Float,
780 &createGroupReduceOpImpl<spirv::GroupFMaxOp,
781 spirv::GroupNonUniformFMaxOp>},
782 {ReduceType::MINIMUMF, ElemType::Float,
783 &createGroupReduceOpImpl<spirv::GroupFMinOp,
784 spirv::GroupNonUniformFMinOp>},
785 {ReduceType::MAXIMUMF, ElemType::Float,
786 &createGroupReduceOpImpl<spirv::GroupFMaxOp,
787 spirv::GroupNonUniformFMaxOp>}};
788
789 for (const OpHandler &handler : handlers)
790 if (handler.kind == opType && elementType == handler.elemType)
791 return handler.func(builder, loc, arg, isGroup, isUniform, clusterSize);
792
793 return std::nullopt;
794}
795
796/// Pattern to convert a gpu.all_reduce op into a SPIR-V group op.
798 : public OpConversionPattern<gpu::AllReduceOp> {
799public:
800 using Base::Base;
801
802 LogicalResult
803 matchAndRewrite(gpu::AllReduceOp op, OpAdaptor adaptor,
804 ConversionPatternRewriter &rewriter) const override {
805 auto opType = op.getOp();
806
807 // gpu.all_reduce can have either reduction op attribute or reduction
808 // region. Only attribute version is supported.
809 if (!opType)
810 return failure();
811
812 auto result =
813 createGroupReduceOp(rewriter, op.getLoc(), adaptor.getValue(), *opType,
814 /*isGroup*/ true, op.getUniform(), std::nullopt);
815 if (!result)
816 return failure();
817
818 rewriter.replaceOp(op, *result);
819 return success();
820 }
821};
822
823/// Pattern to convert a gpu.subgroup_reduce op into a SPIR-V group op.
825 : public OpConversionPattern<gpu::SubgroupReduceOp> {
826public:
827 using Base::Base;
828
829 LogicalResult
830 matchAndRewrite(gpu::SubgroupReduceOp op, OpAdaptor adaptor,
831 ConversionPatternRewriter &rewriter) const override {
832 if (op.getClusterStride() > 1) {
833 return rewriter.notifyMatchFailure(
834 op, "lowering for cluster stride > 1 is not implemented");
835 }
836
837 if (!isa<spirv::ScalarType>(adaptor.getValue().getType()))
838 return rewriter.notifyMatchFailure(op, "reduction type is not a scalar");
839
841 rewriter, op.getLoc(), adaptor.getValue(), adaptor.getOp(),
842 /*isGroup=*/false, adaptor.getUniform(), op.getClusterSize());
843 if (!result)
844 return failure();
845
846 rewriter.replaceOp(op, *result);
847 return success();
848 }
849};
850
851// Formulate a unique variable/constant name after
852// searching in the module for existing variable/constant names.
853// This is to avoid name collision with existing variables.
854// Example: printfMsg0, printfMsg1, printfMsg2, ...
855static std::string makeVarName(spirv::ModuleOp moduleOp, llvm::Twine prefix) {
856 std::string name;
857 unsigned number = 0;
858
859 do {
860 name.clear();
861 name = (prefix + llvm::Twine(number++)).str();
862 } while (moduleOp.lookupSymbol(name));
863
864 return name;
865}
866
867/// Pattern to convert a gpu.printf op into a SPIR-V CLPrintf op.
868
869LogicalResult GPUPrintfConversion::matchAndRewrite(
870 gpu::PrintfOp gpuPrintfOp, OpAdaptor adaptor,
871 ConversionPatternRewriter &rewriter) const {
872
873 Location loc = gpuPrintfOp.getLoc();
874
875 auto moduleOp = gpuPrintfOp->getParentOfType<spirv::ModuleOp>();
876 if (!moduleOp)
877 return failure();
878
879 // SPIR-V global variable is used to initialize printf
880 // format string value, if there are multiple printf messages,
881 // each global var needs to be created with a unique name.
882 std::string globalVarName = makeVarName(moduleOp, llvm::Twine("printfMsg"));
883 spirv::GlobalVariableOp globalVar;
884
885 IntegerType i8Type = rewriter.getI8Type();
886 IntegerType i32Type = rewriter.getI32Type();
887
888 // Each character of printf format string is
889 // stored as a spec constant. We need to create
890 // unique name for this spec constant like
891 // @printfMsg0_sc0, @printfMsg0_sc1, ... by searching in the module
892 // for existing spec constant names.
893 auto createSpecConstant = [&](unsigned value) {
894 auto attr = rewriter.getI8IntegerAttr(value);
895 std::string specCstName =
896 makeVarName(moduleOp, llvm::Twine(globalVarName) + "_sc");
897
898 return spirv::SpecConstantOp::create(
899 rewriter, loc, rewriter.getStringAttr(specCstName), attr);
900 };
901 {
902 Operation *parent =
903 SymbolTable::getNearestSymbolTable(gpuPrintfOp->getParentOp());
904
905 ConversionPatternRewriter::InsertionGuard guard(rewriter);
906
907 Block &entryBlock = *parent->getRegion(0).begin();
908 rewriter.setInsertionPointToStart(
909 &entryBlock); // insertion point at module level
910
911 // Create Constituents with SpecConstant by scanning format string
912 // Each character of format string is stored as a spec constant
913 // and then these spec constants are used to create a
914 // SpecConstantCompositeOp.
915 llvm::SmallString<20> formatString(adaptor.getFormat());
916 formatString.push_back('\0'); // Null terminate for C.
917 SmallVector<Attribute, 4> constituents;
918 for (char c : formatString) {
919 spirv::SpecConstantOp cSpecConstantOp = createSpecConstant(c);
920 constituents.push_back(SymbolRefAttr::get(cSpecConstantOp));
921 }
922
923 // Create SpecConstantCompositeOp to initialize the global variable
924 size_t contentSize = constituents.size();
925 auto globalType = spirv::ArrayType::get(i8Type, contentSize);
926 spirv::SpecConstantCompositeOp specCstComposite;
927 // There will be one SpecConstantCompositeOp per printf message/global var,
928 // so no need do lookup for existing ones.
929 std::string specCstCompositeName =
930 (llvm::Twine(globalVarName) + "_scc").str();
931
932 specCstComposite = spirv::SpecConstantCompositeOp::create(
933 rewriter, loc, TypeAttr::get(globalType),
934 rewriter.getStringAttr(specCstCompositeName),
935 rewriter.getArrayAttr(constituents));
936
937 auto ptrType = spirv::PointerType::get(
938 globalType, spirv::StorageClass::UniformConstant);
939
940 // Define a GlobalVarOp initialized using specialized constants
941 // that is used to specify the printf format string
942 // to be passed to the SPIRV CLPrintfOp.
943 globalVar = spirv::GlobalVariableOp::create(
944 rewriter, loc, ptrType, globalVarName,
945 FlatSymbolRefAttr::get(specCstComposite));
946
947 globalVar->setAttr("Constant", rewriter.getUnitAttr());
948 }
949 // Get SSA value of Global variable and create pointer to i8 to point to
950 // the format string.
951 Value globalPtr = spirv::AddressOfOp::create(rewriter, loc, globalVar);
952 Value fmtStr = spirv::BitcastOp::create(
953 rewriter, loc,
954 spirv::PointerType::get(i8Type, spirv::StorageClass::UniformConstant),
955 globalPtr);
956
957 // Get printf arguments.
958 auto printfArgs = llvm::to_vector_of<Value, 4>(adaptor.getArgs());
959
960 spirv::CLPrintfOp::create(rewriter, loc, i32Type, fmtStr, printfArgs);
961
962 // Need to erase the gpu.printf op as gpu.printf does not use result vs
963 // spirv::CLPrintfOp has i32 resultType so cannot replace with new SPIR-V
964 // printf op.
965 rewriter.eraseOp(gpuPrintfOp);
966
967 return success();
968}
969
970//===----------------------------------------------------------------------===//
971// GPU To SPIRV Patterns.
972//===----------------------------------------------------------------------===//
973
975 RewritePatternSet &patterns) {
976 patterns.add<
977 GPUBarrierConversion, GPUInitializeNamedBarrierConversion,
978 GPUBallotConversion, GPUFuncOpConversion, GPUModuleConversion,
979 GPUReturnOpConversion, GPUShuffleConversion, GPURotateConversion,
980 GPUSubgroupBroadcastConversion,
981 LaunchConfigConversion<gpu::BlockIdOp, spirv::BuiltIn::WorkgroupId>,
982 LaunchConfigConversion<gpu::GridDimOp, spirv::BuiltIn::NumWorkgroups>,
983 LaunchConfigConversion<gpu::BlockDimOp, spirv::BuiltIn::WorkgroupSize>,
984 LaunchConfigConversion<gpu::ThreadIdOp,
985 spirv::BuiltIn::LocalInvocationId>,
986 LaunchConfigConversion<gpu::GlobalIdOp,
987 spirv::BuiltIn::GlobalInvocationId>,
988 SingleDimLaunchConfigConversion<gpu::SubgroupIdOp,
989 spirv::BuiltIn::SubgroupId>,
990 SingleDimLaunchConfigConversion<gpu::NumSubgroupsOp,
991 spirv::BuiltIn::NumSubgroups>,
992 SingleDimLaunchConfigConversion<gpu::SubgroupSizeOp,
993 spirv::BuiltIn::SubgroupSize>,
994 SingleDimLaunchConfigConversion<
995 gpu::LaneIdOp, spirv::BuiltIn::SubgroupLocalInvocationId>,
996 WorkGroupSizeConversion, GPUAllReduceConversion,
997 GPUSubgroupReduceConversion, GPUPrintfConversion>(typeConverter,
998 patterns.getContext());
999}
1000
1002 SPIRVTypeConverter &typeConverter) {
1003 typeConverter.addConversion([](gpu::NamedBarrierType type) {
1004 return spirv::NamedBarrierType::get(type.getContext());
1005 });
1006}
return success()
static std::optional< Value > createGroupReduceOp(OpBuilder &builder, Location loc, Value arg, gpu::AllReduceOperation opType, bool isGroup, bool isUniform, std::optional< uint32_t > clusterSize)
static FailureOr< spirv::Scope > mapGPUBarrierScopeToSPIRV(gpu::BarrierScope gpuScope)
Map gpu::BarrierScope to spirv::Scope.
static LogicalResult getDefaultABIAttrs(const spirv::TargetEnv &targetEnv, gpu::GPUFuncOp funcOp, SmallVectorImpl< spirv::InterfaceVarABIAttr > &argABI)
Populates argABI with spirv.interface_var_abi attributes for lowering gpu.func to spirv....
static constexpr const char kSPIRVModule[]
static Value createGroupReduceOpImpl(OpBuilder &builder, Location loc, Value arg, bool isGroup, bool isUniform, std::optional< uint32_t > clusterSize)
static spirv::FuncOp lowerAsEntryFunction(gpu::GPUFuncOp funcOp, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter, spirv::EntryPointABIAttr entryPointInfo, ArrayRef< spirv::InterfaceVarABIAttr > argABIInfo)
static std::string makeVarName(spirv::ModuleOp moduleOp, llvm::Twine prefix)
ArrayAttr()
b getContext())
Pattern to convert a gpu.all_reduce op into a SPIR-V group op.
LogicalResult matchAndRewrite(gpu::AllReduceOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
Pattern to convert a gpu.subgroup_reduce op into a SPIR-V group op.
LogicalResult matchAndRewrite(gpu::SubgroupReduceOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:233
IntegerType getI32Type()
Definition Builders.cpp:67
MLIRContext * getContext() const
Definition Builders.h:56
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:209
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:711
Block & back()
Definition Region.h:64
iterator begin()
Definition Region.h:55
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Type conversion from builtin types to SPIR-V types for shader interface.
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition SymbolTable.h:76
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static ArrayType get(Type elementType, unsigned elementCount)
An attribute that specifies the information regarding the interface variable: descriptor set,...
static NamedBarrierType get(MLIRContext *context)
static PointerType get(Type pointeeType, StorageClass storageClass)
ResourceLimitsAttr getResourceLimits() const
Returns the target resource limits.
A wrapper class around a spirv::TargetEnvAttr to provide query methods for allowed version/capabiliti...
TargetEnvAttr getAttr() const
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
StringRef getInterfaceVarABIAttrName()
Returns the attribute name for specifying argument ABI information.
bool needsInterfaceVarABIAttrs(TargetEnvAttr targetAttr)
Returns whether the given SPIR-V target (described by TargetEnvAttr) needs ABI attributes for interfa...
InterfaceVarABIAttr getInterfaceVarABIAttr(unsigned descriptorSet, unsigned binding, std::optional< StorageClass > storageClass, MLIRContext *context)
Gets the InterfaceVarABIAttr given its fields.
Value getBuiltinVariableValue(Operation *op, BuiltIn builtin, Type integerType, OpBuilder &builder, StringRef prefix="__builtin__", StringRef suffix="__")
Returns the value for the given builtin variable.
EntryPointABIAttr lookupEntryPointABI(Operation *op)
Queries the entry point ABI on the nearest function-like op containing the given op.
StringRef getTargetEnvAttrName()
Returns the attribute name for specifying SPIR-V target environment.
DenseI32ArrayAttr lookupLocalWorkGroupSize(Operation *op)
Queries the local workgroup size from entry point ABI on the nearest function-like op containing the ...
AddressingModel getAddressingModel(TargetEnvAttr targetAttr, bool use64bitAddress)
Returns addressing model selected based on target environment.
FailureOr< MemoryModel > getMemoryModel(TargetEnvAttr targetAttr)
Returns memory model selected based on target environment.
StringRef getEntryPointABIAttrName()
Returns the attribute name for specifying entry point information.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
void populateGPUNamedBarrierToSPIRVTypeConversion(SPIRVTypeConverter &typeConverter)
Adds gpu::NamedBarrierType to spirv::NamedBarrierType conversion.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
void populateGPUToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating GPU Ops to SPIR-V ops.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369