MLIR 24.0.0git
MathToSPIRV.cpp
Go to the documentation of this file.
1//===- MathToSPIRV.cpp - Math to SPIR-V Patterns --------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements patterns to convert Math dialect to SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Matchers.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/TypeSwitch.h"
23#include "llvm/Support/FormatVariadic.h"
24
25#define DEBUG_TYPE "math-to-spirv-pattern"
26
27using namespace mlir;
28
29//===----------------------------------------------------------------------===//
30// Utility functions
31//===----------------------------------------------------------------------===//
32
33/// Creates a 32-bit scalar/vector integer constant. Returns nullptr if the
34/// given type is not a 32-bit scalar/vector type.
36 OpBuilder &builder, Location loc) {
37 if (auto vectorType = dyn_cast<VectorType>(type)) {
38 if (!vectorType.getElementType().isInteger(32))
39 return nullptr;
40 SmallVector<int> values(vectorType.getNumElements(), value);
41 return spirv::ConstantOp::create(builder, loc, type,
42 builder.getI32VectorAttr(values));
43 }
44 if (type.isInteger(32))
45 return spirv::ConstantOp::create(builder, loc, type,
46 builder.getI32IntegerAttr(value));
47
48 return nullptr;
49}
50
51/// Check if the type is supported by math-to-spirv conversion. We expect to
52/// only see scalars and vectors at this point, with higher-level types already
53/// lowered.
54static bool isSupportedSourceType(Type originalType) {
55 if (originalType.isIntOrIndexOrFloat())
56 return true;
57
58 if (auto vecTy = dyn_cast<VectorType>(originalType)) {
59 if (!vecTy.getElementType().isIntOrIndexOrFloat())
60 return false;
61 if (vecTy.isScalable())
62 return false;
63 if (vecTy.getRank() > 1)
64 return false;
65
66 return true;
67 }
68
69 return false;
70}
71
72/// Check if all `sourceOp` types are supported by math-to-spirv conversion.
73/// Notify of a match failure othwerise and return a `failure` result.
74/// This is intended to simplify type checks in `OpConversionPattern`s.
75static LogicalResult checkSourceOpTypes(ConversionPatternRewriter &rewriter,
76 Operation *sourceOp) {
77 auto allTypes = llvm::to_vector(sourceOp->getOperandTypes());
78 llvm::append_range(allTypes, sourceOp->getResultTypes());
79
80 for (Type ty : allTypes) {
81 if (!isSupportedSourceType(ty)) {
82 return rewriter.notifyMatchFailure(
83 sourceOp,
84 llvm::formatv(
85 "unsupported source type for Math to SPIR-V conversion: {0}",
86 ty));
87 }
88 }
89
90 return success();
91}
92
93//===----------------------------------------------------------------------===//
94// Operation conversion
95//===----------------------------------------------------------------------===//
96
97// Note that DRR cannot be used for the patterns in this file: we may need to
98// convert type along the way, which requires ConversionPattern. DRR generates
99// normal RewritePattern.
100
101namespace {
102/// Converts elementwise unary, binary, and ternary standard operations to
103/// SPIR-V operations. Checks that source `Op` types are supported.
104template <typename Op, typename SPIRVOp>
105struct CheckedElementwiseOpPattern final
106 : public spirv::ElementwiseOpPattern<Op, SPIRVOp> {
107 using BasePattern = typename spirv::ElementwiseOpPattern<Op, SPIRVOp>;
108 using BasePattern::BasePattern;
109
110 LogicalResult
111 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
112 ConversionPatternRewriter &rewriter) const override {
113 if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
114 return res;
115
116 return BasePattern::matchAndRewrite(op, adaptor, rewriter);
117 }
118};
119
120/// Converts math.copysign to SPIR-V ops.
121struct CopySignPattern final : public OpConversionPattern<math::CopySignOp> {
122 using Base::Base;
123
124 LogicalResult
125 matchAndRewrite(math::CopySignOp copySignOp, OpAdaptor adaptor,
126 ConversionPatternRewriter &rewriter) const override {
127 if (LogicalResult res = checkSourceOpTypes(rewriter, copySignOp);
128 failed(res))
129 return res;
130
131 // Defer to the CL copysign op on Kernel targets.
132 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
133 if (typeConverter.getTargetEnv().allows(spirv::Capability::Kernel))
134 return rewriter.notifyMatchFailure(copySignOp,
135 "Kernel target has native CL op");
136
137 Type type = getTypeConverter()->convertType(copySignOp.getType());
138 if (!type)
139 return failure();
140
141 FloatType floatType;
142 if (auto scalarType = dyn_cast<FloatType>(copySignOp.getType())) {
143 floatType = scalarType;
144 } else if (auto vectorType = dyn_cast<VectorType>(copySignOp.getType())) {
145 floatType = cast<FloatType>(vectorType.getElementType());
146 } else {
147 return failure();
148 }
149
150 Location loc = copySignOp.getLoc();
151 int bitwidth = floatType.getWidth();
152 Type intType = rewriter.getIntegerType(bitwidth);
153 uint64_t intValue = uint64_t(1) << (bitwidth - 1);
154
155 Value signMask = spirv::ConstantOp::create(
156 rewriter, loc, intType, rewriter.getIntegerAttr(intType, intValue));
157 Value valueMask = spirv::ConstantOp::create(
158 rewriter, loc, intType,
159 rewriter.getIntegerAttr(intType, intValue - 1u));
160
161 if (auto vectorType = dyn_cast<VectorType>(type)) {
162 assert(vectorType.getRank() == 1);
163 int count = vectorType.getNumElements();
164 intType = VectorType::get(count, intType);
165
166 Repeated<Value> signSplat(count, signMask);
167 signMask = spirv::CompositeConstructOp::create(rewriter, loc, intType,
168 signSplat);
169
170 Repeated<Value> valueSplat(count, valueMask);
171 valueMask = spirv::CompositeConstructOp::create(rewriter, loc, intType,
172 valueSplat);
173 }
174
175 Value lhsCast =
176 spirv::BitcastOp::create(rewriter, loc, intType, adaptor.getLhs());
177 Value rhsCast =
178 spirv::BitcastOp::create(rewriter, loc, intType, adaptor.getRhs());
179
180 Value value = spirv::BitwiseAndOp::create(rewriter, loc, intType,
181 ValueRange{lhsCast, valueMask});
182 Value sign = spirv::BitwiseAndOp::create(rewriter, loc, intType,
183 ValueRange{rhsCast, signMask});
184
185 Value result = spirv::BitwiseOrOp::create(rewriter, loc, intType,
186 ValueRange{value, sign});
187 rewriter.replaceOpWithNewOp<spirv::BitcastOp>(copySignOp, type, result);
188 return success();
189 }
190};
191
192/// Converts math.ctlz to SPIR-V ops.
193///
194/// OpenCL targets lower math.ctlz directly to OpenCL.std clz via the generic
195/// elementwise pattern. This pattern handles the shader fallback.
196///
197/// SPIR-V does not have a direct operations for counting leading zeros for
198/// glsl. If Shader capability is supported, we can leverage GL FindUMsb to
199/// calculate it.
200struct CountLeadingZerosPattern final
201 : public OpConversionPattern<math::CountLeadingZerosOp> {
202 using Base::Base;
203
204 LogicalResult
205 matchAndRewrite(math::CountLeadingZerosOp countOp, OpAdaptor adaptor,
206 ConversionPatternRewriter &rewriter) const override {
207 if (LogicalResult res = checkSourceOpTypes(rewriter, countOp); failed(res))
208 return res;
209
210 Type type = getTypeConverter()->convertType(countOp.getType());
211 if (!type)
212 return failure();
213
214 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
215 if (!typeConverter.getTargetEnv().allows(spirv::Capability::Shader))
216 return rewriter.notifyMatchFailure(countOp, "requires Shader capability");
217
218 // The GL FindUMsb fallback only supports 32-bit integer types for now.
219 unsigned bitwidth = 0;
220 if (isa<IntegerType>(type))
221 bitwidth = type.getIntOrFloatBitWidth();
222 if (auto vectorType = dyn_cast<VectorType>(type))
223 bitwidth = vectorType.getElementTypeBitWidth();
224 if (bitwidth != 32)
225 return failure();
226
227 Location loc = countOp.getLoc();
228 Value input = adaptor.getOperand();
229 Value val1 = getScalarOrVectorI32Constant(type, 1, rewriter, loc);
230 Value val31 = getScalarOrVectorI32Constant(type, 31, rewriter, loc);
231 Value val32 = getScalarOrVectorI32Constant(type, 32, rewriter, loc);
232
233 Value msb = spirv::GLFindUMsbOp::create(rewriter, loc, input);
234 // We need to subtract from 31 given that the index returned by GLSL
235 // FindUMsb is counted from the least significant bit. Theoretically this
236 // also gives the correct result even if the integer has all zero bits, in
237 // which case GL FindUMsb would return -1.
238 Value subMsb = spirv::ISubOp::create(rewriter, loc, val31, msb);
239 // However, certain Vulkan implementations have driver bugs for the corner
240 // case where the input is zero. And.. it can be smart to optimize a select
241 // only involving the corner case. So separately compute the result when the
242 // input is either zero or one.
243 Value subInput = spirv::ISubOp::create(rewriter, loc, val32, input);
244 Value cmp = spirv::ULessThanEqualOp::create(rewriter, loc, input, val1);
245 rewriter.replaceOpWithNewOp<spirv::SelectOp>(countOp, cmp, subInput,
246 subMsb);
247 return success();
248 }
249};
250
251/// Converts math.cttz to GL FindILsb. GL FindILsb returns -1 for a zero
252/// input while math.cttz must return the bitwidth, so the zero case is
253/// patched up with a select.
254///
255/// OpenCL targets use OpenCL.std ctz. This pattern handles the shader fallback.
256struct CountTrailingZerosPattern final
257 : public OpConversionPattern<math::CountTrailingZerosOp> {
258 using Base::Base;
259
260 LogicalResult
261 matchAndRewrite(math::CountTrailingZerosOp countOp, OpAdaptor adaptor,
262 ConversionPatternRewriter &rewriter) const override {
263 if (LogicalResult res = checkSourceOpTypes(rewriter, countOp); failed(res))
264 return res;
265
266 Type type = getTypeConverter()->convertType(countOp.getType());
267 if (!type)
268 return failure();
269
270 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
271 if (!typeConverter.getTargetEnv().allows(spirv::Capability::Shader))
272 return rewriter.notifyMatchFailure(countOp, "requires Shader capability");
273
274 unsigned bitwidth = 0;
275 if (isa<IntegerType>(type))
276 bitwidth = type.getIntOrFloatBitWidth();
277 else if (auto vectorType = dyn_cast<VectorType>(type))
278 bitwidth = vectorType.getElementTypeBitWidth();
279
280 Location loc = countOp.getLoc();
281 Value input = adaptor.getOperand();
282 Value val0 = spirv::ConstantOp::getZero(type, loc, rewriter);
283 Type elemType = getElementTypeOrSelf(type);
284 Attribute bwAttr = IntegerAttr::get(elemType, bitwidth);
285 if (auto vecType = dyn_cast<VectorType>(type))
286 bwAttr = SplatElementsAttr::get(vecType, bwAttr);
287 Value valBitwidth = spirv::ConstantOp::create(rewriter, loc, type, bwAttr);
288
289 Value lsb = spirv::GLFindILsbOp::create(rewriter, loc, input);
290 Value isZero = spirv::IEqualOp::create(rewriter, loc, input, val0);
291 rewriter.replaceOpWithNewOp<spirv::SelectOp>(countOp, isZero, valBitwidth,
292 lsb);
293 return success();
294 }
295};
296
297/// Converts math.cttz to OpenCL.std ctz, preserving the source bit width when
298/// narrow integers are emulated using wider integers.
299struct CountTrailingZerosOpenCLPattern final
300 : public OpConversionPattern<math::CountTrailingZerosOp> {
301 using Base::Base;
302
303 LogicalResult
304 matchAndRewrite(math::CountTrailingZerosOp op, OpAdaptor adaptor,
305 ConversionPatternRewriter &rewriter) const override {
306 if (failed(checkSourceOpTypes(rewriter, op)))
307 return failure();
308
309 auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
310 if (!typeConverter.getTargetEnv().allows(spirv::Capability::Kernel))
311 return rewriter.notifyMatchFailure(op, "requires Kernel capability");
312 Type type = typeConverter.convertType(op.getType());
313 if (!type)
314 return failure();
315
316 Value input = adaptor.getOperand();
317 Type elementType = getElementTypeOrSelf(type);
318 if (elementType.isInteger(1)) {
319 // cttz(false) is true and cttz(true) is false.
320 rewriter.replaceOpWithNewOp<spirv::LogicalNotOp>(op, type, input);
321 return success();
322 }
323
324 Type sourceElementType = getElementTypeOrSelf(op.getType());
325 unsigned targetWidth = elementType.getIntOrFloatBitWidth();
326 // Index values use the bit width chosen by the type converter.
327 unsigned sourceWidth = sourceElementType.isIndex()
328 ? targetWidth
329 : sourceElementType.getIntOrFloatBitWidth();
330 if (sourceWidth < targetWidth) {
331 // Set the bit immediately above the original value. This bounds the
332 // count by sourceWidth, even when the emulated value has nonzero high
333 // bits, and gives the correct result for a zero source value.
334 Attribute sentinel = IntegerAttr::get(
335 elementType, APInt::getOneBitSet(targetWidth, sourceWidth));
336 if (auto vectorType = dyn_cast<VectorType>(type))
337 sentinel = SplatElementsAttr::get(vectorType, sentinel);
338 Value mask =
339 spirv::ConstantOp::create(rewriter, op.getLoc(), type, sentinel);
340 input = spirv::BitwiseOrOp::create(rewriter, op.getLoc(), input, mask);
341 }
342 rewriter.replaceOpWithNewOp<spirv::CLCtzOp>(op, type, input);
343 return success();
344 }
345};
346
347/// Converts math.expm1 to SPIR-V ops.
348///
349/// SPIR-V does not have a direct operations for exp(x)-1. Explicitly lower to
350/// these operations.
351template <typename ExpOp>
352struct ExpM1OpPattern final : public OpConversionPattern<math::ExpM1Op> {
353 using Base::Base;
354
355 LogicalResult
356 matchAndRewrite(math::ExpM1Op operation, OpAdaptor adaptor,
357 ConversionPatternRewriter &rewriter) const override {
358 assert(adaptor.getOperands().size() == 1);
359 if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
360 failed(res))
361 return res;
362
363 Location loc = operation.getLoc();
364 Type type = this->getTypeConverter()->convertType(operation.getType());
365 if (!type)
366 return failure();
367
368 Value exp = ExpOp::create(rewriter, loc, type, adaptor.getOperand());
369 auto one = spirv::ConstantOp::getOne(type, loc, rewriter);
370 rewriter.replaceOpWithNewOp<spirv::FSubOp>(operation, exp, one);
371 return success();
372 }
373};
374
375/// Converts math.log1p to SPIR-V ops.
376///
377/// SPIR-V does not have a direct operations for log(1+x). Explicitly lower to
378/// these operations.
379template <typename LogOp>
380struct Log1pOpPattern final : public OpConversionPattern<math::Log1pOp> {
381 using Base::Base;
382
383 LogicalResult
384 matchAndRewrite(math::Log1pOp operation, OpAdaptor adaptor,
385 ConversionPatternRewriter &rewriter) const override {
386 assert(adaptor.getOperands().size() == 1);
387 if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
388 failed(res))
389 return res;
390
391 Location loc = operation.getLoc();
392 Type type = this->getTypeConverter()->convertType(operation.getType());
393 if (!type)
394 return failure();
395
396 auto one = spirv::ConstantOp::getOne(type, operation.getLoc(), rewriter);
397 Value onePlus =
398 spirv::FAddOp::create(rewriter, loc, one, adaptor.getOperand());
399 rewriter.replaceOpWithNewOp<LogOp>(operation, type, onePlus);
400 return success();
401 }
402};
403
404/// Converts math.log10 to GLSL SPIR-V ops.
405///
406/// GLSL.std.450 has no Log10 instruction. Lower it as:
407/// log10(x) = log(x) * 1/log(10)
408struct Log10OpPattern final : public OpConversionPattern<math::Log10Op> {
409 using Base::Base;
410
411 static constexpr double log10Reciprocal =
412 0.4342944819032518276511289189166050822943970058036665661144537832;
413
414 LogicalResult
415 matchAndRewrite(math::Log10Op operation, OpAdaptor adaptor,
416 ConversionPatternRewriter &rewriter) const override {
417 assert(adaptor.getOperands().size() == 1);
418 if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
419 failed(res))
420 return res;
421
422 Location loc = operation.getLoc();
423 Type type = this->getTypeConverter()->convertType(operation.getType());
424 if (!type)
425 return rewriter.notifyMatchFailure(operation, "type conversion failed");
426
427 auto getConstantValue = [&](double value) {
428 if (auto floatType = dyn_cast<FloatType>(type)) {
429 return spirv::ConstantOp::create(
430 rewriter, loc, type, rewriter.getFloatAttr(floatType, value));
431 }
432 if (auto vectorType = dyn_cast<VectorType>(type)) {
433 Type elemType = vectorType.getElementType();
434
435 if (isa<FloatType>(elemType)) {
436 return spirv::ConstantOp::create(
437 rewriter, loc, type,
439 vectorType, FloatAttr::get(elemType, value).getValue()));
440 }
441 }
442 llvm_unreachable("unimplemented type for log10");
443 };
444
445 Value constantValue = getConstantValue(log10Reciprocal);
446 Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getOperand());
447 rewriter.replaceOpWithNewOp<spirv::FMulOp>(operation, type, log,
448 constantValue);
449 return success();
450 }
451};
452
453/// Converts math.powf to SPIRV-Ops.
454struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
455 using Base::Base;
456
457 LogicalResult
458 matchAndRewrite(math::PowFOp powfOp, OpAdaptor adaptor,
459 ConversionPatternRewriter &rewriter) const override {
460 if (LogicalResult res = checkSourceOpTypes(rewriter, powfOp); failed(res))
461 return res;
462
463 Type dstType = getTypeConverter()->convertType(powfOp.getType());
464 if (!dstType)
465 return failure();
466
467 Location loc = powfOp.getLoc();
468 Type operandType = adaptor.getRhs().getType();
469
470 // Parity-based lowering requires an integer-valued constant exponent.
471 // Otherwise fall back to exp(y*log(x)), which yields NaN for x<0 (matches
472 // C).
473 auto isOdd = [](const APFloat &v) {
474 APSInt i(/*BitWidth=*/64, /*isUnsigned=*/false);
475 bool ignored;
476 v.convertToInteger(i, APFloat::rmTowardZero, &ignored);
477 return i[0];
478 };
479
480 SmallVector<bool> oddMask;
481 Attribute rhsAttr;
482 if (matchPattern(adaptor.getRhs(), m_Constant(&rhsAttr))) {
483 TypeSwitch<Attribute>(rhsAttr)
484 .Case([&](FloatAttr a) {
485 if (a.getValue().isInteger())
486 oddMask.push_back(isOdd(a.getValue()));
487 })
488 .Case([&](SplatElementsAttr a) {
489 APFloat splat = a.getSplatValue<APFloat>();
490 if (splat.isInteger())
491 oddMask.push_back(isOdd(splat));
492 })
493 .Case([&](DenseElementsAttr a) {
494 SmallVector<bool> mask;
495 for (const APFloat &elt : a.getValues<APFloat>()) {
496 if (!elt.isInteger())
497 return;
498 mask.push_back(isOdd(elt));
499 }
500 oddMask = std::move(mask);
501 });
502 }
503
504 if (oddMask.empty()) {
505 Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getLhs());
506 Value mul = spirv::FMulOp::create(rewriter, loc, adaptor.getRhs(), log);
507 rewriter.replaceOpWithNewOp<spirv::GLExpOp>(powfOp, mul);
508 return success();
509 }
510
511 // GL.Pow is undefined for x < 0; take abs and conditionally negate the
512 // result for lanes whose exponent is odd.
513 Value abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getLhs());
514 Value pow = spirv::GLPowOp::create(rewriter, loc, abs, adaptor.getRhs());
515
516 // No odd-parity element: result has the same sign as |lhs|^rhs >= 0.
517 if (llvm::none_of(oddMask, [](bool b) { return b; })) {
518 rewriter.replaceOp(powfOp, pow);
519 return success();
520 }
521
522 Value zero = spirv::ConstantOp::getZero(operandType, loc, rewriter);
523 Value lessThan =
524 spirv::FOrdLessThanOp::create(rewriter, loc, adaptor.getLhs(), zero);
525 Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
526
527 Value shouldNegate;
528 if (llvm::all_equal(oddMask)) {
529 // Every lane has odd exponent: negate iff lhs < 0.
530 shouldNegate = lessThan;
531 } else {
532 // Mixed parity (non-splat dense vector): AND lhs<0 with a per-element
533 // constant odd-mask.
534 auto vecType = cast<VectorType>(operandType);
535 auto maskType = VectorType::get(vecType.getShape(), rewriter.getI1Type());
536 Value oddConst = spirv::ConstantOp::create(
537 rewriter, loc, maskType, DenseElementsAttr::get(maskType, oddMask));
538 shouldNegate =
539 spirv::LogicalAndOp::create(rewriter, loc, lessThan, oddConst);
540 }
541
542 rewriter.replaceOpWithNewOp<spirv::SelectOp>(powfOp, shouldNegate, negate,
543 pow);
544 return success();
545 }
546};
547
548/// Converts math.fpowi to spirv.CL.pown.
549struct PowIOpPattern final : public OpConversionPattern<math::FPowIOp> {
550 using Base::Base;
551
552 LogicalResult
553 matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
554 ConversionPatternRewriter &rewriter) const override {
555 if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
556 return res;
557
558 Type dstType = getTypeConverter()->convertType(op.getType());
559 if (!dstType)
560 return failure();
561
562 rewriter.replaceOpWithNewOp<spirv::CLPownOp>(op, dstType, adaptor.getLhs(),
563 adaptor.getRhs());
564 return success();
565 }
566};
567
568/// Converts math.fpowi to GLSL SPIR-V ops. GL has no integer-power op, so the
569/// exponent is converted to float and lowered through spirv.GL.Pow. As GL.Pow
570/// is undefined for a negative base, the base is made positive and the result
571/// is negated when the base is negative and the exponent is odd.
572struct PowIOpGLPattern final : public OpConversionPattern<math::FPowIOp> {
573 using Base::Base;
574
575 LogicalResult
576 matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
577 ConversionPatternRewriter &rewriter) const override {
578 if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
579 return res;
580
581 Type dstType = getTypeConverter()->convertType(op.getType());
582 if (!dstType)
583 return failure();
584
585 Location loc = op.getLoc();
586 Value base = adaptor.getLhs();
587 Value power = adaptor.getRhs();
588
589 Value expFloat =
590 spirv::ConvertSToFOp::create(rewriter, loc, dstType, power);
591 Value abs = spirv::GLFAbsOp::create(rewriter, loc, base);
592 Value pow = spirv::GLPowOp::create(rewriter, loc, abs, expFloat);
593
594 Value zeroF = spirv::ConstantOp::getZero(dstType, loc, rewriter);
595 Value lessThan = spirv::FOrdLessThanOp::create(rewriter, loc, base, zeroF);
596
597 Type powerType = power.getType();
598 Value oneI = spirv::ConstantOp::getOne(powerType, loc, rewriter);
599 Value lowBit = spirv::BitwiseAndOp::create(rewriter, loc, power, oneI);
600 Value isOdd = spirv::IEqualOp::create(rewriter, loc, lowBit, oneI);
601
602 Value shouldNegate =
603 spirv::LogicalAndOp::create(rewriter, loc, lessThan, isOdd);
604 Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
605 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, shouldNegate, negate, pow);
606 return success();
607 }
608};
609
610/// Converts math.sincos to SPIR-V ops.
611///
612/// SPIR-V has no fused sincos instruction, so emit separate sin and cos ops
613/// sharing the same operand.
614template <typename SinOp, typename CosOp>
615struct SincosOpPattern final : public OpConversionPattern<math::SincosOp> {
616 using Base::Base;
617
618 LogicalResult
619 matchAndRewrite(math::SincosOp operation, OpAdaptor adaptor,
620 ConversionPatternRewriter &rewriter) const override {
621 if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
622 failed(res))
623 return res;
624
625 Type type =
626 getTypeConverter()->convertType(operation.getOperand().getType());
627 if (!type)
628 return failure();
629
630 Location loc = operation.getLoc();
631 Value sin = SinOp::create(rewriter, loc, type, adaptor.getOperand());
632 Value cos = CosOp::create(rewriter, loc, type, adaptor.getOperand());
633 rewriter.replaceOp(operation, {sin, cos});
634 return success();
635 }
636};
637
638/// Converts math.round to GLSL SPIRV extended ops.
639struct RoundOpPattern final : public OpConversionPattern<math::RoundOp> {
640 using Base::Base;
641
642 LogicalResult
643 matchAndRewrite(math::RoundOp roundOp, OpAdaptor adaptor,
644 ConversionPatternRewriter &rewriter) const override {
645 if (LogicalResult res = checkSourceOpTypes(rewriter, roundOp); failed(res))
646 return res;
647
648 Location loc = roundOp.getLoc();
649 auto ty = getTypeConverter()->convertType(adaptor.getOperand().getType());
650 if (!ty) {
651 return rewriter.notifyMatchFailure(
652 roundOp->getLoc(),
653 llvm::formatv("failed to convert type {0} for SPIR-V",
654 roundOp.getType()));
655 }
656
657 Type ety = getElementTypeOrSelf(ty);
658
659 auto zero = spirv::ConstantOp::getZero(ty, loc, rewriter);
660 auto one = spirv::ConstantOp::getOne(ty, loc, rewriter);
661 Value half;
662 if (VectorType vty = dyn_cast<VectorType>(ty)) {
663 half = spirv::ConstantOp::create(
664 rewriter, loc, vty,
666 rewriter.getFloatAttr(ety, 0.5).getValue()));
667 } else {
668 half = spirv::ConstantOp::create(rewriter, loc, ty,
669 rewriter.getFloatAttr(ety, 0.5));
670 }
671
672 auto abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getOperand());
673 auto floor = spirv::GLFloorOp::create(rewriter, loc, abs);
674 auto sub = spirv::FSubOp::create(rewriter, loc, abs, floor);
675 auto greater =
676 spirv::FOrdGreaterThanEqualOp::create(rewriter, loc, sub, half);
677 auto select = spirv::SelectOp::create(rewriter, loc, greater, one, zero);
678 auto add = spirv::FAddOp::create(rewriter, loc, floor, select);
679 rewriter.replaceOpWithNewOp<math::CopySignOp>(roundOp, add,
680 adaptor.getOperand());
681 return success();
682 }
683};
684} // namespace
685
686//===----------------------------------------------------------------------===//
687// Pattern population
688//===----------------------------------------------------------------------===//
689
690namespace mlir {
692 RewritePatternSet &patterns) {
693 // Core patterns
694 patterns
695 .add<CopySignPattern,
696 CheckedElementwiseOpPattern<math::CtPopOp, spirv::BitCountOp>,
697 CheckedElementwiseOpPattern<math::IsInfOp, spirv::IsInfOp>,
698 CheckedElementwiseOpPattern<math::IsNaNOp, spirv::IsNanOp>,
699 CheckedElementwiseOpPattern<math::IsFiniteOp, spirv::IsFiniteOp>,
700 CheckedElementwiseOpPattern<math::IsNormalOp, spirv::IsNormalOp>>(
701 typeConverter, patterns.getContext());
702
703 // GLSL patterns
704 patterns
705 .add<CountLeadingZerosPattern, CountTrailingZerosPattern,
706 Log1pOpPattern<spirv::GLLogOp>, Log10OpPattern,
707 ExpM1OpPattern<spirv::GLExpOp>, PowFOpPattern, PowIOpGLPattern,
708 RoundOpPattern, SincosOpPattern<spirv::GLSinOp, spirv::GLCosOp>,
709 CheckedElementwiseOpPattern<math::AbsFOp, spirv::GLFAbsOp>,
710 CheckedElementwiseOpPattern<math::AbsIOp, spirv::GLSAbsOp>,
711 CheckedElementwiseOpPattern<math::AtanOp, spirv::GLAtanOp>,
712 CheckedElementwiseOpPattern<math::Atan2Op, spirv::GLAtan2Op>,
713 CheckedElementwiseOpPattern<math::CeilOp, spirv::GLCeilOp>,
714 CheckedElementwiseOpPattern<math::ClampFOp, spirv::GLFClampOp>,
715 CheckedElementwiseOpPattern<math::CosOp, spirv::GLCosOp>,
716 CheckedElementwiseOpPattern<math::ExpOp, spirv::GLExpOp>,
717 CheckedElementwiseOpPattern<math::Exp2Op, spirv::GLExp2Op>,
718 CheckedElementwiseOpPattern<math::FloorOp, spirv::GLFloorOp>,
719 CheckedElementwiseOpPattern<math::FmaOp, spirv::GLFmaOp>,
720 CheckedElementwiseOpPattern<math::LogOp, spirv::GLLogOp>,
721 CheckedElementwiseOpPattern<math::Log2Op, spirv::GLLog2Op>,
722 CheckedElementwiseOpPattern<math::RoundEvenOp, spirv::GLRoundEvenOp>,
723 CheckedElementwiseOpPattern<math::RsqrtOp, spirv::GLInverseSqrtOp>,
724 CheckedElementwiseOpPattern<math::SinOp, spirv::GLSinOp>,
725 CheckedElementwiseOpPattern<math::SqrtOp, spirv::GLSqrtOp>,
726 CheckedElementwiseOpPattern<math::TanhOp, spirv::GLTanhOp>,
727 CheckedElementwiseOpPattern<math::TanOp, spirv::GLTanOp>,
728 CheckedElementwiseOpPattern<math::TruncOp, spirv::GLTruncOp>,
729 CheckedElementwiseOpPattern<math::AsinOp, spirv::GLAsinOp>,
730 CheckedElementwiseOpPattern<math::AcosOp, spirv::GLAcosOp>,
731 CheckedElementwiseOpPattern<math::SinhOp, spirv::GLSinhOp>,
732 CheckedElementwiseOpPattern<math::CoshOp, spirv::GLCoshOp>,
733 CheckedElementwiseOpPattern<math::AsinhOp, spirv::GLAsinhOp>,
734 CheckedElementwiseOpPattern<math::AcoshOp, spirv::GLAcoshOp>,
735 CheckedElementwiseOpPattern<math::AtanhOp, spirv::GLAtanhOp>>(
736 typeConverter, patterns.getContext());
737
738 // OpenCL patterns
739 patterns.add<
740 Log1pOpPattern<spirv::CLLogOp>, ExpM1OpPattern<spirv::CLExpOp>,
741 SincosOpPattern<spirv::CLSinOp, spirv::CLCosOp>,
742 CheckedElementwiseOpPattern<math::AbsFOp, spirv::CLFAbsOp>,
743 CheckedElementwiseOpPattern<math::AbsIOp, spirv::CLSAbsOp>,
744 CheckedElementwiseOpPattern<math::CountLeadingZerosOp, spirv::CLClzOp>,
745 CountTrailingZerosOpenCLPattern,
746 CheckedElementwiseOpPattern<math::AtanOp, spirv::CLAtanOp>,
747 CheckedElementwiseOpPattern<math::Atan2Op, spirv::CLAtan2Op>,
748 CheckedElementwiseOpPattern<math::CbrtOp, spirv::CLCbrtOp>,
749 CheckedElementwiseOpPattern<math::CeilOp, spirv::CLCeilOp>,
750 CheckedElementwiseOpPattern<math::CopySignOp, spirv::CLCopysignOp>,
751 CheckedElementwiseOpPattern<math::CosOp, spirv::CLCosOp>,
752 CheckedElementwiseOpPattern<math::ErfOp, spirv::CLErfOp>,
753 CheckedElementwiseOpPattern<math::ErfcOp, spirv::CLErfcOp>,
754 CheckedElementwiseOpPattern<math::ExpOp, spirv::CLExpOp>,
755 CheckedElementwiseOpPattern<math::Exp2Op, spirv::CLExp2Op>,
756 CheckedElementwiseOpPattern<math::FloorOp, spirv::CLFloorOp>,
757 CheckedElementwiseOpPattern<math::FmaOp, spirv::CLFmaOp>,
758 CheckedElementwiseOpPattern<math::LogOp, spirv::CLLogOp>,
759 CheckedElementwiseOpPattern<math::Log2Op, spirv::CLLog2Op>,
760 CheckedElementwiseOpPattern<math::Log10Op, spirv::CLLog10Op>,
761 CheckedElementwiseOpPattern<math::PowFOp, spirv::CLPowOp>, PowIOpPattern,
762 CheckedElementwiseOpPattern<math::RoundEvenOp, spirv::CLRintOp>,
763 CheckedElementwiseOpPattern<math::RoundOp, spirv::CLRoundOp>,
764 CheckedElementwiseOpPattern<math::RsqrtOp, spirv::CLRsqrtOp>,
765 CheckedElementwiseOpPattern<math::SinOp, spirv::CLSinOp>,
766 CheckedElementwiseOpPattern<math::SqrtOp, spirv::CLSqrtOp>,
767 CheckedElementwiseOpPattern<math::TanhOp, spirv::CLTanhOp>,
768 CheckedElementwiseOpPattern<math::TanOp, spirv::CLTanOp>,
769 CheckedElementwiseOpPattern<math::TruncOp, spirv::CLTruncOp>,
770 CheckedElementwiseOpPattern<math::AsinOp, spirv::CLAsinOp>,
771 CheckedElementwiseOpPattern<math::AcosOp, spirv::CLAcosOp>,
772 CheckedElementwiseOpPattern<math::SinhOp, spirv::CLSinhOp>,
773 CheckedElementwiseOpPattern<math::CoshOp, spirv::CLCoshOp>,
774 CheckedElementwiseOpPattern<math::AsinhOp, spirv::CLAsinhOp>,
775 CheckedElementwiseOpPattern<math::AcoshOp, spirv::CLAcoshOp>,
776 CheckedElementwiseOpPattern<math::AtanhOp, spirv::CLAtanhOp>>(
777 typeConverter, patterns.getContext());
778}
779} // namespace mlir
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static LogicalResult checkSourceOpTypes(ConversionPatternRewriter &rewriter, Operation *sourceOp)
Check if all sourceOp types are supported by math-to-spirv conversion.
static bool isSupportedSourceType(Type originalType)
Check if the type is supported by math-to-spirv conversion.
static Value getScalarOrVectorI32Constant(Type type, int value, OpBuilder &builder, Location loc)
Creates a 32-bit scalar/vector integer constant.
#define mul(a, b)
#define add(a, b)
IntegerAttr getI32IntegerAttr(int32_t value)
Definition Builders.cpp:208
DenseIntElementsAttr getI32VectorAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:130
auto getValues() const
Return the held element values as a range of the given type.
std::enable_if_t<!std::is_base_of< Attribute, T >::value||std::is_same< Attribute, T >::value, T > getSplatValue() const
Return the splat value for this attribute.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
static DenseFPElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseFPElementsAttr with the given arguments.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
operand_type_range getOperandTypes()
Definition Operation.h:422
result_type_range getResultTypes()
Definition Operation.h:453
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Type conversion from builtin types to SPIR-V types for shader interface.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIndex() const
Definition Types.cpp:56
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
Definition Types.cpp:122
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
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 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
DynamicAPInt floor(const Fraction &f)
Definition Fraction.h:77
Fraction abs(const Fraction &f)
Definition Fraction.h:107
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
void populateMathToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating Math ops to SPIR-V ops.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
Converts elementwise unary, binary and ternary standard operations to SPIR-V operations.
Definition Pattern.h:24