Index: mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td =================================================================== --- mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td +++ mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td @@ -60,6 +60,14 @@ def LLVM_RoundOp : LLVM_UnaryIntrinsicOp<"round">; def LLVM_SqrtOp : LLVM_UnaryIntrinsicOp<"sqrt">; def LLVM_PowOp : LLVM_BinarySameArgsIntrinsicOp<"pow">; +// TBD: +// Should LLVM::PowIOp be restricted to support only +// the cases supported by llvm.powi.* intrinsic (e.g. +// exponent must be scalar 'i32', +// see "POWI exponent does not match sizeof(int)" verification +// in LegalizeDAG.cpp)? +// Or, should LLVM::PowIOp translation to LLVM IR handle +// all the cases allowed here? def LLVM_PowIOp : LLVM_BinaryIntrinsicOp<"powi">; def LLVM_BitReverseOp : LLVM_UnaryIntrinsicOp<"bitreverse">; def LLVM_CountLeadingZerosOp : LLVM_CountZerosIntrinsicOp<"ctlz">; Index: mlir/lib/Conversion/MathToLLVM/MathToLLVM.cpp =================================================================== --- mlir/lib/Conversion/MathToLLVM/MathToLLVM.cpp +++ mlir/lib/Conversion/MathToLLVM/MathToLLVM.cpp @@ -248,6 +248,70 @@ } }; +// `fpwosi` is converted into llvm.powi intrinsic operation. +struct FPowIOpLowering : public ConvertOpToLLVMPattern { + using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; + + LogicalResult + matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto baseType = adaptor.getOperands()[0].getType(); + auto powerType = adaptor.getOperands()[1].getType(); + + if (!baseType || !LLVM::isCompatibleType(baseType) || !powerType || + !LLVM::isCompatibleType(powerType)) + return failure(); + + auto loc = op.getLoc(); + if (baseType.isa()) { + // TBD: while LLVM::PowIOp is defined quite loosely, + // its support in LLVM IR is very narrow. In particular, + // only scalar 'i32' exponent is supported. + // Should we convert to an inline implementation in cases + // that are not supported by llvm.powi intrinsic? + rewriter.replaceOpWithNewOp(op, baseType, + adaptor.getOperands()); + return success(); + } + + auto resultType = op.getResult().getType(); + auto vectorType = resultType.dyn_cast(); + if (!vectorType) + return failure(); + + auto elementType = vectorType.getElementType().dyn_cast(); + if (!elementType) + return failure(); + + // Convert to a sequence of scalar LLVM::PowIOp's. + return LLVM::detail::handleMultidimensionalVectors( + op.getOperation(), adaptor.getOperands(), *getTypeConverter(), + [&](Type llvm1DVectorTy, ValueRange operands) { + Value result = rewriter.create(loc, llvm1DVectorTy); + auto vecType = llvm1DVectorTy.cast(); + // This loop is not needed, if LLVM::PowIOp has normal elementwise + // semantics (which is not clear since it is defined as a MLIR + // equivalent of llvm.powi.* that applies a scalar exponent + // to a vector base). + for (unsigned idx = 0, numElements = vecType.getNumElements(); + idx < numElements; ++idx) { + Value pos = rewriter.create( + loc, rewriter.getI64Type(), rewriter.getI64IntegerAttr(idx)); + Value baseOp = + rewriter.create(loc, operands[0], pos); + Value powerOp = + rewriter.create(loc, operands[1], pos); + Value eltResult = rewriter.create( + loc, baseOp.getType(), ValueRange{baseOp, powerOp}); + result = rewriter.create( + loc, llvm1DVectorTy, result, eltResult, pos); + } + return result; + }, + rewriter); + } +}; + struct IPowIOpLowering : public ConvertOpToLLVMPattern { private: /// Create linkonce_odr function to implement the power function with @@ -555,6 +619,7 @@ Log2OpLowering, LogOpLowering, PowFOpLowering, + FPowIOpLowering, IPowIOpLowering, RoundOpLowering, RsqrtOpLowering, Index: mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir =================================================================== --- mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir +++ mlir/test/Conversion/MathToLLVM/math-to-llvm.mlir @@ -347,3 +347,52 @@ %0 = math.ipowi %arg0, %arg1 : vector<2x3xi64> func.return } + +// ----- + +// CHECK-LABEL: func @fpowi( +// CHECK-SAME: %[[ARG0:.+]]: f64, +// CHECK-SAME: %[[ARG1:.+]]: i64, +// CHECK-SAME: %[[ARG2:.+]]: f16, +// CHECK-SAME: %[[ARG3:.+]]: i32) +func.func @fpowi(%arg0: f64, %arg1: i64, %arg2: f16, %arg3: i32) { +// CHECK: "llvm.intr.powi"(%[[ARG0]], %[[ARG1]]) : (f64, i64) -> f64 + %0 = math.fpowi %arg0, %arg1 : (f64, i64) -> f64 +// CHECK: "llvm.intr.powi"(%[[ARG2]], %[[ARG3]]) : (f16, i32) -> f16 + %1 = math.fpowi %arg2, %arg3 : (f16, i32) -> f16 + func.return +} + +// ----- + +// CHECK-LABEL: func.func @fpowi_vec( +// CHECK-SAME: %[[VAL_0:.*]]: vector<2x3xf64>, +// CHECK-SAME: %[[VAL_1:.*]]: vector<2x3xi32>) { +func.func @fpowi_vec(%arg0: vector<2x3xf64>, %arg1: vector<2x3xi32>) { +// CHECK: %[[VAL_4:.*]] = llvm.mlir.undef : !llvm.array<2 x vector<3xf64>> +// CHECK: %[[VAL_5:.*]] = llvm.extractvalue %[[VAL_2:.*]][0] : !llvm.array<2 x vector<3xf64>> +// CHECK: %[[VAL_6:.*]] = llvm.extractvalue %[[VAL_3:.*]][0] : !llvm.array<2 x vector<3xi32>> +// CHECK: %[[VAL_7:.*]] = llvm.mlir.undef : vector<3xf64> +// CHECK: %[[VAL_8:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[VAL_9:.*]] = llvm.extractelement %[[VAL_5]]{{\[}}%[[VAL_8]] : i64] : vector<3xf64> +// CHECK: %[[VAL_10:.*]] = llvm.extractelement %[[VAL_6]]{{\[}}%[[VAL_8]] : i64] : vector<3xi32> +// CHECK: %[[VAL_11:.*]] = "llvm.intr.powi"(%[[VAL_9]], %[[VAL_10]]) : (f64, i32) -> f64 +// CHECK: %[[VAL_12:.*]] = llvm.insertelement %[[VAL_11]], %[[VAL_7]]{{\[}}%[[VAL_8]] : i64] : vector<3xf64> +// CHECK: %[[VAL_13:.*]] = llvm.mlir.constant(1 : i64) : i64 +// CHECK: %[[VAL_14:.*]] = llvm.extractelement %[[VAL_5]]{{\[}}%[[VAL_13]] : i64] : vector<3xf64> +// CHECK: %[[VAL_15:.*]] = llvm.extractelement %[[VAL_6]]{{\[}}%[[VAL_13]] : i64] : vector<3xi32> +// CHECK: %[[VAL_16:.*]] = "llvm.intr.powi"(%[[VAL_14]], %[[VAL_15]]) : (f64, i32) -> f64 +// CHECK: %[[VAL_17:.*]] = llvm.insertelement %[[VAL_16]], %[[VAL_12]]{{\[}}%[[VAL_13]] : i64] : vector<3xf64> +// CHECK: %[[VAL_18:.*]] = llvm.mlir.constant(2 : i64) : i64 +// CHECK: %[[VAL_19:.*]] = llvm.extractelement %[[VAL_5]]{{\[}}%[[VAL_18]] : i64] : vector<3xf64> +// CHECK: %[[VAL_20:.*]] = llvm.extractelement %[[VAL_6]]{{\[}}%[[VAL_18]] : i64] : vector<3xi32> +// CHECK: %[[VAL_21:.*]] = "llvm.intr.powi"(%[[VAL_19]], %[[VAL_20]]) : (f64, i32) -> f64 +// CHECK: %[[VAL_22:.*]] = llvm.insertelement %[[VAL_21]], %[[VAL_17]]{{\[}}%[[VAL_18]] : i64] : vector<3xf64> +// CHECK: %[[VAL_23:.*]] = llvm.insertvalue %[[VAL_22]], %[[VAL_4]][0] : !llvm.array<2 x vector<3xf64>> + +// CHECK: "llvm.intr.powi"({{.*}}, {{.*}}) : (f64, i32) -> f64 +// CHECK: "llvm.intr.powi"({{.*}}, {{.*}}) : (f64, i32) -> f64 +// CHECK: "llvm.intr.powi"({{.*}}, {{.*}}) : (f64, i32) -> f64 + %0 = math.fpowi %arg0, %arg1 : (vector<2x3xf64>, vector<2x3xi32>) -> vector<2x3xf64> + func.return +}