MLIR 23.0.0git
SPIRVToLLVM.cpp
Go to the documentation of this file.
1//===- SPIRVToLLVM.cpp - SPIR-V to LLVM 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 SPIR-V dialect to LLVM dialect.
10//
11//===----------------------------------------------------------------------===//
12
20#include "mlir/IR/BuiltinOps.h"
23#include "llvm/ADT/TypeSwitch.h"
24#include "llvm/Support/FormatVariadic.h"
25
26#define DEBUG_TYPE "spirv-to-llvm-pattern"
27
28using namespace mlir;
29
30//===----------------------------------------------------------------------===//
31// Utility functions
32//===----------------------------------------------------------------------===//
33
34/// Returns true if the given type is a signed integer or vector type.
35static bool isSignedIntegerOrVector(Type type) {
36 if (type.isSignedInteger())
37 return true;
38 if (auto vecType = dyn_cast<VectorType>(type))
39 return vecType.getElementType().isSignedInteger();
40 return false;
41}
42
43/// Returns true if the given type is an unsigned integer or vector type
45 if (type.isUnsignedInteger())
46 return true;
47 if (auto vecType = dyn_cast<VectorType>(type))
48 return vecType.getElementType().isUnsignedInteger();
49 return false;
50}
51
52/// Returns the width of an integer or of the element type of an integer vector,
53/// if applicable.
54static std::optional<uint64_t> getIntegerOrVectorElementWidth(Type type) {
55 if (auto intType = dyn_cast<IntegerType>(type))
56 return intType.getWidth();
57 if (auto vecType = dyn_cast<VectorType>(type))
58 if (auto intType = dyn_cast<IntegerType>(vecType.getElementType()))
59 return intType.getWidth();
60 return std::nullopt;
61}
62
63/// Returns the bit width of integer, float or vector of float or integer values
64static unsigned getBitWidth(Type type) {
65 assert((type.isIntOrFloat() || isa<VectorType>(type)) &&
66 "bitwidth is not supported for this type");
67 if (type.isIntOrFloat())
68 return type.getIntOrFloatBitWidth();
69 auto vecType = dyn_cast<VectorType>(type);
70 auto elementType = vecType.getElementType();
71 assert(elementType.isIntOrFloat() &&
72 "only integers and floats have a bitwidth");
73 return elementType.getIntOrFloatBitWidth();
74}
75
76/// Returns the bit width of LLVMType integer or vector.
77static unsigned getLLVMTypeBitWidth(Type type) {
78 if (auto vecTy = dyn_cast<VectorType>(type))
79 type = vecTy.getElementType();
80 return cast<IntegerType>(type).getWidth();
81}
82
83/// Creates `IntegerAttribute` with all bits set for given type
84static IntegerAttr minusOneIntegerAttribute(Type type, Builder builder) {
85 if (auto vecType = dyn_cast<VectorType>(type)) {
86 auto integerType = cast<IntegerType>(vecType.getElementType());
87 return builder.getIntegerAttr(integerType, -1);
88 }
89 auto integerType = cast<IntegerType>(type);
90 return builder.getIntegerAttr(integerType, -1);
91}
92
93/// Creates `llvm.mlir.constant` with all bits set for the given type.
94static Value createConstantAllBitsSet(Location loc, Type srcType, Type dstType,
95 PatternRewriter &rewriter) {
96 if (isa<VectorType>(srcType)) {
97 return LLVM::ConstantOp::create(
98 rewriter, loc, dstType,
99 SplatElementsAttr::get(cast<ShapedType>(srcType),
100 minusOneIntegerAttribute(srcType, rewriter)));
101 }
102 return LLVM::ConstantOp::create(rewriter, loc, dstType,
103 minusOneIntegerAttribute(srcType, rewriter));
104}
105
106/// Creates `llvm.mlir.constant` with a floating-point scalar or vector value.
107static Value createFPConstant(Location loc, Type srcType, Type dstType,
108 PatternRewriter &rewriter, double value) {
109 if (auto vecType = dyn_cast<VectorType>(srcType)) {
110 auto floatType = cast<FloatType>(vecType.getElementType());
111 return LLVM::ConstantOp::create(
112 rewriter, loc, dstType,
114 rewriter.getFloatAttr(floatType, value)));
115 }
116 auto floatType = cast<FloatType>(srcType);
117 return LLVM::ConstantOp::create(rewriter, loc, dstType,
118 rewriter.getFloatAttr(floatType, value));
119}
120
121/// Utility function for bitfield ops:
122/// - `BitFieldInsert`
123/// - `BitFieldSExtract`
124/// - `BitFieldUExtract`
125/// Truncates or extends the value. If the bitwidth of the value is the same as
126/// `llvmType` bitwidth, the value remains unchanged.
128 Type llvmType,
129 PatternRewriter &rewriter) {
130 auto srcType = value.getType();
131 unsigned targetBitWidth = getLLVMTypeBitWidth(llvmType);
132 unsigned valueBitWidth = LLVM::isCompatibleType(srcType)
133 ? getLLVMTypeBitWidth(srcType)
134 : getBitWidth(srcType);
135
136 if (valueBitWidth < targetBitWidth)
137 return LLVM::ZExtOp::create(rewriter, loc, llvmType, value);
138 // If the bit widths of `Count` and `Offset` are greater than the bit width
139 // of the target type, they are truncated. Truncation is safe since `Count`
140 // and `Offset` must be no more than 64 for op behaviour to be defined. Hence,
141 // both values can be expressed in 8 bits.
142 if (valueBitWidth > targetBitWidth)
143 return LLVM::TruncOp::create(rewriter, loc, llvmType, value);
144 return value;
145}
146
147/// Broadcasts the value to vector with `numElements` number of elements.
148static Value broadcast(Location loc, Value toBroadcast, unsigned numElements,
149 const TypeConverter &typeConverter,
150 ConversionPatternRewriter &rewriter) {
151 auto vectorType = VectorType::get(numElements, toBroadcast.getType());
152 auto llvmVectorType = typeConverter.convertType(vectorType);
153 auto llvmI32Type = typeConverter.convertType(rewriter.getIntegerType(32));
154 Value broadcasted = LLVM::PoisonOp::create(rewriter, loc, llvmVectorType);
155 for (unsigned i = 0; i < numElements; ++i) {
156 auto index = LLVM::ConstantOp::create(rewriter, loc, llvmI32Type,
157 rewriter.getI32IntegerAttr(i));
158 broadcasted = LLVM::InsertElementOp::create(
159 rewriter, loc, llvmVectorType, broadcasted, toBroadcast, index);
160 }
161 return broadcasted;
162}
163
164/// Broadcasts the value. If `srcType` is a scalar, the value remains unchanged.
165static Value optionallyBroadcast(Location loc, Value value, Type srcType,
166 const TypeConverter &typeConverter,
167 ConversionPatternRewriter &rewriter) {
168 if (auto vectorType = dyn_cast<VectorType>(srcType)) {
169 unsigned numElements = vectorType.getNumElements();
170 return broadcast(loc, value, numElements, typeConverter, rewriter);
171 }
172 return value;
173}
174
175/// Utility function for bitfield ops: `BitFieldInsert`, `BitFieldSExtract` and
176/// `BitFieldUExtract`.
177/// Broadcast `Offset` and `Count` to match the type of `Base`. If `Base` is of
178/// a vector type, construct a vector that has:
179/// - same number of elements as `Base`
180/// - each element has the type that is the same as the type of `Offset` or
181/// `Count`
182/// - each element has the same value as `Offset` or `Count`
183/// Then cast `Offset` and `Count` if their bit width is different
184/// from `Base` bit width.
185static Value processCountOrOffset(Location loc, Value value, Type srcType,
186 Type dstType, const TypeConverter &converter,
187 ConversionPatternRewriter &rewriter) {
188 Value broadcasted =
189 optionallyBroadcast(loc, value, srcType, converter, rewriter);
190 return optionallyTruncateOrExtend(loc, broadcasted, dstType, rewriter);
191}
192
193/// Converts SPIR-V struct with a regular (according to `VulkanLayoutUtils`)
194/// offset to LLVM struct. Otherwise, the conversion is not supported.
196 const TypeConverter &converter) {
197 if (type != VulkanLayoutUtils::decorateType(type))
198 return nullptr;
199
200 SmallVector<Type> elementsVector;
201 if (failed(converter.convertTypes(type.getElementTypes(), elementsVector)))
202 return nullptr;
203 return LLVM::LLVMStructType::getLiteral(type.getContext(), elementsVector,
204 /*isPacked=*/false);
205}
206
207/// Converts SPIR-V struct with no offset to packed LLVM struct.
209 const TypeConverter &converter) {
210 SmallVector<Type> elementsVector;
211 if (failed(converter.convertTypes(type.getElementTypes(), elementsVector)))
212 return nullptr;
213 return LLVM::LLVMStructType::getLiteral(type.getContext(), elementsVector,
214 /*isPacked=*/true);
215}
216
217/// Creates LLVM dialect constant with the given value.
219 unsigned value) {
220 return LLVM::ConstantOp::create(
221 rewriter, loc, IntegerType::get(rewriter.getContext(), 32),
222 rewriter.getIntegerAttr(rewriter.getI32Type(), value));
223}
224
225/// Utility for `spirv.Load` and `spirv.Store` conversion.
226static LogicalResult replaceWithLoadOrStore(Operation *op, ValueRange operands,
227 ConversionPatternRewriter &rewriter,
228 const TypeConverter &typeConverter,
229 unsigned alignment, bool isVolatile,
230 bool isNonTemporal) {
231 if (auto loadOp = dyn_cast<spirv::LoadOp>(op)) {
232 auto dstType = typeConverter.convertType(loadOp.getType());
233 if (!dstType)
234 return rewriter.notifyMatchFailure(op, "type conversion failed");
235 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(
236 loadOp, dstType, spirv::LoadOpAdaptor(operands).getPtr(), alignment,
237 isVolatile, isNonTemporal);
238 return success();
239 }
240 auto storeOp = cast<spirv::StoreOp>(op);
241 spirv::StoreOpAdaptor adaptor(operands);
242 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(storeOp, adaptor.getValue(),
243 adaptor.getPtr(), alignment,
244 isVolatile, isNonTemporal);
245 return success();
246}
247
248//===----------------------------------------------------------------------===//
249// Type conversion
250//===----------------------------------------------------------------------===//
251
252/// Converts SPIR-V array type to LLVM array. Natural stride (according to
253/// `VulkanLayoutUtils`) is also mapped to LLVM array. This has to be respected
254/// when converting ops that manipulate array types.
255static std::optional<Type> convertArrayType(spirv::ArrayType type,
256 TypeConverter &converter) {
257 unsigned stride = type.getArrayStride();
258 Type elementType = type.getElementType();
259 auto sizeInBytes = cast<spirv::SPIRVType>(elementType).getSizeInBytes();
260 if (stride != 0 && (!sizeInBytes || *sizeInBytes != stride))
261 return std::nullopt;
262
263 auto llvmElementType = converter.convertType(elementType);
264 unsigned numElements = type.getNumElements();
265 return LLVM::LLVMArrayType::get(llvmElementType, numElements);
266}
267
268/// Converts SPIR-V pointer type to LLVM pointer. Pointer's storage class is not
269/// modelled at the moment.
271 const TypeConverter &converter,
272 spirv::ClientAPI clientAPI) {
273 unsigned addressSpace =
275 return LLVM::LLVMPointerType::get(type.getContext(), addressSpace);
276}
277
278/// Converts SPIR-V runtime array to LLVM array. Since LLVM allows indexing over
279/// the bounds, the runtime array is converted to a 0-sized LLVM array. There is
280/// no modelling of array stride at the moment.
281static std::optional<Type> convertRuntimeArrayType(spirv::RuntimeArrayType type,
282 TypeConverter &converter) {
283 if (type.getArrayStride() != 0)
284 return std::nullopt;
285 auto elementType = converter.convertType(type.getElementType());
286 return LLVM::LLVMArrayType::get(elementType, 0);
287}
288
289/// Converts SPIR-V struct to LLVM struct. There is no support of structs with
290/// member decorations. Also, only natural offset is supported.
292 const TypeConverter &converter) {
294 type.getMemberDecorations(memberDecorations);
295 if (!memberDecorations.empty())
296 return nullptr;
297 if (type.hasOffset())
298 return convertStructTypeWithOffset(type, converter);
299 return convertStructTypePacked(type, converter);
300}
301
302//===----------------------------------------------------------------------===//
303// Operation conversion
304//===----------------------------------------------------------------------===//
305
306namespace {
307
308class AccessChainPattern : public SPIRVToLLVMConversion<spirv::AccessChainOp> {
309public:
310 using SPIRVToLLVMConversion<spirv::AccessChainOp>::SPIRVToLLVMConversion;
311
312 LogicalResult
313 matchAndRewrite(spirv::AccessChainOp op, OpAdaptor adaptor,
314 ConversionPatternRewriter &rewriter) const override {
315 auto dstType =
316 getTypeConverter()->convertType(op.getComponentPtr().getType());
317 if (!dstType)
318 return rewriter.notifyMatchFailure(op, "type conversion failed");
319 // To use GEP we need to add a first 0 index to go through the pointer.
320 auto indices = llvm::to_vector<4>(adaptor.getIndices());
321 Type indexType = op.getIndices().front().getType();
322 auto llvmIndexType = getTypeConverter()->convertType(indexType);
323 if (!llvmIndexType)
324 return rewriter.notifyMatchFailure(op, "type conversion failed");
325 Value zero =
326 LLVM::ConstantOp::create(rewriter, op.getLoc(), llvmIndexType,
327 rewriter.getIntegerAttr(indexType, 0));
328 indices.insert(indices.begin(), zero);
329
330 auto elementType = getTypeConverter()->convertType(
331 cast<spirv::PointerType>(op.getBasePtr().getType()).getPointeeType());
332 if (!elementType)
333 return rewriter.notifyMatchFailure(op, "type conversion failed");
334 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, dstType, elementType,
335 adaptor.getBasePtr(), indices);
336 return success();
337 }
338};
339
340class AddressOfPattern : public SPIRVToLLVMConversion<spirv::AddressOfOp> {
341public:
342 using SPIRVToLLVMConversion<spirv::AddressOfOp>::SPIRVToLLVMConversion;
343
344 LogicalResult
345 matchAndRewrite(spirv::AddressOfOp op, OpAdaptor adaptor,
346 ConversionPatternRewriter &rewriter) const override {
347 auto dstType = getTypeConverter()->convertType(op.getPointer().getType());
348 if (!dstType)
349 return rewriter.notifyMatchFailure(op, "type conversion failed");
350 rewriter.replaceOpWithNewOp<LLVM::AddressOfOp>(op, dstType,
351 op.getVariable());
352 return success();
353 }
354};
355
356class BitFieldInsertPattern
357 : public SPIRVToLLVMConversion<spirv::BitFieldInsertOp> {
358public:
359 using SPIRVToLLVMConversion<spirv::BitFieldInsertOp>::SPIRVToLLVMConversion;
360
361 LogicalResult
362 matchAndRewrite(spirv::BitFieldInsertOp op, OpAdaptor adaptor,
363 ConversionPatternRewriter &rewriter) const override {
364 auto srcType = op.getType();
365 auto dstType = getTypeConverter()->convertType(srcType);
366 if (!dstType)
367 return rewriter.notifyMatchFailure(op, "type conversion failed");
368 Location loc = op.getLoc();
369
370 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
371 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
372 *getTypeConverter(), rewriter);
373 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
374 *getTypeConverter(), rewriter);
375
376 // Create a mask with bits set outside [Offset, Offset + Count - 1].
377 Value minusOne = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
378 Value maskShiftedByCount =
379 LLVM::ShlOp::create(rewriter, loc, dstType, minusOne, count);
380 Value negated = LLVM::XOrOp::create(rewriter, loc, dstType,
381 maskShiftedByCount, minusOne);
382 Value maskShiftedByCountAndOffset =
383 LLVM::ShlOp::create(rewriter, loc, dstType, negated, offset);
384 Value mask = LLVM::XOrOp::create(rewriter, loc, dstType,
385 maskShiftedByCountAndOffset, minusOne);
386
387 // Extract unchanged bits from the `Base` that are outside of
388 // [Offset, Offset + Count - 1]. Then `or` with shifted `Insert`.
389 Value baseAndMask =
390 LLVM::AndOp::create(rewriter, loc, dstType, op.getBase(), mask);
391 Value insertShiftedByOffset =
392 LLVM::ShlOp::create(rewriter, loc, dstType, op.getInsert(), offset);
393 rewriter.replaceOpWithNewOp<LLVM::OrOp>(op, dstType, baseAndMask,
394 insertShiftedByOffset);
395 return success();
396 }
397};
398
399/// Converts SPIR-V ConstantOp with scalar or vector type.
400class ConstantScalarAndVectorPattern
401 : public SPIRVToLLVMConversion<spirv::ConstantOp> {
402public:
403 using SPIRVToLLVMConversion<spirv::ConstantOp>::SPIRVToLLVMConversion;
404
405 LogicalResult
406 matchAndRewrite(spirv::ConstantOp constOp, OpAdaptor adaptor,
407 ConversionPatternRewriter &rewriter) const override {
408 auto srcType = constOp.getType();
409 if (!isa<VectorType>(srcType) && !srcType.isIntOrFloat())
410 return failure();
411
412 auto dstType = getTypeConverter()->convertType(srcType);
413 if (!dstType)
414 return rewriter.notifyMatchFailure(constOp, "type conversion failed");
415
416 // SPIR-V constant can be a signed/unsigned integer, which has to be
417 // casted to signless integer when converting to LLVM dialect. Removing the
418 // sign bit may have unexpected behaviour. However, it is better to handle
419 // it case-by-case, given that the purpose of the conversion is not to
420 // cover all possible corner cases.
421 if (isSignedIntegerOrVector(srcType) ||
422 isUnsignedIntegerOrVector(srcType)) {
423 auto signlessType = rewriter.getIntegerType(getBitWidth(srcType));
424
425 if (isa<VectorType>(srcType)) {
426 auto dstElementsAttr = cast<DenseIntElementsAttr>(constOp.getValue());
427 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
428 constOp, dstType,
429 dstElementsAttr.mapValues(
430 signlessType, [&](const APInt &value) { return value; }));
431 return success();
432 }
433 auto srcAttr = cast<IntegerAttr>(constOp.getValue());
434 auto dstAttr = rewriter.getIntegerAttr(signlessType, srcAttr.getValue());
435 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(constOp, dstType, dstAttr);
436 return success();
437 }
438 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
439 constOp, dstType, adaptor.getOperands(), constOp->getAttrs());
440 return success();
441 }
442};
443
444class BitFieldSExtractPattern
445 : public SPIRVToLLVMConversion<spirv::BitFieldSExtractOp> {
446public:
447 using SPIRVToLLVMConversion<spirv::BitFieldSExtractOp>::SPIRVToLLVMConversion;
448
449 LogicalResult
450 matchAndRewrite(spirv::BitFieldSExtractOp op, OpAdaptor adaptor,
451 ConversionPatternRewriter &rewriter) const override {
452 auto srcType = op.getType();
453 auto dstType = getTypeConverter()->convertType(srcType);
454 if (!dstType)
455 return rewriter.notifyMatchFailure(op, "type conversion failed");
456 Location loc = op.getLoc();
457
458 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
459 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
460 *getTypeConverter(), rewriter);
461 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
462 *getTypeConverter(), rewriter);
463
464 // Create a constant that holds the size of the `Base`.
465 IntegerType integerType;
466 if (auto vecType = dyn_cast<VectorType>(srcType))
467 integerType = cast<IntegerType>(vecType.getElementType());
468 else
469 integerType = cast<IntegerType>(srcType);
470
471 auto baseSize = rewriter.getIntegerAttr(integerType, getBitWidth(srcType));
472 Value size =
473 isa<VectorType>(srcType)
474 ? LLVM::ConstantOp::create(
475 rewriter, loc, dstType,
476 SplatElementsAttr::get(cast<ShapedType>(srcType), baseSize))
477 : LLVM::ConstantOp::create(rewriter, loc, dstType, baseSize);
478
479 // Shift `Base` left by [sizeof(Base) - (Count + Offset)], so that the bit
480 // at Offset + Count - 1 is the most significant bit now.
481 Value countPlusOffset =
482 LLVM::AddOp::create(rewriter, loc, dstType, count, offset);
483 Value amountToShiftLeft =
484 LLVM::SubOp::create(rewriter, loc, dstType, size, countPlusOffset);
485 Value baseShiftedLeft = LLVM::ShlOp::create(
486 rewriter, loc, dstType, op.getBase(), amountToShiftLeft);
487
488 // Shift the result right, filling the bits with the sign bit.
489 Value amountToShiftRight =
490 LLVM::AddOp::create(rewriter, loc, dstType, offset, amountToShiftLeft);
491 rewriter.replaceOpWithNewOp<LLVM::AShrOp>(op, dstType, baseShiftedLeft,
492 amountToShiftRight);
493 return success();
494 }
495};
496
497class BitFieldUExtractPattern
498 : public SPIRVToLLVMConversion<spirv::BitFieldUExtractOp> {
499public:
500 using SPIRVToLLVMConversion<spirv::BitFieldUExtractOp>::SPIRVToLLVMConversion;
501
502 LogicalResult
503 matchAndRewrite(spirv::BitFieldUExtractOp op, OpAdaptor adaptor,
504 ConversionPatternRewriter &rewriter) const override {
505 auto srcType = op.getType();
506 auto dstType = getTypeConverter()->convertType(srcType);
507 if (!dstType)
508 return rewriter.notifyMatchFailure(op, "type conversion failed");
509 Location loc = op.getLoc();
510
511 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
512 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
513 *getTypeConverter(), rewriter);
514 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
515 *getTypeConverter(), rewriter);
516
517 // Create a mask with bits set at [0, Count - 1].
518 Value minusOne = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
519 Value maskShiftedByCount =
520 LLVM::ShlOp::create(rewriter, loc, dstType, minusOne, count);
521 Value mask = LLVM::XOrOp::create(rewriter, loc, dstType, maskShiftedByCount,
522 minusOne);
523
524 // Shift `Base` by `Offset` and apply the mask on it.
525 Value shiftedBase =
526 LLVM::LShrOp::create(rewriter, loc, dstType, op.getBase(), offset);
527 rewriter.replaceOpWithNewOp<LLVM::AndOp>(op, dstType, shiftedBase, mask);
528 return success();
529 }
530};
531
532class BranchConversionPattern : public SPIRVToLLVMConversion<spirv::BranchOp> {
533public:
534 using SPIRVToLLVMConversion<spirv::BranchOp>::SPIRVToLLVMConversion;
535
536 LogicalResult
537 matchAndRewrite(spirv::BranchOp branchOp, OpAdaptor adaptor,
538 ConversionPatternRewriter &rewriter) const override {
539 rewriter.replaceOpWithNewOp<LLVM::BrOp>(branchOp, adaptor.getOperands(),
540 branchOp.getTarget());
541 return success();
542 }
543};
544
545class BranchConditionalConversionPattern
546 : public SPIRVToLLVMConversion<spirv::BranchConditionalOp> {
547public:
548 using SPIRVToLLVMConversion<
549 spirv::BranchConditionalOp>::SPIRVToLLVMConversion;
550
551 LogicalResult
552 matchAndRewrite(spirv::BranchConditionalOp op, OpAdaptor adaptor,
553 ConversionPatternRewriter &rewriter) const override {
554 // If branch weights exist, map them to 32-bit integer vector.
555 DenseI32ArrayAttr branchWeights = nullptr;
556 if (auto weights = op.getBranchWeights()) {
557 SmallVector<int32_t> weightValues;
558 for (auto weight : weights->getAsRange<IntegerAttr>())
559 weightValues.push_back(weight.getInt());
560 branchWeights = DenseI32ArrayAttr::get(getContext(), weightValues);
561 }
562
563 rewriter.replaceOpWithNewOp<LLVM::CondBrOp>(
564 op, op.getCondition(), op.getTrueBlockArguments(),
565 op.getFalseBlockArguments(), branchWeights, op.getTrueBlock(),
566 op.getFalseBlock());
567 return success();
568 }
569};
570
571/// Converts `spirv.getCompositeExtract` to `llvm.extractvalue` if the container
572/// type is an aggregate type (struct or array). Otherwise, converts to
573/// `llvm.extractelement` that operates on vectors.
574class CompositeExtractPattern
575 : public SPIRVToLLVMConversion<spirv::CompositeExtractOp> {
576public:
577 using SPIRVToLLVMConversion<spirv::CompositeExtractOp>::SPIRVToLLVMConversion;
578
579 LogicalResult
580 matchAndRewrite(spirv::CompositeExtractOp op, OpAdaptor adaptor,
581 ConversionPatternRewriter &rewriter) const override {
582 auto dstType = this->getTypeConverter()->convertType(op.getType());
583 if (!dstType)
584 return rewriter.notifyMatchFailure(op, "type conversion failed");
585
586 Type containerType = op.getComposite().getType();
587 if (isa<VectorType>(containerType)) {
588 Location loc = op.getLoc();
589 IntegerAttr value = cast<IntegerAttr>(op.getIndices()[0]);
590 Value index = createI32ConstantOf(loc, rewriter, value.getInt());
591 rewriter.replaceOpWithNewOp<LLVM::ExtractElementOp>(
592 op, dstType, adaptor.getComposite(), index);
593 return success();
594 }
595
596 rewriter.replaceOpWithNewOp<LLVM::ExtractValueOp>(
597 op, adaptor.getComposite(),
598 LLVM::convertArrayToIndices(op.getIndices()));
599 return success();
600 }
601};
602
603/// Converts `spirv.getCompositeInsert` to `llvm.insertvalue` if the container
604/// type is an aggregate type (struct or array). Otherwise, converts to
605/// `llvm.insertelement` that operates on vectors.
606class CompositeInsertPattern
607 : public SPIRVToLLVMConversion<spirv::CompositeInsertOp> {
608public:
609 using SPIRVToLLVMConversion<spirv::CompositeInsertOp>::SPIRVToLLVMConversion;
610
611 LogicalResult
612 matchAndRewrite(spirv::CompositeInsertOp op, OpAdaptor adaptor,
613 ConversionPatternRewriter &rewriter) const override {
614 auto dstType = this->getTypeConverter()->convertType(op.getType());
615 if (!dstType)
616 return rewriter.notifyMatchFailure(op, "type conversion failed");
617
618 Type containerType = op.getComposite().getType();
619 if (isa<VectorType>(containerType)) {
620 Location loc = op.getLoc();
621 IntegerAttr value = cast<IntegerAttr>(op.getIndices()[0]);
622 Value index = createI32ConstantOf(loc, rewriter, value.getInt());
623 rewriter.replaceOpWithNewOp<LLVM::InsertElementOp>(
624 op, dstType, adaptor.getComposite(), adaptor.getObject(), index);
625 return success();
626 }
627
628 rewriter.replaceOpWithNewOp<LLVM::InsertValueOp>(
629 op, adaptor.getComposite(), adaptor.getObject(),
630 LLVM::convertArrayToIndices(op.getIndices()));
631 return success();
632 }
633};
634
635/// Converts SPIR-V operations that have straightforward LLVM equivalent
636/// into LLVM dialect operations.
637template <typename SPIRVOp, typename LLVMOp>
638class DirectConversionPattern : public SPIRVToLLVMConversion<SPIRVOp> {
639public:
640 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
641
642 LogicalResult
643 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
644 ConversionPatternRewriter &rewriter) const override {
645 auto dstType = this->getTypeConverter()->convertType(op.getType());
646 if (!dstType)
647 return rewriter.notifyMatchFailure(op, "type conversion failed");
648 rewriter.template replaceOpWithNewOp<LLVMOp>(
649 op, dstType, adaptor.getOperands(), op->getAttrs());
650 return success();
651 }
652};
653
654/// Converts `spirv.ExecutionMode` into a global struct constant that holds
655/// execution mode information.
656class ExecutionModePattern
657 : public SPIRVToLLVMConversion<spirv::ExecutionModeOp> {
658public:
659 using SPIRVToLLVMConversion<spirv::ExecutionModeOp>::SPIRVToLLVMConversion;
660
661 LogicalResult
662 matchAndRewrite(spirv::ExecutionModeOp op, OpAdaptor adaptor,
663 ConversionPatternRewriter &rewriter) const override {
664 // First, create the global struct's name that would be associated with
665 // this entry point's execution mode. We set it to be:
666 // __spv__{SPIR-V module name}_{function name}_execution_mode_info_{mode}
667 ModuleOp module = op->getParentOfType<ModuleOp>();
668 spirv::ExecutionModeAttr executionModeAttr = op.getExecutionModeAttr();
669 std::string moduleName;
670 if (module.getName().has_value())
671 moduleName = "_" + module.getName()->str();
672 else
673 moduleName = "";
674 std::string executionModeInfoName = llvm::formatv(
675 "__spv_{0}_{1}_execution_mode_info_{2}", moduleName, op.getFn().str(),
676 static_cast<uint32_t>(executionModeAttr.getValue()));
677
678 MLIRContext *context = rewriter.getContext();
679 OpBuilder::InsertionGuard guard(rewriter);
680 rewriter.setInsertionPointToStart(module.getBody());
681
682 // Create a struct type, corresponding to the C struct below.
683 // struct {
684 // int32_t executionMode;
685 // int32_t values[]; // optional values
686 // };
687 auto llvmI32Type = IntegerType::get(context, 32);
688 SmallVector<Type, 2> fields;
689 fields.push_back(llvmI32Type);
690 ArrayAttr values = op.getValues();
691 if (!values.empty()) {
692 auto arrayType = LLVM::LLVMArrayType::get(llvmI32Type, values.size());
693 fields.push_back(arrayType);
694 }
695 auto structType = LLVM::LLVMStructType::getLiteral(context, fields);
696
697 // Create `llvm.mlir.global` with initializer region containing one block.
698 auto global = LLVM::GlobalOp::create(
699 rewriter, UnknownLoc::get(context), structType, /*isConstant=*/true,
700 LLVM::Linkage::External, executionModeInfoName, Attribute(),
701 /*alignment=*/0);
702 Location loc = global.getLoc();
703 Region &region = global.getInitializerRegion();
704 Block *block = rewriter.createBlock(&region);
705
706 // Initialize the struct and set the execution mode value.
707 rewriter.setInsertionPointToStart(block);
708 Value structValue = LLVM::PoisonOp::create(rewriter, loc, structType);
709 Value executionMode = LLVM::ConstantOp::create(
710 rewriter, loc, llvmI32Type,
711 rewriter.getI32IntegerAttr(
712 static_cast<uint32_t>(executionModeAttr.getValue())));
713 SmallVector<int64_t> position{0};
714 structValue = LLVM::InsertValueOp::create(rewriter, loc, structValue,
715 executionMode, position);
716
717 // Insert extra operands if they exist into execution mode info struct.
718 for (unsigned i = 0, e = values.size(); i < e; ++i) {
719 auto attr = values.getValue()[i];
720 Value entry = LLVM::ConstantOp::create(rewriter, loc, llvmI32Type, attr);
721 structValue = LLVM::InsertValueOp::create(
722 rewriter, loc, structValue, entry, ArrayRef<int64_t>({1, i}));
723 }
724 LLVM::ReturnOp::create(rewriter, loc, ArrayRef<Value>({structValue}));
725 rewriter.eraseOp(op);
726 return success();
727 }
728};
729
730/// Converts `spirv.GlobalVariable` to `llvm.mlir.global`. Note that SPIR-V
731/// global returns a pointer, whereas in LLVM dialect the global holds an actual
732/// value. This difference is handled by `spirv.mlir.addressof` and
733/// `llvm.mlir.addressof`ops that both return a pointer.
734class GlobalVariablePattern
735 : public SPIRVToLLVMConversion<spirv::GlobalVariableOp> {
736public:
737 template <typename... Args>
738 GlobalVariablePattern(spirv::ClientAPI clientAPI, Args &&...args)
739 : SPIRVToLLVMConversion<spirv::GlobalVariableOp>(
740 std::forward<Args>(args)...),
741 clientAPI(clientAPI) {}
742
743 LogicalResult
744 matchAndRewrite(spirv::GlobalVariableOp op, OpAdaptor adaptor,
745 ConversionPatternRewriter &rewriter) const override {
746 // Currently, there is no support of initialization with a constant value in
747 // SPIR-V dialect. Specialization constants are not considered as well.
748 if (op.getInitializer())
749 return failure();
750
751 auto srcType = cast<spirv::PointerType>(op.getType());
752 auto dstType = getTypeConverter()->convertType(srcType.getPointeeType());
753 if (!dstType)
754 return rewriter.notifyMatchFailure(op, "type conversion failed");
755
756 // Limit conversion to the current invocation only or `StorageBuffer`
757 // required by SPIR-V runner.
758 // This is okay because multiple invocations are not supported yet.
759 auto storageClass = srcType.getStorageClass();
760 switch (storageClass) {
761 case spirv::StorageClass::Input:
762 case spirv::StorageClass::Private:
763 case spirv::StorageClass::Output:
764 case spirv::StorageClass::StorageBuffer:
765 case spirv::StorageClass::UniformConstant:
766 break;
767 default:
768 return failure();
769 }
770
771 // LLVM dialect spec: "If the global value is a constant, storing into it is
772 // not allowed.". This corresponds to SPIR-V 'Input' and 'UniformConstant'
773 // storage class that is read-only.
774 bool isConstant = (storageClass == spirv::StorageClass::Input) ||
775 (storageClass == spirv::StorageClass::UniformConstant);
776 // SPIR-V spec: "By default, functions and global variables are private to a
777 // module and cannot be accessed by other modules. However, a module may be
778 // written to export or import functions and global (module scope)
779 // variables.". Therefore, map 'Private' storage class to private linkage,
780 // 'Input' and 'Output' to external linkage.
781 auto linkage = storageClass == spirv::StorageClass::Private
782 ? LLVM::Linkage::Private
783 : LLVM::Linkage::External;
784 StringAttr locationAttrName = op.getLocationAttrName();
785 IntegerAttr locationAttr = op.getLocationAttr();
786 auto newGlobalOp = rewriter.replaceOpWithNewOp<LLVM::GlobalOp>(
787 op, dstType, isConstant, linkage, op.getSymName(), Attribute(),
788 /*alignment=*/0, storageClassToAddressSpace(clientAPI, storageClass));
789
790 // Attach location attribute if applicable
791 if (locationAttr)
792 newGlobalOp->setAttr(locationAttrName, locationAttr);
793
794 return success();
795 }
796
797private:
798 spirv::ClientAPI clientAPI;
799};
800
801/// Converts SPIR-V cast ops that do not have straightforward LLVM
802/// equivalent in LLVM dialect.
803template <typename SPIRVOp, typename LLVMExtOp, typename LLVMTruncOp>
804class IndirectCastPattern : public SPIRVToLLVMConversion<SPIRVOp> {
805public:
806 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
807
808 LogicalResult
809 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
810 ConversionPatternRewriter &rewriter) const override {
811
812 Type fromType = op.getOperand().getType();
813 Type toType = op.getType();
814
815 auto dstType = this->getTypeConverter()->convertType(toType);
816 if (!dstType)
817 return rewriter.notifyMatchFailure(op, "type conversion failed");
818
819 if (getBitWidth(fromType) < getBitWidth(toType)) {
820 rewriter.template replaceOpWithNewOp<LLVMExtOp>(op, dstType,
821 adaptor.getOperands());
822 return success();
823 }
824 if (getBitWidth(fromType) > getBitWidth(toType)) {
825 rewriter.template replaceOpWithNewOp<LLVMTruncOp>(op, dstType,
826 adaptor.getOperands());
827 return success();
828 }
829 return failure();
830 }
831};
832
833class FunctionCallPattern
834 : public SPIRVToLLVMConversion<spirv::FunctionCallOp> {
835public:
836 using SPIRVToLLVMConversion<spirv::FunctionCallOp>::SPIRVToLLVMConversion;
837
838 LogicalResult
839 matchAndRewrite(spirv::FunctionCallOp callOp, OpAdaptor adaptor,
840 ConversionPatternRewriter &rewriter) const override {
841 if (callOp.getNumResults() == 0) {
842 auto newOp = rewriter.replaceOpWithNewOp<LLVM::CallOp>(
843 callOp, TypeRange(), adaptor.getOperands(), callOp->getAttrs());
844 newOp.getProperties().operandSegmentSizes = {
845 static_cast<int32_t>(adaptor.getOperands().size()), 0};
846 newOp.getProperties().op_bundle_sizes = rewriter.getDenseI32ArrayAttr({});
847 return success();
848 }
849
850 // Function returns a single result.
851 auto dstType = getTypeConverter()->convertType(callOp.getType(0));
852 if (!dstType)
853 return rewriter.notifyMatchFailure(callOp, "type conversion failed");
854 auto newOp = rewriter.replaceOpWithNewOp<LLVM::CallOp>(
855 callOp, dstType, adaptor.getOperands(), callOp->getAttrs());
856 newOp.getProperties().operandSegmentSizes = {
857 static_cast<int32_t>(adaptor.getOperands().size()), 0};
858 newOp.getProperties().op_bundle_sizes = rewriter.getDenseI32ArrayAttr({});
859 return success();
860 }
861};
862
863/// Converts SPIR-V floating-point comparisons to llvm.fcmp "predicate"
864template <typename SPIRVOp, LLVM::FCmpPredicate predicate>
865class FComparePattern : public SPIRVToLLVMConversion<SPIRVOp> {
866public:
867 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
868
869 LogicalResult
870 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
871 ConversionPatternRewriter &rewriter) const override {
872
873 auto dstType = this->getTypeConverter()->convertType(op.getType());
874 if (!dstType)
875 return rewriter.notifyMatchFailure(op, "type conversion failed");
876
877 rewriter.template replaceOpWithNewOp<LLVM::FCmpOp>(
878 op, dstType, predicate, op.getOperand1(), op.getOperand2());
879 return success();
880 }
881};
882
883/// Converts SPIR-V integer comparisons to llvm.icmp "predicate"
884template <typename SPIRVOp, LLVM::ICmpPredicate predicate>
885class IComparePattern : public SPIRVToLLVMConversion<SPIRVOp> {
886public:
887 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
888
889 LogicalResult
890 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
891 ConversionPatternRewriter &rewriter) const override {
892
893 auto dstType = this->getTypeConverter()->convertType(op.getType());
894 if (!dstType)
895 return rewriter.notifyMatchFailure(op, "type conversion failed");
896
897 rewriter.template replaceOpWithNewOp<LLVM::ICmpOp>(
898 op, dstType, predicate, op.getOperand1(), op.getOperand2());
899 return success();
900 }
901};
902
903class InverseSqrtPattern
904 : public SPIRVToLLVMConversion<spirv::GLInverseSqrtOp> {
905public:
906 using SPIRVToLLVMConversion<spirv::GLInverseSqrtOp>::SPIRVToLLVMConversion;
907
908 LogicalResult
909 matchAndRewrite(spirv::GLInverseSqrtOp op, OpAdaptor adaptor,
910 ConversionPatternRewriter &rewriter) const override {
911 auto srcType = op.getType();
912 auto dstType = getTypeConverter()->convertType(srcType);
913 if (!dstType)
914 return rewriter.notifyMatchFailure(op, "type conversion failed");
915
916 Location loc = op.getLoc();
917 Value one = createFPConstant(loc, srcType, dstType, rewriter, 1.0);
918 Value sqrt = LLVM::SqrtOp::create(rewriter, loc, dstType, op.getOperand());
919 rewriter.replaceOpWithNewOp<LLVM::FDivOp>(op, dstType, one, sqrt);
920 return success();
921 }
922};
923
924/// Converts the GLSL clamp ops (FClamp, SClamp, UClamp) into a nested
925/// min/max sequence, following the op semantics `min(max(x, minVal), maxVal)`.
926template <typename SPIRVOp, typename LLVMMinOp, typename LLVMMaxOp>
927class ClampPattern : public SPIRVToLLVMConversion<SPIRVOp> {
928public:
929 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
930
931 LogicalResult
932 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
933 ConversionPatternRewriter &rewriter) const override {
934 Type dstType = this->getTypeConverter()->convertType(op.getType());
935 if (!dstType)
936 return rewriter.notifyMatchFailure(op, "type conversion failed");
937
938 Location loc = op.getLoc();
939 Value max = LLVMMaxOp::create(rewriter, loc, dstType, adaptor.getX(),
940 adaptor.getY());
941 rewriter.template replaceOpWithNewOp<LLVMMinOp>(op, dstType, max,
942 adaptor.getZ());
943 return success();
944 }
945};
946
947/// Converts `spirv.Load` and `spirv.Store` to LLVM dialect.
948template <typename SPIRVOp>
949class LoadStorePattern : public SPIRVToLLVMConversion<SPIRVOp> {
950public:
951 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
952
953 LogicalResult
954 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
955 ConversionPatternRewriter &rewriter) const override {
956 if (!op.getMemoryAccess()) {
957 return replaceWithLoadOrStore(op, adaptor.getOperands(), rewriter,
958 *this->getTypeConverter(), /*alignment=*/0,
959 /*isVolatile=*/false,
960 /*isNonTemporal=*/false);
961 }
962 auto memoryAccess = *op.getMemoryAccess();
963 switch (memoryAccess) {
964 case spirv::MemoryAccess::Aligned:
965 case spirv::MemoryAccess::None:
966 case spirv::MemoryAccess::Nontemporal:
967 case spirv::MemoryAccess::Volatile: {
968 unsigned alignment =
969 memoryAccess == spirv::MemoryAccess::Aligned ? *op.getAlignment() : 0;
970 bool isNonTemporal = memoryAccess == spirv::MemoryAccess::Nontemporal;
971 bool isVolatile = memoryAccess == spirv::MemoryAccess::Volatile;
972 return replaceWithLoadOrStore(op, adaptor.getOperands(), rewriter,
973 *this->getTypeConverter(), alignment,
974 isVolatile, isNonTemporal);
975 }
976 default:
977 // There is no support of other memory access attributes.
978 return failure();
979 }
980 }
981};
982
983/// Converts `spirv.Not` and `spirv.LogicalNot` into LLVM dialect.
984template <typename SPIRVOp>
985class NotPattern : public SPIRVToLLVMConversion<SPIRVOp> {
986public:
987 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
988
989 LogicalResult
990 matchAndRewrite(SPIRVOp notOp, typename SPIRVOp::Adaptor adaptor,
991 ConversionPatternRewriter &rewriter) const override {
992 auto srcType = notOp.getType();
993 auto dstType = this->getTypeConverter()->convertType(srcType);
994 if (!dstType)
995 return rewriter.notifyMatchFailure(notOp, "type conversion failed");
996
997 Location loc = notOp.getLoc();
998 IntegerAttr minusOne = minusOneIntegerAttribute(srcType, rewriter);
999 auto mask =
1000 isa<VectorType>(srcType)
1001 ? LLVM::ConstantOp::create(
1002 rewriter, loc, dstType,
1003 SplatElementsAttr::get(cast<VectorType>(srcType), minusOne))
1004 : LLVM::ConstantOp::create(rewriter, loc, dstType, minusOne);
1005 rewriter.template replaceOpWithNewOp<LLVM::XOrOp>(notOp, dstType,
1006 notOp.getOperand(), mask);
1007 return success();
1008 }
1009};
1010
1011/// A template pattern that erases the given `SPIRVOp`.
1012template <typename SPIRVOp>
1013class ErasePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1014public:
1015 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1016
1017 LogicalResult
1018 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1019 ConversionPatternRewriter &rewriter) const override {
1020 rewriter.eraseOp(op);
1021 return success();
1022 }
1023};
1024
1025class ReturnPattern : public SPIRVToLLVMConversion<spirv::ReturnOp> {
1026public:
1027 using SPIRVToLLVMConversion<spirv::ReturnOp>::SPIRVToLLVMConversion;
1028
1029 LogicalResult
1030 matchAndRewrite(spirv::ReturnOp returnOp, OpAdaptor adaptor,
1031 ConversionPatternRewriter &rewriter) const override {
1032 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(returnOp, ArrayRef<Type>(),
1033 ArrayRef<Value>());
1034 return success();
1035 }
1036};
1037
1038class ReturnValuePattern : public SPIRVToLLVMConversion<spirv::ReturnValueOp> {
1039public:
1040 using SPIRVToLLVMConversion<spirv::ReturnValueOp>::SPIRVToLLVMConversion;
1041
1042 LogicalResult
1043 matchAndRewrite(spirv::ReturnValueOp returnValueOp, OpAdaptor adaptor,
1044 ConversionPatternRewriter &rewriter) const override {
1045 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(returnValueOp, ArrayRef<Type>(),
1046 adaptor.getOperands());
1047 return success();
1048 }
1049};
1050
1051class UnreachablePattern : public SPIRVToLLVMConversion<spirv::UnreachableOp> {
1052public:
1053 using SPIRVToLLVMConversion<spirv::UnreachableOp>::SPIRVToLLVMConversion;
1054
1055 LogicalResult
1056 matchAndRewrite(spirv::UnreachableOp unreachableOp, OpAdaptor adaptor,
1057 ConversionPatternRewriter &rewriter) const override {
1058 rewriter.replaceOpWithNewOp<LLVM::UnreachableOp>(unreachableOp);
1059 return success();
1060 }
1061};
1062
1063static LLVM::LLVMFuncOp lookupOrCreateSPIRVFn(Operation *symbolTable,
1064 StringRef name,
1065 ArrayRef<Type> paramTypes,
1066 Type resultType,
1067 bool convergent = true) {
1068 auto func = dyn_cast_or_null<LLVM::LLVMFuncOp>(
1069 SymbolTable::lookupSymbolIn(symbolTable, name));
1070 if (func)
1071 return func;
1072
1073 OpBuilder b(symbolTable->getRegion(0));
1074 func = LLVM::LLVMFuncOp::create(
1075 b, symbolTable->getLoc(), name,
1076 LLVM::LLVMFunctionType::get(resultType, paramTypes));
1077 func.setCConv(LLVM::cconv::CConv::SPIR_FUNC);
1078 func.setConvergent(convergent);
1079 func.setNoUnwind(true);
1080 func.setWillReturn(true);
1081 return func;
1082}
1083
1084static LLVM::CallOp createSPIRVBuiltinCall(Location loc, OpBuilder &builder,
1085 LLVM::LLVMFuncOp func,
1086 ValueRange args) {
1087 auto call = LLVM::CallOp::create(builder, loc, func, args);
1088 call.setCConv(func.getCConv());
1089 call.setConvergentAttr(func.getConvergentAttr());
1090 call.setNoUnwindAttr(func.getNoUnwindAttr());
1091 call.setWillReturnAttr(func.getWillReturnAttr());
1092 return call;
1093}
1094
1095template <typename BarrierOpTy>
1096class ControlBarrierPattern : public SPIRVToLLVMConversion<BarrierOpTy> {
1097public:
1098 using OpAdaptor = typename SPIRVToLLVMConversion<BarrierOpTy>::OpAdaptor;
1099
1100 using SPIRVToLLVMConversion<BarrierOpTy>::SPIRVToLLVMConversion;
1101
1102 static constexpr StringRef getFuncName();
1103
1104 LogicalResult
1105 matchAndRewrite(BarrierOpTy controlBarrierOp, OpAdaptor adaptor,
1106 ConversionPatternRewriter &rewriter) const override {
1107 constexpr StringRef funcName = getFuncName();
1108 Operation *symbolTable =
1109 controlBarrierOp->template getParentWithTrait<OpTrait::SymbolTable>();
1110
1111 Type i32 = rewriter.getI32Type();
1112
1113 Type voidTy = rewriter.getType<LLVM::LLVMVoidType>();
1114 LLVM::LLVMFuncOp func =
1115 lookupOrCreateSPIRVFn(symbolTable, funcName, {i32, i32, i32}, voidTy);
1116
1117 Location loc = controlBarrierOp->getLoc();
1118 Value execution = LLVM::ConstantOp::create(
1119 rewriter, loc, i32, static_cast<int32_t>(adaptor.getExecutionScope()));
1120 Value memory = LLVM::ConstantOp::create(
1121 rewriter, loc, i32, static_cast<int32_t>(adaptor.getMemoryScope()));
1122 Value semantics = LLVM::ConstantOp::create(
1123 rewriter, loc, i32, static_cast<int32_t>(adaptor.getMemorySemantics()));
1124
1125 auto call = createSPIRVBuiltinCall(loc, rewriter, func,
1126 {execution, memory, semantics});
1127
1128 rewriter.replaceOp(controlBarrierOp, call);
1129 return success();
1130 }
1131};
1132
1133namespace {
1134
1135StringRef getTypeMangling(Type type, bool isSigned) {
1137 .Case([](Float16Type) { return "Dh"; })
1138 .Case([](Float32Type) { return "f"; })
1139 .Case([](Float64Type) { return "d"; })
1140 .Case([isSigned](IntegerType intTy) {
1141 switch (intTy.getWidth()) {
1142 case 1:
1143 return "b";
1144 case 8:
1145 return (isSigned) ? "a" : "c";
1146 case 16:
1147 return (isSigned) ? "s" : "t";
1148 case 32:
1149 return (isSigned) ? "i" : "j";
1150 case 64:
1151 return (isSigned) ? "l" : "m";
1152 default:
1153 llvm_unreachable("Unsupported integer width");
1154 }
1155 })
1156 .DefaultUnreachable("No mangling defined");
1157}
1158
1159template <typename ReduceOp>
1160constexpr StringLiteral getGroupFuncName();
1161
1162template <>
1163constexpr StringLiteral getGroupFuncName<spirv::GroupIAddOp>() {
1164 return "_Z17__spirv_GroupIAddii";
1165}
1166template <>
1167constexpr StringLiteral getGroupFuncName<spirv::GroupFAddOp>() {
1168 return "_Z17__spirv_GroupFAddii";
1169}
1170template <>
1171constexpr StringLiteral getGroupFuncName<spirv::GroupSMinOp>() {
1172 return "_Z17__spirv_GroupSMinii";
1173}
1174template <>
1175constexpr StringLiteral getGroupFuncName<spirv::GroupUMinOp>() {
1176 return "_Z17__spirv_GroupUMinii";
1177}
1178template <>
1179constexpr StringLiteral getGroupFuncName<spirv::GroupFMinOp>() {
1180 return "_Z17__spirv_GroupFMinii";
1181}
1182template <>
1183constexpr StringLiteral getGroupFuncName<spirv::GroupSMaxOp>() {
1184 return "_Z17__spirv_GroupSMaxii";
1185}
1186template <>
1187constexpr StringLiteral getGroupFuncName<spirv::GroupUMaxOp>() {
1188 return "_Z17__spirv_GroupUMaxii";
1189}
1190template <>
1191constexpr StringLiteral getGroupFuncName<spirv::GroupFMaxOp>() {
1192 return "_Z17__spirv_GroupFMaxii";
1193}
1194template <>
1195constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformIAddOp>() {
1196 return "_Z27__spirv_GroupNonUniformIAddii";
1197}
1198template <>
1199constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFAddOp>() {
1200 return "_Z27__spirv_GroupNonUniformFAddii";
1201}
1202template <>
1203constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformIMulOp>() {
1204 return "_Z27__spirv_GroupNonUniformIMulii";
1205}
1206template <>
1207constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMulOp>() {
1208 return "_Z27__spirv_GroupNonUniformFMulii";
1209}
1210template <>
1211constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformSMinOp>() {
1212 return "_Z27__spirv_GroupNonUniformSMinii";
1213}
1214template <>
1215constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformUMinOp>() {
1216 return "_Z27__spirv_GroupNonUniformUMinii";
1217}
1218template <>
1219constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMinOp>() {
1220 return "_Z27__spirv_GroupNonUniformFMinii";
1221}
1222template <>
1223constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformSMaxOp>() {
1224 return "_Z27__spirv_GroupNonUniformSMaxii";
1225}
1226template <>
1227constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformUMaxOp>() {
1228 return "_Z27__spirv_GroupNonUniformUMaxii";
1229}
1230template <>
1231constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMaxOp>() {
1232 return "_Z27__spirv_GroupNonUniformFMaxii";
1233}
1234template <>
1235constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseAndOp>() {
1236 return "_Z33__spirv_GroupNonUniformBitwiseAndii";
1237}
1238template <>
1239constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseOrOp>() {
1240 return "_Z32__spirv_GroupNonUniformBitwiseOrii";
1241}
1242template <>
1243constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseXorOp>() {
1244 return "_Z33__spirv_GroupNonUniformBitwiseXorii";
1245}
1246template <>
1247constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalAndOp>() {
1248 return "_Z33__spirv_GroupNonUniformLogicalAndii";
1249}
1250template <>
1251constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalOrOp>() {
1252 return "_Z32__spirv_GroupNonUniformLogicalOrii";
1253}
1254template <>
1255constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalXorOp>() {
1256 return "_Z33__spirv_GroupNonUniformLogicalXorii";
1257}
1258} // namespace
1259
1260template <typename ReduceOp, bool Signed = false, bool NonUniform = false>
1261class GroupReducePattern : public SPIRVToLLVMConversion<ReduceOp> {
1262public:
1263 using SPIRVToLLVMConversion<ReduceOp>::SPIRVToLLVMConversion;
1264
1265 LogicalResult
1266 matchAndRewrite(ReduceOp op, typename ReduceOp::Adaptor adaptor,
1267 ConversionPatternRewriter &rewriter) const override {
1268
1269 Type retTy = op.getResult().getType();
1270 if (!retTy.isIntOrFloat()) {
1271 return failure();
1272 }
1273 SmallString<36> funcName = getGroupFuncName<ReduceOp>();
1274 funcName += getTypeMangling(retTy, false);
1275
1276 Type i32Ty = rewriter.getI32Type();
1277 SmallVector<Type> paramTypes{i32Ty, i32Ty, retTy};
1278 if constexpr (NonUniform) {
1279 if (adaptor.getClusterSize()) {
1280 funcName += "j";
1281 paramTypes.push_back(i32Ty);
1282 }
1283 }
1284
1285 Operation *symbolTable =
1286 op->template getParentWithTrait<OpTrait::SymbolTable>();
1287
1288 LLVM::LLVMFuncOp func =
1289 lookupOrCreateSPIRVFn(symbolTable, funcName, paramTypes, retTy);
1290
1291 Location loc = op.getLoc();
1292 Value scope = LLVM::ConstantOp::create(
1293 rewriter, loc, i32Ty,
1294 static_cast<int32_t>(adaptor.getExecutionScope()));
1295 Value groupOp = LLVM::ConstantOp::create(
1296 rewriter, loc, i32Ty,
1297 static_cast<int32_t>(adaptor.getGroupOperation()));
1298 SmallVector<Value> operands{scope, groupOp};
1299 operands.append(adaptor.getOperands().begin(), adaptor.getOperands().end());
1300
1301 auto call = createSPIRVBuiltinCall(loc, rewriter, func, operands);
1302 rewriter.replaceOp(op, call);
1303 return success();
1304 }
1305};
1306
1307template <>
1308constexpr StringRef
1309ControlBarrierPattern<spirv::ControlBarrierOp>::getFuncName() {
1310 return "_Z22__spirv_ControlBarrieriii";
1311}
1312
1313template <>
1314constexpr StringRef
1315ControlBarrierPattern<spirv::INTELControlBarrierArriveOp>::getFuncName() {
1316 return "_Z33__spirv_ControlBarrierArriveINTELiii";
1317}
1318
1319template <>
1320constexpr StringRef
1321ControlBarrierPattern<spirv::INTELControlBarrierWaitOp>::getFuncName() {
1322 return "_Z31__spirv_ControlBarrierWaitINTELiii";
1323}
1324
1325/// Converts `spirv.mlir.loop` to LLVM dialect. All blocks within selection
1326/// should be reachable for conversion to succeed. The structure of the loop in
1327/// LLVM dialect will be the following:
1328///
1329/// +------------------------------------+
1330/// | <code before spirv.mlir.loop> |
1331/// | llvm.br ^header |
1332/// +------------------------------------+
1333/// |
1334/// +----------------+ |
1335/// | | |
1336/// | V V
1337/// | +------------------------------------+
1338/// | | ^header: |
1339/// | | <header code> |
1340/// | | llvm.cond_br %cond, ^body, ^exit |
1341/// | +------------------------------------+
1342/// | |
1343/// | |----------------------+
1344/// | | |
1345/// | V |
1346/// | +------------------------------------+ |
1347/// | | ^body: | |
1348/// | | <body code> | |
1349/// | | llvm.br ^continue | |
1350/// | +------------------------------------+ |
1351/// | | |
1352/// | V |
1353/// | +------------------------------------+ |
1354/// | | ^continue: | |
1355/// | | <continue code> | |
1356/// | | llvm.br ^header | |
1357/// | +------------------------------------+ |
1358/// | | |
1359/// +---------------+ +----------------------+
1360/// |
1361/// V
1362/// +------------------------------------+
1363/// | ^exit: |
1364/// | llvm.br ^remaining |
1365/// +------------------------------------+
1366/// |
1367/// V
1368/// +------------------------------------+
1369/// | ^remaining: |
1370/// | <code after spirv.mlir.loop> |
1371/// +------------------------------------+
1372///
1373class LoopPattern : public SPIRVToLLVMConversion<spirv::LoopOp> {
1374public:
1375 using SPIRVToLLVMConversion<spirv::LoopOp>::SPIRVToLLVMConversion;
1376
1377 LogicalResult
1378 matchAndRewrite(spirv::LoopOp loopOp, OpAdaptor adaptor,
1379 ConversionPatternRewriter &rewriter) const override {
1380 // There is no support of loop control at the moment.
1381 if (loopOp.getLoopControl() != spirv::LoopControl::None)
1382 return failure();
1383
1384 // `spirv.mlir.loop` with empty region is redundant and should be erased.
1385 if (loopOp.getBody().empty()) {
1386 rewriter.eraseOp(loopOp);
1387 return success();
1388 }
1389
1390 Location loc = loopOp.getLoc();
1391
1392 // Split the current block after `spirv.mlir.loop`. The remaining ops will
1393 // be used in `endBlock`.
1394 Block *currentBlock = rewriter.getBlock();
1395 auto position = Block::iterator(loopOp);
1396 Block *endBlock = rewriter.splitBlock(currentBlock, position);
1397
1398 // Remove entry block and create a branch in the current block going to the
1399 // header block.
1400 Block *entryBlock = loopOp.getEntryBlock();
1401 assert(entryBlock->getOperations().size() == 1);
1402 auto brOp = dyn_cast<spirv::BranchOp>(entryBlock->getOperations().front());
1403 if (!brOp)
1404 return failure();
1405 Block *headerBlock = loopOp.getHeaderBlock();
1406 rewriter.setInsertionPointToEnd(currentBlock);
1407 LLVM::BrOp::create(rewriter, loc, brOp.getBlockArguments(), headerBlock);
1408 rewriter.eraseBlock(entryBlock);
1409
1410 // Branch from merge block to end block.
1411 Block *mergeBlock = loopOp.getMergeBlock();
1412 Operation *terminator = mergeBlock->getTerminator();
1413 ValueRange terminatorOperands = terminator->getOperands();
1414 rewriter.setInsertionPointToEnd(mergeBlock);
1415 LLVM::BrOp::create(rewriter, loc, terminatorOperands, endBlock);
1416
1417 rewriter.inlineRegionBefore(loopOp.getBody(), endBlock);
1418 rewriter.replaceOp(loopOp, endBlock->getArguments());
1419 return success();
1420 }
1421};
1422
1423/// Converts `spirv.mlir.selection` with `spirv.BranchConditional` in its header
1424/// block. All blocks within selection should be reachable for conversion to
1425/// succeed.
1426class SelectionPattern : public SPIRVToLLVMConversion<spirv::SelectionOp> {
1427public:
1428 using SPIRVToLLVMConversion<spirv::SelectionOp>::SPIRVToLLVMConversion;
1429
1430 LogicalResult
1431 matchAndRewrite(spirv::SelectionOp op, OpAdaptor adaptor,
1432 ConversionPatternRewriter &rewriter) const override {
1433 // There is no support for `Flatten` or `DontFlatten` selection control at
1434 // the moment. This are just compiler hints and can be performed during the
1435 // optimization passes.
1436 if (op.getSelectionControl() != spirv::SelectionControl::None)
1437 return failure();
1438
1439 // `spirv.mlir.selection` should have at least two blocks: one selection
1440 // header block and one merge block. If no blocks are present, or control
1441 // flow branches straight to merge block (two blocks are present), the op is
1442 // redundant and it is erased.
1443 if (op.getBody().getBlocks().size() <= 2) {
1444 rewriter.eraseOp(op);
1445 return success();
1446 }
1447
1448 Location loc = op.getLoc();
1449
1450 // Split the current block after `spirv.mlir.selection`. The remaining ops
1451 // will be used in `continueBlock`.
1452 auto *currentBlock = rewriter.getInsertionBlock();
1453 rewriter.setInsertionPointAfter(op);
1454 auto position = rewriter.getInsertionPoint();
1455 auto *continueBlock = rewriter.splitBlock(currentBlock, position);
1456
1457 // Extract conditional branch information from the header block. By SPIR-V
1458 // dialect spec, it should contain `spirv.BranchConditional` or
1459 // `spirv.Switch` op. Note that `spirv.Switch op` is not supported at the
1460 // moment in the SPIR-V dialect. Remove this block when finished.
1461 auto *headerBlock = op.getHeaderBlock();
1462 assert(headerBlock->getOperations().size() == 1);
1463 auto condBrOp = dyn_cast<spirv::BranchConditionalOp>(
1464 headerBlock->getOperations().front());
1465 if (!condBrOp)
1466 return failure();
1467
1468 // Branch from merge block to continue block.
1469 auto *mergeBlock = op.getMergeBlock();
1470 Operation *terminator = mergeBlock->getTerminator();
1471 ValueRange terminatorOperands = terminator->getOperands();
1472 rewriter.setInsertionPointToEnd(mergeBlock);
1473 LLVM::BrOp::create(rewriter, loc, terminatorOperands, continueBlock);
1474
1475 // Link current block to `true` and `false` blocks within the selection.
1476 Block *trueBlock = condBrOp.getTrueBlock();
1477 Block *falseBlock = condBrOp.getFalseBlock();
1478 rewriter.setInsertionPointToEnd(currentBlock);
1479 LLVM::CondBrOp::create(rewriter, loc, condBrOp.getCondition(), trueBlock,
1480 condBrOp.getTrueTargetOperands(), falseBlock,
1481 condBrOp.getFalseTargetOperands());
1482
1483 rewriter.eraseBlock(headerBlock);
1484 rewriter.inlineRegionBefore(op.getBody(), continueBlock);
1485 rewriter.replaceOp(op, continueBlock->getArguments());
1486 return success();
1487 }
1488};
1489
1490/// Converts SPIR-V shift ops to LLVM shift ops. Since LLVM dialect
1491/// puts a restriction on `Shift` and `Base` to have the same bit width,
1492/// `Shift` is zero or sign extended to match this specification. Cases when
1493/// `Shift` bit width > `Base` bit width are considered to be illegal.
1494template <typename SPIRVOp, typename LLVMOp>
1495class ShiftPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1496public:
1497 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1498
1499 LogicalResult
1500 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1501 ConversionPatternRewriter &rewriter) const override {
1502
1503 auto dstType = this->getTypeConverter()->convertType(op.getType());
1504 if (!dstType)
1505 return rewriter.notifyMatchFailure(op, "type conversion failed");
1506
1507 Type op1Type = op.getOperand1().getType();
1508 Type op2Type = op.getOperand2().getType();
1509
1510 if (op1Type == op2Type) {
1511 rewriter.template replaceOpWithNewOp<LLVMOp>(op, dstType,
1512 adaptor.getOperands());
1513 return success();
1514 }
1515
1516 std::optional<uint64_t> dstTypeWidth =
1518 std::optional<uint64_t> op2TypeWidth =
1520
1521 if (!dstTypeWidth || !op2TypeWidth)
1522 return failure();
1523
1524 Location loc = op.getLoc();
1525 Value extended;
1526 if (op2TypeWidth < dstTypeWidth) {
1527 if (isUnsignedIntegerOrVector(op2Type)) {
1528 extended =
1529 LLVM::ZExtOp::create(rewriter, loc, dstType, adaptor.getOperand2());
1530 } else {
1531 extended =
1532 LLVM::SExtOp::create(rewriter, loc, dstType, adaptor.getOperand2());
1533 }
1534 } else if (op2TypeWidth == dstTypeWidth) {
1535 extended = adaptor.getOperand2();
1536 } else {
1537 return failure();
1538 }
1539
1540 Value result =
1541 LLVMOp::create(rewriter, loc, dstType, adaptor.getOperand1(), extended);
1542 rewriter.replaceOp(op, result);
1543 return success();
1544 }
1545};
1546
1547class TanPattern : public SPIRVToLLVMConversion<spirv::GLTanOp> {
1548public:
1549 using SPIRVToLLVMConversion<spirv::GLTanOp>::SPIRVToLLVMConversion;
1550
1551 LogicalResult
1552 matchAndRewrite(spirv::GLTanOp tanOp, OpAdaptor adaptor,
1553 ConversionPatternRewriter &rewriter) const override {
1554 auto dstType = getTypeConverter()->convertType(tanOp.getType());
1555 if (!dstType)
1556 return rewriter.notifyMatchFailure(tanOp, "type conversion failed");
1557
1558 rewriter.replaceOpWithNewOp<LLVM::TanOp>(tanOp, dstType,
1559 adaptor.getOperands());
1560 return success();
1561 }
1562};
1563
1564class TanhPattern : public SPIRVToLLVMConversion<spirv::GLTanhOp> {
1565public:
1566 using SPIRVToLLVMConversion<spirv::GLTanhOp>::SPIRVToLLVMConversion;
1567
1568 LogicalResult
1569 matchAndRewrite(spirv::GLTanhOp tanhOp, OpAdaptor adaptor,
1570 ConversionPatternRewriter &rewriter) const override {
1571 auto srcType = tanhOp.getType();
1572 auto dstType = getTypeConverter()->convertType(srcType);
1573 if (!dstType)
1574 return rewriter.notifyMatchFailure(tanhOp, "type conversion failed");
1575
1576 rewriter.replaceOpWithNewOp<LLVM::TanhOp>(tanhOp, dstType,
1577 adaptor.getOperands());
1578 return success();
1579 }
1580};
1581
1582// `llvm.intr.abs` requires an `is_int_min_poison` immarg that `spirv.GL.SAbs`
1583// does not carry; default to `false` to preserve SPIR-V's well-defined
1584// behavior on INT_MIN.
1585class SAbsPattern : public SPIRVToLLVMConversion<spirv::GLSAbsOp> {
1586public:
1587 using SPIRVToLLVMConversion<spirv::GLSAbsOp>::SPIRVToLLVMConversion;
1588
1589 LogicalResult
1590 matchAndRewrite(spirv::GLSAbsOp op, OpAdaptor adaptor,
1591 ConversionPatternRewriter &rewriter) const override {
1592 Type dstType = getTypeConverter()->convertType(op.getType());
1593 if (!dstType)
1594 return rewriter.notifyMatchFailure(op, "type conversion failed");
1595
1596 rewriter.replaceOpWithNewOp<LLVM::AbsOp>(op, dstType, adaptor.getOperand(),
1597 /*is_int_min_poison=*/false);
1598 return success();
1599 }
1600};
1601
1602/// Converts `spirv.GL.Fract` to `x - floor(x)`.
1603class FractPattern : public SPIRVToLLVMConversion<spirv::GLFractOp> {
1604public:
1605 using SPIRVToLLVMConversion<spirv::GLFractOp>::SPIRVToLLVMConversion;
1606
1607 LogicalResult
1608 matchAndRewrite(spirv::GLFractOp op, OpAdaptor adaptor,
1609 ConversionPatternRewriter &rewriter) const override {
1610 Type dstType = getTypeConverter()->convertType(op.getType());
1611 if (!dstType)
1612 return rewriter.notifyMatchFailure(op, "type conversion failed");
1613
1614 Location loc = op.getLoc();
1615 Value operand = adaptor.getOperand();
1616 Value floored = LLVM::FFloorOp::create(rewriter, loc, dstType, operand);
1617 rewriter.replaceOpWithNewOp<LLVM::FSubOp>(op, dstType, operand, floored);
1618 return success();
1619 }
1620};
1621
1622/// Converts `spirv.GL.FMix` to `x * (1 - a) + y * a` as specified by
1623/// GL.std.450.
1624class GLFMixPattern : public SPIRVToLLVMConversion<spirv::GLFMixOp> {
1625public:
1626 using SPIRVToLLVMConversion<spirv::GLFMixOp>::SPIRVToLLVMConversion;
1627
1628 LogicalResult
1629 matchAndRewrite(spirv::GLFMixOp op, OpAdaptor adaptor,
1630 ConversionPatternRewriter &rewriter) const override {
1631 Type dstType = getTypeConverter()->convertType(op.getType());
1632 if (!dstType)
1633 return rewriter.notifyMatchFailure(op, "type conversion failed");
1634
1635 Location loc = op.getLoc();
1636 Value x = adaptor.getX();
1637 Value y = adaptor.getY();
1638 Value a = adaptor.getA();
1639 Value one = createFPConstant(loc, op.getType(), dstType, rewriter, 1.0);
1640 Value oneMinusA = LLVM::FSubOp::create(rewriter, loc, dstType, one, a);
1641 Value lhs = LLVM::FMulOp::create(rewriter, loc, dstType, x, oneMinusA);
1642 Value rhs = LLVM::FMulOp::create(rewriter, loc, dstType, y, a);
1643 rewriter.replaceOpWithNewOp<LLVM::FAddOp>(op, dstType, lhs, rhs);
1644 return success();
1645 }
1646};
1647
1648/// Converts `spirv.CL.mix` to `fma(a, y - x, x)`. The OpenCL spec defines
1649/// mix as `x + (y - x) * a` and explicitly permits FMA contractions.
1650class CLMixPattern : public SPIRVToLLVMConversion<spirv::CLMixOp> {
1651public:
1652 using SPIRVToLLVMConversion<spirv::CLMixOp>::SPIRVToLLVMConversion;
1653
1654 LogicalResult
1655 matchAndRewrite(spirv::CLMixOp op, OpAdaptor adaptor,
1656 ConversionPatternRewriter &rewriter) const override {
1657 Type dstType = getTypeConverter()->convertType(op.getType());
1658 if (!dstType)
1659 return rewriter.notifyMatchFailure(op, "type conversion failed");
1660
1661 Location loc = op.getLoc();
1662 Value x = adaptor.getX();
1663 Value y = adaptor.getY();
1664 Value a = adaptor.getZ();
1665 Value diff = LLVM::FSubOp::create(rewriter, loc, dstType, y, x);
1666 rewriter.replaceOpWithNewOp<LLVM::FMAOp>(op, dstType, a, diff, x);
1667 return success();
1668 }
1669};
1670
1671// Converts spirv.GL.Radians (scale = pi/180) and spirv.GL.Degrees
1672// (scale = 180/pi) by multiplying the operand by a compile-time constant.
1673template <typename SPIRVOp>
1674class ScalePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1675public:
1676 template <typename... Args>
1677 ScalePattern(double scale, Args &&...args)
1678 : SPIRVToLLVMConversion<SPIRVOp>(std::forward<Args>(args)...),
1679 scale(scale) {}
1680
1681 LogicalResult
1682 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1683 ConversionPatternRewriter &rewriter) const override {
1684 Type srcType = op.getType();
1685 Type dstType = this->getTypeConverter()->convertType(srcType);
1686 if (!dstType)
1687 return rewriter.notifyMatchFailure(op, "type conversion failed");
1688
1689 Location loc = op.getLoc();
1690 Value factor = createFPConstant(loc, srcType, dstType, rewriter, scale);
1691 rewriter.replaceOpWithNewOp<LLVM::FMulOp>(op, dstType, adaptor.getOperand(),
1692 factor);
1693 return success();
1694 }
1695
1696private:
1697 double scale;
1698};
1699
1700class VariablePattern : public SPIRVToLLVMConversion<spirv::VariableOp> {
1701public:
1702 using SPIRVToLLVMConversion<spirv::VariableOp>::SPIRVToLLVMConversion;
1703
1704 LogicalResult
1705 matchAndRewrite(spirv::VariableOp varOp, OpAdaptor adaptor,
1706 ConversionPatternRewriter &rewriter) const override {
1707 auto srcType = varOp.getType();
1708 // Initialization is supported for scalars and vectors only.
1709 auto pointerTo = cast<spirv::PointerType>(srcType).getPointeeType();
1710 auto init = varOp.getInitializer();
1711 if (init && !pointerTo.isIntOrFloat() && !isa<VectorType>(pointerTo))
1712 return failure();
1713
1714 auto dstType = getTypeConverter()->convertType(srcType);
1715 if (!dstType)
1716 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
1717
1718 Location loc = varOp.getLoc();
1719 Value size = createI32ConstantOf(loc, rewriter, 1);
1720 if (!init) {
1721 auto elementType = getTypeConverter()->convertType(pointerTo);
1722 if (!elementType)
1723 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
1724 rewriter.replaceOpWithNewOp<LLVM::AllocaOp>(varOp, dstType, elementType,
1725 size);
1726 return success();
1727 }
1728 auto elementType = getTypeConverter()->convertType(pointerTo);
1729 if (!elementType)
1730 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
1731 Value allocated =
1732 LLVM::AllocaOp::create(rewriter, loc, dstType, elementType, size);
1733 LLVM::StoreOp::create(rewriter, loc, adaptor.getInitializer(), allocated);
1734 rewriter.replaceOp(varOp, allocated);
1735 return success();
1736 }
1737};
1738
1739//===----------------------------------------------------------------------===//
1740// BitcastOp conversion
1741//===----------------------------------------------------------------------===//
1742
1743class BitcastConversionPattern
1744 : public SPIRVToLLVMConversion<spirv::BitcastOp> {
1745public:
1746 using SPIRVToLLVMConversion<spirv::BitcastOp>::SPIRVToLLVMConversion;
1747
1748 LogicalResult
1749 matchAndRewrite(spirv::BitcastOp bitcastOp, OpAdaptor adaptor,
1750 ConversionPatternRewriter &rewriter) const override {
1751 auto dstType = getTypeConverter()->convertType(bitcastOp.getType());
1752 if (!dstType)
1753 return rewriter.notifyMatchFailure(bitcastOp, "type conversion failed");
1754
1755 // LLVM's opaque pointers do not require bitcasts.
1756 if (isa<LLVM::LLVMPointerType>(dstType)) {
1757 rewriter.replaceOp(bitcastOp, adaptor.getOperand());
1758 return success();
1759 }
1760
1761 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(
1762 bitcastOp, dstType, adaptor.getOperands(), bitcastOp->getAttrs());
1763 return success();
1764 }
1765};
1766
1767//===----------------------------------------------------------------------===//
1768// FuncOp conversion
1769//===----------------------------------------------------------------------===//
1770
1771class FuncConversionPattern : public SPIRVToLLVMConversion<spirv::FuncOp> {
1772public:
1773 using SPIRVToLLVMConversion<spirv::FuncOp>::SPIRVToLLVMConversion;
1774
1775 LogicalResult
1776 matchAndRewrite(spirv::FuncOp funcOp, OpAdaptor adaptor,
1777 ConversionPatternRewriter &rewriter) const override {
1778
1779 // Convert function signature. At the moment LLVMType converter is enough
1780 // for currently supported types.
1781 auto funcType = funcOp.getFunctionType();
1782 TypeConverter::SignatureConversion signatureConverter(
1783 funcType.getNumInputs());
1784 auto llvmType = static_cast<const LLVMTypeConverter *>(getTypeConverter())
1785 ->convertFunctionSignature(
1786 funcType, /*isVariadic=*/false,
1787 /*useBarePtrCallConv=*/false, signatureConverter);
1788 if (!llvmType)
1789 return failure();
1790
1791 // Create a new `LLVMFuncOp`
1792 Location loc = funcOp.getLoc();
1793 StringRef name = funcOp.getName();
1794 auto newFuncOp = LLVM::LLVMFuncOp::create(rewriter, loc, name, llvmType);
1795
1796 // Convert SPIR-V Function Control to equivalent LLVM function attribute
1797 MLIRContext *context = funcOp.getContext();
1798 switch (funcOp.getFunctionControl()) {
1799 case spirv::FunctionControl::Inline:
1800 newFuncOp.setAlwaysInline(true);
1801 break;
1802 case spirv::FunctionControl::DontInline:
1803 newFuncOp.setNoInline(true);
1804 break;
1805
1806#define DISPATCH(functionControl, llvmAttr) \
1807 case functionControl: \
1808 newFuncOp->setAttr("passthrough", ArrayAttr::get(context, {llvmAttr})); \
1809 break;
1810
1811 DISPATCH(spirv::FunctionControl::Pure,
1812 StringAttr::get(context, "readonly"));
1813 DISPATCH(spirv::FunctionControl::Const,
1814 StringAttr::get(context, "readnone"));
1815
1816#undef DISPATCH
1817
1818 // Default: if `spirv::FunctionControl::None`, then no attributes are
1819 // needed.
1820 default:
1821 break;
1822 }
1823
1824 rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
1825 newFuncOp.end());
1826 if (failed(rewriter.convertRegionTypes(
1827 &newFuncOp.getBody(), *getTypeConverter(), &signatureConverter))) {
1828 return failure();
1829 }
1830 rewriter.eraseOp(funcOp);
1831 return success();
1832 }
1833};
1834
1835//===----------------------------------------------------------------------===//
1836// ModuleOp conversion
1837//===----------------------------------------------------------------------===//
1838
1839class ModuleConversionPattern : public SPIRVToLLVMConversion<spirv::ModuleOp> {
1840public:
1841 using SPIRVToLLVMConversion<spirv::ModuleOp>::SPIRVToLLVMConversion;
1842
1843 LogicalResult
1844 matchAndRewrite(spirv::ModuleOp spvModuleOp, OpAdaptor adaptor,
1845 ConversionPatternRewriter &rewriter) const override {
1846
1847 auto newModuleOp =
1848 ModuleOp::create(rewriter, spvModuleOp.getLoc(), spvModuleOp.getName());
1849 rewriter.inlineRegionBefore(spvModuleOp.getRegion(), newModuleOp.getBody());
1850
1851 // Remove the terminator block that was automatically added by builder
1852 rewriter.eraseBlock(&newModuleOp.getBodyRegion().back());
1853 rewriter.eraseOp(spvModuleOp);
1854 return success();
1855 }
1856};
1857
1858//===----------------------------------------------------------------------===//
1859// VectorShuffleOp conversion
1860//===----------------------------------------------------------------------===//
1861
1862class VectorShufflePattern
1863 : public SPIRVToLLVMConversion<spirv::VectorShuffleOp> {
1864public:
1865 using SPIRVToLLVMConversion<spirv::VectorShuffleOp>::SPIRVToLLVMConversion;
1866 LogicalResult
1867 matchAndRewrite(spirv::VectorShuffleOp op, OpAdaptor adaptor,
1868 ConversionPatternRewriter &rewriter) const override {
1869 Location loc = op.getLoc();
1870 auto components = adaptor.getComponents();
1871 auto vector1 = adaptor.getVector1();
1872 auto vector2 = adaptor.getVector2();
1873 int vector1Size = cast<VectorType>(vector1.getType()).getNumElements();
1874 int vector2Size = cast<VectorType>(vector2.getType()).getNumElements();
1875 if (vector1Size == vector2Size) {
1876 rewriter.replaceOpWithNewOp<LLVM::ShuffleVectorOp>(
1877 op, vector1, vector2,
1879 return success();
1880 }
1881
1882 auto dstType = getTypeConverter()->convertType(op.getType());
1883 if (!dstType)
1884 return rewriter.notifyMatchFailure(op, "type conversion failed");
1885 auto scalarType = cast<VectorType>(dstType).getElementType();
1886 auto componentsArray = components.getValue();
1887 auto *context = rewriter.getContext();
1888 auto llvmI32Type = IntegerType::get(context, 32);
1889 Value targetOp = LLVM::PoisonOp::create(rewriter, loc, dstType);
1890 for (unsigned i = 0; i < componentsArray.size(); i++) {
1891 if (!isa<IntegerAttr>(componentsArray[i]))
1892 return op.emitError("unable to support non-constant component");
1893
1894 int indexVal = cast<IntegerAttr>(componentsArray[i]).getInt();
1895 if (indexVal == -1)
1896 continue;
1897
1898 int offsetVal = 0;
1899 Value baseVector = vector1;
1900 if (indexVal >= vector1Size) {
1901 offsetVal = vector1Size;
1902 baseVector = vector2;
1903 }
1904
1905 Value dstIndex = LLVM::ConstantOp::create(
1906 rewriter, loc, llvmI32Type,
1907 rewriter.getIntegerAttr(rewriter.getI32Type(), i));
1908 Value index = LLVM::ConstantOp::create(
1909 rewriter, loc, llvmI32Type,
1910 rewriter.getIntegerAttr(rewriter.getI32Type(), indexVal - offsetVal));
1911
1912 auto extractOp = LLVM::ExtractElementOp::create(rewriter, loc, scalarType,
1913 baseVector, index);
1914 targetOp = LLVM::InsertElementOp::create(rewriter, loc, dstType, targetOp,
1915 extractOp, dstIndex);
1916 }
1917 rewriter.replaceOp(op, targetOp);
1918 return success();
1919 }
1920};
1921} // namespace
1922
1923//===----------------------------------------------------------------------===//
1924// Pattern population
1925//===----------------------------------------------------------------------===//
1926
1928 spirv::ClientAPI clientAPI) {
1929 typeConverter.addConversion([&](spirv::ArrayType type) {
1930 return convertArrayType(type, typeConverter);
1931 });
1932 typeConverter.addConversion([&, clientAPI](spirv::PointerType type) {
1933 return convertPointerType(type, typeConverter, clientAPI);
1934 });
1935 typeConverter.addConversion([&](spirv::RuntimeArrayType type) {
1936 return convertRuntimeArrayType(type, typeConverter);
1937 });
1938 typeConverter.addConversion([&](spirv::StructType type) {
1939 return convertStructType(type, typeConverter);
1940 });
1941}
1942
1944 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns,
1945 spirv::ClientAPI clientAPI) {
1946 patterns.add<
1947 // Arithmetic ops
1948 DirectConversionPattern<spirv::IAddOp, LLVM::AddOp>,
1949 DirectConversionPattern<spirv::IMulOp, LLVM::MulOp>,
1950 DirectConversionPattern<spirv::ISubOp, LLVM::SubOp>,
1951 DirectConversionPattern<spirv::FAddOp, LLVM::FAddOp>,
1952 DirectConversionPattern<spirv::FDivOp, LLVM::FDivOp>,
1953 DirectConversionPattern<spirv::FMulOp, LLVM::FMulOp>,
1954 DirectConversionPattern<spirv::FNegateOp, LLVM::FNegOp>,
1955 DirectConversionPattern<spirv::FRemOp, LLVM::FRemOp>,
1956 DirectConversionPattern<spirv::FSubOp, LLVM::FSubOp>,
1957 DirectConversionPattern<spirv::SDivOp, LLVM::SDivOp>,
1958 DirectConversionPattern<spirv::SRemOp, LLVM::SRemOp>,
1959 DirectConversionPattern<spirv::UDivOp, LLVM::UDivOp>,
1960 DirectConversionPattern<spirv::UModOp, LLVM::URemOp>,
1961
1962 // Bitwise ops
1963 BitFieldInsertPattern, BitFieldUExtractPattern, BitFieldSExtractPattern,
1964 DirectConversionPattern<spirv::BitCountOp, LLVM::CtPopOp>,
1965 DirectConversionPattern<spirv::BitReverseOp, LLVM::BitReverseOp>,
1966 DirectConversionPattern<spirv::BitwiseAndOp, LLVM::AndOp>,
1967 DirectConversionPattern<spirv::BitwiseOrOp, LLVM::OrOp>,
1968 DirectConversionPattern<spirv::BitwiseXorOp, LLVM::XOrOp>,
1969 NotPattern<spirv::NotOp>,
1970
1971 // Cast ops
1972 BitcastConversionPattern,
1973 DirectConversionPattern<spirv::ConvertFToSOp, LLVM::FPToSIOp>,
1974 DirectConversionPattern<spirv::ConvertFToUOp, LLVM::FPToUIOp>,
1975 DirectConversionPattern<spirv::ConvertSToFOp, LLVM::SIToFPOp>,
1976 DirectConversionPattern<spirv::ConvertUToFOp, LLVM::UIToFPOp>,
1977 IndirectCastPattern<spirv::FConvertOp, LLVM::FPExtOp, LLVM::FPTruncOp>,
1978 IndirectCastPattern<spirv::SConvertOp, LLVM::SExtOp, LLVM::TruncOp>,
1979 IndirectCastPattern<spirv::UConvertOp, LLVM::ZExtOp, LLVM::TruncOp>,
1980 DirectConversionPattern<spirv::ConvertPtrToUOp, LLVM::PtrToIntOp>,
1981 DirectConversionPattern<spirv::ConvertUToPtrOp, LLVM::IntToPtrOp>,
1982 DirectConversionPattern<spirv::PtrCastToGenericOp, LLVM::AddrSpaceCastOp>,
1983 DirectConversionPattern<spirv::GenericCastToPtrOp, LLVM::AddrSpaceCastOp>,
1984 DirectConversionPattern<spirv::GenericCastToPtrExplicitOp,
1985 LLVM::AddrSpaceCastOp>,
1986
1987 // Comparison ops
1988 IComparePattern<spirv::IEqualOp, LLVM::ICmpPredicate::eq>,
1989 IComparePattern<spirv::INotEqualOp, LLVM::ICmpPredicate::ne>,
1990 FComparePattern<spirv::FOrdEqualOp, LLVM::FCmpPredicate::oeq>,
1991 FComparePattern<spirv::FOrdGreaterThanOp, LLVM::FCmpPredicate::ogt>,
1992 FComparePattern<spirv::FOrdGreaterThanEqualOp, LLVM::FCmpPredicate::oge>,
1993 FComparePattern<spirv::FOrdLessThanEqualOp, LLVM::FCmpPredicate::ole>,
1994 FComparePattern<spirv::FOrdLessThanOp, LLVM::FCmpPredicate::olt>,
1995 FComparePattern<spirv::FOrdNotEqualOp, LLVM::FCmpPredicate::one>,
1996 FComparePattern<spirv::FUnordEqualOp, LLVM::FCmpPredicate::ueq>,
1997 FComparePattern<spirv::FUnordGreaterThanOp, LLVM::FCmpPredicate::ugt>,
1998 FComparePattern<spirv::FUnordGreaterThanEqualOp,
1999 LLVM::FCmpPredicate::uge>,
2000 FComparePattern<spirv::FUnordLessThanEqualOp, LLVM::FCmpPredicate::ule>,
2001 FComparePattern<spirv::FUnordLessThanOp, LLVM::FCmpPredicate::ult>,
2002 FComparePattern<spirv::FUnordNotEqualOp, LLVM::FCmpPredicate::une>,
2003 FComparePattern<spirv::OrderedOp, LLVM::FCmpPredicate::ord>,
2004 FComparePattern<spirv::UnorderedOp, LLVM::FCmpPredicate::uno>,
2005 IComparePattern<spirv::SGreaterThanOp, LLVM::ICmpPredicate::sgt>,
2006 IComparePattern<spirv::SGreaterThanEqualOp, LLVM::ICmpPredicate::sge>,
2007 IComparePattern<spirv::SLessThanEqualOp, LLVM::ICmpPredicate::sle>,
2008 IComparePattern<spirv::SLessThanOp, LLVM::ICmpPredicate::slt>,
2009 IComparePattern<spirv::UGreaterThanOp, LLVM::ICmpPredicate::ugt>,
2010 IComparePattern<spirv::UGreaterThanEqualOp, LLVM::ICmpPredicate::uge>,
2011 IComparePattern<spirv::ULessThanEqualOp, LLVM::ICmpPredicate::ule>,
2012 IComparePattern<spirv::ULessThanOp, LLVM::ICmpPredicate::ult>,
2013
2014 // Constant op
2015 ConstantScalarAndVectorPattern,
2016
2017 // Control Flow ops
2018 BranchConversionPattern, BranchConditionalConversionPattern,
2019 FunctionCallPattern, LoopPattern, SelectionPattern,
2020 ErasePattern<spirv::MergeOp>,
2021
2022 // Entry points and execution mode are handled separately.
2023 ErasePattern<spirv::EntryPointOp>, ExecutionModePattern,
2024
2025 // GLSL extended instruction set ops
2026 DirectConversionPattern<spirv::GLCeilOp, LLVM::FCeilOp>,
2027 DirectConversionPattern<spirv::GLCosOp, LLVM::CosOp>,
2028 DirectConversionPattern<spirv::GLExpOp, LLVM::ExpOp>,
2029 DirectConversionPattern<spirv::GLExp2Op, LLVM::Exp2Op>,
2030 DirectConversionPattern<spirv::GLFAbsOp, LLVM::FAbsOp>,
2031 DirectConversionPattern<spirv::GLFloorOp, LLVM::FFloorOp>,
2032 DirectConversionPattern<spirv::GLFmaOp, LLVM::FMAOp>,
2033 ClampPattern<spirv::GLFClampOp, LLVM::MinNumOp, LLVM::MaxNumOp>,
2034 ClampPattern<spirv::GLSClampOp, LLVM::SMinOp, LLVM::SMaxOp>,
2035 ClampPattern<spirv::GLUClampOp, LLVM::UMinOp, LLVM::UMaxOp>,
2036 DirectConversionPattern<spirv::GLFMaxOp, LLVM::MaxNumOp>,
2037 DirectConversionPattern<spirv::GLFMinOp, LLVM::MinNumOp>,
2038 DirectConversionPattern<spirv::GLNMaxOp, LLVM::MaxNumOp>,
2039 DirectConversionPattern<spirv::GLNMinOp, LLVM::MinNumOp>,
2040 DirectConversionPattern<spirv::GLLogOp, LLVM::LogOp>,
2041 DirectConversionPattern<spirv::GLLog2Op, LLVM::Log2Op>,
2042 DirectConversionPattern<spirv::GLPowOp, LLVM::PowOp>,
2043 DirectConversionPattern<spirv::GLRoundOp, LLVM::RoundOp>,
2044 DirectConversionPattern<spirv::GLRoundEvenOp, LLVM::RoundEvenOp>,
2045 DirectConversionPattern<spirv::GLSinOp, LLVM::SinOp>,
2046 DirectConversionPattern<spirv::GLSinhOp, LLVM::SinhOp>,
2047 DirectConversionPattern<spirv::GLCoshOp, LLVM::CoshOp>,
2048 DirectConversionPattern<spirv::GLSMaxOp, LLVM::SMaxOp>,
2049 DirectConversionPattern<spirv::GLSMinOp, LLVM::SMinOp>,
2050 DirectConversionPattern<spirv::GLSqrtOp, LLVM::SqrtOp>,
2051 DirectConversionPattern<spirv::GLUMaxOp, LLVM::UMaxOp>,
2052 DirectConversionPattern<spirv::GLUMinOp, LLVM::UMinOp>,
2053 DirectConversionPattern<spirv::GLTruncOp, LLVM::FTruncOp>,
2054 DirectConversionPattern<spirv::GLAsinOp, LLVM::ASinOp>,
2055 DirectConversionPattern<spirv::GLAcosOp, LLVM::ACosOp>,
2056 DirectConversionPattern<spirv::GLAtanOp, LLVM::ATanOp>,
2057 InverseSqrtPattern, SAbsPattern, TanPattern, TanhPattern, FractPattern,
2058 GLFMixPattern,
2059
2060 // OpenCL extended instruction set ops
2061 DirectConversionPattern<spirv::CLCeilOp, LLVM::FCeilOp>,
2062 DirectConversionPattern<spirv::CLCosOp, LLVM::CosOp>,
2063 DirectConversionPattern<spirv::CLExpOp, LLVM::ExpOp>,
2064 DirectConversionPattern<spirv::CLExp2Op, LLVM::Exp2Op>,
2065 DirectConversionPattern<spirv::CLExp10Op, LLVM::Exp10Op>,
2066 DirectConversionPattern<spirv::CLFAbsOp, LLVM::FAbsOp>,
2067 DirectConversionPattern<spirv::CLFloorOp, LLVM::FFloorOp>,
2068 DirectConversionPattern<spirv::CLFmaOp, LLVM::FMAOp>,
2069 DirectConversionPattern<spirv::CLFMaxOp, LLVM::MaxNumOp>,
2070 DirectConversionPattern<spirv::CLFMinOp, LLVM::MinNumOp>,
2071 DirectConversionPattern<spirv::CLLogOp, LLVM::LogOp>,
2072 DirectConversionPattern<spirv::CLLog2Op, LLVM::Log2Op>,
2073 DirectConversionPattern<spirv::CLLog10Op, LLVM::Log10Op>,
2074 DirectConversionPattern<spirv::CLPowOp, LLVM::PowOp>,
2075 DirectConversionPattern<spirv::CLRintOp, LLVM::RintOp>,
2076 DirectConversionPattern<spirv::CLRoundOp, LLVM::RoundOp>,
2077 DirectConversionPattern<spirv::CLSinOp, LLVM::SinOp>,
2078 DirectConversionPattern<spirv::CLSinhOp, LLVM::SinhOp>,
2079 DirectConversionPattern<spirv::CLCoshOp, LLVM::CoshOp>,
2080 DirectConversionPattern<spirv::CLTanOp, LLVM::TanOp>,
2081 DirectConversionPattern<spirv::CLTanhOp, LLVM::TanhOp>,
2082 DirectConversionPattern<spirv::CLAsinOp, LLVM::ASinOp>,
2083 DirectConversionPattern<spirv::CLAcosOp, LLVM::ACosOp>,
2084 DirectConversionPattern<spirv::CLAtanOp, LLVM::ATanOp>,
2085 DirectConversionPattern<spirv::CLAtan2Op, LLVM::ATan2Op>,
2086 DirectConversionPattern<spirv::CLSqrtOp, LLVM::SqrtOp>,
2087 DirectConversionPattern<spirv::CLTruncOp, LLVM::FTruncOp>,
2088 DirectConversionPattern<spirv::CLCopysignOp, LLVM::CopySignOp>,
2089 DirectConversionPattern<spirv::CLFmodOp, LLVM::FRemOp>,
2090 DirectConversionPattern<spirv::CLSMaxOp, LLVM::SMaxOp>,
2091 DirectConversionPattern<spirv::CLSMinOp, LLVM::SMinOp>,
2092 DirectConversionPattern<spirv::CLUMaxOp, LLVM::UMaxOp>,
2093 DirectConversionPattern<spirv::CLUMinOp, LLVM::UMinOp>, CLMixPattern,
2094
2095 // Logical ops
2096 DirectConversionPattern<spirv::LogicalAndOp, LLVM::AndOp>,
2097 DirectConversionPattern<spirv::LogicalOrOp, LLVM::OrOp>,
2098 IComparePattern<spirv::LogicalEqualOp, LLVM::ICmpPredicate::eq>,
2099 IComparePattern<spirv::LogicalNotEqualOp, LLVM::ICmpPredicate::ne>,
2100 NotPattern<spirv::LogicalNotOp>,
2101
2102 // Memory ops
2103 AccessChainPattern, AddressOfPattern, LoadStorePattern<spirv::LoadOp>,
2104 LoadStorePattern<spirv::StoreOp>, VariablePattern,
2105
2106 // Miscellaneous ops
2107 CompositeExtractPattern, CompositeInsertPattern,
2108 DirectConversionPattern<spirv::SelectOp, LLVM::SelectOp>,
2109 DirectConversionPattern<spirv::UndefOp, LLVM::UndefOp>,
2110 VectorShufflePattern,
2111
2112 // Shift ops
2113 ShiftPattern<spirv::ShiftRightArithmeticOp, LLVM::AShrOp>,
2114 ShiftPattern<spirv::ShiftRightLogicalOp, LLVM::LShrOp>,
2115 ShiftPattern<spirv::ShiftLeftLogicalOp, LLVM::ShlOp>,
2116
2117 // Return ops
2118 ReturnPattern, ReturnValuePattern,
2119
2120 // Unreachable op
2121 UnreachablePattern,
2122
2123 // Barrier ops
2124 ControlBarrierPattern<spirv::ControlBarrierOp>,
2125 ControlBarrierPattern<spirv::INTELControlBarrierArriveOp>,
2126 ControlBarrierPattern<spirv::INTELControlBarrierWaitOp>,
2127
2128 // Group reduction operations
2129 GroupReducePattern<spirv::GroupIAddOp>,
2130 GroupReducePattern<spirv::GroupFAddOp>,
2131 GroupReducePattern<spirv::GroupFMinOp>,
2132 GroupReducePattern<spirv::GroupUMinOp>,
2133 GroupReducePattern<spirv::GroupSMinOp, /*Signed=*/true>,
2134 GroupReducePattern<spirv::GroupFMaxOp>,
2135 GroupReducePattern<spirv::GroupUMaxOp>,
2136 GroupReducePattern<spirv::GroupSMaxOp, /*Signed=*/true>,
2137 GroupReducePattern<spirv::GroupNonUniformIAddOp, /*Signed=*/false,
2138 /*NonUniform=*/true>,
2139 GroupReducePattern<spirv::GroupNonUniformFAddOp, /*Signed=*/false,
2140 /*NonUniform=*/true>,
2141 GroupReducePattern<spirv::GroupNonUniformIMulOp, /*Signed=*/false,
2142 /*NonUniform=*/true>,
2143 GroupReducePattern<spirv::GroupNonUniformFMulOp, /*Signed=*/false,
2144 /*NonUniform=*/true>,
2145 GroupReducePattern<spirv::GroupNonUniformSMinOp, /*Signed=*/true,
2146 /*NonUniform=*/true>,
2147 GroupReducePattern<spirv::GroupNonUniformUMinOp, /*Signed=*/false,
2148 /*NonUniform=*/true>,
2149 GroupReducePattern<spirv::GroupNonUniformFMinOp, /*Signed=*/false,
2150 /*NonUniform=*/true>,
2151 GroupReducePattern<spirv::GroupNonUniformSMaxOp, /*Signed=*/true,
2152 /*NonUniform=*/true>,
2153 GroupReducePattern<spirv::GroupNonUniformUMaxOp, /*Signed=*/false,
2154 /*NonUniform=*/true>,
2155 GroupReducePattern<spirv::GroupNonUniformFMaxOp, /*Signed=*/false,
2156 /*NonUniform=*/true>,
2157 GroupReducePattern<spirv::GroupNonUniformBitwiseAndOp, /*Signed=*/false,
2158 /*NonUniform=*/true>,
2159 GroupReducePattern<spirv::GroupNonUniformBitwiseOrOp, /*Signed=*/false,
2160 /*NonUniform=*/true>,
2161 GroupReducePattern<spirv::GroupNonUniformBitwiseXorOp, /*Signed=*/false,
2162 /*NonUniform=*/true>,
2163 GroupReducePattern<spirv::GroupNonUniformLogicalAndOp, /*Signed=*/false,
2164 /*NonUniform=*/true>,
2165 GroupReducePattern<spirv::GroupNonUniformLogicalOrOp, /*Signed=*/false,
2166 /*NonUniform=*/true>,
2167 GroupReducePattern<spirv::GroupNonUniformLogicalXorOp, /*Signed=*/false,
2168 /*NonUniform=*/true>>(patterns.getContext(),
2169 typeConverter);
2170
2171 patterns.add<GlobalVariablePattern>(clientAPI, patterns.getContext(),
2172 typeConverter);
2173 // pi / 180
2174 patterns.add<ScalePattern<spirv::GLRadiansOp>>(
2175 0.017453292519943295, patterns.getContext(), typeConverter);
2176 // 180 / pi
2177 patterns.add<ScalePattern<spirv::GLDegreesOp>>(
2178 57.29577951308232, patterns.getContext(), typeConverter);
2179}
2180
2182 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) {
2183 patterns.add<FuncConversionPattern>(patterns.getContext(), typeConverter);
2184}
2185
2187 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) {
2188 patterns.add<ModuleConversionPattern>(patterns.getContext(), typeConverter);
2189}
2190
2191//===----------------------------------------------------------------------===//
2192// Pre-conversion hooks
2193//===----------------------------------------------------------------------===//
2194
2195/// Hook for descriptor set and binding number encoding.
2196static constexpr StringRef kBinding = "binding";
2197static constexpr StringRef kDescriptorSet = "descriptor_set";
2198void mlir::encodeBindAttribute(ModuleOp module) {
2199 auto spvModules = module.getOps<spirv::ModuleOp>();
2200 for (auto spvModule : spvModules) {
2201 spvModule.walk([&](spirv::GlobalVariableOp op) {
2202 IntegerAttr descriptorSet =
2203 op->getAttrOfType<IntegerAttr>(kDescriptorSet);
2204 IntegerAttr binding = op->getAttrOfType<IntegerAttr>(kBinding);
2205 // For every global variable in the module, get the ones with descriptor
2206 // set and binding numbers.
2207 if (descriptorSet && binding) {
2208 // Encode these numbers into the variable's symbolic name. If the
2209 // SPIR-V module has a name, add it at the beginning.
2210 auto moduleAndName =
2211 spvModule.getName().has_value()
2212 ? spvModule.getName()->str() + "_" + op.getSymName().str()
2213 : op.getSymName().str();
2214 std::string name =
2215 llvm::formatv("{0}_descriptor_set{1}_binding{2}", moduleAndName,
2216 std::to_string(descriptorSet.getInt()),
2217 std::to_string(binding.getInt()));
2218 auto nameAttr = StringAttr::get(op->getContext(), name);
2219
2220 // Replace all symbol uses and set the new symbol name. Finally, remove
2221 // descriptor set and binding attributes.
2222 if (failed(SymbolTable::replaceAllSymbolUses(op, nameAttr, spvModule)))
2223 op.emitError("unable to replace all symbol uses for ") << name;
2224 SymbolTable::setSymbolName(op, nameAttr);
2225 op->removeAttr(kDescriptorSet);
2226 op->removeAttr(kBinding);
2227 }
2228 });
2229 }
2230}
return success()
static LLVM::CallOp createSPIRVBuiltinCall(Location loc, ConversionPatternRewriter &rewriter, LLVM::LLVMFuncOp func, ValueRange args)
static LLVM::LLVMFuncOp lookupOrCreateSPIRVFn(Operation *symbolTable, StringRef name, ArrayRef< Type > paramTypes, Type resultType, bool isMemNone, bool isConvergent)
lhs
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value optionallyTruncateOrExtend(Location loc, Value value, Type llvmType, PatternRewriter &rewriter)
Utility function for bitfield ops:
static Value createFPConstant(Location loc, Type srcType, Type dstType, PatternRewriter &rewriter, double value)
Creates llvm.mlir.constant with a floating-point scalar or vector value.
static constexpr StringRef kDescriptorSet
static Value createI32ConstantOf(Location loc, PatternRewriter &rewriter, unsigned value)
Creates LLVM dialect constant with the given value.
static Type convertPointerType(spirv::PointerType type, const TypeConverter &converter, spirv::ClientAPI clientAPI)
Converts SPIR-V pointer type to LLVM pointer.
static Value processCountOrOffset(Location loc, Value value, Type srcType, Type dstType, const TypeConverter &converter, ConversionPatternRewriter &rewriter)
Utility function for bitfield ops: BitFieldInsert, BitFieldSExtract and BitFieldUExtract.
static unsigned getBitWidth(Type type)
Returns the bit width of integer, float or vector of float or integer values.
static LogicalResult replaceWithLoadOrStore(Operation *op, ValueRange operands, ConversionPatternRewriter &rewriter, const TypeConverter &typeConverter, unsigned alignment, bool isVolatile, bool isNonTemporal)
Utility for spirv.Load and spirv.Store conversion.
static Type convertStructTypePacked(spirv::StructType type, const TypeConverter &converter)
Converts SPIR-V struct with no offset to packed LLVM struct.
static bool isSignedIntegerOrVector(Type type)
Returns true if the given type is a signed integer or vector type.
static bool isUnsignedIntegerOrVector(Type type)
Returns true if the given type is an unsigned integer or vector type.
static std::optional< Type > convertRuntimeArrayType(spirv::RuntimeArrayType type, TypeConverter &converter)
Converts SPIR-V runtime array to LLVM array.
static constexpr StringRef kBinding
Hook for descriptor set and binding number encoding.
static IntegerAttr minusOneIntegerAttribute(Type type, Builder builder)
Creates IntegerAttribute with all bits set for given type.
static Value optionallyBroadcast(Location loc, Value value, Type srcType, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value. If srcType is a scalar, the value remains unchanged.
static Value createConstantAllBitsSet(Location loc, Type srcType, Type dstType, PatternRewriter &rewriter)
Creates llvm.mlir.constant with all bits set for the given type.
static unsigned getLLVMTypeBitWidth(Type type)
Returns the bit width of LLVMType integer or vector.
static std::optional< uint64_t > getIntegerOrVectorElementWidth(Type type)
Returns the width of an integer or of the element type of an integer vector, if applicable.
#define DISPATCH(functionControl, llvmAttr)
static Type convertStructTypeWithOffset(spirv::StructType type, const TypeConverter &converter)
Converts SPIR-V struct with a regular (according to VulkanLayoutUtils) offset to LLVM struct.
static Type convertStructType(spirv::StructType type, const TypeConverter &converter)
Converts SPIR-V struct to LLVM struct.
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static std::optional< Type > convertArrayType(spirv::ArrayType type, TypeConverter &converter)
Converts SPIR-V array type to LLVM array.
OpListType::iterator iterator
Definition Block.h:164
OpListType & getOperations()
Definition Block.h:161
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:111
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:233
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:259
IntegerType getI32Type()
Definition Builders.cpp:67
MLIRContext * getContext() const
Definition Builders.h:56
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
Conversion from types to the LLVM IR dialect.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:209
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:711
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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.
static LogicalResult replaceAllSymbolUses(StringAttr oldSymbol, StringAttr newSymbol, Operation *from)
Attempt to replace all uses of the given symbol 'oldSymbol' with the provided symbol 'newSymbol' that...
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static void setSymbolName(Operation *symbol, StringAttr name)
Sets the name of the given symbol operation.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
Definition Types.cpp:78
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
Definition Types.cpp:90
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
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 spirv::StructType decorateType(spirv::StructType structType)
Returns a new StructType with layout decoration.
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
Type getElementType() const
unsigned getArrayStride() const
Returns the array stride in bytes.
unsigned getNumElements() const
StorageClass getStorageClass() const
unsigned getArrayStride() const
Returns the array stride in bytes.
SPIR-V struct type.
Definition SPIRVTypes.h:274
void getMemberDecorations(SmallVectorImpl< StructType::MemberDecorationInfo > &memberDecorations) const
TypeRange getElementTypes() const
SmallVector< IntT > convertArrayToIndices(ArrayRef< Attribute > attrs)
Convert an array of integer attributes to a vector of integers that can be used as indices in LLVM op...
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
unsigned storageClassToAddressSpace(spirv::ClientAPI clientAPI, spirv::StorageClass storageClass)
void populateSPIRVToLLVMTypeConversion(LLVMTypeConverter &typeConverter, spirv::ClientAPI clientAPIForAddressSpaceMapping=spirv::ClientAPI::Unknown)
Populates type conversions with additional SPIR-V types.
void populateSPIRVToLLVMFunctionConversionPatterns(const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns)
Populates the given list with patterns for function conversion from SPIR-V to LLVM.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
void populateSPIRVToLLVMConversionPatterns(const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns, spirv::ClientAPI clientAPIForAddressSpaceMapping=spirv::ClientAPI::Unknown)
Populates the given list with patterns that convert from SPIR-V to LLVM.
void encodeBindAttribute(ModuleOp module)
Encodes global variable's descriptor set and binding into its name if they both exist.
void populateSPIRVToLLVMModuleConversionPatterns(const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns)
Populates the given patterns for module conversion from SPIR-V to LLVM.