| //===- Pattern.cpp - Conversion pattern to the LLVM dialect ---------------===// | |
| // | |
| // 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 "mlir/Conversion/LLVMCommon/Pattern.h" | |
| #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h" | |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" | |
| #include "mlir/Dialect/LLVMIR/LLVMTypes.h" | |
| #include "mlir/IR/AffineMap.h" | |
| #include "mlir/IR/BuiltinAttributes.h" | |
| #include "llvm/Support/CheckedArithmetic.h" | |
| #include "llvm/Support/MathExtras.h" | |
| using namespace mlir; | |
| //===----------------------------------------------------------------------===// | |
| // ConvertToLLVMPattern | |
| //===----------------------------------------------------------------------===// | |
| ConvertToLLVMPattern::ConvertToLLVMPattern( | |
| StringRef rootOpName, MLIRContext *context, | |
| const LLVMTypeConverter &typeConverter, PatternBenefit benefit) | |
| : ConversionPattern(typeConverter, rootOpName, benefit, context) {} | |
| const LLVMTypeConverter *ConvertToLLVMPattern::getTypeConverter() const { | |
| return static_cast<const LLVMTypeConverter *>( | |
| ConversionPattern::getTypeConverter()); | |
| } | |
| LLVM::LLVMDialect &ConvertToLLVMPattern::getDialect() const { | |
| return *getTypeConverter()->getDialect(); | |
| } | |
| Type ConvertToLLVMPattern::getIndexType() const { | |
| return getTypeConverter()->getIndexType(); | |
| } | |
| Type ConvertToLLVMPattern::getIntPtrType(unsigned addressSpace) const { | |
| return IntegerType::get(&getTypeConverter()->getContext(), | |
| getTypeConverter()->getPointerBitwidth(addressSpace)); | |
| } | |
| Type ConvertToLLVMPattern::getVoidType() const { | |
| return LLVM::LLVMVoidType::get(&getTypeConverter()->getContext()); | |
| } | |
| Type ConvertToLLVMPattern::getPtrType(unsigned addressSpace) const { | |
| return LLVM::LLVMPointerType::get(&getTypeConverter()->getContext(), | |
| addressSpace); | |
| } | |
| Type ConvertToLLVMPattern::getVoidPtrType() const { return getPtrType(); } | |
| Value ConvertToLLVMPattern::createIndexAttrConstant(OpBuilder &builder, | |
| Location loc, | |
| Type resultType, | |
| int64_t value) { | |
| return LLVM::ConstantOp::create(builder, loc, resultType, | |
| builder.getIndexAttr(value)); | |
| } | |
| Value ConvertToLLVMPattern::getStridedElementPtr( | |
| ConversionPatternRewriter &rewriter, Location loc, MemRefType type, | |
| Value memRefDesc, ValueRange indices, | |
| LLVM::GEPNoWrapFlags noWrapFlags) const { | |
| return LLVM::getStridedElementPtr(rewriter, loc, *getTypeConverter(), type, | |
| memRefDesc, indices, noWrapFlags); | |
| } | |
| // Check if the MemRefType `type` is supported by the lowering. We currently | |
| // only support memrefs with identity maps. | |
| bool ConvertToLLVMPattern::isConvertibleAndHasIdentityMaps( | |
| MemRefType type) const { | |
| if (!type.getLayout().isIdentity()) | |
| return false; | |
| return static_cast<bool>(typeConverter->convertType(type)); | |
| } | |
| Type ConvertToLLVMPattern::getElementPtrType(MemRefType type) const { | |
| auto addressSpace = getTypeConverter()->getMemRefAddressSpace(type); | |
| if (failed(addressSpace)) | |
| return {}; | |
| return LLVM::LLVMPointerType::get(type.getContext(), *addressSpace); | |
| } | |
| void ConvertToLLVMPattern::getMemRefDescriptorSizes( | |
| Location loc, MemRefType memRefType, ValueRange dynamicSizes, | |
| ConversionPatternRewriter &rewriter, SmallVectorImpl<Value> &sizes, | |
| SmallVectorImpl<Value> &strides, Value &size, bool sizeInBytes) const { | |
| assert(isConvertibleAndHasIdentityMaps(memRefType) && | |
| "layout maps must have been normalized away"); | |
| assert(count(memRefType.getShape(), ShapedType::kDynamic) == | |
| static_cast<ssize_t>(dynamicSizes.size()) && | |
| "dynamicSizes size doesn't match dynamic sizes count in memref shape"); | |
| sizes.reserve(memRefType.getRank()); | |
| unsigned dynamicIndex = 0; | |
| Type indexType = getIndexType(); | |
| for (int64_t size : memRefType.getShape()) { | |
| sizes.push_back( | |
| size == ShapedType::kDynamic | |
| ? dynamicSizes[dynamicIndex++] | |
| : createIndexAttrConstant(rewriter, loc, indexType, size)); | |
| } | |
| // Strides: iterate sizes in reverse order and multiply. | |
| int64_t stride = 1; | |
| bool overflowed = false; | |
| Value runningStride = createIndexAttrConstant(rewriter, loc, indexType, 1); | |
| strides.resize(memRefType.getRank()); | |
| for (auto i = memRefType.getRank(); i-- > 0;) { | |
| strides[i] = overflowed ? LLVM::PoisonOp::create(rewriter, loc, indexType) | |
| : runningStride; | |
| int64_t staticSize = memRefType.getShape()[i]; | |
| bool useSizeAsStride = stride == 1; | |
| if (staticSize == ShapedType::kDynamic) | |
| stride = ShapedType::kDynamic; | |
| if (stride != ShapedType::kDynamic) { | |
| std::optional<int64_t> res = llvm::checkedMul(stride, staticSize); | |
| if (!res) | |
| overflowed = true; | |
| else | |
| stride = res.value(); | |
| } | |
| if (overflowed) | |
| runningStride = LLVM::PoisonOp::create(rewriter, loc, indexType); | |
| else if (useSizeAsStride) | |
| runningStride = sizes[i]; | |
| else if (stride == ShapedType::kDynamic) | |
| runningStride = | |
| LLVM::MulOp::create(rewriter, loc, runningStride, sizes[i]); | |
| else | |
| runningStride = createIndexAttrConstant(rewriter, loc, indexType, stride); | |
| } | |
| if (sizeInBytes) { | |
| // Buffer size in bytes. | |
| Type elementType = typeConverter->convertType(memRefType.getElementType()); | |
| auto elementPtrType = LLVM::LLVMPointerType::get(rewriter.getContext()); | |
| Value nullPtr = LLVM::ZeroOp::create(rewriter, loc, elementPtrType); | |
| Value gepPtr = LLVM::GEPOp::create(rewriter, loc, elementPtrType, | |
| elementType, nullPtr, runningStride); | |
| size = LLVM::PtrToIntOp::create(rewriter, loc, getIndexType(), gepPtr); | |
| } else { | |
| size = runningStride; | |
| } | |
| } | |
| Value ConvertToLLVMPattern::getSizeInBytes( | |
| Location loc, Type type, ConversionPatternRewriter &rewriter) const { | |
| // Compute the size of an individual element. This emits the MLIR equivalent | |
| // of the following sizeof(...) implementation in LLVM IR: | |
| // %0 = getelementptr %elementType* null, %indexType 1 | |
| // %1 = ptrtoint %elementType* %0 to %indexType | |
| // which is a common pattern of getting the size of a type in bytes. | |
| Type llvmType = typeConverter->convertType(type); | |
| auto convertedPtrType = LLVM::LLVMPointerType::get(rewriter.getContext()); | |
| auto nullPtr = LLVM::ZeroOp::create(rewriter, loc, convertedPtrType); | |
| auto gep = LLVM::GEPOp::create(rewriter, loc, convertedPtrType, llvmType, | |
| nullPtr, ArrayRef<LLVM::GEPArg>{1}); | |
| return LLVM::PtrToIntOp::create(rewriter, loc, getIndexType(), gep); | |
| } | |
| Value ConvertToLLVMPattern::getNumElements( | |
| Location loc, MemRefType memRefType, ValueRange dynamicSizes, | |
| ConversionPatternRewriter &rewriter) const { | |
| assert(count(memRefType.getShape(), ShapedType::kDynamic) == | |
| static_cast<ssize_t>(dynamicSizes.size()) && | |
| "dynamicSizes size doesn't match dynamic sizes count in memref shape"); | |
| Type indexType = getIndexType(); | |
| Value numElements = memRefType.getRank() == 0 | |
| ? createIndexAttrConstant(rewriter, loc, indexType, 1) | |
| : nullptr; | |
| unsigned dynamicIndex = 0; | |
| // Compute the total number of memref elements. | |
| for (int64_t staticSize : memRefType.getShape()) { | |
| if (numElements) { | |
| Value size = | |
| staticSize == ShapedType::kDynamic | |
| ? dynamicSizes[dynamicIndex++] | |
| : createIndexAttrConstant(rewriter, loc, indexType, staticSize); | |
| numElements = LLVM::MulOp::create(rewriter, loc, numElements, size); | |
| } else { | |
| numElements = | |
| staticSize == ShapedType::kDynamic | |
| ? dynamicSizes[dynamicIndex++] | |
| : createIndexAttrConstant(rewriter, loc, indexType, staticSize); | |
| } | |
| } | |
| return numElements; | |
| } | |
| /// Creates and populates the memref descriptor struct given all its fields. | |
| MemRefDescriptor ConvertToLLVMPattern::createMemRefDescriptor( | |
| Location loc, MemRefType memRefType, Value allocatedPtr, Value alignedPtr, | |
| ArrayRef<Value> sizes, ArrayRef<Value> strides, | |
| ConversionPatternRewriter &rewriter) const { | |
| auto structType = typeConverter->convertType(memRefType); | |
| auto memRefDescriptor = MemRefDescriptor::poison(rewriter, loc, structType); | |
| // Field 1: Allocated pointer, used for malloc/free. | |
| memRefDescriptor.setAllocatedPtr(rewriter, loc, allocatedPtr); | |
| // Field 2: Actual aligned pointer to payload. | |
| memRefDescriptor.setAlignedPtr(rewriter, loc, alignedPtr); | |
| // Field 3: Offset in aligned pointer. | |
| Type indexType = getIndexType(); | |
| memRefDescriptor.setOffset( | |
| rewriter, loc, createIndexAttrConstant(rewriter, loc, indexType, 0)); | |
| // Fields 4: Sizes. | |
| for (const auto &en : llvm::enumerate(sizes)) | |
| memRefDescriptor.setSize(rewriter, loc, en.index(), en.value()); | |
| // Field 5: Strides. | |
| for (const auto &en : llvm::enumerate(strides)) | |
| memRefDescriptor.setStride(rewriter, loc, en.index(), en.value()); | |
| return memRefDescriptor; | |
| } | |
| Value ConvertToLLVMPattern::copyUnrankedDescriptor( | |
| OpBuilder &builder, Location loc, UnrankedMemRefType memRefType, | |
| Value operand, bool toDynamic) const { | |
| // Convert memory space. | |
| FailureOr<unsigned> addressSpace = | |
| getTypeConverter()->getMemRefAddressSpace(memRefType); | |
| if (failed(addressSpace)) | |
| return {}; | |
| // Get frequently used types. | |
| Type indexType = getTypeConverter()->getIndexType(); | |
| // Find the malloc and free, or declare them if necessary. | |
| auto module = builder.getInsertionPoint()->getParentOfType<ModuleOp>(); | |
| FailureOr<LLVM::LLVMFuncOp> freeFunc, mallocFunc; | |
| if (toDynamic) { | |
| mallocFunc = LLVM::lookupOrCreateMallocFn(builder, module, indexType); | |
| if (failed(mallocFunc)) | |
| return {}; | |
| } | |
| if (!toDynamic) { | |
| freeFunc = LLVM::lookupOrCreateFreeFn(builder, module); | |
| if (failed(freeFunc)) | |
| return {}; | |
| } | |
| UnrankedMemRefDescriptor desc(operand); | |
| Value allocationSize = UnrankedMemRefDescriptor::computeSize( | |
| builder, loc, *getTypeConverter(), desc, *addressSpace); | |
| // Allocate memory, copy, and free the source if necessary. | |
| Value memory = toDynamic | |
| ? LLVM::CallOp::create(builder, loc, mallocFunc.value(), | |
| allocationSize) | |
| .getResult() | |
| : LLVM::AllocaOp::create(builder, loc, getPtrType(), | |
| IntegerType::get(getContext(), 8), | |
| allocationSize, | |
| /*alignment=*/0); | |
| Value source = desc.memRefDescPtr(builder, loc); | |
| LLVM::MemcpyOp::create(builder, loc, memory, source, allocationSize, false); | |
| if (!toDynamic) | |
| LLVM::CallOp::create(builder, loc, freeFunc.value(), source); | |
| // Create a new descriptor. The same descriptor can be returned multiple | |
| // times, attempting to modify its pointer can lead to memory leaks | |
| // (allocated twice and overwritten) or double frees (the caller does not | |
| // know if the descriptor points to the same memory). | |
| Type descriptorType = getTypeConverter()->convertType(memRefType); | |
| if (!descriptorType) | |
| return {}; | |
| auto updatedDesc = | |
| UnrankedMemRefDescriptor::poison(builder, loc, descriptorType); | |
| Value rank = desc.rank(builder, loc); | |
| updatedDesc.setRank(builder, loc, rank); | |
| updatedDesc.setMemRefDescPtr(builder, loc, memory); | |
| return updatedDesc; | |
| } | |
| LogicalResult ConvertToLLVMPattern::copyUnrankedDescriptors( | |
| OpBuilder &builder, Location loc, TypeRange origTypes, | |
| SmallVectorImpl<Value> &operands, bool toDynamic) const { | |
| assert(origTypes.size() == operands.size() && | |
| "expected as may original types as operands"); | |
| for (unsigned i = 0, e = operands.size(); i < e; ++i) { | |
| if (auto memRefType = dyn_cast<UnrankedMemRefType>(origTypes[i])) { | |
| Value updatedDesc = copyUnrankedDescriptor(builder, loc, memRefType, | |
| operands[i], toDynamic); | |
| if (!updatedDesc) | |
| return failure(); | |
| operands[i] = updatedDesc; | |
| } | |
| } | |
| return success(); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Detail methods | |
| //===----------------------------------------------------------------------===// | |
| /// Replaces the given operation "op" with a new operation of type "targetOp" | |
| /// and given operands. | |
| LogicalResult LLVM::detail::oneToOneRewrite( | |
| Operation *op, StringRef targetOp, ValueRange operands, | |
| ArrayRef<NamedAttribute> targetAttrs, Attribute propertiesAttr, | |
| const LLVMTypeConverter &typeConverter, | |
| ConversionPatternRewriter &rewriter) { | |
| unsigned numResults = op->getNumResults(); | |
| SmallVector<Type> resultTypes; | |
| if (numResults != 0) { | |
| resultTypes.push_back( | |
| typeConverter.packOperationResults(op->getResultTypes())); | |
| if (!resultTypes.back()) | |
| return failure(); | |
| } | |
| // Create the operation through state since we don't know its C++ type. | |
| OperationState state(op->getLoc(), rewriter.getStringAttr(targetOp), operands, | |
| resultTypes, targetAttrs); | |
| state.propertiesAttr = propertiesAttr; | |
| Operation *newOp = rewriter.create(state); | |
| // If the operation produced 0 or 1 result, return them immediately. | |
| if (numResults == 0) | |
| return rewriter.eraseOp(op), success(); | |
| if (numResults == 1) | |
| return rewriter.replaceOp(op, newOp->getResult(0)), success(); | |
| // Otherwise, it had been converted to an operation producing a structure. | |
| // Extract individual results from the structure and return them as list. | |
| SmallVector<Value, 4> results; | |
| results.reserve(numResults); | |
| for (unsigned i = 0; i < numResults; ++i) { | |
| results.push_back(LLVM::ExtractValueOp::create(rewriter, op->getLoc(), | |
| newOp->getResult(0), i)); | |
| } | |
| rewriter.replaceOp(op, results); | |
| return success(); | |
| } | |
| LogicalResult LLVM::detail::intrinsicRewrite( | |
| Operation *op, StringRef intrinsic, ValueRange operands, | |
| const LLVMTypeConverter &typeConverter, RewriterBase &rewriter) { | |
| auto loc = op->getLoc(); | |
| if (!llvm::all_of(operands, [](Value value) { | |
| return LLVM::isCompatibleType(value.getType()); | |
| })) | |
| return failure(); | |
| unsigned numResults = op->getNumResults(); | |
| Type resType; | |
| if (numResults != 0) | |
| resType = typeConverter.packOperationResults(op->getResultTypes()); | |
| auto callIntrOp = LLVM::CallIntrinsicOp::create( | |
| rewriter, loc, resType, rewriter.getStringAttr(intrinsic), operands); | |
| // Propagate attributes. | |
| callIntrOp->setAttrs(op->getAttrDictionary()); | |
| if (numResults <= 1) { | |
| // Directly replace the original op. | |
| rewriter.replaceOp(op, callIntrOp); | |
| return success(); | |
| } | |
| // Extract individual results from packed structure and use them as | |
| // replacements. | |
| SmallVector<Value, 4> results; | |
| results.reserve(numResults); | |
| Value intrRes = callIntrOp.getResults(); | |
| for (unsigned i = 0; i < numResults; ++i) | |
| results.push_back(LLVM::ExtractValueOp::create(rewriter, loc, intrRes, i)); | |
| rewriter.replaceOp(op, results); | |
| return success(); | |
| } | |
| static unsigned getBitWidth(Type type) { | |
| if (type.isIntOrFloat()) | |
| return type.getIntOrFloatBitWidth(); | |
| auto vec = cast<VectorType>(type); | |
| assert(!vec.isScalable() && "scalable vectors are not supported"); | |
| return vec.getNumElements() * getBitWidth(vec.getElementType()); | |
| } | |
| /// Returns true if every leaf in `type` (recursing through LLVM arrays and | |
| /// structs) is either equal to `dstType` or has a fixed bit width. | |
| static bool isFixedSizeAggregate(Type type, Type dstType) { | |
| if (type == dstType) | |
| return true; | |
| if (auto arrayType = dyn_cast<LLVM::LLVMArrayType>(type)) | |
| return isFixedSizeAggregate(arrayType.getElementType(), dstType); | |
| if (auto structType = dyn_cast<LLVM::LLVMStructType>(type)) | |
| return llvm::all_of(structType.getBody(), [&](Type fieldType) { | |
| return isFixedSizeAggregate(fieldType, dstType); | |
| }); | |
| if (auto vecTy = dyn_cast<VectorType>(type)) | |
| return !vecTy.isScalable(); | |
| return type.isIntOrFloat(); | |
| } | |
| static Value createI32Constant(OpBuilder &builder, Location loc, | |
| int32_t value) { | |
| Type i32 = builder.getI32Type(); | |
| return LLVM::ConstantOp::create(builder, loc, i32, value); | |
| } | |
| /// Recursive implementation of decomposeValue. When | |
| /// `permitVariablySizedScalars` is false, callers must ensure | |
| /// isFixedSizeAggregate() holds before calling this. | |
| static void decomposeValueImpl(OpBuilder &builder, Location loc, Value src, | |
| Type dstType, SmallVectorImpl<Value> &result) { | |
| Type srcType = src.getType(); | |
| if (srcType == dstType) { | |
| result.push_back(src); | |
| return; | |
| } | |
| if (auto arrayType = dyn_cast<LLVM::LLVMArrayType>(srcType)) { | |
| for (auto i : llvm::seq(arrayType.getNumElements())) { | |
| Value elem = LLVM::ExtractValueOp::create(builder, loc, src, i); | |
| decomposeValueImpl(builder, loc, elem, dstType, result); | |
| } | |
| return; | |
| } | |
| if (auto structType = dyn_cast<LLVM::LLVMStructType>(srcType)) { | |
| for (auto [i, fieldType] : llvm::enumerate(structType.getBody())) { | |
| Value field = LLVM::ExtractValueOp::create(builder, loc, src, | |
| static_cast<int64_t>(i)); | |
| decomposeValueImpl(builder, loc, field, dstType, result); | |
| } | |
| return; | |
| } | |
| // Variably sized leaf types (e.g., ptr) — pass through as-is. | |
| if (!srcType.isIntOrFloat() && !isa<VectorType>(srcType)) { | |
| result.push_back(src); | |
| return; | |
| } | |
| unsigned srcBitWidth = getBitWidth(srcType); | |
| unsigned dstBitWidth = getBitWidth(dstType); | |
| if (srcBitWidth == dstBitWidth) { | |
| Value cast = LLVM::BitcastOp::create(builder, loc, dstType, src); | |
| result.push_back(cast); | |
| return; | |
| } | |
| if (dstBitWidth > srcBitWidth) { | |
| auto smallerInt = builder.getIntegerType(srcBitWidth); | |
| if (srcType != smallerInt) | |
| src = LLVM::BitcastOp::create(builder, loc, smallerInt, src); | |
| auto largerInt = builder.getIntegerType(dstBitWidth); | |
| Value res = LLVM::ZExtOp::create(builder, loc, largerInt, src); | |
| result.push_back(res); | |
| return; | |
| } | |
| int64_t numElements = llvm::divideCeil(srcBitWidth, dstBitWidth); | |
| int64_t roundedBitWidth = numElements * dstBitWidth; | |
| // Pad out values that don't decompose evenly before creating a vector. | |
| if (roundedBitWidth != srcBitWidth) { | |
| auto srcInt = builder.getIntegerType(srcBitWidth); | |
| if (srcType != srcInt) | |
| src = LLVM::BitcastOp::create(builder, loc, srcInt, src); | |
| auto roundedInt = builder.getIntegerType(roundedBitWidth); | |
| src = LLVM::ZExtOp::create(builder, loc, roundedInt, src); | |
| } | |
| auto vecType = VectorType::get(numElements, dstType); | |
| src = LLVM::BitcastOp::create(builder, loc, vecType, src); | |
| for (auto i : llvm::seq(numElements)) { | |
| Value idx = createI32Constant(builder, loc, i); | |
| Value elem = LLVM::ExtractElementOp::create(builder, loc, src, idx); | |
| result.push_back(elem); | |
| } | |
| } | |
| LogicalResult mlir::LLVM::decomposeValue(OpBuilder &builder, Location loc, | |
| Value src, Type dstType, | |
| SmallVectorImpl<Value> &result, | |
| bool permitVariablySizedScalars) { | |
| // Check the type tree before emitting any IR, so that a failing pattern | |
| // leaves the IR unmodified. | |
| if (!permitVariablySizedScalars && | |
| !isFixedSizeAggregate(src.getType(), dstType)) | |
| return failure(); | |
| decomposeValueImpl(builder, loc, src, dstType, result); | |
| return success(); | |
| } | |
| /// Recursive implementation of composeValue. Consumes elements from `src` | |
| /// starting at `offset`, advancing it past the consumed elements. | |
| static Value composeValueImpl(OpBuilder &builder, Location loc, ValueRange src, | |
| size_t &offset, Type dstType) { | |
| if (auto arrayType = dyn_cast<LLVM::LLVMArrayType>(dstType)) { | |
| Value result = LLVM::PoisonOp::create(builder, loc, arrayType); | |
| Type elemType = arrayType.getElementType(); | |
| for (auto i : llvm::seq(arrayType.getNumElements())) { | |
| Value elem = composeValueImpl(builder, loc, src, offset, elemType); | |
| result = LLVM::InsertValueOp::create(builder, loc, result, elem, i); | |
| } | |
| return result; | |
| } | |
| if (auto structType = dyn_cast<LLVM::LLVMStructType>(dstType)) { | |
| Value result = LLVM::PoisonOp::create(builder, loc, structType); | |
| for (auto [i, fieldType] : llvm::enumerate(structType.getBody())) { | |
| Value field = composeValueImpl(builder, loc, src, offset, fieldType); | |
| result = LLVM::InsertValueOp::create(builder, loc, result, field, | |
| static_cast<int64_t>(i)); | |
| } | |
| return result; | |
| } | |
| // Variably sized leaf types (e.g., ptr) — consume and return as-is. | |
| if (!dstType.isIntOrFloat() && !isa<VectorType>(dstType)) | |
| return src[offset++]; | |
| unsigned dstBitWidth = getBitWidth(dstType); | |
| Value front = src[offset]; | |
| if (front.getType() == dstType) { | |
| ++offset; | |
| return front; | |
| } | |
| // Single element wider than or equal to dst: bitcast/trunc. | |
| if (front.getType().isIntOrFloat() || isa<VectorType>(front.getType())) { | |
| unsigned srcBitWidth = getBitWidth(front.getType()); | |
| if (srcBitWidth >= dstBitWidth) { | |
| ++offset; | |
| Value res = front; | |
| if (dstBitWidth < srcBitWidth) { | |
| auto largerInt = builder.getIntegerType(srcBitWidth); | |
| if (res.getType() != largerInt) | |
| res = LLVM::BitcastOp::create(builder, loc, largerInt, res); | |
| auto smallerInt = builder.getIntegerType(dstBitWidth); | |
| res = LLVM::TruncOp::create(builder, loc, smallerInt, res); | |
| } | |
| if (res.getType() != dstType) | |
| res = LLVM::BitcastOp::create(builder, loc, dstType, res); | |
| return res; | |
| } | |
| } | |
| // Multiple elements narrower than dst: gather into a vector and bitcast. | |
| unsigned elemBitWidth = getBitWidth(front.getType()); | |
| int64_t numElements = llvm::divideCeil(dstBitWidth, elemBitWidth); | |
| int64_t roundedBitWidth = numElements * elemBitWidth; | |
| auto vecType = VectorType::get(numElements, front.getType()); | |
| Value res = LLVM::PoisonOp::create(builder, loc, vecType); | |
| for (auto i : llvm::seq(numElements)) { | |
| Value idx = createI32Constant(builder, loc, i); | |
| res = LLVM::InsertElementOp::create(builder, loc, vecType, res, | |
| src[offset++], idx); | |
| } | |
| // Undo any padding decomposition might have introduced. | |
| if (roundedBitWidth != dstBitWidth) { | |
| auto roundedInt = builder.getIntegerType(roundedBitWidth); | |
| res = LLVM::BitcastOp::create(builder, loc, roundedInt, res); | |
| auto dstInt = builder.getIntegerType(dstBitWidth); | |
| res = LLVM::TruncOp::create(builder, loc, dstInt, res); | |
| if (dstType != dstInt) | |
| res = LLVM::BitcastOp::create(builder, loc, dstType, res); | |
| } else { | |
| if (res.getType() != dstType) | |
| res = LLVM::BitcastOp::create(builder, loc, dstType, res); | |
| } | |
| return res; | |
| } | |
| Value mlir::LLVM::composeValue(OpBuilder &builder, Location loc, ValueRange src, | |
| Type dstType) { | |
| assert(!src.empty() && "src range must not be empty"); | |
| size_t offset = 0; | |
| Value result = composeValueImpl(builder, loc, src, offset, dstType); | |
| assert(offset == src.size() && "not all decomposed values were consumed"); | |
| return result; | |
| } | |
| Value mlir::LLVM::getStridedElementPtr(OpBuilder &builder, Location loc, | |
| const LLVMTypeConverter &converter, | |
| MemRefType type, Value memRefDesc, | |
| ValueRange indices, | |
| LLVM::GEPNoWrapFlags noWrapFlags) { | |
| auto [strides, offset] = type.getStridesAndOffset(); | |
| MemRefDescriptor memRefDescriptor(memRefDesc); | |
| // Use a canonical representation of the start address so that later | |
| // optimizations have a longer sequence of instructions to CSE. | |
| // If we don't do that we would sprinkle the memref.offset in various | |
| // position of the different address computations. | |
| Value base = memRefDescriptor.bufferPtr(builder, loc, converter, type); | |
| LLVM::IntegerOverflowFlags intOverflowFlags = | |
| LLVM::IntegerOverflowFlags::none; | |
| if (LLVM::bitEnumContainsAny(noWrapFlags, LLVM::GEPNoWrapFlags::nusw)) { | |
| intOverflowFlags = intOverflowFlags | LLVM::IntegerOverflowFlags::nsw; | |
| } | |
| if (LLVM::bitEnumContainsAny(noWrapFlags, LLVM::GEPNoWrapFlags::nuw)) { | |
| intOverflowFlags = intOverflowFlags | LLVM::IntegerOverflowFlags::nuw; | |
| } | |
| Type indexType = converter.getIndexType(); | |
| Value index; | |
| for (int i = 0, e = indices.size(); i < e; ++i) { | |
| Value increment = indices[i]; | |
| if (strides[i] != 1) { // Skip if stride is 1. | |
| Value stride = | |
| ShapedType::isDynamic(strides[i]) | |
| ? memRefDescriptor.stride(builder, loc, i) | |
| : LLVM::ConstantOp::create(builder, loc, indexType, | |
| builder.getIndexAttr(strides[i])); | |
| increment = LLVM::MulOp::create(builder, loc, increment, stride, | |
| intOverflowFlags); | |
| } | |
| index = index ? LLVM::AddOp::create(builder, loc, index, increment, | |
| intOverflowFlags) | |
| : increment; | |
| } | |
| Type elementPtrType = memRefDescriptor.getElementPtrType(); | |
| return index | |
| ? LLVM::GEPOp::create(builder, loc, elementPtrType, | |
| converter.convertType(type.getElementType()), | |
| base, index, noWrapFlags) | |
| : base; | |
| } | |
| /// Return the given type if it's a floating point type. If the given type is | |
| /// a vector type, return its element type if it's a floating point type. | |
| static FloatType getFloatingPointType(Type type) { | |
| if (auto floatType = dyn_cast<FloatType>(type)) | |
| return floatType; | |
| if (auto vecType = dyn_cast<VectorType>(type)) | |
| return dyn_cast<FloatType>(vecType.getElementType()); | |
| return nullptr; | |
| } | |
| bool LLVM::detail::isUnsupportedFloatingPointType( | |
| const TypeConverter &typeConverter, Type type) { | |
| FloatType floatType = getFloatingPointType(type); | |
| if (!floatType) | |
| return false; | |
| Type convertedType = typeConverter.convertType(floatType); | |
| if (!convertedType) | |
| return true; | |
| return !isa<FloatType>(convertedType); | |
| } | |
| bool LLVM::detail::opHasUnsupportedFloatingPointTypes( | |
| Operation *op, const TypeConverter &typeConverter) { | |
| for (Value operand : op->getOperands()) | |
| if (isUnsupportedFloatingPointType(typeConverter, operand.getType())) | |
| return true; | |
| return llvm::any_of(op->getResults(), [&typeConverter](OpResult r) { | |
| return isUnsupportedFloatingPointType(typeConverter, r.getType()); | |
| }); | |
| } |