23#include "llvm/ADT/DenseMap.h"
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/Support/DebugLog.h"
28#define GEN_PASS_DEF_CONVERTMATHTOFUNCS
29#include "mlir/Conversion/Passes.h.inc"
34#define DEBUG_TYPE "math-to-funcs"
54 IPowIOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
60 LogicalResult matchAndRewrite(math::IPowIOp op,
64 GetFuncCallbackTy getFuncOpCallback;
71 FPowIOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
77 LogicalResult matchAndRewrite(math::FPowIOp op,
81 GetFuncCallbackTy getFuncOpCallback;
88 CtlzOpLowering(
MLIRContext *context, GetFuncCallbackTy cb)
90 getFuncOpCallback(cb) {}
94 LogicalResult matchAndRewrite(math::CountLeadingZerosOp op,
98 GetFuncCallbackTy getFuncOpCallback;
102template <
typename Op>
105 Type opType = op.getType();
107 auto vecType = dyn_cast<VectorType>(opType);
111 if (!vecType.hasRank())
114 int64_t numElements = vecType.getNumElements();
116 Type resultElementType = vecType.getElementType();
118 if (isa<FloatType>(resultElementType))
119 initValueAttr = FloatAttr::get(resultElementType, 0.0);
121 initValueAttr = IntegerAttr::get(resultElementType, 0);
125 for (
int64_t linearIndex = 0; linearIndex < numElements; ++linearIndex) {
128 for (
Value input : op->getOperands())
130 vector::ExtractOp::create(rewriter, loc, input, positions));
131 Value scalarOp = Op::create(
132 rewriter, loc,
TypeRange{vecType.getElementType()}, operands,
133 op.
getProperties(), op->getDiscardableAttrDictionary().getValue());
135 vector::InsertOp::create(rewriter, loc, scalarOp,
result, positions);
146 [](
Type ty) { return getElementTypeOrSelf(ty); });
149 [](
Type ty) { return getElementTypeOrSelf(ty); });
150 return FunctionType::get(op->
getContext(), inputTys, resultTys);
185 assert(isa<IntegerType>(elementType) &&
186 "non-integer element type for IPowIOp");
191 std::string funcName(
"__mlir_math_ipowi");
192 llvm::raw_string_ostream nameOS(funcName);
193 nameOS <<
'_' << elementType;
195 FunctionType funcType = FunctionType::get(
196 builder.
getContext(), {elementType, elementType}, elementType);
197 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
198 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
200 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
201 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
204 Block *entryBlock = funcOp.addEntryBlock();
207 Value bArg = funcOp.getArgument(0);
208 Value pArg = funcOp.getArgument(1);
210 Value zeroValue = arith::ConstantOp::create(
212 Value oneValue = arith::ConstantOp::create(
214 Value minusOneValue = arith::ConstantOp::create(
215 builder, elementType,
223 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg, zeroValue);
225 func::ReturnOp::create(builder, oneValue);
229 cf::CondBranchOp::create(builder, pIsZero, thenBlock, fallthroughBlock);
233 auto pIsNeg = arith::CmpIOp::create(builder, arith::CmpIPredicate::sle, pArg,
238 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, bArg, zeroValue);
241 func::ReturnOp::create(
243 arith::DivSIOp::create(builder, oneValue, zeroValue).getResult());
247 cf::CondBranchOp::create(builder, bIsZero, thenBlock, fallthroughBlock);
252 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, bArg, oneValue);
255 func::ReturnOp::create(builder, oneValue);
259 cf::CondBranchOp::create(builder, bIsOne, thenBlock, fallthroughBlock);
263 auto bIsMinusOne = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
264 bArg, minusOneValue);
267 auto pIsOdd = arith::CmpIOp::create(
268 builder, arith::CmpIPredicate::ne,
269 arith::AndIOp::create(builder, pArg, oneValue), zeroValue);
272 func::ReturnOp::create(builder, minusOneValue);
276 cf::CondBranchOp::create(builder, pIsOdd, thenBlock, fallthroughBlock);
281 func::ReturnOp::create(builder, oneValue);
285 cf::CondBranchOp::create(builder, bIsMinusOne, pIsOdd->
getBlock(),
291 func::ReturnOp::create(builder, zeroValue);
293 funcBody, funcBody->
end(), {elementType, elementType, elementType},
294 {builder.getLoc(), builder.getLoc(), builder.getLoc()});
298 cf::CondBranchOp::create(builder, pIsNeg, bIsZero->
getBlock(), loopHeader,
316 auto powerTmpIsOdd = arith::CmpIOp::create(
317 builder, arith::CmpIPredicate::ne,
318 arith::AndIOp::create(builder, powerTmp, oneValue), zeroValue);
321 Value newResultTmp = arith::MulIOp::create(builder, resultTmp, baseTmp);
322 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), elementType,
325 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
328 cf::CondBranchOp::create(builder, powerTmpIsOdd, thenBlock, fallthroughBlock,
335 Value newPowerTmp = arith::ShRUIOp::create(builder, powerTmp, oneValue);
338 auto newPowerIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
339 newPowerTmp, zeroValue);
342 func::ReturnOp::create(builder, newResultTmp);
346 cf::CondBranchOp::create(builder, newPowerIsZero, thenBlock,
352 Value newBaseTmp = arith::MulIOp::create(builder, baseTmp, baseTmp);
354 cf::BranchOp::create(
355 builder,
ValueRange{newResultTmp, newBaseTmp, newPowerTmp}, loopHeader);
363IPowIOpLowering::matchAndRewrite(math::IPowIOp op,
365 auto baseType = dyn_cast<IntegerType>(op.getOperands()[0].getType());
372 func::FuncOp elementFunc = getFuncOpCallback(op, baseType);
414 FunctionType funcType) {
415 auto baseType = cast<FloatType>(funcType.getInput(0));
416 auto powType = cast<IntegerType>(funcType.getInput(1));
420 std::string funcName(
"__mlir_math_fpowi");
421 llvm::raw_string_ostream nameOS(funcName);
422 nameOS <<
'_' << baseType;
423 nameOS <<
'_' << powType;
424 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
425 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
427 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
428 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
431 Block *entryBlock = funcOp.addEntryBlock();
434 Value bArg = funcOp.getArgument(0);
435 Value pArg = funcOp.getArgument(1);
437 Value oneBValue = arith::ConstantOp::create(
438 builder, baseType, builder.
getFloatAttr(baseType, 1.0));
439 Value zeroPValue = arith::ConstantOp::create(
441 Value onePValue = arith::ConstantOp::create(
443 Value minPValue = arith::ConstantOp::create(
446 powType, llvm::APInt::getSignedMinValue(powType.getWidth())));
447 Value maxPValue = arith::ConstantOp::create(
450 powType, llvm::APInt::getSignedMaxValue(powType.getWidth())));
454 auto pIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg,
457 func::ReturnOp::create(builder, oneBValue);
461 cf::CondBranchOp::create(builder, pIsZero, thenBlock, fallthroughBlock);
465 auto pIsNeg = arith::CmpIOp::create(builder, arith::CmpIPredicate::sle, pArg,
469 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, pArg, minPValue);
476 Value negP = arith::SubIOp::create(builder, zeroPValue, pArg);
477 auto pInit = arith::SelectOp::create(builder, pIsNeg, negP, pArg);
478 pInit = arith::SelectOp::create(builder, pIsMin, maxPValue, pInit);
491 funcBody, funcBody->
end(), {baseType, baseType, powType},
492 {builder.getLoc(), builder.getLoc(), builder.getLoc()});
495 cf::BranchOp::create(builder, loopHeader,
ValueRange{oneBValue, bArg, pInit});
504 auto powerTmpIsOdd = arith::CmpIOp::create(
505 builder, arith::CmpIPredicate::ne,
506 arith::AndIOp::create(builder, powerTmp, onePValue), zeroPValue);
509 Value newResultTmp = arith::MulFOp::create(builder, resultTmp, baseTmp);
510 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), baseType,
513 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
516 cf::CondBranchOp::create(builder, powerTmpIsOdd, thenBlock, fallthroughBlock,
523 Value newPowerTmp = arith::ShRUIOp::create(builder, powerTmp, onePValue);
526 auto newPowerIsZero = arith::CmpIOp::create(builder, arith::CmpIPredicate::eq,
527 newPowerTmp, zeroPValue);
537 Value newBaseTmp = arith::MulFOp::create(builder, baseTmp, baseTmp);
539 cf::BranchOp::create(
540 builder,
ValueRange{newResultTmp, newBaseTmp, newPowerTmp}, loopHeader);
548 cf::CondBranchOp::create(builder, newPowerIsZero, loopExit, newResultTmp,
554 newResultTmp = loopExit->getArgument(0);
556 fallthroughBlock = builder.
createBlock(funcBody, funcBody->
end(), baseType,
559 cf::CondBranchOp::create(builder, pIsMin, thenBlock, fallthroughBlock,
562 newResultTmp = arith::MulFOp::create(builder, newResultTmp, bArg);
563 cf::BranchOp::create(builder, newResultTmp, fallthroughBlock);
573 cf::CondBranchOp::create(builder, pIsNeg, thenBlock, returnBlock,
576 newResultTmp = arith::DivFOp::create(builder, oneBValue, newResultTmp);
577 cf::BranchOp::create(builder, newResultTmp, returnBlock);
581 func::ReturnOp::create(builder, returnBlock->
getArgument(0));
590FPowIOpLowering::matchAndRewrite(math::FPowIOp op,
591 PatternRewriter &rewriter)
const {
592 if (isa<VectorType>(op.getType()))
599 func::FuncOp elementFunc = getFuncOpCallback(op, funcType);
655 if (!isa<IntegerType>(elementType)) {
656 LDBG() <<
"non-integer element type for CtlzFunc; type was: "
658 llvm_unreachable(
"non-integer element type");
666 std::string funcName(
"__mlir_math_ctlz");
667 llvm::raw_string_ostream nameOS(funcName);
668 nameOS <<
'_' << elementType;
669 FunctionType funcType =
670 FunctionType::get(builder.
getContext(), {elementType}, elementType);
671 auto funcOp = func::FuncOp::create(builder, funcName, funcType);
675 LLVM::linkage::Linkage inlineLinkage = LLVM::linkage::Linkage::LinkonceODR;
677 LLVM::LinkageAttr::get(builder.
getContext(), inlineLinkage);
678 funcOp->setDiscardableAttr(
"llvm.linkage", linkage);
682 Block *funcBody = funcOp.addEntryBlock();
685 Value arg = funcOp.getArgument(0);
687 Value bitWidthValue = arith::ConstantOp::create(
688 builder, elementType, builder.
getIntegerAttr(elementType, bitWidth));
689 Value zeroValue = arith::ConstantOp::create(
693 arith::CmpIOp::create(builder, arith::CmpIPredicate::eq, arg, zeroValue);
697 scf::IfOp::create(builder, elementType, inputEqZero,
699 auto thenBuilder = ifOp.getThenBodyBuilder();
700 scf::YieldOp::create(thenBuilder, loc, bitWidthValue);
705 Value oneIndex = arith::ConstantOp::create(elseBuilder, indexType,
706 elseBuilder.getIndexAttr(1));
707 Value oneValue = arith::ConstantOp::create(
708 elseBuilder, elementType, elseBuilder.getIntegerAttr(elementType, 1));
709 Value bitWidthIndex = arith::ConstantOp::create(
710 elseBuilder, indexType, elseBuilder.getIndexAttr(bitWidth));
711 Value nValue = arith::ConstantOp::create(
712 elseBuilder, elementType, elseBuilder.getIntegerAttr(elementType, 0));
714 auto loop = scf::ForOp::create(
715 elseBuilder, oneIndex, bitWidthIndex, oneIndex,
728 Value argIter = args[0];
729 Value nIter = args[1];
731 Value argIsNonNegative = arith::CmpIOp::create(
732 b, loc, arith::CmpIPredicate::slt, argIter, zeroValue);
733 scf::IfOp ifOp = scf::IfOp::create(
734 b, loc, argIsNonNegative,
737 scf::YieldOp::create(
b, loc,
ValueRange{argIter, nIter});
741 Value nNext = arith::AddIOp::create(
b, loc, nIter, oneValue);
742 Value argNext = arith::ShLIOp::create(
b, loc, argIter, oneValue);
743 scf::YieldOp::create(
b, loc,
ValueRange{argNext, nNext});
745 scf::YieldOp::create(
b, loc, ifOp.getResults());
747 scf::YieldOp::create(elseBuilder, loop.getResult(1));
749 func::ReturnOp::create(builder, ifOp.getResult(0));
755LogicalResult CtlzOpLowering::matchAndRewrite(math::CountLeadingZerosOp op,
756 PatternRewriter &rewriter)
const {
757 if (isa<VectorType>(op.getType()))
761 func::FuncOp elementFunc = getFuncOpCallback(op, type);
764 diag <<
"Missing software implementation for op " << op->getName()
765 <<
" and type " << type;
773struct ConvertMathToFuncsPass
774 :
public impl::ConvertMathToFuncsBase<ConvertMathToFuncsPass> {
775 ConvertMathToFuncsPass() =
default;
776 ConvertMathToFuncsPass(
const ConvertMathToFuncsOptions &
options)
777 : impl::ConvertMathToFuncsBase<ConvertMathToFuncsPass>(
options) {}
779 void runOnOperation()
override;
785 bool isFPowIConvertible(math::FPowIOp op);
788 bool isConvertible(Operation *op);
792 void generateOpImplementations();
801bool ConvertMathToFuncsPass::isFPowIConvertible(math::FPowIOp op) {
804 return (expTy && expTy.getWidth() >= minWidthOfFPowIExponent);
807bool ConvertMathToFuncsPass::isConvertible(Operation *op) {
811void ConvertMathToFuncsPass::generateOpImplementations() {
812 ModuleOp module = getOperation();
814 module.walk([&](Operation *op) {
815 TypeSwitch<Operation *>(op)
816 .Case([&](math::CountLeadingZerosOp op) {
817 if (!convertCtlz || !isConvertible(op))
823 auto key = std::pair(op->
getName(), resultType);
824 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
828 .Case([&](math::IPowIOp op) {
829 if (!isConvertible(op))
836 auto key = std::pair(op->getName(), resultType);
837 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
841 .Case([&](math::FPowIOp op) {
842 if (!isFPowIConvertible(op))
851 auto key = std::pair(op->getName(), funcType);
852 auto entry = funcImpls.try_emplace(key, func::FuncOp{});
859void ConvertMathToFuncsPass::runOnOperation() {
860 ModuleOp module = getOperation();
863 generateOpImplementations();
866 patterns.add<VecOpToScalarOp<math::IPowIOp>, VecOpToScalarOp<math::FPowIOp>,
867 VecOpToScalarOp<math::CountLeadingZerosOp>>(
868 patterns.getContext());
871 auto getFuncOpByType = [&](Operation *op, Type type) -> func::FuncOp {
872 auto it = funcImpls.find(std::pair(op->
getName(), type));
873 if (it == funcImpls.end())
878 patterns.add<IPowIOpLowering, FPowIOpLowering>(patterns.getContext(),
882 patterns.add<CtlzOpLowering>(patterns.getContext(), getFuncOpByType);
885 target.addLegalDialect<arith::ArithDialect, cf::ControlFlowDialect,
886 func::FuncDialect, scf::SCFDialect,
887 vector::VectorDialect>();
889 target.addDynamicallyLegalOp<math::IPowIOp>(
890 [
this](math::IPowIOp op) {
return !isConvertible(op); });
892 target.addDynamicallyLegalOp<math::CountLeadingZerosOp>(
893 [
this](math::CountLeadingZerosOp op) {
return !isConvertible(op); });
895 target.addDynamicallyLegalOp<math::FPowIOp>(
896 [
this](math::FPowIOp op) {
return !isFPowIConvertible(op); });
897 if (
failed(applyPartialConversion(module,
target, std::move(patterns))))
static func::FuncOp createElementIPowIFunc(ModuleOp *module, Type elementType)
Create linkonce_odr function to implement the power function with the given elementType type inside m...
static FunctionType getElementalFuncTypeForOp(Operation *op)
static func::FuncOp createElementFPowIFunc(ModuleOp *module, FunctionType funcType)
Create linkonce_odr function to implement the power function with the given funcType type inside modu...
static func::FuncOp createCtlzFunc(ModuleOp *module, Type elementType)
Create function to implement the ctlz function the given elementType type inside module.
static std::string diag(const llvm::Value &value)
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.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
operand_type_iterator operand_type_end()
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...
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...