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