|
11 | 11 | //
|
12 | 12 | //===----------------------------------------------------------------------===//
|
13 | 13 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
|
| 14 | +#include "LLVMInlining.h" |
14 | 15 | #include "TypeDetail.h"
|
15 | 16 | #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
|
16 | 17 | #include "mlir/Dialect/LLVMIR/LLVMInterfaces.h"
|
|
22 | 23 | #include "mlir/IR/FunctionImplementation.h"
|
23 | 24 | #include "mlir/IR/MLIRContext.h"
|
24 | 25 | #include "mlir/IR/Matchers.h"
|
25 |
| -#include "mlir/Transforms/InliningUtils.h" |
26 | 26 |
|
27 | 27 | #include "llvm/ADT/SCCIterator.h"
|
28 | 28 | #include "llvm/ADT/TypeSwitch.h"
|
@@ -2777,237 +2777,6 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
|
2777 | 2777 | };
|
2778 | 2778 | } // namespace
|
2779 | 2779 |
|
2780 |
| -//===----------------------------------------------------------------------===// |
2781 |
| -// DialectInlinerInterface |
2782 |
| -//===----------------------------------------------------------------------===// |
2783 |
| - |
2784 |
| -/// Check whether the given alloca is an input to a lifetime intrinsic, |
2785 |
| -/// optionally passing through one or more casts on the way. This is not |
2786 |
| -/// transitive through block arguments. |
2787 |
| -static bool hasLifetimeMarkers(LLVM::AllocaOp allocaOp) { |
2788 |
| - SmallVector<Operation *> stack(allocaOp->getUsers().begin(), |
2789 |
| - allocaOp->getUsers().end()); |
2790 |
| - while (!stack.empty()) { |
2791 |
| - Operation *op = stack.pop_back_val(); |
2792 |
| - if (isa<LLVM::LifetimeStartOp, LLVM::LifetimeEndOp>(op)) |
2793 |
| - return true; |
2794 |
| - if (isa<LLVM::BitcastOp>(op)) |
2795 |
| - stack.append(op->getUsers().begin(), op->getUsers().end()); |
2796 |
| - } |
2797 |
| - return false; |
2798 |
| -} |
2799 |
| - |
2800 |
| -/// Move all alloca operations with a constant size in the former entry block of |
2801 |
| -/// the newly inlined callee into the entry block of the caller, and insert |
2802 |
| -/// lifetime intrinsics that limit their scope to the inlined blocks. |
2803 |
| -static void moveConstantAllocasToEntryBlock( |
2804 |
| - iterator_range<Region::iterator> inlinedBlocks) { |
2805 |
| - Block *calleeEntryBlock = &(*inlinedBlocks.begin()); |
2806 |
| - Block *callerEntryBlock = &(*calleeEntryBlock->getParent()->begin()); |
2807 |
| - if (calleeEntryBlock == callerEntryBlock) |
2808 |
| - // Nothing to do. |
2809 |
| - return; |
2810 |
| - SmallVector<std::tuple<LLVM::AllocaOp, IntegerAttr, bool>> allocasToMove; |
2811 |
| - bool shouldInsertLifetimes = false; |
2812 |
| - // Conservatively only move alloca operations that are part of the entry block |
2813 |
| - // and do not inspect nested regions, since they may execute conditionally or |
2814 |
| - // have other unknown semantics. |
2815 |
| - for (auto allocaOp : calleeEntryBlock->getOps<LLVM::AllocaOp>()) { |
2816 |
| - IntegerAttr arraySize; |
2817 |
| - if (!matchPattern(allocaOp.getArraySize(), m_Constant(&arraySize))) |
2818 |
| - continue; |
2819 |
| - bool shouldInsertLifetime = |
2820 |
| - arraySize.getValue() != 0 && !hasLifetimeMarkers(allocaOp); |
2821 |
| - shouldInsertLifetimes |= shouldInsertLifetime; |
2822 |
| - allocasToMove.emplace_back(allocaOp, arraySize, shouldInsertLifetime); |
2823 |
| - } |
2824 |
| - if (allocasToMove.empty()) |
2825 |
| - return; |
2826 |
| - OpBuilder builder(callerEntryBlock, callerEntryBlock->begin()); |
2827 |
| - for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) { |
2828 |
| - auto newConstant = builder.create<LLVM::ConstantOp>( |
2829 |
| - allocaOp->getLoc(), allocaOp.getArraySize().getType(), arraySize); |
2830 |
| - // Insert a lifetime start intrinsic where the alloca was before moving it. |
2831 |
| - if (shouldInsertLifetime) { |
2832 |
| - OpBuilder::InsertionGuard insertionGuard(builder); |
2833 |
| - builder.setInsertionPoint(allocaOp); |
2834 |
| - builder.create<LLVM::LifetimeStartOp>( |
2835 |
| - allocaOp.getLoc(), arraySize.getValue().getLimitedValue(), |
2836 |
| - allocaOp.getResult()); |
2837 |
| - } |
2838 |
| - allocaOp->moveAfter(newConstant); |
2839 |
| - allocaOp.getArraySizeMutable().assign(newConstant.getResult()); |
2840 |
| - } |
2841 |
| - if (!shouldInsertLifetimes) |
2842 |
| - return; |
2843 |
| - // Insert a lifetime end intrinsic before each return in the callee function. |
2844 |
| - for (Block &block : inlinedBlocks) { |
2845 |
| - if (!block.getTerminator()->hasTrait<OpTrait::ReturnLike>()) |
2846 |
| - continue; |
2847 |
| - builder.setInsertionPoint(block.getTerminator()); |
2848 |
| - for (auto &[allocaOp, arraySize, shouldInsertLifetime] : allocasToMove) { |
2849 |
| - if (!shouldInsertLifetime) |
2850 |
| - continue; |
2851 |
| - builder.create<LLVM::LifetimeEndOp>( |
2852 |
| - allocaOp.getLoc(), arraySize.getValue().getLimitedValue(), |
2853 |
| - allocaOp.getResult()); |
2854 |
| - } |
2855 |
| - } |
2856 |
| -} |
2857 |
| - |
2858 |
| -static Value handleByValArgument(OpBuilder &builder, Operation *callable, |
2859 |
| - Value argument, |
2860 |
| - NamedAttribute byValAttribute) { |
2861 |
| - auto func = cast<LLVM::LLVMFuncOp>(callable); |
2862 |
| - LLVM::MemoryEffectsAttr memoryEffects = func.getMemoryAttr(); |
2863 |
| - // If there is no memory effects attribute, assume that the function is |
2864 |
| - // not read-only. |
2865 |
| - bool isReadOnly = memoryEffects && |
2866 |
| - memoryEffects.getArgMem() != ModRefInfo::ModRef && |
2867 |
| - memoryEffects.getArgMem() != ModRefInfo::Mod; |
2868 |
| - if (isReadOnly) |
2869 |
| - return argument; |
2870 |
| - // Resolve the pointee type and its size. |
2871 |
| - auto ptrType = cast<LLVM::LLVMPointerType>(argument.getType()); |
2872 |
| - Type elementType = cast<TypeAttr>(byValAttribute.getValue()).getValue(); |
2873 |
| - unsigned int typeSize = |
2874 |
| - DataLayout(callable->getParentOfType<DataLayoutOpInterface>()) |
2875 |
| - .getTypeSize(elementType); |
2876 |
| - // Allocate the new value on the stack. |
2877 |
| - Value one = builder.create<LLVM::ConstantOp>( |
2878 |
| - func.getLoc(), builder.getI64Type(), builder.getI64IntegerAttr(1)); |
2879 |
| - Value allocaOp = |
2880 |
| - builder.create<LLVM::AllocaOp>(func.getLoc(), ptrType, elementType, one); |
2881 |
| - // Copy the pointee to the newly allocated value. |
2882 |
| - Value copySize = builder.create<LLVM::ConstantOp>( |
2883 |
| - func.getLoc(), builder.getI64Type(), builder.getI64IntegerAttr(typeSize)); |
2884 |
| - Value isVolatile = builder.create<LLVM::ConstantOp>( |
2885 |
| - func.getLoc(), builder.getI1Type(), builder.getBoolAttr(false)); |
2886 |
| - builder.create<LLVM::MemcpyOp>(func.getLoc(), allocaOp, argument, copySize, |
2887 |
| - isVolatile); |
2888 |
| - return allocaOp; |
2889 |
| -} |
2890 |
| - |
2891 |
| -namespace { |
2892 |
| -struct LLVMInlinerInterface : public DialectInlinerInterface { |
2893 |
| - using DialectInlinerInterface::DialectInlinerInterface; |
2894 |
| - |
2895 |
| - bool isLegalToInline(Operation *call, Operation *callable, |
2896 |
| - bool wouldBeCloned) const final { |
2897 |
| - if (!wouldBeCloned) |
2898 |
| - return false; |
2899 |
| - auto callOp = dyn_cast<LLVM::CallOp>(call); |
2900 |
| - auto funcOp = dyn_cast<LLVM::LLVMFuncOp>(callable); |
2901 |
| - if (!callOp || !funcOp) |
2902 |
| - return false; |
2903 |
| - if (auto attrs = funcOp.getArgAttrs()) { |
2904 |
| - for (Attribute attr : *attrs) { |
2905 |
| - auto attrDict = cast<DictionaryAttr>(attr); |
2906 |
| - for (NamedAttribute attr : attrDict) { |
2907 |
| - if (attr.getName() == LLVMDialect::getByValAttrName()) |
2908 |
| - continue; |
2909 |
| - // TODO: Handle all argument attributes; |
2910 |
| - return false; |
2911 |
| - } |
2912 |
| - } |
2913 |
| - } |
2914 |
| - // TODO: Handle result attributes; |
2915 |
| - if (funcOp.getResAttrs()) |
2916 |
| - return false; |
2917 |
| - // TODO: Handle exceptions. |
2918 |
| - if (funcOp.getPersonality()) |
2919 |
| - return false; |
2920 |
| - if (funcOp.getPassthrough()) { |
2921 |
| - // TODO: Used attributes should not be passthrough. |
2922 |
| - DenseSet<StringAttr> disallowed( |
2923 |
| - {StringAttr::get(funcOp->getContext(), "noduplicate"), |
2924 |
| - StringAttr::get(funcOp->getContext(), "noinline"), |
2925 |
| - StringAttr::get(funcOp->getContext(), "optnone"), |
2926 |
| - StringAttr::get(funcOp->getContext(), "presplitcoroutine"), |
2927 |
| - StringAttr::get(funcOp->getContext(), "returns_twice"), |
2928 |
| - StringAttr::get(funcOp->getContext(), "strictfp")}); |
2929 |
| - if (llvm::any_of(*funcOp.getPassthrough(), [&](Attribute attr) { |
2930 |
| - auto stringAttr = dyn_cast<StringAttr>(attr); |
2931 |
| - if (!stringAttr) |
2932 |
| - return false; |
2933 |
| - return disallowed.contains(stringAttr); |
2934 |
| - })) |
2935 |
| - return false; |
2936 |
| - } |
2937 |
| - return true; |
2938 |
| - } |
2939 |
| - |
2940 |
| - bool isLegalToInline(Region *, Region *, bool, IRMapping &) const final { |
2941 |
| - return true; |
2942 |
| - } |
2943 |
| - |
2944 |
| - /// Conservative allowlist of operations supported so far. |
2945 |
| - bool isLegalToInline(Operation *op, Region *, bool, IRMapping &) const final { |
2946 |
| - if (isPure(op)) |
2947 |
| - return true; |
2948 |
| - // Some attributes on memory operations require handling during |
2949 |
| - // inlining. Since this is not yet implemented, refuse to inline memory |
2950 |
| - // operations that have any of these attributes. |
2951 |
| - if (auto iface = dyn_cast<AliasAnalysisOpInterface>(op)) |
2952 |
| - if (iface.getAliasScopesOrNull() || iface.getNoAliasScopesOrNull()) |
2953 |
| - return false; |
2954 |
| - if (auto iface = dyn_cast<AccessGroupOpInterface>(op)) |
2955 |
| - if (iface.getAccessGroupsOrNull()) |
2956 |
| - return false; |
2957 |
| - return isa<LLVM::CallOp, LLVM::AllocaOp, LLVM::LifetimeStartOp, |
2958 |
| - LLVM::LifetimeEndOp, LLVM::LoadOp, LLVM::StoreOp>(op); |
2959 |
| - } |
2960 |
| - |
2961 |
| - /// Handle the given inlined return by replacing it with a branch. This |
2962 |
| - /// overload is called when the inlined region has more than one block. |
2963 |
| - void handleTerminator(Operation *op, Block *newDest) const final { |
2964 |
| - // Only return needs to be handled here. |
2965 |
| - auto returnOp = dyn_cast<LLVM::ReturnOp>(op); |
2966 |
| - if (!returnOp) |
2967 |
| - return; |
2968 |
| - |
2969 |
| - // Replace the return with a branch to the dest. |
2970 |
| - OpBuilder builder(op); |
2971 |
| - builder.create<LLVM::BrOp>(op->getLoc(), returnOp.getOperands(), newDest); |
2972 |
| - op->erase(); |
2973 |
| - } |
2974 |
| - |
2975 |
| - /// Handle the given inlined return by replacing the uses of the call with the |
2976 |
| - /// operands of the return. This overload is called when the inlined region |
2977 |
| - /// only contains one block. |
2978 |
| - void handleTerminator(Operation *op, |
2979 |
| - ArrayRef<Value> valuesToRepl) const final { |
2980 |
| - // Return will be the only terminator present. |
2981 |
| - auto returnOp = cast<LLVM::ReturnOp>(op); |
2982 |
| - |
2983 |
| - // Replace the values directly with the return operands. |
2984 |
| - assert(returnOp.getNumOperands() == valuesToRepl.size()); |
2985 |
| - for (const auto &[dst, src] : |
2986 |
| - llvm::zip(valuesToRepl, returnOp.getOperands())) |
2987 |
| - dst.replaceAllUsesWith(src); |
2988 |
| - } |
2989 |
| - |
2990 |
| - Value handleArgument(OpBuilder &builder, Operation *call, Operation *callable, |
2991 |
| - Value argument, Type targetType, |
2992 |
| - DictionaryAttr argumentAttrs) const final { |
2993 |
| - if (auto attr = argumentAttrs.getNamed(LLVMDialect::getByValAttrName())) |
2994 |
| - return handleByValArgument(builder, callable, argument, *attr); |
2995 |
| - return argument; |
2996 |
| - } |
2997 |
| - |
2998 |
| - void processInlinedCallBlocks( |
2999 |
| - Operation *call, |
3000 |
| - iterator_range<Region::iterator> inlinedBlocks) const override { |
3001 |
| - // Alloca operations with a constant size that were in the entry block of |
3002 |
| - // the callee should be moved to the entry block of the caller, as this will |
3003 |
| - // fold into prologue/epilogue code during code generation. |
3004 |
| - // This is not implemented as a standalone pattern because we need to know |
3005 |
| - // which newly inlined block was previously the entry block of the callee. |
3006 |
| - moveConstantAllocasToEntryBlock(inlinedBlocks); |
3007 |
| - } |
3008 |
| -}; |
3009 |
| -} // end anonymous namespace |
3010 |
| - |
3011 | 2780 | //===----------------------------------------------------------------------===//
|
3012 | 2781 | // LLVMDialect initialization, type parsing, and registration.
|
3013 | 2782 | //===----------------------------------------------------------------------===//
|
@@ -3037,9 +2806,9 @@ void LLVMDialect::initialize() {
|
3037 | 2806 | // Support unknown operations because not all LLVM operations are registered.
|
3038 | 2807 | allowUnknownOperations();
|
3039 | 2808 | // clang-format off
|
3040 |
| - addInterfaces<LLVMOpAsmDialectInterface, |
3041 |
| - LLVMInlinerInterface>(); |
| 2809 | + addInterfaces<LLVMOpAsmDialectInterface>(); |
3042 | 2810 | // clang-format on
|
| 2811 | + detail::addLLVMInlinerInterface(this); |
3043 | 2812 | }
|
3044 | 2813 |
|
3045 | 2814 | #define GET_OP_CLASSES
|
|
0 commit comments