24#include "llvm/ADT/DenseMap.h"
25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/DebugLog.h"
31#define GEN_PASS_DEF_CONVERTMATHTOFUNCS
32#include "mlir/Conversion/Passes.h.inc"
37#define DEBUG_TYPE "math-to-funcs"
57 IPowIOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
63 LogicalResult matchAndRewrite(math::IPowIOp op,
67 GetFuncCallbackTy getFuncOpCallback;
74 FPowIOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
80 LogicalResult matchAndRewrite(math::FPowIOp op,
84 GetFuncCallbackTy getFuncOpCallback;
91 CtlzOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
93 getFuncOpCallback(cb) {}
97 LogicalResult matchAndRewrite(math::CountLeadingZerosOp op,
101 GetFuncCallbackTy getFuncOpCallback;
105template <
typename Op>
108 Type opType = op.getType();
110 auto vecType = dyn_cast<VectorType>(opType);
114 if (!vecType.hasRank())
117 int64_t numElements = vecType.getNumElements();
119 Type resultElementType = vecType.getElementType();
121 if (isa<FloatType>(resultElementType))
122 initValueAttr = FloatAttr::get(resultElementType, 0.0);
124 initValueAttr = IntegerAttr::get(resultElementType, 0);
128 for (
int64_t linearIndex = 0; linearIndex < numElements; ++linearIndex) {
131 for (
Value input : op->getOperands())
133 vector::ExtractOp::create(rewriter, loc, input, positions));
134 Value scalarOp = Op::create(
135 rewriter, loc,
TypeRange{vecType.getElementType()}, operands,
136 op.
getProperties(), op->getDiscardableAttrDictionary().getValue());
138 vector::InsertOp::create(rewriter, loc, scalarOp,
result, positions);
149 [](
Type ty) { return getElementTypeOrSelf(ty); });
152 [](
Type ty) { return getElementTypeOrSelf(ty); });
153 return FunctionType::get(op->
getContext(), inputTys, resultTys);
187 bool setLLVMLinkage) {
188 assert(isa<IntegerType>(elementType) &&
189 "non-integer element type for IPowIOp");
194 std::string funcName(
"__mlir_math_ipowi");
195 llvm::raw_string_ostream nameOS(funcName);
196 nameOS <<
'_' << elementType;
198 FunctionType funcType = FunctionType::get(
199 builder.
getContext(), {elementType, elementType}, elementType);
200 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
201 if (setLLVMLinkage) {
202 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
204 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
205 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
209 Block *entryBlock = funcOp.addEntryBlock();
212 Value bArg = funcOp.getArgument(0);
213 Value pArg = funcOp.getArgument(1);
215 Value zeroValue = arith::ConstantOp::create(
217 Value oneValue = arith::ConstantOp::create(
219 Value minusOneValue = arith::ConstantOp::create(
220 builder, elementType,
228 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg, zeroValue);
230 func::ReturnOp::create(builder, oneValue);
234 cf::CondBranchOp::create(builder, pIsZero, thenBlock, fallthroughBlock);
238 auto pIsNeg = arith::CmpIOp::create(builder, arith::CmpIPredicate::sle, pArg,
243 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, bArg, oneValue);
246 func::ReturnOp::create(builder, oneValue);
250 cf::CondBranchOp::create(builder, bIsOne, thenBlock, fallthroughBlock);
254 auto bIsMinusOne = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
255 bArg, minusOneValue);
258 auto pIsOdd = arith::CmpIOp::create(
259 builder, arith::CmpIPredicate::ne,
260 arith::AndIOp::create(builder, pArg, oneValue), zeroValue);
263 func::ReturnOp::create(builder, minusOneValue);
267 cf::CondBranchOp::create(builder, pIsOdd, thenBlock, fallthroughBlock);
272 func::ReturnOp::create(builder, oneValue);
276 cf::CondBranchOp::create(builder, bIsMinusOne, pIsOdd->
getBlock(),
282 func::ReturnOp::create(builder, zeroValue);
284 funcBody, funcBody->
end(), {elementType, elementType, elementType},
285 {builder.getLoc(), builder.getLoc(), builder.getLoc()});
289 cf::CondBranchOp::create(builder, pIsNeg, bIsOne->
getBlock(), loopHeader,
307 auto powerTmpIsOdd = arith::CmpIOp::create(
308 builder, arith::CmpIPredicate::ne,
309 arith::AndIOp::create(builder, powerTmp, oneValue), zeroValue);
312 Value newResultTmp = arith::MulIOp::create(builder, resultTmp, baseTmp);
313 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), elementType,
316 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
319 cf::CondBranchOp::create(builder, powerTmpIsOdd, thenBlock, fallthroughBlock,
326 Value newPowerTmp = arith::ShRUIOp::create(builder, powerTmp, oneValue);
329 auto newPowerIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
330 newPowerTmp, zeroValue);
333 func::ReturnOp::create(builder, newResultTmp);
337 cf::CondBranchOp::create(builder, newPowerIsZero, thenBlock,
343 Value newBaseTmp = arith::MulIOp::create(builder, baseTmp, baseTmp);
345 cf::BranchOp::create(
346 builder,
ValueRange{newResultTmp, newBaseTmp, newPowerTmp}, loopHeader);
354IPowIOpLowering::matchAndRewrite(math::IPowIOp op,
356 auto baseType = dyn_cast<IntegerType>(op.getOperands()[0].getType());
363 func::FuncOp elementFunc = getFuncOpCallback(op, baseType);
405 FunctionType funcType,
406 bool setLLVMLinkage) {
407 auto baseType = cast<FloatType>(funcType.getInput(0));
408 auto powType = cast<IntegerType>(funcType.getInput(1));
412 std::string funcName(
"__mlir_math_fpowi");
413 llvm::raw_string_ostream nameOS(funcName);
414 nameOS <<
'_' << baseType;
415 nameOS <<
'_' << powType;
416 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
417 if (setLLVMLinkage) {
418 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
420 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
421 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
425 Block *entryBlock = funcOp.addEntryBlock();
428 Value bArg = funcOp.getArgument(0);
429 Value pArg = funcOp.getArgument(1);
431 Value oneBValue = arith::ConstantOp::create(
432 builder, baseType, builder.
getFloatAttr(baseType, 1.0));
433 Value zeroPValue = arith::ConstantOp::create(
435 Value onePValue = arith::ConstantOp::create(
437 Value minPValue = arith::ConstantOp::create(
440 powType, llvm::APInt::getSignedMinValue(powType.getWidth())));
441 Value maxPValue = arith::ConstantOp::create(
444 powType, llvm::APInt::getSignedMaxValue(powType.getWidth())));
448 auto pIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg,
451 func::ReturnOp::create(builder, oneBValue);
455 cf::CondBranchOp::create(builder, pIsZero, thenBlock, fallthroughBlock);
459 auto pIsNeg = arith::CmpIOp::create(builder, arith::CmpIPredicate::sle, pArg,
463 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg, minPValue);
470 Value negP = arith::SubIOp::create(builder, zeroPValue, pArg);
471 auto pInit = arith::SelectOp::create(builder, pIsNeg, negP, pArg);
472 pInit = arith::SelectOp::create(builder, pIsMin, maxPValue, pInit);
485 funcBody, funcBody->
end(), {baseType, baseType, powType},
486 {builder.getLoc(), builder.getLoc(), builder.getLoc()});
489 cf::BranchOp::create(builder, loopHeader,
ValueRange{oneBValue, bArg, pInit});
498 auto powerTmpIsOdd = arith::CmpIOp::create(
499 builder, arith::CmpIPredicate::ne,
500 arith::AndIOp::create(builder, powerTmp, onePValue), zeroPValue);
503 Value newResultTmp = arith::MulFOp::create(builder, resultTmp, baseTmp);
504 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), baseType,
507 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
510 cf::CondBranchOp::create(builder, powerTmpIsOdd, thenBlock, fallthroughBlock,
517 Value newPowerTmp = arith::ShRUIOp::create(builder, powerTmp, onePValue);
520 auto newPowerIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
521 newPowerTmp, zeroPValue);
531 Value newBaseTmp = arith::MulFOp::create(builder, baseTmp, baseTmp);
533 cf::BranchOp::create(
534 builder,
ValueRange{newResultTmp, newBaseTmp, newPowerTmp}, loopHeader);
542 cf::CondBranchOp::create(builder, newPowerIsZero, loopExit, newResultTmp,
548 newResultTmp = loopExit->getArgument(0);
550 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), baseType,
553 cf::CondBranchOp::create(builder, pIsMin, thenBlock, fallthroughBlock,
556 newResultTmp = arith::MulFOp::create(builder, newResultTmp, bArg);
557 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
567 cf::CondBranchOp::create(builder, pIsNeg, thenBlock, returnBlock,
570 newResultTmp = arith::DivFOp::create(builder, oneBValue, newResultTmp);
571 cf::BranchOp::create(builder, newResultTmp, returnBlock);
575 func::ReturnOp::create(builder, returnBlock->
getArgument(0));
584FPowIOpLowering::matchAndRewrite(math::FPowIOp op,
585 PatternRewriter &rewriter)
const {
586 if (isa<VectorType>(op.getType()))
593 func::FuncOp elementFunc = getFuncOpCallback(op, funcType);
649 bool setLLVMLinkage) {
650 if (!isa<IntegerType>(elementType)) {
651 LDBG() <<
"non-integer element type for CtlzFunc; type was: "
653 llvm_unreachable(
"non-integer element type");
661 std::string funcName(
"__mlir_math_ctlz");
662 llvm::raw_string_ostream nameOS(funcName);
663 nameOS <<
'_' << elementType;
664 FunctionType funcType =
665 FunctionType::get(builder.
getContext(), {elementType}, elementType);
666 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
670 if (setLLVMLinkage) {
671 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
673 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
674 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
679 Block *funcBody = funcOp.addEntryBlock();
682 Value arg = funcOp.getArgument(0);
684 Value bitWidthValue = arith::ConstantOp::create(
685 builder, elementType, builder.
getIntegerAttr(elementType, bitWidth));
686 Value zeroValue = arith::ConstantOp::create(
690 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, arg, zeroValue);
694 scf::IfOp::create(builder, elementType, inputEqZero,
696 auto thenBuilder = ifOp.getThenBodyBuilder();
697 scf::YieldOp::create(thenBuilder, loc, bitWidthValue);
702 Value oneIndex = arith::ConstantOp::create(elseBuilder, indexType,
703 elseBuilder.getIndexAttr(1));
704 Value oneValue = arith::ConstantOp::create(
705 elseBuilder, elementType, elseBuilder.getIntegerAttr(elementType, 1));
706 Value bitWidthIndex = arith::ConstantOp::create(
707 elseBuilder, indexType, elseBuilder.getIndexAttr(bitWidth));
708 Value nValue = arith::ConstantOp::create(
709 elseBuilder, elementType, elseBuilder.getIntegerAttr(elementType, 0));
711 auto loop = scf::ForOp::create(
712 elseBuilder, oneIndex, bitWidthIndex, oneIndex,
725 Value argIter = args[0];
726 Value nIter = args[1];
728 Value argIsNonNegative = arith::CmpIOp::create(
729 b, loc, arith::CmpIPredicate::slt, argIter, zeroValue);
730 scf::IfOp ifOp = scf::IfOp::create(
731 b, loc, argIsNonNegative,
734 scf::YieldOp::create(
b, loc,
ValueRange{argIter, nIter});
738 Value nNext = arith::AddIOp::create(
b, loc, nIter, oneValue);
739 Value argNext = arith::ShLIOp::create(
b, loc, argIter, oneValue);
740 scf::YieldOp::create(
b, loc,
ValueRange{argNext, nNext});
742 scf::YieldOp::create(
b, loc, ifOp.getResults());
744 scf::YieldOp::create(elseBuilder, loop.getResult(1));
746 func::ReturnOp::create(builder, ifOp.getResult(0));
752LogicalResult CtlzOpLowering::matchAndRewrite(math::CountLeadingZerosOp op,
753 PatternRewriter &rewriter)
const {
754 if (isa<VectorType>(op.getType()))
758 func::FuncOp elementFunc = getFuncOpCallback(op, type);
761 diag <<
"Missing software implementation for op " << op->getName()
762 <<
" and type " << type;
770struct ConvertMathToFuncsPass
771 :
public impl::ConvertMathToFuncsBase<ConvertMathToFuncsPass> {
772 ConvertMathToFuncsPass() =
default;
773 ConvertMathToFuncsPass(
const ConvertMathToFuncsOptions &
options)
774 : impl::ConvertMathToFuncsBase<ConvertMathToFuncsPass>(
options) {}
776 void runOnOperation()
override;
782 bool isFPowIConvertible(math::FPowIOp op);
785 bool isConvertible(Operation *op);
789 void generateOpImplementations();
798bool ConvertMathToFuncsPass::isFPowIConvertible(math::FPowIOp op) {
801 return (expTy && expTy.getWidth() >= minWidthOfFPowIExponent);
804bool ConvertMathToFuncsPass::isConvertible(Operation *op) {
808void ConvertMathToFuncsPass::generateOpImplementations() {
809 ModuleOp module = getOperation();
811 module.walk([&](Operation *op) {
812 TypeSwitch<Operation *>(op)
813 .Case([&](math::CountLeadingZerosOp op) {
814 if (!convertCtlz || !isConvertible(op))
821 auto key = std::make_tuple(op->
getName(), resultType, symbolTable);
822 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
824 entry.first->second =
827 .Case([&](math::IPowIOp op) {
828 if (!isConvertible(op))
836 auto key = std::make_tuple(op->getName(), resultType, symbolTable);
837 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
839 entry.first->second =
842 .Case([&](math::FPowIOp op) {
843 if (!isFPowIConvertible(op))
853 auto key = std::make_tuple(op->getName(), funcType, symbolTable);
854 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
856 entry.first->second =
862void ConvertMathToFuncsPass::runOnOperation() {
863 ModuleOp module = getOperation();
866 generateOpImplementations();
869 patterns.add<VecOpToScalarOp<math::IPowIOp>, VecOpToScalarOp<math::FPowIOp>,
870 VecOpToScalarOp<math::CountLeadingZerosOp>>(
871 patterns.getContext());
874 auto getFuncOpByType = [&](Operation *op, Type type) -> func::FuncOp {
875 auto key = std::make_tuple(op->
getName(), type,
877 auto it = funcImpls.find(key);
878 if (it == funcImpls.end())
883 patterns.add<IPowIOpLowering, FPowIOpLowering>(patterns.getContext(),
887 patterns.add<CtlzOpLowering>(patterns.getContext(), getFuncOpByType);
890 target.addLegalDialect<arith::ArithDialect, cf::ControlFlowDialect,
891 func::FuncDialect, scf::SCFDialect,
892 vector::VectorDialect>();
894 target.addDynamicallyLegalOp<math::IPowIOp>(
895 [
this](math::IPowIOp op) {
return !isConvertible(op); });
897 target.addDynamicallyLegalOp<math::CountLeadingZerosOp>(
898 [
this](math::CountLeadingZerosOp op) {
return !isConvertible(op); });
900 target.addDynamicallyLegalOp<math::FPowIOp>(
901 [
this](math::FPowIOp op) {
return !isFPowIConvertible(op); });
902 if (
failed(applyPartialConversion(module,
target, std::move(patterns))))
static func::FuncOp createCtlzFunc(Operation *symbolTable, Type elementType, bool setLLVMLinkage)
Create function to implement the ctlz function the given elementType type inside symbolTable.
static FunctionType getElementalFuncTypeForOp(Operation *op)
static func::FuncOp createElementFPowIFunc(Operation *symbolTable, FunctionType funcType, bool setLLVMLinkage)
Create linkonce_odr function to implement the power function with the given funcType type inside symb...
static func::FuncOp createElementIPowIFunc(Operation *symbolTable, Type elementType, bool setLLVMLinkage)
Create linkonce_odr function to implement the power function with the given elementType type inside s...
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
IntegerAttr getIntegerAttr(Type type, int64_t value)
FloatAttr getFloatAttr(Type type, double value)
MLIRContext * getContext() const
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Location getLoc() const
Accessors for the implied location.
static ImplicitLocOpBuilder atBlockEnd(Location loc, Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to after the last operation in the block but still insid...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Block * getBlock() const
Returns the current block of the builder.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
InferredProperties< T > & getProperties()
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
operand_type_iterator operand_type_end()
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
result_type_iterator result_type_end()
result_type_iterator result_type_begin()
OperationName getName()
The name of an operation is the key identifier for it.
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumResults()
Return the number of results held by this operation.
operand_type_iterator operand_type_begin()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Include the generated interface declarations.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...