| //===- MathToAPFloat.cpp - Mathmetic to APFloat Conversion ----------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "Utils.h" | |
| #include "mlir/Conversion/ArithAndMathToAPFloat/MathToAPFloat.h" | |
| #include "mlir/Dialect/Arith/IR/Arith.h" | |
| #include "mlir/Dialect/Func/IR/FuncOps.h" | |
| #include "mlir/Dialect/Func/Utils/Utils.h" | |
| #include "mlir/Dialect/Math/IR/Math.h" | |
| #include "mlir/Dialect/Vector/IR/VectorOps.h" | |
| #include "mlir/IR/PatternMatch.h" | |
| #include "mlir/IR/Verifier.h" | |
| #include "mlir/Pass/Pass.h" | |
| #include "mlir/Transforms/WalkPatternRewriteDriver.h" | |
| namespace mlir { | |
| #define GEN_PASS_DEF_MATHTOAPFLOATCONVERSIONPASS | |
| #include "mlir/Conversion/Passes.h.inc" | |
| } // namespace mlir | |
| using namespace mlir; | |
| using namespace mlir::func; | |
| struct AbsFOpToAPFloatConversion final : OpRewritePattern<math::AbsFOp> { | |
| AbsFOpToAPFloatConversion(MLIRContext *context, SymbolOpInterface symTable, | |
| PatternBenefit benefit = 1) | |
| : OpRewritePattern<math::AbsFOp>(context, benefit), symTable(symTable) {} | |
| LogicalResult matchAndRewrite(math::AbsFOp op, | |
| PatternRewriter &rewriter) const override { | |
| if (failed(checkPreconditions(rewriter, op))) | |
| return failure(); | |
| // Get APFloat function from runtime library. | |
| auto i32Type = IntegerType::get(symTable->getContext(), 32); | |
| auto i64Type = IntegerType::get(symTable->getContext(), 64); | |
| FailureOr<FuncOp> fn = lookupOrCreateFnDecl( | |
| rewriter, symTable, "_mlir_apfloat_abs", {i32Type, i64Type}); | |
| if (failed(fn)) | |
| return fn; | |
| Location loc = op.getLoc(); | |
| rewriter.setInsertionPoint(op); | |
| // Scalarize and convert to APFloat runtime calls. | |
| Value repl = forEachScalarValue( | |
| rewriter, loc, op.getOperand(), /*operand2=*/Value(), op.getType(), | |
| [&](Value operand, Value, Type resultType) { | |
| auto floatTy = cast<FloatType>(operand.getType()); | |
| auto intWType = rewriter.getIntegerType(floatTy.getWidth()); | |
| Value operandBits = arith::ExtUIOp::create( | |
| rewriter, loc, i64Type, | |
| arith::BitcastOp::create(rewriter, loc, intWType, operand)); | |
| // Call APFloat function. | |
| Value semValue = getAPFloatSemanticsValue(rewriter, loc, floatTy); | |
| SmallVector<Value> params = {semValue, operandBits}; | |
| Value negatedBits = | |
| func::CallOp::create(rewriter, loc, TypeRange(i64Type), | |
| SymbolRefAttr::get(*fn), params) | |
| ->getResult(0); | |
| // Truncate result to the original width. | |
| auto truncatedBits = | |
| arith::TruncIOp::create(rewriter, loc, intWType, negatedBits); | |
| return arith::BitcastOp::create(rewriter, loc, floatTy, | |
| truncatedBits); | |
| }); | |
| rewriter.replaceOp(op, repl); | |
| return success(); | |
| } | |
| SymbolOpInterface symTable; | |
| }; | |
| template <typename OpTy> | |
| struct IsOpToAPFloatConversion final : OpRewritePattern<OpTy> { | |
| IsOpToAPFloatConversion(MLIRContext *context, const char *APFloatName, | |
| SymbolOpInterface symTable, | |
| PatternBenefit benefit = 1) | |
| : OpRewritePattern<OpTy>(context, benefit), symTable(symTable), | |
| APFloatName(APFloatName) {}; | |
| LogicalResult matchAndRewrite(OpTy op, | |
| PatternRewriter &rewriter) const override { | |
| if (failed(checkPreconditions(rewriter, op))) | |
| return failure(); | |
| // Get APFloat function from runtime library. | |
| auto i1 = IntegerType::get(symTable->getContext(), 1); | |
| auto i32Type = IntegerType::get(symTable->getContext(), 32); | |
| auto i64Type = IntegerType::get(symTable->getContext(), 64); | |
| std::string funcName = | |
| (llvm::Twine("_mlir_apfloat_is") + APFloatName).str(); | |
| FailureOr<FuncOp> fn = lookupOrCreateFnDecl( | |
| rewriter, symTable, funcName, {i32Type, i64Type}, nullptr, i1); | |
| if (failed(fn)) | |
| return fn; | |
| Location loc = op.getLoc(); | |
| rewriter.setInsertionPoint(op); | |
| // Scalarize and convert to APFloat runtime calls. | |
| Value repl = forEachScalarValue( | |
| rewriter, loc, op.getOperand(), /*operand2=*/Value(), op.getType(), | |
| [&](Value operand, Value, Type resultType) { | |
| auto floatTy = cast<FloatType>(operand.getType()); | |
| auto intWType = rewriter.getIntegerType(floatTy.getWidth()); | |
| Value operandBits = arith::ExtUIOp::create( | |
| rewriter, loc, i64Type, | |
| arith::BitcastOp::create(rewriter, loc, intWType, operand)); | |
| // Call APFloat function. | |
| Value semValue = getAPFloatSemanticsValue(rewriter, loc, floatTy); | |
| Value params[] = {semValue, operandBits}; | |
| return func::CallOp::create(rewriter, loc, TypeRange(i1), | |
| SymbolRefAttr::get(*fn), params) | |
| .getResult(0); | |
| }); | |
| rewriter.replaceOp(op, repl); | |
| return success(); | |
| } | |
| SymbolOpInterface symTable; | |
| const char *APFloatName; | |
| }; | |
| struct FmaOpToAPFloatConversion final : OpRewritePattern<math::FmaOp> { | |
| FmaOpToAPFloatConversion(MLIRContext *context, SymbolOpInterface symTable, | |
| PatternBenefit benefit = 1) | |
| : OpRewritePattern<math::FmaOp>(context, benefit), symTable(symTable) {}; | |
| LogicalResult matchAndRewrite(math::FmaOp op, | |
| PatternRewriter &rewriter) const override { | |
| if (failed(checkPreconditions(rewriter, op))) | |
| return failure(); | |
| // Cast operands to 64-bit integers. | |
| mlir::Type resType = op.getResult().getType(); | |
| auto floatTy = dyn_cast<FloatType>(resType); | |
| if (!floatTy) { | |
| auto vecTy1 = cast<VectorType>(resType); | |
| floatTy = llvm::cast<FloatType>(vecTy1.getElementType()); | |
| } | |
| auto i32Type = IntegerType::get(symTable->getContext(), 32); | |
| auto i64Type = IntegerType::get(symTable->getContext(), 64); | |
| FailureOr<FuncOp> fn = lookupOrCreateFnDecl( | |
| rewriter, symTable, "_mlir_apfloat_fused_multiply_add", | |
| {i32Type, i64Type, i64Type, i64Type}); | |
| if (failed(fn)) | |
| return fn; | |
| Location loc = op.getLoc(); | |
| rewriter.setInsertionPoint(op); | |
| IntegerType intWType = rewriter.getIntegerType(floatTy.getWidth()); | |
| IntegerType int64Type = rewriter.getI64Type(); | |
| auto scalarFMA = [&rewriter, &loc, &floatTy, &fn, &intWType, | |
| &int64Type](Value a, Value b, Value c) { | |
| Value operand = arith::ExtUIOp::create( | |
| rewriter, loc, int64Type, | |
| arith::BitcastOp::create(rewriter, loc, intWType, a)); | |
| Value multiplicand = arith::ExtUIOp::create( | |
| rewriter, loc, int64Type, | |
| arith::BitcastOp::create(rewriter, loc, intWType, b)); | |
| Value addend = arith::ExtUIOp::create( | |
| rewriter, loc, int64Type, | |
| arith::BitcastOp::create(rewriter, loc, intWType, c)); | |
| // Call APFloat function. | |
| Value semValue = getAPFloatSemanticsValue(rewriter, loc, floatTy); | |
| SmallVector<Value> params = {semValue, operand, multiplicand, addend}; | |
| auto resultOp = | |
| func::CallOp::create(rewriter, loc, TypeRange(rewriter.getI64Type()), | |
| SymbolRefAttr::get(*fn), params); | |
| // Truncate result to the original width. | |
| auto trunc = arith::TruncIOp::create(rewriter, loc, intWType, | |
| resultOp->getResult(0)); | |
| return arith::BitcastOp::create(rewriter, loc, floatTy, trunc); | |
| }; | |
| if (auto vecTy1 = dyn_cast<VectorType>(op.getA().getType())) { | |
| // Sanity check: Operand types must match. | |
| assert(vecTy1 == dyn_cast<VectorType>(op.getB().getType()) && | |
| "expected same vector types"); | |
| assert(vecTy1 == dyn_cast<VectorType>(op.getC().getType()) && | |
| "expected same vector types"); | |
| // Prepare scalar operands. | |
| ResultRange scalarOperands = | |
| vector::ToElementsOp::create(rewriter, loc, op.getA())->getResults(); | |
| ResultRange scalarMultiplicands = | |
| vector::ToElementsOp::create(rewriter, loc, op.getB())->getResults(); | |
| ResultRange scalarAddends = | |
| vector::ToElementsOp::create(rewriter, loc, op.getC())->getResults(); | |
| // Call the function for each pair of scalar operands. | |
| SmallVector<Value> results; | |
| for (auto [operand, multiplicand, addend] : llvm::zip_equal( | |
| scalarOperands, scalarMultiplicands, scalarAddends)) { | |
| results.push_back(scalarFMA(operand, multiplicand, addend)); | |
| } | |
| // Package the results into a vector. | |
| auto fromElements = vector::FromElementsOp::create( | |
| rewriter, loc, | |
| vecTy1.cloneWith(/*shape=*/std::nullopt, results.front().getType()), | |
| results); | |
| rewriter.replaceOp(op, fromElements); | |
| return success(); | |
| } | |
| Value repl = scalarFMA(op.getA(), op.getB(), op.getC()); | |
| rewriter.replaceOp(op, repl); | |
| return success(); | |
| } | |
| SymbolOpInterface symTable; | |
| }; | |
| namespace { | |
| struct MathToAPFloatConversionPass final | |
| : impl::MathToAPFloatConversionPassBase<MathToAPFloatConversionPass> { | |
| using Base::Base; | |
| void runOnOperation() override; | |
| }; | |
| void MathToAPFloatConversionPass::runOnOperation() { | |
| MLIRContext *context = &getContext(); | |
| RewritePatternSet patterns(context); | |
| patterns.add<AbsFOpToAPFloatConversion>(context, getOperation()); | |
| patterns.add<IsOpToAPFloatConversion<math::IsFiniteOp>>(context, "finite", | |
| getOperation()); | |
| patterns.add<IsOpToAPFloatConversion<math::IsInfOp>>(context, "infinite", | |
| getOperation()); | |
| patterns.add<IsOpToAPFloatConversion<math::IsNaNOp>>(context, "nan", | |
| getOperation()); | |
| patterns.add<IsOpToAPFloatConversion<math::IsNormalOp>>(context, "normal", | |
| getOperation()); | |
| patterns.add<FmaOpToAPFloatConversion>(context, getOperation()); | |
| LogicalResult result = success(); | |
| ScopedDiagnosticHandler scopedHandler(context, [&result](Diagnostic &diag) { | |
| if (diag.getSeverity() == DiagnosticSeverity::Error) { | |
| result = failure(); | |
| } | |
| // NB: if you don't return failure, no other diag handlers will fire (see | |
| // mlir/lib/IR/Diagnostics.cpp:DiagnosticEngineImpl::emit). | |
| return failure(); | |
| }); | |
| walkAndApplyPatterns(getOperation(), std::move(patterns)); | |
| if (failed(result)) | |
| return signalPassFailure(); | |
| } | |
| } // namespace |