MLIR 24.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"
24#include "llvm/ADT/FloatingPointMode.h"
25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/FormatVariadic.h"
27
28#define DEBUG_TYPE "spirv-to-llvm-pattern"
29
30using namespace mlir;
31
32//===----------------------------------------------------------------------===//
33// Utility functions
34//===----------------------------------------------------------------------===//
35
36/// Returns true if the given type is a signed integer or vector type.
37static bool isSignedIntegerOrVector(Type type) {
38 if (type.isSignedInteger())
39 return true;
40 if (auto vecType = dyn_cast<VectorType>(type))
41 return vecType.getElementType().isSignedInteger();
42 return false;
43}
44
45/// Returns true if the given type is an unsigned integer or vector type
47 if (type.isUnsignedInteger())
48 return true;
49 if (auto vecType = dyn_cast<VectorType>(type))
50 return vecType.getElementType().isUnsignedInteger();
51 return false;
52}
53
54/// Returns the width of an integer or of the element type of an integer vector,
55/// if applicable.
56static std::optional<uint64_t> getIntegerOrVectorElementWidth(Type type) {
57 if (auto intType = dyn_cast<IntegerType>(type))
58 return intType.getWidth();
59 if (auto vecType = dyn_cast<VectorType>(type))
60 if (auto intType = dyn_cast<IntegerType>(vecType.getElementType()))
61 return intType.getWidth();
62 return std::nullopt;
63}
64
65/// Returns the bit width of integer, float or vector of float or integer values
66static unsigned getBitWidth(Type type) {
67 assert((type.isIntOrFloat() || isa<VectorType>(type)) &&
68 "bitwidth is not supported for this type");
69 if (type.isIntOrFloat())
70 return type.getIntOrFloatBitWidth();
71 auto vecType = dyn_cast<VectorType>(type);
72 auto elementType = vecType.getElementType();
73 assert(elementType.isIntOrFloat() &&
74 "only integers and floats have a bitwidth");
75 return elementType.getIntOrFloatBitWidth();
76}
77
78/// Returns the bit width of LLVMType integer or vector.
79static unsigned getLLVMTypeBitWidth(Type type) {
80 if (auto vecTy = dyn_cast<VectorType>(type))
81 type = vecTy.getElementType();
82 return cast<IntegerType>(type).getWidth();
83}
84
85/// Creates `llvm.mlir.constant` with a scalar or vector integer value,
86/// broadcasting `scalarAttr` across the vector if `srcType` is a vector.
87static Value createIntegerConstant(Location loc, Type srcType, Type dstType,
88 PatternRewriter &rewriter,
89 IntegerAttr scalarAttr) {
90 if (auto vecType = dyn_cast<VectorType>(srcType))
91 return LLVM::ConstantOp::create(
92 rewriter, loc, dstType, SplatElementsAttr::get(vecType, scalarAttr));
93 return LLVM::ConstantOp::create(rewriter, loc, dstType, scalarAttr);
94}
95
96/// Creates `llvm.mlir.constant` with all bits set for the given type.
97static Value createConstantAllBitsSet(Location loc, Type srcType, Type dstType,
98 PatternRewriter &rewriter) {
99 auto integerType = cast<IntegerType>(
100 isa<VectorType>(srcType) ? cast<VectorType>(srcType).getElementType()
101 : srcType);
102 return createIntegerConstant(loc, srcType, dstType, rewriter,
103 rewriter.getIntegerAttr(integerType, -1));
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
308template <typename TargetOp, typename SourceOp>
309static LogicalResult
310collectAttrsForConversion(SourceOp op,
311 typename TargetOp::Properties &properties,
312 SmallVectorImpl<NamedAttribute> &discardableAttrs) {
313 NamedAttrList attrs(op->getDiscardableAttrDictionary());
314 if (auto sourceProperties =
315 dyn_cast_or_null<DictionaryAttr>(op->getPropertiesAsAttribute()))
316 attrs.append(sourceProperties.getValue());
317
318 if constexpr (std::is_same_v<typename TargetOp::Properties,
320 llvm::append_range(discardableAttrs, attrs);
321 return success();
322 }
323
324 TargetOp::populateDefaultProperties(
325 OperationName(TargetOp::getOperationName(), op->getContext()),
326 properties);
327 if (failed(TargetOp::setPropertiesFromAttr(
328 properties, attrs.getDictionary(op->getContext()),
329 [&]() { return op.emitError("failed to convert properties"); })))
330 return failure();
331
332 auto convertedProperties = dyn_cast_or_null<DictionaryAttr>(
333 TargetOp::getPropertiesAsAttr(op->getContext(), properties));
334 for (NamedAttribute attr : attrs) {
335 StringRef name = attr.getName().getValue();
336 bool isProperty = convertedProperties && convertedProperties.contains(name);
337 if (name == "operand_segment_sizes")
338 isProperty |= convertedProperties &&
339 convertedProperties.contains("operandSegmentSizes");
340 if (name == "result_segment_sizes")
341 isProperty |= convertedProperties &&
342 convertedProperties.contains("resultSegmentSizes");
343 if (!isProperty)
344 discardableAttrs.push_back(attr);
345 }
346 return success();
347}
348
349template <typename SPIRVOp, bool InBounds>
350class AccessChainPattern : public SPIRVToLLVMConversion<SPIRVOp> {
351public:
352 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
353
354 LogicalResult
355 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
356 ConversionPatternRewriter &rewriter) const override {
357 auto dstType =
358 this->getTypeConverter()->convertType(op.getComponentPtr().getType());
359 if (!dstType)
360 return rewriter.notifyMatchFailure(op, "type conversion failed");
361 if (op.getIndices().empty()) {
362 rewriter.replaceOp(op, adaptor.getBasePtr());
363 return success();
364 }
365 auto elementType = this->getTypeConverter()->convertType(
366 cast<spirv::PointerType>(op.getBasePtr().getType()).getPointeeType());
367 if (!elementType)
368 return rewriter.notifyMatchFailure(op, "type conversion failed");
369 // To use GEP we need to add a first 0 index to go through the pointer.
370 auto indices = llvm::to_vector<4>(adaptor.getIndices());
371 Type indexType = op.getIndices().front().getType();
372 auto llvmIndexType = this->getTypeConverter()->convertType(indexType);
373 if (!llvmIndexType)
374 return rewriter.notifyMatchFailure(op, "type conversion failed");
375 Value zero =
376 LLVM::ConstantOp::create(rewriter, op.getLoc(), llvmIndexType,
377 rewriter.getIntegerAttr(llvmIndexType, 0));
378 indices.insert(indices.begin(), zero);
379
380 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(
381 op, dstType, elementType, adaptor.getBasePtr(), indices,
382 InBounds ? LLVM::GEPNoWrapFlags::inbounds : LLVM::GEPNoWrapFlags::none);
383 return success();
384 }
385};
386
387/// Converts `spirv.PtrAccessChain` and `spirv.InBoundsPtrAccessChain` to
388/// `llvm.getelementptr`, using `element` as the first index and preserving
389/// `inbounds` for the latter.
390template <typename SPIRVOp, bool InBounds>
391class PtrAccessChainPattern : public SPIRVToLLVMConversion<SPIRVOp> {
392public:
393 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
394
395 LogicalResult
396 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
397 ConversionPatternRewriter &rewriter) const override {
398 auto baseType = cast<spirv::PointerType>(op.getBasePtr().getType());
399 // These storage classes may use a pointer ArrayStride decoration, which
400 // the SPIR-V dialect cannot represent.
401 switch (baseType.getStorageClass()) {
402 case spirv::StorageClass::Uniform:
403 case spirv::StorageClass::StorageBuffer:
404 case spirv::StorageClass::PushConstant:
405 case spirv::StorageClass::PhysicalStorageBuffer:
406 return rewriter.notifyMatchFailure(
407 op, "pointer ArrayStride is not supported for this storage class");
408 default:
409 break;
410 }
411
412 auto dstType = this->getTypeConverter()->convertType(op.getType());
413 if (!dstType)
414 return rewriter.notifyMatchFailure(op, "type conversion failed");
415
416 auto elementType =
417 this->getTypeConverter()->convertType(baseType.getPointeeType());
418 if (!elementType)
419 return rewriter.notifyMatchFailure(op, "type conversion failed");
420
421 SmallVector<Value, 4> indices;
422 indices.push_back(adaptor.getElement());
423 llvm::append_range(indices, adaptor.getIndices());
424
425 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(
426 op, dstType, elementType, adaptor.getBasePtr(), indices,
427 InBounds ? LLVM::GEPNoWrapFlags::inbounds : LLVM::GEPNoWrapFlags::none);
428 return success();
429 }
430};
431
432class AddressOfPattern : public SPIRVToLLVMConversion<spirv::AddressOfOp> {
433public:
434 using SPIRVToLLVMConversion<spirv::AddressOfOp>::SPIRVToLLVMConversion;
435
436 LogicalResult
437 matchAndRewrite(spirv::AddressOfOp op, OpAdaptor adaptor,
438 ConversionPatternRewriter &rewriter) const override {
439 auto dstType = getTypeConverter()->convertType(op.getPointer().getType());
440 if (!dstType)
441 return rewriter.notifyMatchFailure(op, "type conversion failed");
442 rewriter.replaceOpWithNewOp<LLVM::AddressOfOp>(op, dstType,
443 op.getVariable());
444 return success();
445 }
446};
447
448class BitFieldInsertPattern
449 : public SPIRVToLLVMConversion<spirv::BitFieldInsertOp> {
450public:
451 using SPIRVToLLVMConversion<spirv::BitFieldInsertOp>::SPIRVToLLVMConversion;
452
453 LogicalResult
454 matchAndRewrite(spirv::BitFieldInsertOp op, OpAdaptor adaptor,
455 ConversionPatternRewriter &rewriter) const override {
456 auto srcType = op.getType();
457 auto dstType = getTypeConverter()->convertType(srcType);
458 if (!dstType)
459 return rewriter.notifyMatchFailure(op, "type conversion failed");
460 Location loc = op.getLoc();
461
462 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
463 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
464 *getTypeConverter(), rewriter);
465 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
466 *getTypeConverter(), rewriter);
467
468 // Create a mask with bits set outside [Offset, Offset + Count - 1].
469 Value minusOne = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
470 Value maskShiftedByCount =
471 LLVM::ShlOp::create(rewriter, loc, dstType, minusOne, count);
472 Value negated = LLVM::XOrOp::create(rewriter, loc, dstType,
473 maskShiftedByCount, minusOne);
474 Value maskShiftedByCountAndOffset =
475 LLVM::ShlOp::create(rewriter, loc, dstType, negated, offset);
476 Value mask = LLVM::XOrOp::create(rewriter, loc, dstType,
477 maskShiftedByCountAndOffset, minusOne);
478
479 // Extract unchanged bits from the `Base` that are outside of
480 // [Offset, Offset + Count - 1]. Then `or` with shifted `Insert`.
481 Value baseAndMask =
482 LLVM::AndOp::create(rewriter, loc, dstType, op.getBase(), mask);
483 Value insertShiftedByOffset =
484 LLVM::ShlOp::create(rewriter, loc, dstType, op.getInsert(), offset);
485 rewriter.replaceOpWithNewOp<LLVM::OrOp>(op, dstType, baseAndMask,
486 insertShiftedByOffset);
487 return success();
488 }
489};
490
491/// Converts SPIR-V ConstantOp with scalar or vector type.
492class ConstantScalarAndVectorPattern
493 : public SPIRVToLLVMConversion<spirv::ConstantOp> {
494public:
495 using SPIRVToLLVMConversion<spirv::ConstantOp>::SPIRVToLLVMConversion;
496
497 LogicalResult
498 matchAndRewrite(spirv::ConstantOp constOp, OpAdaptor adaptor,
499 ConversionPatternRewriter &rewriter) const override {
500 auto srcType = constOp.getType();
501 if (!isa<VectorType>(srcType) && !srcType.isIntOrFloat())
502 return failure();
503
504 auto dstType = getTypeConverter()->convertType(srcType);
505 if (!dstType)
506 return rewriter.notifyMatchFailure(constOp, "type conversion failed");
507
508 // SPIR-V constant can be a signed/unsigned integer, which has to be
509 // casted to signless integer when converting to LLVM dialect. Removing the
510 // sign bit may have unexpected behaviour. However, it is better to handle
511 // it case-by-case, given that the purpose of the conversion is not to
512 // cover all possible corner cases.
513 if (isSignedIntegerOrVector(srcType) ||
514 isUnsignedIntegerOrVector(srcType)) {
515 auto signlessType = rewriter.getIntegerType(getBitWidth(srcType));
516
517 if (isa<VectorType>(srcType)) {
518 auto dstElementsAttr = cast<DenseIntElementsAttr>(constOp.getValue());
519 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
520 constOp, dstType,
521 dstElementsAttr.mapValues(
522 signlessType, [&](const APInt &value) { return value; }));
523 return success();
524 }
525 auto srcAttr = cast<IntegerAttr>(constOp.getValue());
526 auto dstAttr = rewriter.getIntegerAttr(signlessType, srcAttr.getValue());
527 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(constOp, dstType, dstAttr);
528 return success();
529 }
530 LLVM::ConstantOp::Properties properties{};
531 SmallVector<NamedAttribute> discardableAttrs;
532 if (failed(collectAttrsForConversion<LLVM::ConstantOp>(constOp, properties,
533 discardableAttrs)))
534 return failure();
535 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
536 constOp, dstType, adaptor.getOperands(), properties, discardableAttrs);
537 return success();
538 }
539};
540
541class BitFieldSExtractPattern
542 : public SPIRVToLLVMConversion<spirv::BitFieldSExtractOp> {
543public:
544 using SPIRVToLLVMConversion<spirv::BitFieldSExtractOp>::SPIRVToLLVMConversion;
545
546 LogicalResult
547 matchAndRewrite(spirv::BitFieldSExtractOp op, OpAdaptor adaptor,
548 ConversionPatternRewriter &rewriter) const override {
549 auto srcType = op.getType();
550 auto dstType = getTypeConverter()->convertType(srcType);
551 if (!dstType)
552 return rewriter.notifyMatchFailure(op, "type conversion failed");
553 Location loc = op.getLoc();
554
555 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
556 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
557 *getTypeConverter(), rewriter);
558 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
559 *getTypeConverter(), rewriter);
560
561 // Create a constant that holds the size of the `Base`.
562 IntegerType integerType;
563 if (auto vecType = dyn_cast<VectorType>(srcType))
564 integerType = cast<IntegerType>(vecType.getElementType());
565 else
566 integerType = cast<IntegerType>(srcType);
567
568 auto baseSize = rewriter.getIntegerAttr(integerType, getBitWidth(srcType));
569 Value size =
570 isa<VectorType>(srcType)
571 ? LLVM::ConstantOp::create(
572 rewriter, loc, dstType,
573 SplatElementsAttr::get(cast<ShapedType>(srcType), baseSize))
574 : LLVM::ConstantOp::create(rewriter, loc, dstType, baseSize);
575
576 // Shift `Base` left by [sizeof(Base) - (Count + Offset)], so that the bit
577 // at Offset + Count - 1 is the most significant bit now.
578 Value countPlusOffset =
579 LLVM::AddOp::create(rewriter, loc, dstType, count, offset);
580 Value amountToShiftLeft =
581 LLVM::SubOp::create(rewriter, loc, dstType, size, countPlusOffset);
582 Value baseShiftedLeft = LLVM::ShlOp::create(
583 rewriter, loc, dstType, op.getBase(), amountToShiftLeft);
584
585 // Shift the result right, filling the bits with the sign bit.
586 Value amountToShiftRight =
587 LLVM::AddOp::create(rewriter, loc, dstType, offset, amountToShiftLeft);
588 rewriter.replaceOpWithNewOp<LLVM::AShrOp>(op, dstType, baseShiftedLeft,
589 amountToShiftRight);
590 return success();
591 }
592};
593
594class BitFieldUExtractPattern
595 : public SPIRVToLLVMConversion<spirv::BitFieldUExtractOp> {
596public:
597 using SPIRVToLLVMConversion<spirv::BitFieldUExtractOp>::SPIRVToLLVMConversion;
598
599 LogicalResult
600 matchAndRewrite(spirv::BitFieldUExtractOp op, OpAdaptor adaptor,
601 ConversionPatternRewriter &rewriter) const override {
602 auto srcType = op.getType();
603 auto dstType = getTypeConverter()->convertType(srcType);
604 if (!dstType)
605 return rewriter.notifyMatchFailure(op, "type conversion failed");
606 Location loc = op.getLoc();
607
608 // Process `Offset` and `Count`: broadcast and extend/truncate if needed.
609 Value offset = processCountOrOffset(loc, op.getOffset(), srcType, dstType,
610 *getTypeConverter(), rewriter);
611 Value count = processCountOrOffset(loc, op.getCount(), srcType, dstType,
612 *getTypeConverter(), rewriter);
613
614 // Create a mask with bits set at [0, Count - 1].
615 Value minusOne = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
616 Value maskShiftedByCount =
617 LLVM::ShlOp::create(rewriter, loc, dstType, minusOne, count);
618 Value mask = LLVM::XOrOp::create(rewriter, loc, dstType, maskShiftedByCount,
619 minusOne);
620
621 // Shift `Base` by `Offset` and apply the mask on it.
622 Value shiftedBase =
623 LLVM::LShrOp::create(rewriter, loc, dstType, op.getBase(), offset);
624 rewriter.replaceOpWithNewOp<LLVM::AndOp>(op, dstType, shiftedBase, mask);
625 return success();
626 }
627};
628
629class BranchConversionPattern : public SPIRVToLLVMConversion<spirv::BranchOp> {
630public:
631 using SPIRVToLLVMConversion<spirv::BranchOp>::SPIRVToLLVMConversion;
632
633 LogicalResult
634 matchAndRewrite(spirv::BranchOp branchOp, OpAdaptor adaptor,
635 ConversionPatternRewriter &rewriter) const override {
636 rewriter.replaceOpWithNewOp<LLVM::BrOp>(branchOp, adaptor.getOperands(),
637 branchOp.getTarget());
638 return success();
639 }
640};
641
642class BranchConditionalConversionPattern
643 : public SPIRVToLLVMConversion<spirv::BranchConditionalOp> {
644public:
645 using SPIRVToLLVMConversion<
646 spirv::BranchConditionalOp>::SPIRVToLLVMConversion;
647
648 LogicalResult
649 matchAndRewrite(spirv::BranchConditionalOp op, OpAdaptor adaptor,
650 ConversionPatternRewriter &rewriter) const override {
651 // If branch weights exist, map them to 32-bit integer vector.
652 DenseI32ArrayAttr branchWeights = nullptr;
653 if (auto weights = op.getBranchWeights()) {
654 SmallVector<int32_t> weightValues;
655 for (auto weight : weights->getAsRange<IntegerAttr>())
656 weightValues.push_back(weight.getInt());
657 branchWeights = DenseI32ArrayAttr::get(getContext(), weightValues);
658 }
659
660 rewriter.replaceOpWithNewOp<LLVM::CondBrOp>(
661 op, op.getCondition(), op.getTrueBlockArguments(),
662 op.getFalseBlockArguments(), branchWeights, op.getTrueBlock(),
663 op.getFalseBlock());
664 return success();
665 }
666};
667
668/// Converts `spirv.getCompositeExtract` to `llvm.extractvalue` if the container
669/// type is an aggregate type (struct or array). Otherwise, converts to
670/// `llvm.extractelement` that operates on vectors.
671class CompositeExtractPattern
672 : public SPIRVToLLVMConversion<spirv::CompositeExtractOp> {
673public:
674 using SPIRVToLLVMConversion<spirv::CompositeExtractOp>::SPIRVToLLVMConversion;
675
676 LogicalResult
677 matchAndRewrite(spirv::CompositeExtractOp op, OpAdaptor adaptor,
678 ConversionPatternRewriter &rewriter) const override {
679 auto dstType = this->getTypeConverter()->convertType(op.getType());
680 if (!dstType)
681 return rewriter.notifyMatchFailure(op, "type conversion failed");
682
683 Type containerType = op.getComposite().getType();
684 if (isa<VectorType>(containerType)) {
685 Location loc = op.getLoc();
686 IntegerAttr value = cast<IntegerAttr>(op.getIndices()[0]);
687 Value index = createI32ConstantOf(loc, rewriter, value.getInt());
688 rewriter.replaceOpWithNewOp<LLVM::ExtractElementOp>(
689 op, dstType, adaptor.getComposite(), index);
690 return success();
691 }
692
693 rewriter.replaceOpWithNewOp<LLVM::ExtractValueOp>(
694 op, adaptor.getComposite(),
695 LLVM::convertArrayToIndices(op.getIndices()));
696 return success();
697 }
698};
699
700/// Converts `spirv.getCompositeInsert` to `llvm.insertvalue` if the container
701/// type is an aggregate type (struct or array). Otherwise, converts to
702/// `llvm.insertelement` that operates on vectors.
703class CompositeInsertPattern
704 : public SPIRVToLLVMConversion<spirv::CompositeInsertOp> {
705public:
706 using SPIRVToLLVMConversion<spirv::CompositeInsertOp>::SPIRVToLLVMConversion;
707
708 LogicalResult
709 matchAndRewrite(spirv::CompositeInsertOp op, OpAdaptor adaptor,
710 ConversionPatternRewriter &rewriter) const override {
711 auto dstType = this->getTypeConverter()->convertType(op.getType());
712 if (!dstType)
713 return rewriter.notifyMatchFailure(op, "type conversion failed");
714
715 Type containerType = op.getComposite().getType();
716 if (isa<VectorType>(containerType)) {
717 Location loc = op.getLoc();
718 IntegerAttr value = cast<IntegerAttr>(op.getIndices()[0]);
719 Value index = createI32ConstantOf(loc, rewriter, value.getInt());
720 rewriter.replaceOpWithNewOp<LLVM::InsertElementOp>(
721 op, dstType, adaptor.getComposite(), adaptor.getObject(), index);
722 return success();
723 }
724
725 rewriter.replaceOpWithNewOp<LLVM::InsertValueOp>(
726 op, adaptor.getComposite(), adaptor.getObject(),
727 LLVM::convertArrayToIndices(op.getIndices()));
728 return success();
729 }
730};
731
732/// Converts SPIR-V operations that have straightforward LLVM equivalent
733/// into LLVM dialect operations.
734template <typename SPIRVOp, typename LLVMOp>
735class DirectConversionPattern : public SPIRVToLLVMConversion<SPIRVOp> {
736public:
737 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
738
739 LogicalResult
740 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
741 ConversionPatternRewriter &rewriter) const override {
742 auto dstType = this->getTypeConverter()->convertType(op.getType());
743 if (!dstType)
744 return rewriter.notifyMatchFailure(op, "type conversion failed");
745 typename LLVMOp::Properties properties{};
746 SmallVector<NamedAttribute> discardableAttrs;
747 if (failed(collectAttrsForConversion<LLVMOp>(op, properties,
748 discardableAttrs)))
749 return failure();
750 rewriter.template replaceOpWithNewOp<LLVMOp>(
751 op, dstType, adaptor.getOperands(), properties, discardableAttrs);
752 return success();
753 }
754};
755
756/// Converts SPIR-V extended arithmetic ops (`spirv.IAddCarry`,
757/// `spirv.ISubBorrow`) that produce a two-member struct of {low-order bits,
758/// carry/borrow} into the matching LLVM `*.with.overflow` intrinsic. The
759/// intrinsic yields an `i1` carry/borrow, which is zero-extended to the full
760/// component width and re-packed into the SPIR-V result struct.
761template <typename SPIRVOp, typename LLVMOp>
762class ArithmeticWithOverflowPattern : public SPIRVToLLVMConversion<SPIRVOp> {
763public:
764 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
765
766 LogicalResult
767 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
768 ConversionPatternRewriter &rewriter) const override {
769 Type dstType = this->getTypeConverter()->convertType(op.getType());
770 if (!dstType)
771 return rewriter.notifyMatchFailure(op, "type conversion failed");
772
773 Location loc = op.getLoc();
774 Type operandType = adaptor.getOperand1().getType();
775 Type overflowType = rewriter.getI1Type();
776 if (auto vecType = dyn_cast<VectorType>(operandType))
777 overflowType = VectorType::get(vecType.getShape(), overflowType);
778
779 Type intrType = LLVM::LLVMStructType::getLiteral(
780 rewriter.getContext(), {operandType, overflowType});
781 Value intrResult = LLVMOp::create(
782 rewriter, loc, intrType, adaptor.getOperand1(), adaptor.getOperand2());
783 Value lowBits = LLVM::ExtractValueOp::create(rewriter, loc, intrResult, 0);
784 Value overflow = LLVM::ExtractValueOp::create(rewriter, loc, intrResult, 1);
785 overflow = LLVM::ZExtOp::create(rewriter, loc, operandType, overflow);
786
787 Value result = LLVM::PoisonOp::create(rewriter, loc, dstType);
788 result = LLVM::InsertValueOp::create(rewriter, loc, result, lowBits,
789 ArrayRef<int64_t>{0});
790 result = LLVM::InsertValueOp::create(rewriter, loc, result, overflow,
791 ArrayRef<int64_t>{1});
792 rewriter.replaceOp(op, result);
793 return success();
794 }
795};
796
797/// Converts `spirv.SMulExtended` and `spirv.UMulExtended` by multiplying
798/// operands extended to twice the width and packing the low and high halves
799/// into the SPIR-V result struct.
800template <typename SPIRVOp, typename LLVMExtOp>
801class MulExtendedPattern : public SPIRVToLLVMConversion<SPIRVOp> {
802public:
803 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
804
805 LogicalResult
806 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
807 ConversionPatternRewriter &rewriter) const override {
808 Type dstType = this->getTypeConverter()->convertType(op.getType());
809 if (!dstType)
810 return rewriter.notifyMatchFailure(op, "type conversion failed");
811
812 Location loc = op.getLoc();
813 Type operandType = adaptor.getOperand1().getType();
814 unsigned bitwidth = getLLVMTypeBitWidth(operandType);
815 IntegerType wideElemType = rewriter.getIntegerType(bitwidth * 2);
816 Type wideType = wideElemType;
817 if (auto vecType = dyn_cast<VectorType>(operandType))
818 wideType = VectorType::get(vecType.getShape(), wideElemType);
819
820 Value lhs =
821 LLVMExtOp::create(rewriter, loc, wideType, adaptor.getOperand1());
822 Value rhs =
823 LLVMExtOp::create(rewriter, loc, wideType, adaptor.getOperand2());
824 Value mul = LLVM::MulOp::create(rewriter, loc, wideType, lhs, rhs);
825 Value low = LLVM::TruncOp::create(rewriter, loc, operandType, mul);
826 Value shift =
827 createIntegerConstant(loc, wideType, wideType, rewriter,
828 rewriter.getIntegerAttr(wideElemType, bitwidth));
829 Value high = LLVM::LShrOp::create(rewriter, loc, mul, shift);
830 high = LLVM::TruncOp::create(rewriter, loc, operandType, high);
831
832 Value result = LLVM::PoisonOp::create(rewriter, loc, dstType);
833 result = LLVM::InsertValueOp::create(rewriter, loc, result, low,
834 ArrayRef<int64_t>{0});
835 result = LLVM::InsertValueOp::create(rewriter, loc, result, high,
836 ArrayRef<int64_t>{1});
837 rewriter.replaceOp(op, result);
838 return success();
839 }
840};
841
842/// Converts `spirv.ExecutionMode` into a global struct constant that holds
843/// execution mode information.
844class ExecutionModePattern
845 : public SPIRVToLLVMConversion<spirv::ExecutionModeOp> {
846public:
847 using SPIRVToLLVMConversion<spirv::ExecutionModeOp>::SPIRVToLLVMConversion;
848
849 LogicalResult
850 matchAndRewrite(spirv::ExecutionModeOp op, OpAdaptor adaptor,
851 ConversionPatternRewriter &rewriter) const override {
852 // First, create the global struct's name that would be associated with
853 // this entry point's execution mode. We set it to be:
854 // __spv__{SPIR-V module name}_{function name}_execution_mode_info_{mode}
855 ModuleOp module = op->getParentOfType<ModuleOp>();
856 spirv::ExecutionModeAttr executionModeAttr = op.getExecutionModeAttr();
857 std::string moduleName;
858 if (module.getName().has_value())
859 moduleName = "_" + module.getName()->str();
860 else
861 moduleName = "";
862 std::string executionModeInfoName = llvm::formatv(
863 "__spv_{0}_{1}_execution_mode_info_{2}", moduleName, op.getFn().str(),
864 static_cast<uint32_t>(executionModeAttr.getValue()));
865
866 MLIRContext *context = rewriter.getContext();
867 OpBuilder::InsertionGuard guard(rewriter);
868 rewriter.setInsertionPointToStart(module.getBody());
869
870 // Create a struct type, corresponding to the C struct below.
871 // struct {
872 // int32_t executionMode;
873 // int32_t values[]; // optional values
874 // };
875 auto llvmI32Type = IntegerType::get(context, 32);
876 SmallVector<Type, 2> fields;
877 fields.push_back(llvmI32Type);
878 ArrayAttr values = op.getValues();
879 if (!values.empty()) {
880 auto arrayType = LLVM::LLVMArrayType::get(llvmI32Type, values.size());
881 fields.push_back(arrayType);
882 }
883 auto structType = LLVM::LLVMStructType::getLiteral(context, fields);
884
885 // Create `llvm.mlir.global` with initializer region containing one block.
886 auto global = LLVM::GlobalOp::create(
887 rewriter, UnknownLoc::get(context), structType, /*isConstant=*/true,
888 LLVM::Linkage::External, executionModeInfoName, Attribute(),
889 /*alignment=*/0);
890 Location loc = global.getLoc();
891 Region &region = global.getInitializerRegion();
892 Block *block = rewriter.createBlock(&region);
893
894 // Initialize the struct and set the execution mode value.
895 rewriter.setInsertionPointToStart(block);
896 Value structValue = LLVM::PoisonOp::create(rewriter, loc, structType);
897 Value executionMode = LLVM::ConstantOp::create(
898 rewriter, loc, llvmI32Type,
899 rewriter.getI32IntegerAttr(
900 static_cast<uint32_t>(executionModeAttr.getValue())));
901 SmallVector<int64_t> position{0};
902 structValue = LLVM::InsertValueOp::create(rewriter, loc, structValue,
903 executionMode, position);
904
905 // Insert extra operands if they exist into execution mode info struct.
906 for (unsigned i = 0, e = values.size(); i < e; ++i) {
907 auto attr = values.getValue()[i];
908 Value entry = LLVM::ConstantOp::create(rewriter, loc, llvmI32Type, attr);
909 structValue = LLVM::InsertValueOp::create(
910 rewriter, loc, structValue, entry, ArrayRef<int64_t>({1, i}));
911 }
912 LLVM::ReturnOp::create(rewriter, loc, ArrayRef<Value>({structValue}));
913 rewriter.eraseOp(op);
914 return success();
915 }
916};
917
918/// Converts `spirv.GlobalVariable` to `llvm.mlir.global`. Note that SPIR-V
919/// global returns a pointer, whereas in LLVM dialect the global holds an actual
920/// value. This difference is handled by `spirv.mlir.addressof` and
921/// `llvm.mlir.addressof`ops that both return a pointer.
922class GlobalVariablePattern
923 : public SPIRVToLLVMConversion<spirv::GlobalVariableOp> {
924public:
925 template <typename... Args>
926 GlobalVariablePattern(spirv::ClientAPI clientAPI, Args &&...args)
927 : SPIRVToLLVMConversion<spirv::GlobalVariableOp>(
928 std::forward<Args>(args)...),
929 clientAPI(clientAPI) {}
930
931 LogicalResult
932 matchAndRewrite(spirv::GlobalVariableOp op, OpAdaptor adaptor,
933 ConversionPatternRewriter &rewriter) const override {
934 // Currently, there is no support of initialization with a constant value in
935 // SPIR-V dialect. Specialization constants are not considered as well.
936 if (op.getInitializer())
937 return failure();
938
939 auto srcType = cast<spirv::PointerType>(op.getType());
940 auto dstType = getTypeConverter()->convertType(srcType.getPointeeType());
941 if (!dstType)
942 return rewriter.notifyMatchFailure(op, "type conversion failed");
943
944 // Limit conversion to the current invocation only or `StorageBuffer`
945 // required by SPIR-V runner.
946 // This is okay because multiple invocations are not supported yet.
947 auto storageClass = srcType.getStorageClass();
948 switch (storageClass) {
949 case spirv::StorageClass::Input:
950 case spirv::StorageClass::Private:
951 case spirv::StorageClass::Output:
952 case spirv::StorageClass::StorageBuffer:
953 case spirv::StorageClass::UniformConstant:
954 break;
955 default:
956 return failure();
957 }
958
959 // LLVM dialect spec: "If the global value is a constant, storing into it is
960 // not allowed.". This corresponds to SPIR-V 'Input' and 'UniformConstant'
961 // storage class that is read-only.
962 bool isConstant = (storageClass == spirv::StorageClass::Input) ||
963 (storageClass == spirv::StorageClass::UniformConstant);
964 // SPIR-V spec: "By default, functions and global variables are private to a
965 // module and cannot be accessed by other modules. However, a module may be
966 // written to export or import functions and global (module scope)
967 // variables.". Therefore, map 'Private' storage class to private linkage,
968 // 'Input' and 'Output' to external linkage.
969 auto linkage = storageClass == spirv::StorageClass::Private
970 ? LLVM::Linkage::Private
971 : LLVM::Linkage::External;
972 StringAttr locationAttrName = op.getLocationAttrName();
973 IntegerAttr locationAttr = op.getLocationAttr();
974 auto newGlobalOp = rewriter.replaceOpWithNewOp<LLVM::GlobalOp>(
975 op, dstType, isConstant, linkage, op.getSymName(), Attribute(),
976 /*alignment=*/0, storageClassToAddressSpace(clientAPI, storageClass));
977
978 // Attach location attribute if applicable
979 if (locationAttr)
980 newGlobalOp->setDiscardableAttr(locationAttrName, locationAttr);
981
982 return success();
983 }
984
985private:
986 spirv::ClientAPI clientAPI;
987};
988
989/// Converts SPIR-V cast ops that do not have straightforward LLVM
990/// equivalent in LLVM dialect.
991template <typename SPIRVOp, typename LLVMExtOp, typename LLVMTruncOp>
992class IndirectCastPattern : public SPIRVToLLVMConversion<SPIRVOp> {
993public:
994 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
995
996 LogicalResult
997 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
998 ConversionPatternRewriter &rewriter) const override {
999
1000 Type fromType = op.getOperand().getType();
1001 Type toType = op.getType();
1002
1003 auto dstType = this->getTypeConverter()->convertType(toType);
1004 if (!dstType)
1005 return rewriter.notifyMatchFailure(op, "type conversion failed");
1006
1007 if (getBitWidth(fromType) < getBitWidth(toType)) {
1008 rewriter.template replaceOpWithNewOp<LLVMExtOp>(op, dstType,
1009 adaptor.getOperands());
1010 return success();
1011 }
1012 if (getBitWidth(fromType) > getBitWidth(toType)) {
1013 rewriter.template replaceOpWithNewOp<LLVMTruncOp>(op, dstType,
1014 adaptor.getOperands());
1015 return success();
1016 }
1017 return failure();
1018 }
1019};
1020
1021class FunctionCallPattern
1022 : public SPIRVToLLVMConversion<spirv::FunctionCallOp> {
1023public:
1024 using SPIRVToLLVMConversion<spirv::FunctionCallOp>::SPIRVToLLVMConversion;
1025
1026 LogicalResult
1027 matchAndRewrite(spirv::FunctionCallOp callOp, OpAdaptor adaptor,
1028 ConversionPatternRewriter &rewriter) const override {
1029 LLVM::CallOp::Properties properties{};
1030 SmallVector<NamedAttribute> discardableAttrs;
1031 if (failed(collectAttrsForConversion<LLVM::CallOp>(callOp, properties,
1032 discardableAttrs)))
1033 return failure();
1034 properties.operandSegmentSizes = {
1035 static_cast<int32_t>(adaptor.getOperands().size()), 0};
1036 properties.op_bundle_sizes = rewriter.getDenseI32ArrayAttr({});
1037
1038 if (callOp.getNumResults() == 0) {
1039 rewriter.replaceOpWithNewOp<LLVM::CallOp>(callOp, TypeRange(),
1040 adaptor.getOperands(),
1041 properties, discardableAttrs);
1042 return success();
1043 }
1044
1045 // Function returns a single result.
1046 auto dstType = getTypeConverter()->convertType(callOp.getType(0));
1047 if (!dstType)
1048 return rewriter.notifyMatchFailure(callOp, "type conversion failed");
1049 rewriter.replaceOpWithNewOp<LLVM::CallOp>(
1050 callOp, dstType, adaptor.getOperands(), properties, discardableAttrs);
1051 return success();
1052 }
1053};
1054
1055/// Converts SPIR-V floating-point comparisons to llvm.fcmp "predicate"
1056template <typename SPIRVOp, LLVM::FCmpPredicate predicate>
1057class FComparePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1058public:
1059 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1060
1061 LogicalResult
1062 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1063 ConversionPatternRewriter &rewriter) const override {
1064
1065 auto dstType = this->getTypeConverter()->convertType(op.getType());
1066 if (!dstType)
1067 return rewriter.notifyMatchFailure(op, "type conversion failed");
1068
1069 rewriter.template replaceOpWithNewOp<LLVM::FCmpOp>(
1070 op, dstType, predicate, op.getOperand1(), op.getOperand2());
1071 return success();
1072 }
1073};
1074
1075/// Converts SPIR-V integer comparisons to llvm.icmp "predicate"
1076template <typename SPIRVOp, LLVM::ICmpPredicate predicate>
1077class IComparePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1078public:
1079 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1080
1081 LogicalResult
1082 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1083 ConversionPatternRewriter &rewriter) const override {
1084
1085 auto dstType = this->getTypeConverter()->convertType(op.getType());
1086 if (!dstType)
1087 return rewriter.notifyMatchFailure(op, "type conversion failed");
1088
1089 rewriter.template replaceOpWithNewOp<LLVM::ICmpOp>(
1090 op, dstType, predicate, op.getOperand1(), op.getOperand2());
1091 return success();
1092 }
1093};
1094
1095/// Converts SPIR-V floating-point classification ops (`spirv.IsInf`,
1096/// `spirv.IsNan`, `spirv.IsFinite`, `spirv.IsNormal`) to
1097/// `llvm.intr.is.fpclass` with the matching `llvm::FPClassTest` mask. The
1098/// intrinsic tests the operand per component, like the SPIR-V ops, and yields
1099/// `i1` or a vector of `i1`.
1100template <typename SPIRVOp, llvm::FPClassTest classMask>
1101class FPClassPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1102public:
1103 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1104
1105 LogicalResult
1106 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1107 ConversionPatternRewriter &rewriter) const override {
1108 auto dstType = this->getTypeConverter()->convertType(op.getType());
1109 if (!dstType)
1110 return rewriter.notifyMatchFailure(op, "type conversion failed");
1111
1112 rewriter.template replaceOpWithNewOp<LLVM::IsFPClass>(
1113 op, dstType, adaptor.getOperand(), classMask);
1114 return success();
1115 }
1116};
1117
1118template <typename SPIRVOp>
1119class InverseSqrtPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1120public:
1121 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1122
1123 LogicalResult
1124 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1125 ConversionPatternRewriter &rewriter) const override {
1126 auto srcType = op.getType();
1127 auto dstType = this->getTypeConverter()->convertType(srcType);
1128 if (!dstType)
1129 return rewriter.notifyMatchFailure(op, "type conversion failed");
1130
1131 Location loc = op.getLoc();
1132 Value one = createFPConstant(loc, srcType, dstType, rewriter, 1.0);
1133 Value sqrt = LLVM::SqrtOp::create(rewriter, loc, dstType, op.getOperand());
1134 rewriter.template replaceOpWithNewOp<LLVM::FDivOp>(op, dstType, one, sqrt);
1135 return success();
1136 }
1137};
1138
1139/// Converts `spirv.VectorTimesScalar` to a broadcast of the scalar followed by
1140/// an `llvm.fmul`.
1141class VectorTimesScalarPattern
1142 : public SPIRVToLLVMConversion<spirv::VectorTimesScalarOp> {
1143public:
1144 using SPIRVToLLVMConversion<
1145 spirv::VectorTimesScalarOp>::SPIRVToLLVMConversion;
1146
1147 LogicalResult
1148 matchAndRewrite(spirv::VectorTimesScalarOp op, OpAdaptor adaptor,
1149 ConversionPatternRewriter &rewriter) const override {
1150 Type srcType = op.getType();
1151 Type dstType = getTypeConverter()->convertType(srcType);
1152 if (!dstType)
1153 return rewriter.notifyMatchFailure(op, "type conversion failed");
1154
1155 unsigned numElements = op.getVector().getType().getNumElements();
1156 Value broadcasted = broadcast(op.getLoc(), adaptor.getScalar(), numElements,
1157 *getTypeConverter(), rewriter);
1158 rewriter.replaceOpWithNewOp<LLVM::FMulOp>(op, dstType, adaptor.getVector(),
1159 broadcasted);
1160 return success();
1161 }
1162};
1163
1164/// Converts `spirv.SNegate` to `0 - x`.
1165class SNegatePattern : public SPIRVToLLVMConversion<spirv::SNegateOp> {
1166public:
1167 using SPIRVToLLVMConversion<spirv::SNegateOp>::SPIRVToLLVMConversion;
1168
1169 LogicalResult
1170 matchAndRewrite(spirv::SNegateOp op, OpAdaptor adaptor,
1171 ConversionPatternRewriter &rewriter) const override {
1172 Type srcType = op.getType();
1173 Type dstType = getTypeConverter()->convertType(srcType);
1174 if (!dstType)
1175 return rewriter.notifyMatchFailure(op, "type conversion failed");
1176
1177 Location loc = op.getLoc();
1178 IntegerAttr zeroAttr = rewriter.getIntegerAttr(
1179 cast<IntegerType>(getElementTypeOrSelf(srcType)), 0);
1180 Value zero =
1181 createIntegerConstant(loc, srcType, dstType, rewriter, zeroAttr);
1182 rewriter.replaceOpWithNewOp<LLVM::SubOp>(op, dstType, zero,
1183 adaptor.getOperand());
1184 return success();
1185 }
1186};
1187
1188/// Converts the GL and CL clamp ops into a nested min/max sequence, following
1189/// the op semantics `min(max(x, minVal), maxVal)`.
1190template <typename SPIRVOp, typename LLVMMinOp, typename LLVMMaxOp>
1191class ClampPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1192public:
1193 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1194
1195 LogicalResult
1196 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1197 ConversionPatternRewriter &rewriter) const override {
1198 Type dstType = this->getTypeConverter()->convertType(op.getType());
1199 if (!dstType)
1200 return rewriter.notifyMatchFailure(op, "type conversion failed");
1201
1202 Location loc = op.getLoc();
1203 Value max = LLVMMaxOp::create(rewriter, loc, dstType, adaptor.getX(),
1204 adaptor.getY());
1205 rewriter.template replaceOpWithNewOp<LLVMMinOp>(op, dstType, max,
1206 adaptor.getZ());
1207 return success();
1208 }
1209};
1210
1211/// Converts `spirv.FMod` to `x - y * floor(x / y)`. The SPIR-V op requires the
1212/// result to take the sign of the divisor, whereas `llvm.frem` keeps the sign
1213/// of the dividend, so `frem` cannot be used directly.
1214class FModPattern : public SPIRVToLLVMConversion<spirv::FModOp> {
1215public:
1216 using SPIRVToLLVMConversion<spirv::FModOp>::SPIRVToLLVMConversion;
1217
1218 LogicalResult
1219 matchAndRewrite(spirv::FModOp op, OpAdaptor adaptor,
1220 ConversionPatternRewriter &rewriter) const override {
1221 Type dstType = getTypeConverter()->convertType(op.getType());
1222 if (!dstType)
1223 return rewriter.notifyMatchFailure(op, "type conversion failed");
1224
1225 Location loc = op.getLoc();
1226 Value lhs = adaptor.getOperand1();
1227 Value rhs = adaptor.getOperand2();
1228 Value div = LLVM::FDivOp::create(rewriter, loc, dstType, lhs, rhs);
1229 Value floored = LLVM::FFloorOp::create(rewriter, loc, dstType, div);
1230 Value scaled = LLVM::FMulOp::create(rewriter, loc, dstType, rhs, floored);
1231 rewriter.replaceOpWithNewOp<LLVM::FSubOp>(op, dstType, lhs, scaled);
1232 return success();
1233 }
1234};
1235
1236/// Converts `spirv.SMod` to a signed remainder corrected to take the sign of
1237/// the divisor. `llvm.srem` keeps the sign of the dividend, so the result is
1238/// adjusted by adding the divisor when the remainder is non-zero and its sign
1239/// differs from the divisor's.
1240class SModPattern : public SPIRVToLLVMConversion<spirv::SModOp> {
1241public:
1242 using SPIRVToLLVMConversion<spirv::SModOp>::SPIRVToLLVMConversion;
1243
1244 LogicalResult
1245 matchAndRewrite(spirv::SModOp op, OpAdaptor adaptor,
1246 ConversionPatternRewriter &rewriter) const override {
1247 Type srcType = op.getType();
1248 Type dstType = getTypeConverter()->convertType(srcType);
1249 if (!dstType)
1250 return rewriter.notifyMatchFailure(op, "type conversion failed");
1251
1252 Location loc = op.getLoc();
1253 Value lhs = adaptor.getOperand1();
1254 Value rhs = adaptor.getOperand2();
1255 Type i1Type = rewriter.getI1Type();
1256 auto vecSrcType = dyn_cast<VectorType>(srcType);
1257 Type cmpType =
1258 vecSrcType ? VectorType::get(vecSrcType.getShape(), i1Type) : i1Type;
1259
1260 Value rem = LLVM::SRemOp::create(rewriter, loc, dstType, lhs, rhs);
1261 IntegerAttr zeroAttr = rewriter.getIntegerAttr(
1262 cast<IntegerType>(getElementTypeOrSelf(srcType)), 0);
1263 Value zero =
1264 createIntegerConstant(loc, srcType, dstType, rewriter, zeroAttr);
1265
1266 Value remNonZero = LLVM::ICmpOp::create(rewriter, loc, cmpType,
1267 LLVM::ICmpPredicate::ne, rem, zero);
1268 Value remNeg = LLVM::ICmpOp::create(rewriter, loc, cmpType,
1269 LLVM::ICmpPredicate::slt, rem, zero);
1270 Value rhsNeg = LLVM::ICmpOp::create(rewriter, loc, cmpType,
1271 LLVM::ICmpPredicate::slt, rhs, zero);
1272 Value signMismatch =
1273 LLVM::XOrOp::create(rewriter, loc, cmpType, remNeg, rhsNeg);
1274 Value needsAdjust =
1275 LLVM::AndOp::create(rewriter, loc, cmpType, remNonZero, signMismatch);
1276
1277 Value adjusted = LLVM::AddOp::create(rewriter, loc, dstType, rem, rhs);
1278 rewriter.replaceOpWithNewOp<LLVM::SelectOp>(op, dstType, needsAdjust,
1279 adjusted, rem);
1280 return success();
1281 }
1282};
1283
1284/// Converts `spirv.Load` and `spirv.Store` to LLVM dialect.
1285template <typename SPIRVOp>
1286class LoadStorePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1287public:
1288 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1289
1290 LogicalResult
1291 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1292 ConversionPatternRewriter &rewriter) const override {
1293 if (!op.getMemoryAccess()) {
1294 return replaceWithLoadOrStore(op, adaptor.getOperands(), rewriter,
1295 *this->getTypeConverter(), /*alignment=*/0,
1296 /*isVolatile=*/false,
1297 /*isNonTemporal=*/false);
1298 }
1299 auto memoryAccess = *op.getMemoryAccess();
1300 switch (memoryAccess) {
1301 case spirv::MemoryAccess::Aligned:
1302 case spirv::MemoryAccess::None:
1303 case spirv::MemoryAccess::Nontemporal:
1304 case spirv::MemoryAccess::Volatile: {
1305 unsigned alignment =
1306 memoryAccess == spirv::MemoryAccess::Aligned ? *op.getAlignment() : 0;
1307 bool isNonTemporal = memoryAccess == spirv::MemoryAccess::Nontemporal;
1308 bool isVolatile = memoryAccess == spirv::MemoryAccess::Volatile;
1309 return replaceWithLoadOrStore(op, adaptor.getOperands(), rewriter,
1310 *this->getTypeConverter(), alignment,
1311 isVolatile, isNonTemporal);
1312 }
1313 default:
1314 // There is no support of other memory access attributes.
1315 return failure();
1316 }
1317 }
1318};
1319
1320/// Returns true if `type` is, or (transitively) includes, a runtime array.
1321/// Such types have no fixed size, so they cannot be copied with a static byte
1322/// count; the SPIR-V spec also forbids them as the copied type of
1323/// `OpCopyMemory`.
1324static bool containsRuntimeArray(Type type) {
1325 if (isa<spirv::RuntimeArrayType>(type))
1326 return true;
1327 if (auto arrayType = dyn_cast<spirv::ArrayType>(type))
1328 return containsRuntimeArray(arrayType.getElementType());
1329 if (auto structType = dyn_cast<spirv::StructType>(type))
1330 return llvm::any_of(structType.getElementTypes(), containsRuntimeArray);
1331 return false;
1332}
1333
1334/// Converts `spirv.CopyMemory` to `llvm.intr.memcpy`.
1335class CopyMemoryPattern : public SPIRVToLLVMConversion<spirv::CopyMemoryOp> {
1336public:
1337 using SPIRVToLLVMConversion<spirv::CopyMemoryOp>::SPIRVToLLVMConversion;
1338
1339 LogicalResult
1340 matchAndRewrite(spirv::CopyMemoryOp op, OpAdaptor adaptor,
1341 ConversionPatternRewriter &rewriter) const override {
1342 Type pointeeType =
1343 cast<spirv::PointerType>(op.getTarget().getType()).getPointeeType();
1344 // The op verifier does not reject runtime arrays, so check here. Note that
1345 // a struct containing a runtime array still converts to an LLVM type with
1346 // a well-defined (but meaningless here) size.
1347 if (containsRuntimeArray(pointeeType))
1348 return rewriter.notifyMatchFailure(op, "pointee has no static size");
1349
1350 Type dstPointeeType = getTypeConverter()->convertType(pointeeType);
1351 if (!dstPointeeType)
1352 return rewriter.notifyMatchFailure(op, "type conversion failed");
1353
1354 // SPIR-V types have no scalable components, so the size is always fixed.
1355 uint64_t byteSize =
1356 DataLayout::closest(op).getTypeSize(dstPointeeType).getFixedValue();
1357
1358 // In SPIR-V < 1.4, a single memory-operands mask applies to both Target
1359 // and Source; in SPIR-V >= 1.4, a second mask (if present) applies to
1360 // Source while the first applies to Target.
1361 using spirv::MemoryAccess;
1362 constexpr MemoryAccess supportedMask = MemoryAccess::Aligned |
1363 MemoryAccess::Volatile |
1364 MemoryAccess::Nontemporal;
1365 MemoryAccess targetAccess =
1366 op.getMemoryAccess().value_or(MemoryAccess::None);
1367 bool hasSourceAccess = op.getSourceMemoryAccess().has_value();
1368 MemoryAccess sourceAccess =
1369 hasSourceAccess ? *op.getSourceMemoryAccess() : targetAccess;
1370 if (((targetAccess | sourceAccess) & ~supportedMask) != MemoryAccess::None)
1371 return rewriter.notifyMatchFailure(
1372 op, "unsupported memory access attributes");
1373
1374 bool isVolatile = spirv::bitEnumContainsAny(targetAccess | sourceAccess,
1375 MemoryAccess::Volatile);
1376 uint32_t targetAlign =
1377 spirv::bitEnumContainsAny(targetAccess, MemoryAccess::Aligned)
1378 ? op.getAlignment().value_or(0)
1379 : 0;
1380 uint32_t sourceAlign =
1381 spirv::bitEnumContainsAny(sourceAccess, MemoryAccess::Aligned)
1382 ? (hasSourceAccess ? op.getSourceAlignment().value_or(0)
1383 : targetAlign)
1384 : 0;
1385
1386 Location loc = op.getLoc();
1387 Value size = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(),
1388 rewriter.getI64IntegerAttr(byteSize));
1389 auto memcpy = rewriter.replaceOpWithNewOp<LLVM::MemcpyOp>(
1390 op, adaptor.getTarget(), adaptor.getSource(), size, isVolatile);
1391
1392 if (targetAlign || sourceAlign) {
1393 auto makeAlignDict = [&](uint32_t align) {
1394 if (!align)
1395 return rewriter.getDictionaryAttr({});
1396 return rewriter.getDictionaryAttr(
1397 {rewriter.getNamedAttr(LLVM::LLVMDialect::getAlignAttrName(),
1398 rewriter.getI64IntegerAttr(align))});
1399 };
1400 memcpy.setArgAttrsAttr(rewriter.getArrayAttr(
1401 {makeAlignDict(targetAlign), makeAlignDict(sourceAlign),
1402 rewriter.getDictionaryAttr({})}));
1403 }
1404 return success();
1405 }
1406};
1407
1408/// Converts `spirv.Not` and `spirv.LogicalNot` into LLVM dialect.
1409template <typename SPIRVOp>
1410class NotPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1411public:
1412 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1413
1414 LogicalResult
1415 matchAndRewrite(SPIRVOp notOp, typename SPIRVOp::Adaptor adaptor,
1416 ConversionPatternRewriter &rewriter) const override {
1417 auto srcType = notOp.getType();
1418 auto dstType = this->getTypeConverter()->convertType(srcType);
1419 if (!dstType)
1420 return rewriter.notifyMatchFailure(notOp, "type conversion failed");
1421
1422 Location loc = notOp.getLoc();
1423 Value mask = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
1424 rewriter.template replaceOpWithNewOp<LLVM::XOrOp>(notOp, dstType,
1425 notOp.getOperand(), mask);
1426 return success();
1427 }
1428};
1429
1430/// A template pattern that erases the given `SPIRVOp`.
1431template <typename SPIRVOp>
1432class ErasePattern : public SPIRVToLLVMConversion<SPIRVOp> {
1433public:
1434 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1435
1436 LogicalResult
1437 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1438 ConversionPatternRewriter &rewriter) const override {
1439 rewriter.eraseOp(op);
1440 return success();
1441 }
1442};
1443
1444class ReturnPattern : public SPIRVToLLVMConversion<spirv::ReturnOp> {
1445public:
1446 using SPIRVToLLVMConversion<spirv::ReturnOp>::SPIRVToLLVMConversion;
1447
1448 LogicalResult
1449 matchAndRewrite(spirv::ReturnOp returnOp, OpAdaptor adaptor,
1450 ConversionPatternRewriter &rewriter) const override {
1451 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(returnOp, ArrayRef<Type>(),
1452 ArrayRef<Value>());
1453 return success();
1454 }
1455};
1456
1457class ReturnValuePattern : public SPIRVToLLVMConversion<spirv::ReturnValueOp> {
1458public:
1459 using SPIRVToLLVMConversion<spirv::ReturnValueOp>::SPIRVToLLVMConversion;
1460
1461 LogicalResult
1462 matchAndRewrite(spirv::ReturnValueOp returnValueOp, OpAdaptor adaptor,
1463 ConversionPatternRewriter &rewriter) const override {
1464 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(returnValueOp, ArrayRef<Type>(),
1465 adaptor.getOperands());
1466 return success();
1467 }
1468};
1469
1470class UnreachablePattern : public SPIRVToLLVMConversion<spirv::UnreachableOp> {
1471public:
1472 using SPIRVToLLVMConversion<spirv::UnreachableOp>::SPIRVToLLVMConversion;
1473
1474 LogicalResult
1475 matchAndRewrite(spirv::UnreachableOp unreachableOp, OpAdaptor adaptor,
1476 ConversionPatternRewriter &rewriter) const override {
1477 rewriter.replaceOpWithNewOp<LLVM::UnreachableOp>(unreachableOp);
1478 return success();
1479 }
1480};
1481
1482static LLVM::LLVMFuncOp lookupOrCreateSPIRVFn(Operation *symbolTable,
1483 StringRef name,
1484 ArrayRef<Type> paramTypes,
1485 Type resultType,
1486 bool convergent = true) {
1487 auto func = dyn_cast_or_null<LLVM::LLVMFuncOp>(
1488 SymbolTable::lookupSymbolIn(symbolTable, name));
1489 if (func)
1490 return func;
1491
1492 OpBuilder b(symbolTable->getRegion(0));
1493 func = LLVM::LLVMFuncOp::create(
1494 b, symbolTable->getLoc(), name,
1495 LLVM::LLVMFunctionType::get(resultType, paramTypes));
1496 func.setCConv(LLVM::cconv::CConv::SPIR_FUNC);
1497 func.setConvergent(convergent);
1498 func.setNoUnwind(true);
1499 func.setWillReturn(true);
1500 return func;
1501}
1502
1503static LLVM::CallOp createSPIRVBuiltinCall(Location loc, OpBuilder &builder,
1504 LLVM::LLVMFuncOp func,
1505 ValueRange args) {
1506 auto call = LLVM::CallOp::create(builder, loc, func, args);
1507 call.setCConv(func.getCConv());
1508 call.setConvergentAttr(func.getConvergentAttr());
1509 call.setNoUnwindAttr(func.getNoUnwindAttr());
1510 call.setWillReturnAttr(func.getWillReturnAttr());
1511 return call;
1512}
1513
1514template <typename BarrierOpTy>
1515class ControlBarrierPattern : public SPIRVToLLVMConversion<BarrierOpTy> {
1516public:
1517 using OpAdaptor = typename SPIRVToLLVMConversion<BarrierOpTy>::OpAdaptor;
1518
1519 using SPIRVToLLVMConversion<BarrierOpTy>::SPIRVToLLVMConversion;
1520
1521 static constexpr StringRef getFuncName();
1522
1523 LogicalResult
1524 matchAndRewrite(BarrierOpTy controlBarrierOp, OpAdaptor adaptor,
1525 ConversionPatternRewriter &rewriter) const override {
1526 constexpr StringRef funcName = getFuncName();
1527 Operation *symbolTable =
1528 controlBarrierOp->template getParentWithTrait<OpTrait::SymbolTable>();
1529
1530 Type i32 = rewriter.getI32Type();
1531
1532 Type voidTy = rewriter.getType<LLVM::LLVMVoidType>();
1533 LLVM::LLVMFuncOp func =
1534 lookupOrCreateSPIRVFn(symbolTable, funcName, {i32, i32, i32}, voidTy);
1535
1536 Location loc = controlBarrierOp->getLoc();
1537 Value execution = LLVM::ConstantOp::create(
1538 rewriter, loc, i32, static_cast<int32_t>(adaptor.getExecutionScope()));
1539 Value memory = LLVM::ConstantOp::create(
1540 rewriter, loc, i32, static_cast<int32_t>(adaptor.getMemoryScope()));
1541 Value semantics = LLVM::ConstantOp::create(
1542 rewriter, loc, i32, static_cast<int32_t>(adaptor.getMemorySemantics()));
1543
1544 auto call = createSPIRVBuiltinCall(loc, rewriter, func,
1545 {execution, memory, semantics});
1546
1547 rewriter.replaceOp(controlBarrierOp, call);
1548 return success();
1549 }
1550};
1551
1552namespace {
1553
1554StringRef getTypeMangling(Type type, bool isSigned) {
1556 .Case([](Float16Type) { return "Dh"; })
1557 .Case([](Float32Type) { return "f"; })
1558 .Case([](Float64Type) { return "d"; })
1559 .Case([isSigned](IntegerType intTy) {
1560 switch (intTy.getWidth()) {
1561 case 1:
1562 return "b";
1563 case 8:
1564 return (isSigned) ? "a" : "c";
1565 case 16:
1566 return (isSigned) ? "s" : "t";
1567 case 32:
1568 return (isSigned) ? "i" : "j";
1569 case 64:
1570 return (isSigned) ? "l" : "m";
1571 default:
1572 llvm_unreachable("Unsupported integer width");
1573 }
1574 })
1575 .DefaultUnreachable("No mangling defined");
1576}
1577
1578template <typename ReduceOp>
1579constexpr StringLiteral getGroupFuncName();
1580
1581template <>
1582constexpr StringLiteral getGroupFuncName<spirv::GroupIAddOp>() {
1583 return "_Z17__spirv_GroupIAddii";
1584}
1585template <>
1586constexpr StringLiteral getGroupFuncName<spirv::GroupFAddOp>() {
1587 return "_Z17__spirv_GroupFAddii";
1588}
1589template <>
1590constexpr StringLiteral getGroupFuncName<spirv::GroupSMinOp>() {
1591 return "_Z17__spirv_GroupSMinii";
1592}
1593template <>
1594constexpr StringLiteral getGroupFuncName<spirv::GroupUMinOp>() {
1595 return "_Z17__spirv_GroupUMinii";
1596}
1597template <>
1598constexpr StringLiteral getGroupFuncName<spirv::GroupFMinOp>() {
1599 return "_Z17__spirv_GroupFMinii";
1600}
1601template <>
1602constexpr StringLiteral getGroupFuncName<spirv::GroupSMaxOp>() {
1603 return "_Z17__spirv_GroupSMaxii";
1604}
1605template <>
1606constexpr StringLiteral getGroupFuncName<spirv::GroupUMaxOp>() {
1607 return "_Z17__spirv_GroupUMaxii";
1608}
1609template <>
1610constexpr StringLiteral getGroupFuncName<spirv::GroupFMaxOp>() {
1611 return "_Z17__spirv_GroupFMaxii";
1612}
1613template <>
1614constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformIAddOp>() {
1615 return "_Z27__spirv_GroupNonUniformIAddii";
1616}
1617template <>
1618constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFAddOp>() {
1619 return "_Z27__spirv_GroupNonUniformFAddii";
1620}
1621template <>
1622constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformIMulOp>() {
1623 return "_Z27__spirv_GroupNonUniformIMulii";
1624}
1625template <>
1626constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMulOp>() {
1627 return "_Z27__spirv_GroupNonUniformFMulii";
1628}
1629template <>
1630constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformSMinOp>() {
1631 return "_Z27__spirv_GroupNonUniformSMinii";
1632}
1633template <>
1634constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformUMinOp>() {
1635 return "_Z27__spirv_GroupNonUniformUMinii";
1636}
1637template <>
1638constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMinOp>() {
1639 return "_Z27__spirv_GroupNonUniformFMinii";
1640}
1641template <>
1642constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformSMaxOp>() {
1643 return "_Z27__spirv_GroupNonUniformSMaxii";
1644}
1645template <>
1646constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformUMaxOp>() {
1647 return "_Z27__spirv_GroupNonUniformUMaxii";
1648}
1649template <>
1650constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformFMaxOp>() {
1651 return "_Z27__spirv_GroupNonUniformFMaxii";
1652}
1653template <>
1654constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseAndOp>() {
1655 return "_Z33__spirv_GroupNonUniformBitwiseAndii";
1656}
1657template <>
1658constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseOrOp>() {
1659 return "_Z32__spirv_GroupNonUniformBitwiseOrii";
1660}
1661template <>
1662constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformBitwiseXorOp>() {
1663 return "_Z33__spirv_GroupNonUniformBitwiseXorii";
1664}
1665template <>
1666constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalAndOp>() {
1667 return "_Z33__spirv_GroupNonUniformLogicalAndii";
1668}
1669template <>
1670constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalOrOp>() {
1671 return "_Z32__spirv_GroupNonUniformLogicalOrii";
1672}
1673template <>
1674constexpr StringLiteral getGroupFuncName<spirv::GroupNonUniformLogicalXorOp>() {
1675 return "_Z33__spirv_GroupNonUniformLogicalXorii";
1676}
1677} // namespace
1678
1679template <typename ReduceOp, bool Signed = false, bool NonUniform = false>
1680class GroupReducePattern : public SPIRVToLLVMConversion<ReduceOp> {
1681public:
1682 using SPIRVToLLVMConversion<ReduceOp>::SPIRVToLLVMConversion;
1683
1684 LogicalResult
1685 matchAndRewrite(ReduceOp op, typename ReduceOp::Adaptor adaptor,
1686 ConversionPatternRewriter &rewriter) const override {
1687
1688 Type retTy = op.getResult().getType();
1689 if (!retTy.isIntOrFloat()) {
1690 return failure();
1691 }
1692 SmallString<36> funcName = getGroupFuncName<ReduceOp>();
1693 funcName += getTypeMangling(retTy, false);
1694
1695 Type i32Ty = rewriter.getI32Type();
1696 SmallVector<Type> paramTypes{i32Ty, i32Ty, retTy};
1697 if constexpr (NonUniform) {
1698 if (adaptor.getClusterSize()) {
1699 funcName += "j";
1700 paramTypes.push_back(i32Ty);
1701 }
1702 }
1703
1704 Operation *symbolTable =
1705 op->template getParentWithTrait<OpTrait::SymbolTable>();
1706
1707 LLVM::LLVMFuncOp func =
1708 lookupOrCreateSPIRVFn(symbolTable, funcName, paramTypes, retTy);
1709
1710 Location loc = op.getLoc();
1711 Value scope = LLVM::ConstantOp::create(
1712 rewriter, loc, i32Ty,
1713 static_cast<int32_t>(adaptor.getExecutionScope()));
1714 Value groupOp = LLVM::ConstantOp::create(
1715 rewriter, loc, i32Ty,
1716 static_cast<int32_t>(adaptor.getGroupOperation()));
1717 SmallVector<Value> operands{scope, groupOp};
1718 operands.append(adaptor.getOperands().begin(), adaptor.getOperands().end());
1719
1720 auto call = createSPIRVBuiltinCall(loc, rewriter, func, operands);
1721 rewriter.replaceOp(op, call);
1722 return success();
1723 }
1724};
1725
1726template <>
1727constexpr StringRef
1728ControlBarrierPattern<spirv::ControlBarrierOp>::getFuncName() {
1729 return "_Z22__spirv_ControlBarrieriii";
1730}
1731
1732template <>
1733constexpr StringRef
1734ControlBarrierPattern<spirv::INTELControlBarrierArriveOp>::getFuncName() {
1735 return "_Z33__spirv_ControlBarrierArriveINTELiii";
1736}
1737
1738template <>
1739constexpr StringRef
1740ControlBarrierPattern<spirv::INTELControlBarrierWaitOp>::getFuncName() {
1741 return "_Z31__spirv_ControlBarrierWaitINTELiii";
1742}
1743
1744/// Converts `spirv.mlir.loop` to LLVM dialect. All blocks within selection
1745/// should be reachable for conversion to succeed. The structure of the loop in
1746/// LLVM dialect will be the following:
1747///
1748/// +------------------------------------+
1749/// | <code before spirv.mlir.loop> |
1750/// | llvm.br ^header |
1751/// +------------------------------------+
1752/// |
1753/// +----------------+ |
1754/// | | |
1755/// | V V
1756/// | +------------------------------------+
1757/// | | ^header: |
1758/// | | <header code> |
1759/// | | llvm.cond_br %cond, ^body, ^exit |
1760/// | +------------------------------------+
1761/// | |
1762/// | |----------------------+
1763/// | | |
1764/// | V |
1765/// | +------------------------------------+ |
1766/// | | ^body: | |
1767/// | | <body code> | |
1768/// | | llvm.br ^continue | |
1769/// | +------------------------------------+ |
1770/// | | |
1771/// | V |
1772/// | +------------------------------------+ |
1773/// | | ^continue: | |
1774/// | | <continue code> | |
1775/// | | llvm.br ^header | |
1776/// | +------------------------------------+ |
1777/// | | |
1778/// +---------------+ +----------------------+
1779/// |
1780/// V
1781/// +------------------------------------+
1782/// | ^exit: |
1783/// | llvm.br ^remaining |
1784/// +------------------------------------+
1785/// |
1786/// V
1787/// +------------------------------------+
1788/// | ^remaining: |
1789/// | <code after spirv.mlir.loop> |
1790/// +------------------------------------+
1791///
1792class LoopPattern : public SPIRVToLLVMConversion<spirv::LoopOp> {
1793public:
1794 using SPIRVToLLVMConversion<spirv::LoopOp>::SPIRVToLLVMConversion;
1795
1796 LogicalResult
1797 matchAndRewrite(spirv::LoopOp loopOp, OpAdaptor adaptor,
1798 ConversionPatternRewriter &rewriter) const override {
1799 // There is no support of loop control at the moment.
1800 if (loopOp.getLoopControl() != spirv::LoopControl::None)
1801 return failure();
1802
1803 // `spirv.mlir.loop` with empty region is redundant and should be erased.
1804 if (loopOp.getBody().empty()) {
1805 rewriter.eraseOp(loopOp);
1806 return success();
1807 }
1808
1809 Location loc = loopOp.getLoc();
1810
1811 // Split the current block after `spirv.mlir.loop`. The remaining ops will
1812 // be used in `endBlock`.
1813 Block *currentBlock = rewriter.getBlock();
1814 auto position = Block::iterator(loopOp);
1815 Block *endBlock = rewriter.splitBlock(currentBlock, position);
1816
1817 // Remove entry block and create a branch in the current block going to the
1818 // header block.
1819 Block *entryBlock = loopOp.getEntryBlock();
1820 assert(entryBlock->getOperations().size() == 1);
1821 auto brOp = dyn_cast<spirv::BranchOp>(entryBlock->getOperations().front());
1822 if (!brOp)
1823 return failure();
1824 Block *headerBlock = loopOp.getHeaderBlock();
1825 rewriter.setInsertionPointToEnd(currentBlock);
1826 LLVM::BrOp::create(rewriter, loc, brOp.getBlockArguments(), headerBlock);
1827 rewriter.eraseBlock(entryBlock);
1828
1829 // Branch from merge block to end block.
1830 Block *mergeBlock = loopOp.getMergeBlock();
1831 Operation *terminator = mergeBlock->getTerminator();
1832 ValueRange terminatorOperands = terminator->getOperands();
1833 rewriter.setInsertionPointToEnd(mergeBlock);
1834 LLVM::BrOp::create(rewriter, loc, terminatorOperands, endBlock);
1835
1836 rewriter.inlineRegionBefore(loopOp.getBody(), endBlock);
1837 rewriter.replaceOp(loopOp, endBlock->getArguments());
1838 return success();
1839 }
1840};
1841
1842/// Converts `spirv.mlir.selection`. All blocks within selection should be
1843/// reachable for conversion to succeed.
1844class SelectionPattern : public SPIRVToLLVMConversion<spirv::SelectionOp> {
1845public:
1846 using SPIRVToLLVMConversion<spirv::SelectionOp>::SPIRVToLLVMConversion;
1847
1848 LogicalResult
1849 matchAndRewrite(spirv::SelectionOp op, OpAdaptor adaptor,
1850 ConversionPatternRewriter &rewriter) const override {
1851 // There is no support for `Flatten` or `DontFlatten` selection control at
1852 // the moment. This are just compiler hints and can be performed during the
1853 // optimization passes.
1854 if (op.getSelectionControl() != spirv::SelectionControl::None)
1855 return failure();
1856
1857 // `spirv.mlir.selection` with empty region is redundant and is erased.
1858 if (op.getBody().empty()) {
1859 rewriter.eraseOp(op);
1860 return success();
1861 }
1862
1863 Location loc = op.getLoc();
1864
1865 SmallVector<Type> resultTypes;
1866 if (failed(
1867 getTypeConverter()->convertTypes(op.getResultTypes(), resultTypes)))
1868 return rewriter.notifyMatchFailure(op, "failed to convert type");
1869
1870 // Split the current block after `spirv.mlir.selection`. The remaining ops
1871 // will be used in `continueBlock`.
1872 auto *currentBlock = rewriter.getInsertionBlock();
1873 rewriter.setInsertionPointAfter(op);
1874 auto position = rewriter.getInsertionPoint();
1875 auto *continueBlock = rewriter.splitBlock(currentBlock, position);
1876
1877 // Add arguments to the continue block for selections that yield values.
1878 for (Type ty : resultTypes)
1879 continueBlock->addArgument(ty, loc);
1880
1881 // Branch from merge block to continue block.
1882 auto *mergeBlock = op.getMergeBlock();
1883 Operation *terminator = mergeBlock->getTerminator();
1884 ValueRange terminatorOperands = terminator->getOperands();
1885 rewriter.setInsertionPointToEnd(mergeBlock);
1886 LLVM::BrOp::create(rewriter, loc, terminatorOperands, continueBlock);
1887
1888 // Header ops, including the terminator, are converted by their own
1889 // patterns.
1890 Block *headerBlock = op.getHeaderBlock();
1891 rewriter.inlineRegionBefore(op.getBody(), continueBlock);
1892 rewriter.mergeBlocks(headerBlock, currentBlock);
1893 rewriter.replaceOp(op, continueBlock->getArguments());
1894 return success();
1895 }
1896};
1897
1898/// Converts SPIR-V shift ops to LLVM shift ops. Since LLVM dialect
1899/// puts a restriction on `Shift` and `Base` to have the same bit width,
1900/// `Shift` is zero or sign extended to match this specification. Cases when
1901/// `Shift` bit width > `Base` bit width are considered to be illegal.
1902template <typename SPIRVOp, typename LLVMOp>
1903class ShiftPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1904public:
1905 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1906
1907 LogicalResult
1908 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1909 ConversionPatternRewriter &rewriter) const override {
1910
1911 auto dstType = this->getTypeConverter()->convertType(op.getType());
1912 if (!dstType)
1913 return rewriter.notifyMatchFailure(op, "type conversion failed");
1914
1915 Type op1Type = op.getOperand1().getType();
1916 Type op2Type = op.getOperand2().getType();
1917
1918 if (op1Type == op2Type) {
1919 rewriter.template replaceOpWithNewOp<LLVMOp>(op, dstType,
1920 adaptor.getOperands());
1921 return success();
1922 }
1923
1924 std::optional<uint64_t> dstTypeWidth =
1926 std::optional<uint64_t> op2TypeWidth =
1928
1929 if (!dstTypeWidth || !op2TypeWidth)
1930 return failure();
1931
1932 Location loc = op.getLoc();
1933 Value extended;
1934 if (op2TypeWidth < dstTypeWidth) {
1935 if (isUnsignedIntegerOrVector(op2Type)) {
1936 extended =
1937 LLVM::ZExtOp::create(rewriter, loc, dstType, adaptor.getOperand2());
1938 } else {
1939 extended =
1940 LLVM::SExtOp::create(rewriter, loc, dstType, adaptor.getOperand2());
1941 }
1942 } else if (op2TypeWidth == dstTypeWidth) {
1943 extended = adaptor.getOperand2();
1944 } else {
1945 return failure();
1946 }
1947
1948 Value result =
1949 LLVMOp::create(rewriter, loc, dstType, adaptor.getOperand1(), extended);
1950 rewriter.replaceOp(op, result);
1951 return success();
1952 }
1953};
1954
1955// `llvm.intr.abs` requires an `is_int_min_poison` immarg that `spirv.GL.SAbs`
1956// and `spirv.CL.s_abs` do not carry; default to `false` to preserve SPIR-V's
1957// well-defined behavior on INT_MIN.
1958template <typename SPIRVOp>
1959class SAbsPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1960public:
1961 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1962
1963 LogicalResult
1964 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1965 ConversionPatternRewriter &rewriter) const override {
1966 Type dstType = this->getTypeConverter()->convertType(op.getType());
1967 if (!dstType)
1968 return rewriter.notifyMatchFailure(op, "type conversion failed");
1969
1970 rewriter.template replaceOpWithNewOp<LLVM::AbsOp>(
1971 op, dstType, adaptor.getOperand(), /*is_int_min_poison=*/false);
1972 return success();
1973 }
1974};
1975
1976/// Converts `spirv.CL.clz` and `spirv.CL.ctz` to `llvm.intr.ctlz` and
1977/// `llvm.intr.cttz`.
1978template <typename SPIRVOp, typename LLVMOp>
1979class CountZerosPattern : public SPIRVToLLVMConversion<SPIRVOp> {
1980public:
1981 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
1982
1983 LogicalResult
1984 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
1985 ConversionPatternRewriter &rewriter) const override {
1986 Type dstType = this->getTypeConverter()->convertType(op.getType());
1987 if (!dstType)
1988 return rewriter.notifyMatchFailure(op, "type conversion failed");
1989
1990 rewriter.template replaceOpWithNewOp<LLVMOp>(op, dstType,
1991 adaptor.getOperand(),
1992 /*is_zero_poison=*/false);
1993 return success();
1994 }
1995};
1996
1997/// Converts `spirv.GL.Fract` to `x - floor(x)`.
1998class FractPattern : public SPIRVToLLVMConversion<spirv::GLFractOp> {
1999public:
2000 using SPIRVToLLVMConversion<spirv::GLFractOp>::SPIRVToLLVMConversion;
2001
2002 LogicalResult
2003 matchAndRewrite(spirv::GLFractOp op, OpAdaptor adaptor,
2004 ConversionPatternRewriter &rewriter) const override {
2005 Type dstType = getTypeConverter()->convertType(op.getType());
2006 if (!dstType)
2007 return rewriter.notifyMatchFailure(op, "type conversion failed");
2008
2009 Location loc = op.getLoc();
2010 Value operand = adaptor.getOperand();
2011 Value floored = LLVM::FFloorOp::create(rewriter, loc, dstType, operand);
2012 rewriter.replaceOpWithNewOp<LLVM::FSubOp>(op, dstType, operand, floored);
2013 return success();
2014 }
2015};
2016
2017/// Converts `spirv.GL.FMix` to `x * (1 - a) + y * a` as specified by
2018/// GL.std.450.
2019class GLFMixPattern : public SPIRVToLLVMConversion<spirv::GLFMixOp> {
2020public:
2021 using SPIRVToLLVMConversion<spirv::GLFMixOp>::SPIRVToLLVMConversion;
2022
2023 LogicalResult
2024 matchAndRewrite(spirv::GLFMixOp op, OpAdaptor adaptor,
2025 ConversionPatternRewriter &rewriter) const override {
2026 Type dstType = getTypeConverter()->convertType(op.getType());
2027 if (!dstType)
2028 return rewriter.notifyMatchFailure(op, "type conversion failed");
2029
2030 Location loc = op.getLoc();
2031 Value x = adaptor.getX();
2032 Value y = adaptor.getY();
2033 Value a = adaptor.getA();
2034 Value one = createFPConstant(loc, op.getType(), dstType, rewriter, 1.0);
2035 Value oneMinusA = LLVM::FSubOp::create(rewriter, loc, dstType, one, a);
2036 Value lhs = LLVM::FMulOp::create(rewriter, loc, dstType, x, oneMinusA);
2037 Value rhs = LLVM::FMulOp::create(rewriter, loc, dstType, y, a);
2038 rewriter.replaceOpWithNewOp<LLVM::FAddOp>(op, dstType, lhs, rhs);
2039 return success();
2040 }
2041};
2042
2043/// Converts `spirv.CL.mix` to `fma(a, y - x, x)`. The OpenCL spec defines
2044/// mix as `x + (y - x) * a` and explicitly permits FMA contractions.
2045class CLMixPattern : public SPIRVToLLVMConversion<spirv::CLMixOp> {
2046public:
2047 using SPIRVToLLVMConversion<spirv::CLMixOp>::SPIRVToLLVMConversion;
2048
2049 LogicalResult
2050 matchAndRewrite(spirv::CLMixOp op, OpAdaptor adaptor,
2051 ConversionPatternRewriter &rewriter) const override {
2052 Type dstType = getTypeConverter()->convertType(op.getType());
2053 if (!dstType)
2054 return rewriter.notifyMatchFailure(op, "type conversion failed");
2055
2056 Location loc = op.getLoc();
2057 Value x = adaptor.getX();
2058 Value y = adaptor.getY();
2059 Value a = adaptor.getZ();
2060 Value diff = LLVM::FSubOp::create(rewriter, loc, dstType, y, x);
2061 rewriter.replaceOpWithNewOp<LLVM::FMAOp>(op, dstType, a, diff, x);
2062 return success();
2063 }
2064};
2065
2066/// Converts `spirv.CL.sincos` to `llvm.intr.sincos`, storing the cosine to
2067/// `cosval` and returning the sine.
2068class CLSincosPattern : public SPIRVToLLVMConversion<spirv::CLSincosOp> {
2069public:
2070 using SPIRVToLLVMConversion<spirv::CLSincosOp>::SPIRVToLLVMConversion;
2071
2072 LogicalResult
2073 matchAndRewrite(spirv::CLSincosOp op, OpAdaptor adaptor,
2074 ConversionPatternRewriter &rewriter) const override {
2075 Type dstType = getTypeConverter()->convertType(op.getType());
2076 if (!dstType)
2077 return rewriter.notifyMatchFailure(op, "type conversion failed");
2078
2079 Location loc = op.getLoc();
2080 auto structType = LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
2081 {dstType, dstType});
2082 Value sincos =
2083 LLVM::SincosOp::create(rewriter, loc, structType, adaptor.getX());
2084 Value sin = LLVM::ExtractValueOp::create(rewriter, loc, sincos, 0);
2085 Value cos = LLVM::ExtractValueOp::create(rewriter, loc, sincos, 1);
2086 LLVM::StoreOp::create(rewriter, loc, cos, adaptor.getCosval());
2087 rewriter.replaceOp(op, sin);
2088 return success();
2089 }
2090};
2091
2092// Converts spirv.GL.Radians (scale = pi/180) and spirv.GL.Degrees
2093// (scale = 180/pi) by multiplying the operand by a compile-time constant.
2094template <typename SPIRVOp>
2095class ScalePattern : public SPIRVToLLVMConversion<SPIRVOp> {
2096public:
2097 template <typename... Args>
2098 ScalePattern(double scale, Args &&...args)
2099 : SPIRVToLLVMConversion<SPIRVOp>(std::forward<Args>(args)...),
2100 scale(scale) {}
2101
2102 LogicalResult
2103 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
2104 ConversionPatternRewriter &rewriter) const override {
2105 Type srcType = op.getType();
2106 Type dstType = this->getTypeConverter()->convertType(srcType);
2107 if (!dstType)
2108 return rewriter.notifyMatchFailure(op, "type conversion failed");
2109
2110 Location loc = op.getLoc();
2111 Value factor = createFPConstant(loc, srcType, dstType, rewriter, scale);
2112 rewriter.replaceOpWithNewOp<LLVM::FMulOp>(op, dstType, adaptor.getOperand(),
2113 factor);
2114 return success();
2115 }
2116
2117private:
2118 double scale;
2119};
2120
2121/// Converts `spirv.GL.FSign`/`spirv.GL.SSign` to a sign(x) sequence that maps
2122/// the operand to -1/0/1 using two comparisons and two selects. The `isFloat`
2123/// flag selects between floating-point and integer comparisons/constants.
2124template <typename SPIRVOp, bool isFloat>
2125class SignPattern : public SPIRVToLLVMConversion<SPIRVOp> {
2126public:
2127 using SPIRVToLLVMConversion<SPIRVOp>::SPIRVToLLVMConversion;
2128
2129 LogicalResult
2130 matchAndRewrite(SPIRVOp op, typename SPIRVOp::Adaptor adaptor,
2131 ConversionPatternRewriter &rewriter) const override {
2132 Type srcType = op.getType();
2133 Type dstType = this->getTypeConverter()->convertType(srcType);
2134 if (!dstType)
2135 return rewriter.notifyMatchFailure(op, "type conversion failed");
2136
2137 Location loc = op.getLoc();
2138 Value operand = adaptor.getOperand();
2139 auto vecSrcType = dyn_cast<VectorType>(srcType);
2140 Type i1Type = rewriter.getI1Type();
2141 Type cmpType =
2142 vecSrcType ? VectorType::get(vecSrcType.getShape(), i1Type) : i1Type;
2143
2144 Value zero, one, minusOne, gt, lt;
2145 if constexpr (isFloat) {
2146 zero = createFPConstant(loc, srcType, dstType, rewriter, 0.0);
2147 one = createFPConstant(loc, srcType, dstType, rewriter, 1.0);
2148 minusOne = createFPConstant(loc, srcType, dstType, rewriter, -1.0);
2149 gt = LLVM::FCmpOp::create(rewriter, loc, cmpType,
2150 LLVM::FCmpPredicate::ogt, operand, zero);
2151 lt = LLVM::FCmpOp::create(rewriter, loc, cmpType,
2152 LLVM::FCmpPredicate::olt, operand, zero);
2153 } else {
2154 auto intElemType = cast<IntegerType>(getElementTypeOrSelf(srcType));
2155 zero = createIntegerConstant(loc, srcType, dstType, rewriter,
2156 rewriter.getIntegerAttr(intElemType, 0));
2157 one = createIntegerConstant(loc, srcType, dstType, rewriter,
2158 rewriter.getIntegerAttr(intElemType, 1));
2159 minusOne = createConstantAllBitsSet(loc, srcType, dstType, rewriter);
2160 gt = LLVM::ICmpOp::create(rewriter, loc, cmpType,
2161 LLVM::ICmpPredicate::sgt, operand, zero);
2162 lt = LLVM::ICmpOp::create(rewriter, loc, cmpType,
2163 LLVM::ICmpPredicate::slt, operand, zero);
2164 }
2165
2166 Value negOrZero =
2167 LLVM::SelectOp::create(rewriter, loc, dstType, lt, minusOne, zero);
2168 rewriter.replaceOpWithNewOp<LLVM::SelectOp>(op, dstType, gt, one,
2169 negOrZero);
2170 return success();
2171 }
2172};
2173
2174class VariablePattern : public SPIRVToLLVMConversion<spirv::VariableOp> {
2175public:
2176 using SPIRVToLLVMConversion<spirv::VariableOp>::SPIRVToLLVMConversion;
2177
2178 LogicalResult
2179 matchAndRewrite(spirv::VariableOp varOp, OpAdaptor adaptor,
2180 ConversionPatternRewriter &rewriter) const override {
2181 auto srcType = varOp.getType();
2182 // Initialization is supported for scalars and vectors only.
2183 auto pointerTo = cast<spirv::PointerType>(srcType).getPointeeType();
2184 auto init = varOp.getInitializer();
2185 if (init && !pointerTo.isIntOrFloat() && !isa<VectorType>(pointerTo))
2186 return failure();
2187
2188 auto dstType = getTypeConverter()->convertType(srcType);
2189 if (!dstType)
2190 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
2191
2192 Location loc = varOp.getLoc();
2193 Value size = createI32ConstantOf(loc, rewriter, 1);
2194 if (!init) {
2195 auto elementType = getTypeConverter()->convertType(pointerTo);
2196 if (!elementType)
2197 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
2198 rewriter.replaceOpWithNewOp<LLVM::AllocaOp>(varOp, dstType, elementType,
2199 size);
2200 return success();
2201 }
2202 auto elementType = getTypeConverter()->convertType(pointerTo);
2203 if (!elementType)
2204 return rewriter.notifyMatchFailure(varOp, "type conversion failed");
2205 Value allocated =
2206 LLVM::AllocaOp::create(rewriter, loc, dstType, elementType, size);
2207 LLVM::StoreOp::create(rewriter, loc, adaptor.getInitializer(), allocated);
2208 rewriter.replaceOp(varOp, allocated);
2209 return success();
2210 }
2211};
2212
2213//===----------------------------------------------------------------------===//
2214// BitcastOp conversion
2215//===----------------------------------------------------------------------===//
2216
2217class BitcastConversionPattern
2218 : public SPIRVToLLVMConversion<spirv::BitcastOp> {
2219public:
2220 using SPIRVToLLVMConversion<spirv::BitcastOp>::SPIRVToLLVMConversion;
2221
2222 LogicalResult
2223 matchAndRewrite(spirv::BitcastOp bitcastOp, OpAdaptor adaptor,
2224 ConversionPatternRewriter &rewriter) const override {
2225 auto dstType = getTypeConverter()->convertType(bitcastOp.getType());
2226 if (!dstType)
2227 return rewriter.notifyMatchFailure(bitcastOp, "type conversion failed");
2228
2229 // LLVM's opaque pointers do not require bitcasts.
2230 if (isa<LLVM::LLVMPointerType>(dstType)) {
2231 rewriter.replaceOp(bitcastOp, adaptor.getOperand());
2232 return success();
2233 }
2234
2235 LLVM::BitcastOp::Properties properties{};
2236 SmallVector<NamedAttribute> discardableAttrs;
2237 if (failed(collectAttrsForConversion<LLVM::BitcastOp>(bitcastOp, properties,
2238 discardableAttrs)))
2239 return failure();
2240 rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(bitcastOp, dstType,
2241 adaptor.getOperands(),
2242 properties, discardableAttrs);
2243 return success();
2244 }
2245};
2246
2247//===----------------------------------------------------------------------===//
2248// FuncOp conversion
2249//===----------------------------------------------------------------------===//
2250
2251class FuncConversionPattern : public SPIRVToLLVMConversion<spirv::FuncOp> {
2252public:
2253 using SPIRVToLLVMConversion<spirv::FuncOp>::SPIRVToLLVMConversion;
2254
2255 LogicalResult
2256 matchAndRewrite(spirv::FuncOp funcOp, OpAdaptor adaptor,
2257 ConversionPatternRewriter &rewriter) const override {
2258
2259 // Convert function signature. At the moment LLVMType converter is enough
2260 // for currently supported types.
2261 auto funcType = funcOp.getFunctionType();
2262 TypeConverter::SignatureConversion signatureConverter(
2263 funcType.getNumInputs());
2264 auto llvmType = static_cast<const LLVMTypeConverter *>(getTypeConverter())
2265 ->convertFunctionSignature(
2266 funcType, /*isVariadic=*/false,
2267 /*useBarePtrCallConv=*/false, signatureConverter);
2268 if (!llvmType)
2269 return failure();
2270
2271 // Create a new `LLVMFuncOp`
2272 Location loc = funcOp.getLoc();
2273 StringRef name = funcOp.getName();
2274 auto newFuncOp = LLVM::LLVMFuncOp::create(rewriter, loc, name, llvmType);
2275
2276 // Convert SPIR-V Function Control to equivalent LLVM function attribute
2277 MLIRContext *context = funcOp.getContext();
2278 switch (funcOp.getFunctionControl()) {
2279 case spirv::FunctionControl::Inline:
2280 newFuncOp.setAlwaysInline(true);
2281 break;
2282 case spirv::FunctionControl::DontInline:
2283 newFuncOp.setNoInline(true);
2284 break;
2285
2286#define DISPATCH(functionControl, llvmAttr) \
2287 case functionControl: \
2288 newFuncOp->setDiscardableAttr("passthrough", \
2289 ArrayAttr::get(context, {llvmAttr})); \
2290 break;
2291
2292 DISPATCH(spirv::FunctionControl::Pure,
2293 StringAttr::get(context, "readonly"));
2294 DISPATCH(spirv::FunctionControl::Const,
2295 StringAttr::get(context, "readnone"));
2296
2297#undef DISPATCH
2298
2299 // Default: if `spirv::FunctionControl::None`, then no attributes are
2300 // needed.
2301 default:
2302 break;
2303 }
2304
2305 rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
2306 newFuncOp.end());
2307 if (failed(rewriter.convertRegionTypes(
2308 &newFuncOp.getBody(), *getTypeConverter(), &signatureConverter))) {
2309 return failure();
2310 }
2311 rewriter.eraseOp(funcOp);
2312 return success();
2313 }
2314};
2315
2316//===----------------------------------------------------------------------===//
2317// ModuleOp conversion
2318//===----------------------------------------------------------------------===//
2319
2320class ModuleConversionPattern : public SPIRVToLLVMConversion<spirv::ModuleOp> {
2321public:
2322 using SPIRVToLLVMConversion<spirv::ModuleOp>::SPIRVToLLVMConversion;
2323
2324 LogicalResult
2325 matchAndRewrite(spirv::ModuleOp spvModuleOp, OpAdaptor adaptor,
2326 ConversionPatternRewriter &rewriter) const override {
2327
2328 auto newModuleOp =
2329 ModuleOp::create(rewriter, spvModuleOp.getLoc(), spvModuleOp.getName());
2330 rewriter.inlineRegionBefore(spvModuleOp.getRegion(), newModuleOp.getBody());
2331
2332 // Remove the terminator block that was automatically added by builder
2333 rewriter.eraseBlock(&newModuleOp.getBodyRegion().back());
2334 rewriter.eraseOp(spvModuleOp);
2335 return success();
2336 }
2337};
2338
2339//===----------------------------------------------------------------------===//
2340// VectorShuffleOp conversion
2341//===----------------------------------------------------------------------===//
2342
2343class VectorShufflePattern
2344 : public SPIRVToLLVMConversion<spirv::VectorShuffleOp> {
2345public:
2346 using SPIRVToLLVMConversion<spirv::VectorShuffleOp>::SPIRVToLLVMConversion;
2347 LogicalResult
2348 matchAndRewrite(spirv::VectorShuffleOp op, OpAdaptor adaptor,
2349 ConversionPatternRewriter &rewriter) const override {
2350 Location loc = op.getLoc();
2351 auto components = adaptor.getComponents();
2352 auto vector1 = adaptor.getVector1();
2353 auto vector2 = adaptor.getVector2();
2354 int vector1Size = cast<VectorType>(vector1.getType()).getNumElements();
2355 int vector2Size = cast<VectorType>(vector2.getType()).getNumElements();
2356 if (vector1Size == vector2Size) {
2357 rewriter.replaceOpWithNewOp<LLVM::ShuffleVectorOp>(
2358 op, vector1, vector2,
2359 LLVM::convertArrayToIndices<int32_t>(components));
2360 return success();
2361 }
2362
2363 auto dstType = getTypeConverter()->convertType(op.getType());
2364 if (!dstType)
2365 return rewriter.notifyMatchFailure(op, "type conversion failed");
2366 auto scalarType = cast<VectorType>(dstType).getElementType();
2367 auto componentsArray = components.getValue();
2368 auto *context = rewriter.getContext();
2369 auto llvmI32Type = IntegerType::get(context, 32);
2370 Value targetOp = LLVM::PoisonOp::create(rewriter, loc, dstType);
2371 for (unsigned i = 0; i < componentsArray.size(); i++) {
2372 if (!isa<IntegerAttr>(componentsArray[i]))
2373 return op.emitError("unable to support non-constant component");
2374
2375 int indexVal = cast<IntegerAttr>(componentsArray[i]).getInt();
2376 if (indexVal == -1)
2377 continue;
2378
2379 int offsetVal = 0;
2380 Value baseVector = vector1;
2381 if (indexVal >= vector1Size) {
2382 offsetVal = vector1Size;
2383 baseVector = vector2;
2384 }
2385
2386 Value dstIndex = LLVM::ConstantOp::create(
2387 rewriter, loc, llvmI32Type,
2388 rewriter.getIntegerAttr(rewriter.getI32Type(), i));
2389 Value index = LLVM::ConstantOp::create(
2390 rewriter, loc, llvmI32Type,
2391 rewriter.getIntegerAttr(rewriter.getI32Type(), indexVal - offsetVal));
2392
2393 auto extractOp = LLVM::ExtractElementOp::create(rewriter, loc, scalarType,
2394 baseVector, index);
2395 targetOp = LLVM::InsertElementOp::create(rewriter, loc, dstType, targetOp,
2396 extractOp, dstIndex);
2397 }
2398 rewriter.replaceOp(op, targetOp);
2399 return success();
2400 }
2401};
2402} // namespace
2403
2404//===----------------------------------------------------------------------===//
2405// Pattern population
2406//===----------------------------------------------------------------------===//
2407
2409 spirv::ClientAPI clientAPI) {
2410 typeConverter.addConversion([&](spirv::ArrayType type) {
2411 return convertArrayType(type, typeConverter);
2412 });
2413 typeConverter.addConversion([&, clientAPI](spirv::PointerType type) {
2414 return convertPointerType(type, typeConverter, clientAPI);
2415 });
2416 typeConverter.addConversion([&](spirv::RuntimeArrayType type) {
2417 return convertRuntimeArrayType(type, typeConverter);
2418 });
2419 typeConverter.addConversion([&](spirv::StructType type) {
2420 return convertStructType(type, typeConverter);
2421 });
2422}
2423
2425 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns,
2426 spirv::ClientAPI clientAPI) {
2427 patterns.add<
2428 // Arithmetic ops
2429 DirectConversionPattern<spirv::IAddOp, LLVM::AddOp>,
2430 DirectConversionPattern<spirv::IMulOp, LLVM::MulOp>,
2431 DirectConversionPattern<spirv::ISubOp, LLVM::SubOp>,
2432 DirectConversionPattern<spirv::FAddOp, LLVM::FAddOp>,
2433 DirectConversionPattern<spirv::FDivOp, LLVM::FDivOp>,
2434 DirectConversionPattern<spirv::FMulOp, LLVM::FMulOp>,
2435 DirectConversionPattern<spirv::FNegateOp, LLVM::FNegOp>,
2436 DirectConversionPattern<spirv::FRemOp, LLVM::FRemOp>,
2437 DirectConversionPattern<spirv::FSubOp, LLVM::FSubOp>,
2438 DirectConversionPattern<spirv::SDivOp, LLVM::SDivOp>,
2439 DirectConversionPattern<spirv::SRemOp, LLVM::SRemOp>,
2440 DirectConversionPattern<spirv::UDivOp, LLVM::UDivOp>,
2441 DirectConversionPattern<spirv::UModOp, LLVM::URemOp>, FModPattern,
2442 SModPattern, VectorTimesScalarPattern, SNegatePattern,
2443 ArithmeticWithOverflowPattern<spirv::IAddCarryOp,
2444 LLVM::UAddWithOverflowOp>,
2445 ArithmeticWithOverflowPattern<spirv::ISubBorrowOp,
2446 LLVM::USubWithOverflowOp>,
2447 MulExtendedPattern<spirv::SMulExtendedOp, LLVM::SExtOp>,
2448 MulExtendedPattern<spirv::UMulExtendedOp, LLVM::ZExtOp>,
2449
2450 // Bitwise ops
2451 BitFieldInsertPattern, BitFieldUExtractPattern, BitFieldSExtractPattern,
2452 DirectConversionPattern<spirv::BitCountOp, LLVM::CtPopOp>,
2453 DirectConversionPattern<spirv::BitReverseOp, LLVM::BitReverseOp>,
2454 DirectConversionPattern<spirv::BitwiseAndOp, LLVM::AndOp>,
2455 DirectConversionPattern<spirv::BitwiseOrOp, LLVM::OrOp>,
2456 DirectConversionPattern<spirv::BitwiseXorOp, LLVM::XOrOp>,
2457 NotPattern<spirv::NotOp>,
2458
2459 // Cast ops
2460 BitcastConversionPattern,
2461 DirectConversionPattern<spirv::ConvertFToSOp, LLVM::FPToSIOp>,
2462 DirectConversionPattern<spirv::ConvertFToUOp, LLVM::FPToUIOp>,
2463 DirectConversionPattern<spirv::ConvertSToFOp, LLVM::SIToFPOp>,
2464 DirectConversionPattern<spirv::ConvertUToFOp, LLVM::UIToFPOp>,
2465 IndirectCastPattern<spirv::FConvertOp, LLVM::FPExtOp, LLVM::FPTruncOp>,
2466 IndirectCastPattern<spirv::SConvertOp, LLVM::SExtOp, LLVM::TruncOp>,
2467 IndirectCastPattern<spirv::UConvertOp, LLVM::ZExtOp, LLVM::TruncOp>,
2468 DirectConversionPattern<spirv::ConvertPtrToUOp, LLVM::PtrToIntOp>,
2469 DirectConversionPattern<spirv::ConvertUToPtrOp, LLVM::IntToPtrOp>,
2470 DirectConversionPattern<spirv::PtrCastToGenericOp, LLVM::AddrSpaceCastOp>,
2471 DirectConversionPattern<spirv::GenericCastToPtrOp, LLVM::AddrSpaceCastOp>,
2472 DirectConversionPattern<spirv::GenericCastToPtrExplicitOp,
2473 LLVM::AddrSpaceCastOp>,
2474
2475 // Comparison ops
2476 IComparePattern<spirv::IEqualOp, LLVM::ICmpPredicate::eq>,
2477 IComparePattern<spirv::INotEqualOp, LLVM::ICmpPredicate::ne>,
2478 FComparePattern<spirv::FOrdEqualOp, LLVM::FCmpPredicate::oeq>,
2479 FComparePattern<spirv::FOrdGreaterThanOp, LLVM::FCmpPredicate::ogt>,
2480 FComparePattern<spirv::FOrdGreaterThanEqualOp, LLVM::FCmpPredicate::oge>,
2481 FComparePattern<spirv::FOrdLessThanEqualOp, LLVM::FCmpPredicate::ole>,
2482 FComparePattern<spirv::FOrdLessThanOp, LLVM::FCmpPredicate::olt>,
2483 FComparePattern<spirv::FOrdNotEqualOp, LLVM::FCmpPredicate::one>,
2484 FComparePattern<spirv::FUnordEqualOp, LLVM::FCmpPredicate::ueq>,
2485 FComparePattern<spirv::FUnordGreaterThanOp, LLVM::FCmpPredicate::ugt>,
2486 FComparePattern<spirv::FUnordGreaterThanEqualOp,
2487 LLVM::FCmpPredicate::uge>,
2488 FComparePattern<spirv::FUnordLessThanEqualOp, LLVM::FCmpPredicate::ule>,
2489 FComparePattern<spirv::FUnordLessThanOp, LLVM::FCmpPredicate::ult>,
2490 FComparePattern<spirv::FUnordNotEqualOp, LLVM::FCmpPredicate::une>,
2491 FComparePattern<spirv::OrderedOp, LLVM::FCmpPredicate::ord>,
2492 FComparePattern<spirv::UnorderedOp, LLVM::FCmpPredicate::uno>,
2493 IComparePattern<spirv::SGreaterThanOp, LLVM::ICmpPredicate::sgt>,
2494 IComparePattern<spirv::SGreaterThanEqualOp, LLVM::ICmpPredicate::sge>,
2495 IComparePattern<spirv::SLessThanEqualOp, LLVM::ICmpPredicate::sle>,
2496 IComparePattern<spirv::SLessThanOp, LLVM::ICmpPredicate::slt>,
2497 IComparePattern<spirv::UGreaterThanOp, LLVM::ICmpPredicate::ugt>,
2498 IComparePattern<spirv::UGreaterThanEqualOp, LLVM::ICmpPredicate::uge>,
2499 IComparePattern<spirv::ULessThanEqualOp, LLVM::ICmpPredicate::ule>,
2500 IComparePattern<spirv::ULessThanOp, LLVM::ICmpPredicate::ult>,
2501
2502 // Constant op
2503 ConstantScalarAndVectorPattern,
2504
2505 // Control Flow ops
2506 BranchConversionPattern, BranchConditionalConversionPattern,
2507 FunctionCallPattern, LoopPattern, SelectionPattern,
2508 ErasePattern<spirv::MergeOp>,
2509
2510 // Entry points and execution mode are handled separately.
2511 ErasePattern<spirv::EntryPointOp>, ExecutionModePattern,
2512
2513 // GLSL extended instruction set ops
2514 DirectConversionPattern<spirv::GLCeilOp, LLVM::FCeilOp>,
2515 DirectConversionPattern<spirv::GLCosOp, LLVM::CosOp>,
2516 DirectConversionPattern<spirv::GLExpOp, LLVM::ExpOp>,
2517 DirectConversionPattern<spirv::GLExp2Op, LLVM::Exp2Op>,
2518 DirectConversionPattern<spirv::GLFAbsOp, LLVM::FAbsOp>,
2519 DirectConversionPattern<spirv::GLFloorOp, LLVM::FFloorOp>,
2520 DirectConversionPattern<spirv::GLFmaOp, LLVM::FMAOp>,
2521 ClampPattern<spirv::GLFClampOp, LLVM::MinNumOp, LLVM::MaxNumOp>,
2522 ClampPattern<spirv::GLSClampOp, LLVM::SMinOp, LLVM::SMaxOp>,
2523 ClampPattern<spirv::GLUClampOp, LLVM::UMinOp, LLVM::UMaxOp>,
2524 DirectConversionPattern<spirv::GLFMaxOp, LLVM::MaxNumOp>,
2525 DirectConversionPattern<spirv::GLFMinOp, LLVM::MinNumOp>,
2526 DirectConversionPattern<spirv::GLNMaxOp, LLVM::MaxNumOp>,
2527 DirectConversionPattern<spirv::GLNMinOp, LLVM::MinNumOp>,
2528 DirectConversionPattern<spirv::GLLdexpOp, LLVM::LoadExpOp>,
2529 DirectConversionPattern<spirv::GLLogOp, LLVM::LogOp>,
2530 DirectConversionPattern<spirv::GLLog2Op, LLVM::Log2Op>,
2531 DirectConversionPattern<spirv::GLPowOp, LLVM::PowOp>,
2532 DirectConversionPattern<spirv::GLRoundOp, LLVM::RoundOp>,
2533 DirectConversionPattern<spirv::GLRoundEvenOp, LLVM::RoundEvenOp>,
2534 DirectConversionPattern<spirv::GLSinOp, LLVM::SinOp>,
2535 DirectConversionPattern<spirv::GLSinhOp, LLVM::SinhOp>,
2536 DirectConversionPattern<spirv::GLCoshOp, LLVM::CoshOp>,
2537 DirectConversionPattern<spirv::GLSMaxOp, LLVM::SMaxOp>,
2538 DirectConversionPattern<spirv::GLSMinOp, LLVM::SMinOp>,
2539 DirectConversionPattern<spirv::GLSqrtOp, LLVM::SqrtOp>,
2540 DirectConversionPattern<spirv::GLUMaxOp, LLVM::UMaxOp>,
2541 DirectConversionPattern<spirv::GLUMinOp, LLVM::UMinOp>,
2542 DirectConversionPattern<spirv::GLTruncOp, LLVM::FTruncOp>,
2543 DirectConversionPattern<spirv::GLAsinOp, LLVM::ASinOp>,
2544 DirectConversionPattern<spirv::GLAcosOp, LLVM::ACosOp>,
2545 DirectConversionPattern<spirv::GLAtanOp, LLVM::ATanOp>,
2546 DirectConversionPattern<spirv::GLAtan2Op, LLVM::ATan2Op>,
2547 DirectConversionPattern<spirv::GLTanOp, LLVM::TanOp>,
2548 DirectConversionPattern<spirv::GLTanhOp, LLVM::TanhOp>,
2549 InverseSqrtPattern<spirv::GLInverseSqrtOp>, SAbsPattern<spirv::GLSAbsOp>,
2550 FractPattern, SignPattern<spirv::GLFSignOp, /*isFloat=*/true>,
2551 SignPattern<spirv::GLSSignOp, /*isFloat=*/false>, GLFMixPattern,
2552
2553 // OpenCL extended instruction set ops
2554 DirectConversionPattern<spirv::CLCeilOp, LLVM::FCeilOp>,
2555 DirectConversionPattern<spirv::CLCosOp, LLVM::CosOp>,
2556 DirectConversionPattern<spirv::CLExpOp, LLVM::ExpOp>,
2557 DirectConversionPattern<spirv::CLExp2Op, LLVM::Exp2Op>,
2558 DirectConversionPattern<spirv::CLExp10Op, LLVM::Exp10Op>,
2559 DirectConversionPattern<spirv::CLFAbsOp, LLVM::FAbsOp>,
2560 DirectConversionPattern<spirv::CLFloorOp, LLVM::FFloorOp>,
2561 DirectConversionPattern<spirv::CLFmaOp, LLVM::FMAOp>,
2562 DirectConversionPattern<spirv::CLFMaxOp, LLVM::MaxNumOp>,
2563 DirectConversionPattern<spirv::CLFMinOp, LLVM::MinNumOp>,
2564 DirectConversionPattern<spirv::CLLdexpOp, LLVM::LoadExpOp>,
2565 DirectConversionPattern<spirv::CLLogOp, LLVM::LogOp>,
2566 DirectConversionPattern<spirv::CLLog2Op, LLVM::Log2Op>,
2567 DirectConversionPattern<spirv::CLLog10Op, LLVM::Log10Op>,
2568 DirectConversionPattern<spirv::CLPowOp, LLVM::PowOp>,
2569 DirectConversionPattern<spirv::CLRintOp, LLVM::RintOp>,
2570 DirectConversionPattern<spirv::CLRoundOp, LLVM::RoundOp>,
2571 DirectConversionPattern<spirv::CLSinOp, LLVM::SinOp>,
2572 DirectConversionPattern<spirv::CLSinhOp, LLVM::SinhOp>,
2573 DirectConversionPattern<spirv::CLCoshOp, LLVM::CoshOp>,
2574 DirectConversionPattern<spirv::CLTanOp, LLVM::TanOp>,
2575 DirectConversionPattern<spirv::CLTanhOp, LLVM::TanhOp>,
2576 DirectConversionPattern<spirv::CLAsinOp, LLVM::ASinOp>,
2577 DirectConversionPattern<spirv::CLAcosOp, LLVM::ACosOp>,
2578 DirectConversionPattern<spirv::CLAtanOp, LLVM::ATanOp>,
2579 DirectConversionPattern<spirv::CLAtan2Op, LLVM::ATan2Op>,
2580 DirectConversionPattern<spirv::CLSqrtOp, LLVM::SqrtOp>,
2581 DirectConversionPattern<spirv::CLTruncOp, LLVM::FTruncOp>,
2582 DirectConversionPattern<spirv::CLCopysignOp, LLVM::CopySignOp>,
2583 DirectConversionPattern<spirv::CLFmodOp, LLVM::FRemOp>,
2584 DirectConversionPattern<spirv::CLSMaxOp, LLVM::SMaxOp>,
2585 DirectConversionPattern<spirv::CLSMinOp, LLVM::SMinOp>,
2586 DirectConversionPattern<spirv::CLUMaxOp, LLVM::UMaxOp>,
2587 DirectConversionPattern<spirv::CLUMinOp, LLVM::UMinOp>, CLMixPattern,
2588 CLSincosPattern, InverseSqrtPattern<spirv::CLRsqrtOp>,
2589 SAbsPattern<spirv::CLSAbsOp>,
2590 ClampPattern<spirv::CLFClampOp, LLVM::MinNumOp, LLVM::MaxNumOp>,
2591 ClampPattern<spirv::CLSClampOp, LLVM::SMinOp, LLVM::SMaxOp>,
2592 ClampPattern<spirv::CLUClampOp, LLVM::UMinOp, LLVM::UMaxOp>,
2593 CountZerosPattern<spirv::CLClzOp, LLVM::CountLeadingZerosOp>,
2594 CountZerosPattern<spirv::CLCtzOp, LLVM::CountTrailingZerosOp>,
2595
2596 // Logical ops
2597 DirectConversionPattern<spirv::LogicalAndOp, LLVM::AndOp>,
2598 DirectConversionPattern<spirv::LogicalOrOp, LLVM::OrOp>,
2599 IComparePattern<spirv::LogicalEqualOp, LLVM::ICmpPredicate::eq>,
2600 IComparePattern<spirv::LogicalNotEqualOp, LLVM::ICmpPredicate::ne>,
2601 NotPattern<spirv::LogicalNotOp>,
2602 FPClassPattern<spirv::IsInfOp, llvm::fcInf>,
2603 FPClassPattern<spirv::IsNanOp, llvm::fcNan>,
2604 FPClassPattern<spirv::IsFiniteOp, llvm::fcFinite>,
2605 FPClassPattern<spirv::IsNormalOp, llvm::fcNormal>,
2606
2607 // Memory ops
2608 AccessChainPattern<spirv::AccessChainOp, /*InBounds=*/false>,
2609 AccessChainPattern<spirv::InBoundsAccessChainOp, /*InBounds=*/true>,
2610 PtrAccessChainPattern<spirv::PtrAccessChainOp, /*InBounds=*/false>,
2611 PtrAccessChainPattern<spirv::InBoundsPtrAccessChainOp, /*InBounds=*/true>,
2612 AddressOfPattern, CopyMemoryPattern, LoadStorePattern<spirv::LoadOp>,
2613 LoadStorePattern<spirv::StoreOp>, VariablePattern,
2614
2615 // Miscellaneous ops
2616 CompositeExtractPattern, CompositeInsertPattern,
2617 DirectConversionPattern<spirv::SelectOp, LLVM::SelectOp>,
2618 DirectConversionPattern<spirv::UndefOp, LLVM::UndefOp>,
2619 VectorShufflePattern,
2620
2621 // Shift ops
2622 ShiftPattern<spirv::ShiftRightArithmeticOp, LLVM::AShrOp>,
2623 ShiftPattern<spirv::ShiftRightLogicalOp, LLVM::LShrOp>,
2624 ShiftPattern<spirv::ShiftLeftLogicalOp, LLVM::ShlOp>,
2625
2626 // Return ops
2627 ReturnPattern, ReturnValuePattern,
2628
2629 // Unreachable op
2630 UnreachablePattern,
2631
2632 // Barrier ops
2633 ControlBarrierPattern<spirv::ControlBarrierOp>,
2634 ControlBarrierPattern<spirv::INTELControlBarrierArriveOp>,
2635 ControlBarrierPattern<spirv::INTELControlBarrierWaitOp>,
2636
2637 // Group reduction operations
2638 GroupReducePattern<spirv::GroupIAddOp>,
2639 GroupReducePattern<spirv::GroupFAddOp>,
2640 GroupReducePattern<spirv::GroupFMinOp>,
2641 GroupReducePattern<spirv::GroupUMinOp>,
2642 GroupReducePattern<spirv::GroupSMinOp, /*Signed=*/true>,
2643 GroupReducePattern<spirv::GroupFMaxOp>,
2644 GroupReducePattern<spirv::GroupUMaxOp>,
2645 GroupReducePattern<spirv::GroupSMaxOp, /*Signed=*/true>,
2646 GroupReducePattern<spirv::GroupNonUniformIAddOp, /*Signed=*/false,
2647 /*NonUniform=*/true>,
2648 GroupReducePattern<spirv::GroupNonUniformFAddOp, /*Signed=*/false,
2649 /*NonUniform=*/true>,
2650 GroupReducePattern<spirv::GroupNonUniformIMulOp, /*Signed=*/false,
2651 /*NonUniform=*/true>,
2652 GroupReducePattern<spirv::GroupNonUniformFMulOp, /*Signed=*/false,
2653 /*NonUniform=*/true>,
2654 GroupReducePattern<spirv::GroupNonUniformSMinOp, /*Signed=*/true,
2655 /*NonUniform=*/true>,
2656 GroupReducePattern<spirv::GroupNonUniformUMinOp, /*Signed=*/false,
2657 /*NonUniform=*/true>,
2658 GroupReducePattern<spirv::GroupNonUniformFMinOp, /*Signed=*/false,
2659 /*NonUniform=*/true>,
2660 GroupReducePattern<spirv::GroupNonUniformSMaxOp, /*Signed=*/true,
2661 /*NonUniform=*/true>,
2662 GroupReducePattern<spirv::GroupNonUniformUMaxOp, /*Signed=*/false,
2663 /*NonUniform=*/true>,
2664 GroupReducePattern<spirv::GroupNonUniformFMaxOp, /*Signed=*/false,
2665 /*NonUniform=*/true>,
2666 GroupReducePattern<spirv::GroupNonUniformBitwiseAndOp, /*Signed=*/false,
2667 /*NonUniform=*/true>,
2668 GroupReducePattern<spirv::GroupNonUniformBitwiseOrOp, /*Signed=*/false,
2669 /*NonUniform=*/true>,
2670 GroupReducePattern<spirv::GroupNonUniformBitwiseXorOp, /*Signed=*/false,
2671 /*NonUniform=*/true>,
2672 GroupReducePattern<spirv::GroupNonUniformLogicalAndOp, /*Signed=*/false,
2673 /*NonUniform=*/true>,
2674 GroupReducePattern<spirv::GroupNonUniformLogicalOrOp, /*Signed=*/false,
2675 /*NonUniform=*/true>,
2676 GroupReducePattern<spirv::GroupNonUniformLogicalXorOp, /*Signed=*/false,
2677 /*NonUniform=*/true>>(patterns.getContext(),
2678 typeConverter);
2679
2680 patterns.add<GlobalVariablePattern>(clientAPI, patterns.getContext(),
2681 typeConverter);
2682 // pi / 180
2683 patterns.add<ScalePattern<spirv::GLRadiansOp>>(
2684 0.017453292519943295, patterns.getContext(), typeConverter);
2685 // 180 / pi
2686 patterns.add<ScalePattern<spirv::GLDegreesOp>>(
2687 57.29577951308232, patterns.getContext(), typeConverter);
2688}
2689
2691 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) {
2692 patterns.add<FuncConversionPattern>(patterns.getContext(), typeConverter);
2693}
2694
2696 const LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) {
2697 patterns.add<ModuleConversionPattern>(patterns.getContext(), typeConverter);
2698}
2699
2700//===----------------------------------------------------------------------===//
2701// Pre-conversion hooks
2702//===----------------------------------------------------------------------===//
2703
2704/// Hook for descriptor set and binding number encoding.
2705void mlir::encodeBindAttribute(ModuleOp module) {
2706 auto spvModules = module.getOps<spirv::ModuleOp>();
2707 for (auto spvModule : spvModules) {
2708 spvModule.walk([&](spirv::GlobalVariableOp op) {
2709 IntegerAttr descriptorSet = op.getDescriptorSetAttr();
2710 IntegerAttr binding = op.getBindingAttr();
2711 // For every global variable in the module, get the ones with descriptor
2712 // set and binding numbers.
2713 if (descriptorSet && binding) {
2714 // Encode these numbers into the variable's symbolic name. If the
2715 // SPIR-V module has a name, add it at the beginning.
2716 auto moduleAndName =
2717 spvModule.getName().has_value()
2718 ? spvModule.getName()->str() + "_" + op.getSymName().str()
2719 : op.getSymName().str();
2720 std::string name =
2721 llvm::formatv("{0}_descriptor_set{1}_binding{2}", moduleAndName,
2722 std::to_string(descriptorSet.getInt()),
2723 std::to_string(binding.getInt()));
2724 auto nameAttr = StringAttr::get(op->getContext(), name);
2725
2726 // Replace all symbol uses and set the new symbol name. Finally, remove
2727 // descriptor set and binding attributes.
2728 if (failed(SymbolTable::replaceAllSymbolUses(op, nameAttr, spvModule)))
2729 op.emitError("unable to replace all symbol uses for ") << name;
2730 SymbolTable::setSymbolName(op, nameAttr);
2731 op.removeDescriptorSetAttr();
2732 op.removeBindingAttr();
2733 }
2734 });
2735 }
2736}
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 Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
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 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 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 Value createIntegerConstant(Location loc, Type srcType, Type dstType, PatternRewriter &rewriter, IntegerAttr scalarAttr)
Creates llvm.mlir.constant with a scalar or vector integer value, broadcasting scalarAttr across the ...
static std::optional< Type > convertArrayType(spirv::ArrayType type, TypeConverter &converter)
Converts SPIR-V array type to LLVM array.
#define mul(a, b)
#define div(a, b)
#define rem(a, b)
OpListType::iterator iterator
Definition Block.h:165
OpListType & getOperations()
Definition Block.h:162
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:112
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
IntegerType getI32Type()
Definition Builders.cpp:71
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
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:166
This class helps build Operations.
Definition Builders.h:210
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:740
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:405
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:392
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
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:853
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.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
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.
Structure used by default as a "marker" when no "Properties" are set on an Operation.