| //===-- FIRToMemRef.cpp - Convert FIR loads and stores to MemRef ---------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This pass lowers FIR dialect memory operations to the MemRef dialect. |
| // In particular it: |
| // |
| // - Rewrites `fir.alloca` to `memref.alloca`. |
| // |
| // - Rewrites `fir.load` / `fir.store` to `memref.load` / `memref.store`. |
| // |
| // - Allows FIR and MemRef to coexist by introducing `fir.convert` at |
| // memory-use sites. Memory operations (`memref.load`, `memref.store`, |
| // `memref.reinterpret_cast`, etc.) see MemRef-typed values, while the |
| // original FIR-typed values remain available for non-memory uses. For |
| // example: |
| // |
| // %fir_ref = ... : !fir.ref<!fir.array<...>> |
| // %memref = fir.convert %fir_ref |
| // : !fir.ref<!fir.array<...>> -> memref<...> |
| // %val = memref.load %memref[...] : memref<...> |
| // fir.call @callee(%fir_ref) : (!fir.ref<!fir.array<...>>) -> () |
| // |
| // Here the MemRef-typed value is used for `memref.load`, while the |
| // original FIR-typed value is preserved for `fir.call`. |
| // |
| // - Computes shapes, strides, and indices as needed for slices and shifts |
| // and emits `memref.reinterpret_cast` when dynamic layout is required |
| // (TODO: use memref.cast instead). |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Optimizer/Builder/CUFCommon.h" |
| #include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h" |
| #include "flang/Optimizer/Dialect/FIROps.h" |
| #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
| #include "flang/Optimizer/Dialect/FIRType.h" |
| #include "flang/Optimizer/Dialect/Support/FIRContext.h" |
| #include "flang/Optimizer/Dialect/Support/KindMapping.h" |
| #include "flang/Optimizer/Transforms/FIRToMemRefTypeConverter.h" |
| #include "flang/Optimizer/Transforms/Passes.h" |
| #include "mlir/Dialect/Arith/IR/Arith.h" |
| #include "mlir/Dialect/Func/IR/FuncOps.h" |
| #include "mlir/Dialect/MemRef/IR/MemRef.h" |
| #include "mlir/Dialect/OpenACC/OpenACC.h" |
| #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
| #include "mlir/Dialect/SCF/IR/SCF.h" |
| #include "mlir/IR/Block.h" |
| #include "mlir/IR/Builders.h" |
| #include "mlir/IR/BuiltinAttributes.h" |
| #include "mlir/IR/BuiltinTypes.h" |
| #include "mlir/IR/Dominance.h" |
| #include "mlir/IR/Location.h" |
| #include "mlir/IR/MLIRContext.h" |
| #include "mlir/IR/Operation.h" |
| #include "mlir/IR/PatternMatch.h" |
| #include "mlir/IR/Region.h" |
| #include "mlir/IR/Value.h" |
| #include "mlir/IR/ValueRange.h" |
| #include "mlir/IR/Verifier.h" |
| #include "mlir/Pass/Pass.h" |
| #include "mlir/Support/LLVM.h" |
| #include "mlir/Transforms/DialectConversion.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #define DEBUG_TYPE "fir-to-memref" |
| |
| using namespace mlir; |
| |
| namespace fir { |
| |
| #define GEN_PASS_DEF_FIRTOMEMREF |
| #include "flang/Optimizer/Transforms/Passes.h.inc" |
| |
| static bool isMarshalLike(Operation *op) { |
| auto convert = dyn_cast_if_present<fir::ConvertOp>(op); |
| if (!convert) |
| return false; |
| |
| bool resIsMemRef = isa<MemRefType>(convert.getType()); |
| bool argIsMemRef = isa<MemRefType>(convert.getValue().getType()); |
| |
| assert(!(resIsMemRef && argIsMemRef) && |
| "unexpected fir.convert memref -> memref in isMarshalLike"); |
| |
| return resIsMemRef || argIsMemRef; |
| } |
| |
| using MemRefInfo = FailureOr<std::pair<Value, SmallVector<Value>>>; |
| |
| static llvm::cl::opt<bool> enableFIRConvertOptimizations( |
| "enable-fir-convert-opts", |
| llvm::cl::desc("enable emilinating redundant fir.convert in FIR-to-MemRef"), |
| llvm::cl::init(false), llvm::cl::Hidden); |
| |
| class FIRToMemRef : public fir::impl::FIRToMemRefBase<FIRToMemRef> { |
| public: |
| void runOnOperation() override; |
| |
| private: |
| llvm::SmallSetVector<Operation *, 32> eraseOps; |
| |
| DominanceInfo *domInfo = nullptr; |
| |
| void rewriteAlloca(fir::AllocaOp, PatternRewriter &, |
| FIRToMemRefTypeConverter &); |
| |
| void rewriteLoadOp(fir::LoadOp, PatternRewriter &, |
| FIRToMemRefTypeConverter &); |
| |
| void rewriteStoreOp(fir::StoreOp, PatternRewriter &, |
| FIRToMemRefTypeConverter &); |
| |
| MemRefInfo getMemRefInfo(Value, PatternRewriter &, FIRToMemRefTypeConverter &, |
| Operation *); |
| |
| MemRefInfo convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp, |
| PatternRewriter &, FIRToMemRefTypeConverter &); |
| |
| void replaceFIRMemrefs(Value, Value, PatternRewriter &) const; |
| |
| FailureOr<Value> getFIRConvert(Operation *memOp, Operation *memref, |
| PatternRewriter &, FIRToMemRefTypeConverter &); |
| |
| FailureOr<SmallVector<Value>> getMemrefIndices(fir::ArrayCoorOp, Operation *, |
| PatternRewriter &, Value, |
| Value) const; |
| |
| bool memrefIsOptional(Operation *) const; |
| |
| Value canonicalizeIndex(Value, PatternRewriter &) const; |
| |
| // Logical section information used by FIRToMemRef. For projected slices, the |
| // descriptor still owns the physical layout, so `sliceVec` intentionally |
| // stays empty while `shapeVec`/`shiftVec` remain available for index math. |
| struct SliceInfo { |
| SmallVector<Value> shapeVec; |
| SmallVector<Value> shiftVec; |
| SmallVector<Value> sliceVec; |
| bool hasProjectedSlice = false; |
| }; |
| |
| template <typename OpTy> |
| void collectSliceInfoFrom(OpTy op, SliceInfo &info) const; |
| |
| void populateShapeAndShift(SmallVectorImpl<Value> &shapeVec, |
| SmallVectorImpl<Value> &shiftVec, |
| fir::ShapeShiftOp shift) const; |
| |
| void populateShift(SmallVectorImpl<Value> &vec, fir::ShiftOp shift) const; |
| |
| void populateShape(SmallVectorImpl<Value> &vec, fir::ShapeOp shape) const; |
| |
| static fir::SliceOp getSliceOp(Value sliceVal) { |
| return sliceVal ? sliceVal.getDefiningOp<fir::SliceOp>() : fir::SliceOp{}; |
| } |
| |
| static bool hasProjectedSlice(fir::SliceOp sliceOp) { |
| return sliceOp && !sliceOp.getFields().empty(); |
| } |
| |
| unsigned getRankFromEmbox(fir::EmboxOp embox) const { |
| auto memrefType = embox.getMemref().getType(); |
| Type unwrappedType = fir::unwrapRefType(memrefType); |
| if (auto seqType = dyn_cast<fir::SequenceType>(unwrappedType)) |
| return seqType.getDimension(); |
| return 0; |
| } |
| |
| bool isCompilerGeneratedAlloca(Operation *op) const; |
| |
| void copyAttribute(Operation *from, Operation *to, |
| llvm::StringRef name) const; |
| |
| Type getBaseType(Type type, bool complexBaseTypes = false) const; |
| |
| bool memrefIsDeviceData(Operation *memref) const; |
| |
| mlir::Attribute findCudaDataAttr(Value val) const; |
| |
| Value materializeBoxAddressIfNeeded(Value basePtr, PatternRewriter &rewriter, |
| Location loc) const; |
| }; |
| |
| void FIRToMemRef::populateShapeAndShift(SmallVectorImpl<Value> &shapeVec, |
| SmallVectorImpl<Value> &shiftVec, |
| fir::ShapeShiftOp shift) const { |
| for (mlir::OperandRange::iterator i = shift.getPairs().begin(), |
| endIter = shift.getPairs().end(); |
| i != endIter;) { |
| shiftVec.push_back(*i++); |
| shapeVec.push_back(*i++); |
| } |
| } |
| |
| bool FIRToMemRef::isCompilerGeneratedAlloca(Operation *op) const { |
| if (!isa<fir::AllocaOp, memref::AllocaOp>(op)) |
| llvm_unreachable("expected alloca op"); |
| |
| return !op->getAttr("bindc_name") && !op->getAttr("uniq_name"); |
| } |
| |
| void FIRToMemRef::copyAttribute(Operation *from, Operation *to, |
| llvm::StringRef name) const { |
| if (Attribute value = from->getAttr(name)) |
| to->setAttr(name, value); |
| } |
| |
| Type FIRToMemRef::getBaseType(Type type, bool complexBaseTypes) const { |
| if (fir::isa_fir_type(type)) { |
| type = fir::getFortranElementType(type); |
| } else if (auto memrefTy = dyn_cast<MemRefType>(type)) { |
| type = memrefTy.getElementType(); |
| } |
| |
| if (!complexBaseTypes) |
| if (auto complexTy = dyn_cast<ComplexType>(type)) |
| type = complexTy.getElementType(); |
| return type; |
| } |
| |
| bool FIRToMemRef::memrefIsDeviceData(Operation *memref) const { |
| if (isa<ACC_DATA_ENTRY_OPS>(memref)) |
| return true; |
| |
| return cuf::hasDeviceDataAttr(memref); |
| } |
| |
| mlir::Attribute FIRToMemRef::findCudaDataAttr(Value val) const { |
| Value currentVal = val; |
| llvm::SmallPtrSet<Operation *, 8> visited; |
| |
| while (currentVal) { |
| Operation *defOp = currentVal.getDefiningOp(); |
| if (!defOp || !visited.insert(defOp).second) |
| break; |
| |
| if (cuf::DataAttributeAttr cudaAttr = cuf::getDataAttr(defOp)) |
| return cudaAttr; |
| |
| // TODO: This is a best-effort backward walk; it is easy to miss attributes |
| // as FIR evolves. Long term, it would be preferable if the necessary |
| // information was carried in the type system (or otherwise made available |
| // without relying on a walk-back through defining ops). |
| if (auto reboxOp = dyn_cast<fir::ReboxOp>(defOp)) { |
| currentVal = reboxOp.getBox(); |
| } else if (auto convertOp = dyn_cast<fir::ConvertOp>(defOp)) { |
| currentVal = convertOp->getOperand(0); |
| } else if (auto emboxOp = dyn_cast<fir::EmboxOp>(defOp)) { |
| currentVal = emboxOp.getMemref(); |
| } else if (auto boxAddrOp = dyn_cast<fir::BoxAddrOp>(defOp)) { |
| currentVal = boxAddrOp.getVal(); |
| } else if (auto declareOp = dyn_cast<fir::DeclareOp>(defOp)) { |
| currentVal = declareOp.getMemref(); |
| } else { |
| break; |
| } |
| } |
| return nullptr; |
| } |
| |
| Value FIRToMemRef::materializeBoxAddressIfNeeded(Value basePtr, |
| PatternRewriter &rewriter, |
| Location loc) const { |
| if (!isa<fir::BoxType>(basePtr.getType())) |
| return basePtr; |
| |
| auto boxAddrOp = fir::BoxAddrOp::create(rewriter, loc, basePtr); |
| if (auto cudaAttr = findCudaDataAttr(basePtr)) |
| boxAddrOp->setAttr(cuf::getDataAttrName(), cudaAttr); |
| return boxAddrOp.getResult(); |
| } |
| |
| void FIRToMemRef::populateShift(SmallVectorImpl<Value> &vec, |
| fir::ShiftOp shift) const { |
| vec.append(shift.getOrigins().begin(), shift.getOrigins().end()); |
| } |
| |
| void FIRToMemRef::populateShape(SmallVectorImpl<Value> &vec, |
| fir::ShapeOp shape) const { |
| vec.append(shape.getExtents().begin(), shape.getExtents().end()); |
| } |
| |
| template <typename OpTy> |
| void FIRToMemRef::collectSliceInfoFrom(OpTy op, SliceInfo &info) const { |
| if constexpr (std::is_same_v<OpTy, fir::ArrayCoorOp> || |
| std::is_same_v<OpTy, fir::ReboxOp> || |
| std::is_same_v<OpTy, fir::EmboxOp>) { |
| Value shapeVal = op.getShape(); |
| |
| if (shapeVal) { |
| Operation *shapeValOp = shapeVal.getDefiningOp(); |
| |
| if (auto shapeOp = dyn_cast<fir::ShapeOp>(shapeValOp)) { |
| populateShape(info.shapeVec, shapeOp); |
| } else if (auto shapeShiftOp = dyn_cast<fir::ShapeShiftOp>(shapeValOp)) { |
| populateShapeAndShift(info.shapeVec, info.shiftVec, shapeShiftOp); |
| } else if (auto shiftOp = dyn_cast<fir::ShiftOp>(shapeValOp)) { |
| populateShift(info.shiftVec, shiftOp); |
| } |
| } |
| |
| if (auto sliceOp = getSliceOp(op.getSlice())) { |
| // A slice path changes the physical projection of the boxed entity (for |
| // example, `complex -> real` for `%re`). Preserve shape/shift for logical |
| // indexing, but do not treat the triplets alone as layout information. |
| if (hasProjectedSlice(sliceOp)) { |
| info.hasProjectedSlice = true; |
| } else { |
| auto triples = sliceOp.getTriples(); |
| info.sliceVec.append(triples.begin(), triples.end()); |
| } |
| } |
| } |
| } |
| |
| void FIRToMemRef::rewriteAlloca(fir::AllocaOp firAlloca, |
| PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| if (!typeConverter.convertibleType(firAlloca.getInType())) |
| return; |
| |
| if (typeConverter.isEmptyArray(firAlloca.getType())) |
| return; |
| |
| rewriter.setInsertionPointAfter(firAlloca); |
| |
| Type type = firAlloca.getType(); |
| MemRefType memrefTy = typeConverter.convertMemrefType(type); |
| |
| Location loc = firAlloca.getLoc(); |
| |
| SmallVector<Value> sizes = firAlloca.getOperands(); |
| std::reverse(sizes.begin(), sizes.end()); |
| |
| auto alloca = memref::AllocaOp::create(rewriter, loc, memrefTy, sizes); |
| copyAttribute(firAlloca, alloca, firAlloca.getBindcNameAttrName()); |
| copyAttribute(firAlloca, alloca, firAlloca.getUniqNameAttrName()); |
| copyAttribute(firAlloca, alloca, cuf::getDataAttrName()); |
| copyAttribute(firAlloca, alloca, acc::getVarNameAttrName()); |
| |
| auto convert = fir::ConvertOp::create(rewriter, loc, type, alloca); |
| |
| rewriter.replaceOp(firAlloca, convert); |
| |
| if (isCompilerGeneratedAlloca(alloca)) { |
| for (Operation *userOp : convert->getUsers()) { |
| if (auto declareOp = dyn_cast<fir::DeclareOp>(userOp)) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: removing declare for compiler temp:\n"; |
| declareOp->dump()); |
| declareOp->replaceAllUsesWith(convert); |
| eraseOps.insert(userOp); |
| } |
| } |
| } |
| } |
| |
| bool FIRToMemRef::memrefIsOptional(Operation *op) const { |
| if (auto declare = dyn_cast<fir::DeclareOp>(op)) { |
| if (fir::FortranVariableOpInterface(declare).isOptional()) |
| return true; |
| |
| Value operand = declare.getMemref(); |
| Operation *operandOp = operand.getDefiningOp(); |
| if (operandOp && isa<fir::AbsentOp>(operandOp)) |
| return true; |
| } |
| |
| for (mlir::Value result : op->getResults()) |
| for (mlir::Operation *userOp : result.getUsers()) |
| if (isa<fir::IsPresentOp>(userOp)) |
| return true; |
| |
| // TODO: If `op` is not a `fir.declare`, OPTIONAL information may still be |
| // present on a related `fir.declare` reached by tracing the address/box |
| // through common forwarding ops (e.g. `fir.convert`, `fir.rebox`, |
| // `fir.embox`, `fir.box_addr`), then checking `declare.isOptional()`. Add the |
| // search after FIR improves on it. |
| return false; |
| } |
| |
| static Value castTypeToIndexType(Value originalValue, |
| PatternRewriter &rewriter) { |
| if (originalValue.getType().isIndex()) |
| return originalValue; |
| |
| Type indexType = rewriter.getIndexType(); |
| return arith::IndexCastOp::create(rewriter, originalValue.getLoc(), indexType, |
| originalValue); |
| } |
| |
| static bool shouldUseBoundaryBitcast(mlir::Type fromTy, mlir::Type toTy) { |
| auto isBitcastCompatibleScalarType = [](mlir::Type ty) { |
| return mlir::isa<mlir::IntegerType, mlir::FloatType, fir::LogicalType>( |
| ty) || |
| (mlir::isa<fir::CharacterType>(ty) && |
| mlir::cast<fir::CharacterType>(ty).getLen() == |
| fir::CharacterType::singleton()); |
| }; |
| auto getKnownScalarBitWidth = [](mlir::Type ty) -> std::optional<unsigned> { |
| if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(ty)) |
| return intTy.getWidth(); |
| if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(ty)) |
| return floatTy.getWidth(); |
| return std::nullopt; |
| }; |
| |
| if (fromTy == toTy) |
| return false; |
| const bool fromStd = fir::isa_std_type(fromTy); |
| const bool toStd = fir::isa_std_type(toTy); |
| if (fromStd == toStd) |
| return false; |
| if (!isBitcastCompatibleScalarType(fromTy) || |
| !isBitcastCompatibleScalarType(toTy)) |
| return false; |
| auto fromBits = getKnownScalarBitWidth(fromTy); |
| auto toBits = getKnownScalarBitWidth(toTy); |
| if (fromBits && toBits && *fromBits != *toBits) |
| return false; |
| return true; |
| } |
| |
| static mlir::Value createTypeConversion(PatternRewriter &rewriter, |
| mlir::Location loc, mlir::Type toTy, |
| mlir::Value value) { |
| if (shouldUseBoundaryBitcast(value.getType(), toTy)) |
| return fir::BitcastOp::create(rewriter, loc, toTy, value); |
| return fir::ConvertOp::create(rewriter, loc, toTy, value); |
| } |
| |
| FailureOr<SmallVector<Value>> |
| FIRToMemRef::getMemrefIndices(fir::ArrayCoorOp arrayCoorOp, Operation *memref, |
| PatternRewriter &rewriter, Value converted, |
| Value one) const { |
| IndexType indexTy = rewriter.getIndexType(); |
| SmallVector<Value> indices; |
| Location loc = arrayCoorOp->getLoc(); |
| SliceInfo sliceInfo; |
| int rank = arrayCoorOp.getIndices().size(); |
| collectSliceInfoFrom(arrayCoorOp, sliceInfo); |
| |
| // Only collect shape/shift from fir.embox, never from fir.rebox. |
| // |
| // The distinction is a Fortran descriptor invariant: |
| // |
| // | fir.embox | fir.rebox |
| // --------------|------------------------|--------------------------- |
| // base_addr | raw pointer | pre-adjusted by (lb-1)*stride |
| // Index formula | index - lb | index - 1 |
| // Collect shift | yes | no |
| // |
| // fir.embox creates a box from a raw pointer so base_addr is not adjusted; |
| // the shift must be collected so index arithmetic subtracts lb correctly. |
| // fir.rebox re-boxes a live descriptor whose base_addr the runtime has |
| // already adjusted downward by (lb-1)*stride; collecting the shift here |
| // would subtract the lower bound a second time, giving the wrong element. |
| if (auto embox = dyn_cast_or_null<fir::EmboxOp>(memref)) { |
| collectSliceInfoFrom(embox, sliceInfo); |
| rank = getRankFromEmbox(embox); |
| } |
| |
| // Projected boxed slices leave `sliceVec` empty on purpose: indices are |
| // computed in the logical section coordinate space, while stride/base come |
| // later from the box descriptor. |
| SmallVector<Value> &shiftVec = sliceInfo.shiftVec; |
| SmallVector<Value> &sliceVec = sliceInfo.sliceVec; |
| SmallVector<Value> sliceLbs, sliceStrides; |
| for (size_t i = 0; i < sliceVec.size(); i += 3) { |
| sliceLbs.push_back(castTypeToIndexType(sliceVec[i], rewriter)); |
| sliceStrides.push_back(castTypeToIndexType(sliceVec[i + 2], rewriter)); |
| } |
| |
| const bool isShifted = !shiftVec.empty(); |
| const bool isSliced = !sliceVec.empty(); |
| |
| ValueRange idxs = arrayCoorOp.getIndices(); |
| Value zero = arith::ConstantIndexOp::create(rewriter, loc, 0); |
| |
| SmallVector<bool> filledPositions(rank, false); |
| for (int i = 0; i < rank; ++i) { |
| Value step = isSliced ? sliceStrides[i] : one; |
| Operation *stepOp = step.getDefiningOp(); |
| if (stepOp && mlir::isa_and_nonnull<fir::UndefOp>(stepOp)) { |
| Value shift = isShifted ? shiftVec[i] : one; |
| Value sliceLb = isSliced ? sliceLbs[i] : shift; |
| Value offset = arith::SubIOp::create(rewriter, loc, sliceLb, shift); |
| indices.push_back(offset); |
| filledPositions[i] = true; |
| } else { |
| indices.push_back(zero); |
| } |
| } |
| |
| int arrayCoorIdx = 0; |
| for (int i = 0; i < rank; ++i) { |
| if (filledPositions[i]) |
| continue; |
| |
| assert((unsigned int)arrayCoorIdx < idxs.size() && |
| "empty dimension should be eliminated\n"); |
| Value index = canonicalizeIndex(idxs[arrayCoorIdx], rewriter); |
| Type cTy = index.getType(); |
| if (!llvm::isa<IndexType>(cTy)) { |
| assert(cTy.isSignlessInteger() && "expected signless integer type"); |
| index = arith::IndexCastOp::create(rewriter, loc, indexTy, index); |
| } |
| |
| Value shift = isShifted ? shiftVec[i] : one; |
| Value stride = isSliced ? sliceStrides[i] : one; |
| Value sliceLb = isSliced ? sliceLbs[i] : shift; |
| |
| // When the array_coor has an explicit slice with a shape_shift (i.e. |
| // non-default lower bounds), the indices are in the shape_shift |
| // coordinate space; subtract the lower bound (shift) to get 0-based |
| // memref indices. Otherwise (the slice comes from an embox, or the |
| // shape has no shift), the indices are 1-based section indices; |
| // subtract 1. |
| bool indicesAreFortran = isShifted && arrayCoorOp.getSlice() != nullptr; |
| Value indexAdjustment = |
| (isSliced && !indicesAreFortran) |
| ? arith::ConstantIndexOp::create(rewriter, loc, 1) |
| : shift; |
| Value delta = arith::SubIOp::create(rewriter, loc, index, indexAdjustment); |
| |
| Value scaled = arith::MulIOp::create(rewriter, loc, delta, stride); |
| |
| Value offset = arith::SubIOp::create(rewriter, loc, sliceLb, shift); |
| |
| Value finalIndex = arith::AddIOp::create(rewriter, loc, scaled, offset); |
| |
| indices[i] = finalIndex; |
| arrayCoorIdx++; |
| } |
| |
| std::reverse(indices.begin(), indices.end()); |
| |
| return indices; |
| } |
| |
| MemRefInfo |
| FIRToMemRef::convertArrayCoorOp(Operation *memOp, fir::ArrayCoorOp arrayCoorOp, |
| PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| IndexType indexTy = rewriter.getIndexType(); |
| Value firMemref = arrayCoorOp.getMemref(); |
| if (!typeConverter.convertibleMemrefType(firMemref.getType())) |
| return failure(); |
| |
| if (typeConverter.isEmptyArray(firMemref.getType())) |
| return failure(); |
| |
| Location loc = arrayCoorOp->getLoc(); |
| |
| // Prefer lowering the array-coordinates computation to a memref + indices. |
| // This allows erasing fir.array_coor when it is only used by load/store even |
| // if the base address is a block argument (e.g. region arguments). |
| Operation *memref = nullptr; |
| FailureOr<Value> converted; |
| if (auto blockArg = dyn_cast<BlockArgument>(firMemref)) { |
| rewriter.setInsertionPoint(arrayCoorOp); |
| Value basePtr = materializeBoxAddressIfNeeded(blockArg, rewriter, loc); |
| Type memrefTy = typeConverter.convertMemrefType(basePtr.getType()); |
| converted = |
| fir::ConvertOp::create(rewriter, loc, memrefTy, basePtr).getResult(); |
| rewriter.setInsertionPointAfter(arrayCoorOp); |
| } else if ((memref = firMemref.getDefiningOp()) && |
| enableFIRConvertOptimizations && isMarshalLike(memref) && |
| !fir::isa_fir_type(firMemref.getType())) { |
| converted = firMemref; |
| rewriter.setInsertionPoint(arrayCoorOp); |
| } else { |
| Operation *arrayCoorOperation = arrayCoorOp.getOperation(); |
| rewriter.setInsertionPoint(arrayCoorOp); |
| if (memrefIsOptional(memref)) { |
| auto ifOp = arrayCoorOperation->getParentOfType<scf::IfOp>(); |
| if (ifOp) { |
| Operation *condition = ifOp.getCondition().getDefiningOp(); |
| if (condition && isa<fir::IsPresentOp>(condition)) |
| if (condition->getOperand(0) == firMemref) { |
| if (arrayCoorOperation->getParentRegion() == &ifOp.getThenRegion()) |
| rewriter.setInsertionPointToStart( |
| &(ifOp.getThenRegion().front())); |
| else if (arrayCoorOperation->getParentRegion() == |
| &ifOp.getElseRegion()) |
| rewriter.setInsertionPointToStart( |
| &(ifOp.getElseRegion().front())); |
| } |
| } |
| } |
| |
| converted = getFIRConvert(memOp, memref, rewriter, typeConverter); |
| if (failed(converted)) |
| return failure(); |
| |
| rewriter.setInsertionPointAfter(arrayCoorOp); |
| } |
| |
| Value one = arith::ConstantIndexOp::create(rewriter, loc, 1); |
| FailureOr<SmallVector<Value>> failureOrIndices = |
| getMemrefIndices(arrayCoorOp, memref, rewriter, *converted, one); |
| if (failed(failureOrIndices)) |
| return failure(); |
| SmallVector<Value> indices = *failureOrIndices; |
| |
| if (converted == firMemref) |
| return std::pair{*converted, indices}; |
| |
| Value convertedVal = *converted; |
| MemRefType memRefTy = dyn_cast<MemRefType>(convertedVal.getType()); |
| |
| bool isRebox = firMemref.getDefiningOp<fir::ReboxOp>() != nullptr; |
| bool isDescriptor = mlir::isa<fir::BaseBoxType>(firMemref.getType()) || |
| firMemref.getDefiningOp<fir::BoxAddrOp>() != nullptr; |
| |
| // Static shape does not imply contiguous layout for descriptor-backed |
| // entities (e.g. boxed array sections with non-unit stride). Keep the |
| // reinterpret-cast path so descriptor strides are preserved. |
| if (memRefTy.hasStaticShape() && !isRebox && !isDescriptor) |
| return std::pair{*converted, indices}; |
| |
| unsigned rank = arrayCoorOp.getIndices().size(); |
| |
| if (auto embox = firMemref.getDefiningOp<fir::EmboxOp>()) |
| rank = getRankFromEmbox(embox); |
| |
| SmallVector<Value> sizes; |
| sizes.reserve(rank); |
| SmallVector<Value> strides; |
| strides.reserve(rank); |
| |
| SliceInfo sliceInfo; |
| collectSliceInfoFrom(arrayCoorOp, sliceInfo); |
| |
| Value box = firMemref; |
| if (!isa<BlockArgument>(firMemref)) { |
| if (auto embox = firMemref.getDefiningOp<fir::EmboxOp>()) { |
| collectSliceInfoFrom(embox, sliceInfo); |
| } else if (auto rebox = firMemref.getDefiningOp<fir::ReboxOp>()) { |
| collectSliceInfoFrom(rebox, sliceInfo); |
| } |
| } |
| |
| SmallVector<Value> &shapeVec = sliceInfo.shapeVec; |
| if (sliceInfo.hasProjectedSlice || shapeVec.empty()) { |
| // Projected slices carry their physical layout in the descriptor. Rebuild |
| // the MemRef view from box metadata instead of from slice triplets. |
| auto boxElementSize = |
| fir::BoxEleSizeOp::create(rewriter, loc, indexTy, box); |
| |
| for (unsigned i = 0; i < rank; ++i) { |
| Value dim = arith::ConstantIndexOp::create(rewriter, loc, rank - i - 1); |
| auto boxDims = fir::BoxDimsOp::create(rewriter, loc, indexTy, indexTy, |
| indexTy, box, dim); |
| |
| Value extent = boxDims->getResult(1); |
| sizes.push_back(castTypeToIndexType(extent, rewriter)); |
| |
| Value byteStride = boxDims->getResult(2); |
| Value div = |
| arith::DivSIOp::create(rewriter, loc, byteStride, boxElementSize); |
| strides.push_back(castTypeToIndexType(div, rewriter)); |
| } |
| |
| } else { |
| Value oneIdx = |
| arith::ConstantIndexOp::create(rewriter, arrayCoorOp->getLoc(), 1); |
| for (unsigned i = rank - 1; i > 0; --i) { |
| Value size = shapeVec[i]; |
| sizes.push_back(castTypeToIndexType(size, rewriter)); |
| |
| Value stride = shapeVec[0]; |
| for (unsigned j = 1; j <= i - 1; ++j) |
| stride = arith::MulIOp::create(rewriter, loc, shapeVec[j], stride); |
| strides.push_back(castTypeToIndexType(stride, rewriter)); |
| } |
| |
| sizes.push_back(castTypeToIndexType(shapeVec[0], rewriter)); |
| strides.push_back(oneIdx); |
| } |
| |
| assert(strides.size() == sizes.size() && sizes.size() == rank); |
| |
| int64_t dynamicOffset = ShapedType::kDynamic; |
| SmallVector<int64_t> dynamicStrides(rank, ShapedType::kDynamic); |
| auto stridedLayout = StridedLayoutAttr::get(convertedVal.getContext(), |
| dynamicOffset, dynamicStrides); |
| |
| SmallVector<int64_t> dynamicShape(rank, ShapedType::kDynamic); |
| memRefTy = |
| MemRefType::get(dynamicShape, memRefTy.getElementType(), stridedLayout); |
| |
| Value offset = arith::ConstantIndexOp::create(rewriter, loc, 0); |
| |
| auto reinterpret = memref::ReinterpretCastOp::create( |
| rewriter, loc, memRefTy, *converted, offset, sizes, strides); |
| |
| Value result = reinterpret->getResult(0); |
| return std::pair{result, indices}; |
| } |
| |
| FailureOr<Value> |
| FIRToMemRef::getFIRConvert(Operation *memOp, Operation *op, |
| PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| if (enableFIRConvertOptimizations && !op->hasOneUse() && |
| !memrefIsOptional(op)) { |
| for (Operation *userOp : op->getUsers()) { |
| if (auto convertOp = dyn_cast<fir::ConvertOp>(userOp)) { |
| Value converted = convertOp.getResult(); |
| if (!isa<MemRefType>(converted.getType())) |
| continue; |
| |
| if (userOp->getParentOp() == memOp->getParentOp() && |
| domInfo->dominates(userOp, memOp)) |
| return converted; |
| } |
| } |
| } |
| |
| assert(op->getNumResults() == 1 && "expecting one result"); |
| |
| Value basePtr = op->getResult(0); |
| |
| MemRefType memrefTy = typeConverter.convertMemrefType(basePtr.getType()); |
| Type baseTy = memrefTy.getElementType(); |
| |
| if (fir::isa_std_type(baseTy) && memrefTy.getRank() == 0) { |
| if (auto convertOp = basePtr.getDefiningOp<fir::ConvertOp>()) { |
| Value input = convertOp.getOperand(); |
| if (auto alloca = input.getDefiningOp<memref::AllocaOp>()) { |
| assert(alloca.getType() == memrefTy && "expected same types"); |
| if (isCompilerGeneratedAlloca(alloca)) |
| return alloca.getResult(); |
| } |
| } |
| } |
| |
| const Location loc = op->getLoc(); |
| |
| if (isa<fir::BoxType>(basePtr.getType())) { |
| Operation *baseOp = basePtr.getDefiningOp(); |
| basePtr = materializeBoxAddressIfNeeded(basePtr, rewriter, loc); |
| memrefTy = typeConverter.convertMemrefType(basePtr.getType()); |
| |
| if (baseOp) { |
| auto sameBaseBoxTypes = [&](Type baseType, Type memrefType) -> bool { |
| Type emboxBaseTy = getBaseType(baseType, true); |
| Type emboxMemrefTy = getBaseType(memrefType, true); |
| return emboxBaseTy == emboxMemrefTy; |
| }; |
| |
| if (auto embox = dyn_cast_or_null<fir::EmboxOp>(baseOp)) { |
| // A projected slice changes the element type of the boxed view. We |
| // can only lower it here when the storage element is complex<T> and |
| // the projection is the real or imaginary part (i.e. %re / %im). For |
| // such cases sizeof(complex<T>) == 2*sizeof(T), so |
| // divsi(byte_stride, elesize) is always an exact integer. |
| // |
| // Derived-type component projections (e.g. a%x, a%y) may produce a |
| // non-integer element-unit stride (e.g. sizeof(T)=24, |
| // sizeof(complex<f64>)=16 -> 24/16 = 1 after truncation, which is |
| // wrong). For those, the type-restriction check below fires and we |
| // bail out, leaving the ops for downstream FIR-to-LLVM lowering. |
| auto isComplexComponentProjection = [&](fir::EmboxOp embox) -> bool { |
| if (!hasProjectedSlice(getSliceOp(embox.getSlice()))) |
| return false; |
| Type memTy = fir::unwrapRefType(embox.getMemref().getType()); |
| if (auto seqTy = dyn_cast<fir::SequenceType>(memTy)) |
| memTy = seqTy.getEleTy(); |
| return mlir::isa<mlir::ComplexType>(memTy); |
| }; |
| bool projectedSlice = isComplexComponentProjection(embox); |
| if (!projectedSlice && |
| !sameBaseBoxTypes(embox.getType(), embox.getMemref().getType())) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: embox base type and memref type are not " |
| "the same, bailing out of conversion\n"); |
| return failure(); |
| } |
| // Keep `box_addr` on the projected box so the descriptor remains the |
| // source of truth for projected element type and stride. |
| if (!projectedSlice && embox.getSlice() && |
| embox.getSlice().getDefiningOp<fir::SliceOp>()) { |
| Type originalType = embox.getMemref().getType(); |
| basePtr = embox.getMemref(); |
| |
| if (typeConverter.convertibleMemrefType(originalType)) { |
| auto convertedMemrefTy = |
| typeConverter.convertMemrefType(originalType); |
| memrefTy = convertedMemrefTy; |
| } else { |
| return failure(); |
| } |
| } |
| } |
| |
| if (auto rebox = dyn_cast<fir::ReboxOp>(baseOp)) { |
| if (!sameBaseBoxTypes(rebox.getType(), rebox.getBox().getType())) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: rebox base type and box type are not the " |
| "same, bailing out of conversion\n"); |
| return failure(); |
| } |
| Type originalType = rebox.getBox().getType(); |
| if (auto boxTy = dyn_cast<fir::BoxType>(originalType)) |
| originalType = boxTy.getElementType(); |
| if (!typeConverter.convertibleMemrefType(originalType)) { |
| return failure(); |
| } else { |
| auto convertedMemrefTy = |
| typeConverter.convertMemrefType(originalType); |
| memrefTy = convertedMemrefTy; |
| } |
| } |
| } |
| } |
| |
| auto convert = fir::ConvertOp::create(rewriter, loc, memrefTy, basePtr); |
| return convert->getResult(0); |
| } |
| |
| Value FIRToMemRef::canonicalizeIndex(Value index, |
| PatternRewriter &rewriter) const { |
| if (auto blockArg = dyn_cast<BlockArgument>(index)) |
| return index; |
| |
| Operation *op = index.getDefiningOp(); |
| |
| if (auto constant = dyn_cast<arith::ConstantIntOp>(op)) { |
| if (!constant.getType().isIndex()) { |
| Value v = arith::ConstantIndexOp::create(rewriter, op->getLoc(), |
| constant.value()); |
| return v; |
| } |
| return constant; |
| } |
| |
| if (auto extsi = dyn_cast<arith::ExtSIOp>(op)) { |
| Value operand = extsi.getOperand(); |
| if (auto indexCast = operand.getDefiningOp<arith::IndexCastOp>()) { |
| Value v = indexCast.getOperand(); |
| return v; |
| } |
| return canonicalizeIndex(operand, rewriter); |
| } |
| |
| if (auto add = dyn_cast<arith::AddIOp>(op)) { |
| Value lhs = canonicalizeIndex(add.getLhs(), rewriter); |
| Value rhs = canonicalizeIndex(add.getRhs(), rewriter); |
| if (lhs.getType() == rhs.getType()) |
| return arith::AddIOp::create(rewriter, op->getLoc(), lhs, rhs); |
| } |
| return index; |
| } |
| |
| MemRefInfo FIRToMemRef::getMemRefInfo(Value firMemref, |
| PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter, |
| Operation *memOp) { |
| Operation *memrefOp = firMemref.getDefiningOp(); |
| if (!memrefOp) { |
| if (auto blockArg = dyn_cast<BlockArgument>(firMemref)) { |
| rewriter.setInsertionPoint(memOp); |
| Type memrefTy = typeConverter.convertMemrefType(blockArg.getType()); |
| if (auto mt = dyn_cast<MemRefType>(memrefTy)) |
| if (auto inner = llvm::dyn_cast<MemRefType>(mt.getElementType())) |
| memrefTy = inner; |
| Value converted = fir::ConvertOp::create(rewriter, blockArg.getLoc(), |
| memrefTy, blockArg); |
| SmallVector<Value> indices; |
| return std::pair{converted, indices}; |
| } |
| llvm_unreachable( |
| "FIRToMemRef: expected defining op or block argument for FIR memref"); |
| } |
| |
| if (auto arrayCoorOp = dyn_cast<fir::ArrayCoorOp>(memrefOp)) { |
| MemRefInfo memrefInfo = |
| convertArrayCoorOp(memOp, arrayCoorOp, rewriter, typeConverter); |
| if (succeeded(memrefInfo)) { |
| for (auto user : memrefOp->getUsers()) { |
| if (!isa<fir::LoadOp, fir::StoreOp>(user)) { |
| LLVM_DEBUG( |
| llvm::dbgs() |
| << "FIRToMemRef: array memref used by unsupported op:\n"; |
| firMemref.dump(); user->dump()); |
| return memrefInfo; |
| } |
| } |
| eraseOps.insert(memrefOp); |
| } |
| return memrefInfo; |
| } |
| |
| rewriter.setInsertionPoint(memOp); |
| |
| if (isMarshalLike(memrefOp)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, memrefOp, rewriter, typeConverter); |
| if (failed(converted)) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: expected FIR memref in convert, bailing " |
| "out:\n"; |
| firMemref.dump()); |
| return failure(); |
| } |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| |
| if (auto declareOp = dyn_cast<fir::DeclareOp>(memrefOp)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, declareOp, rewriter, typeConverter); |
| if (failed(converted)) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: unable to create convert for scalar " |
| "memref:\n"; |
| firMemref.dump()); |
| return failure(); |
| } |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| |
| if (auto coordinateOp = dyn_cast<fir::CoordinateOp>(memrefOp)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, coordinateOp, rewriter, typeConverter); |
| if (failed(converted)) { |
| LLVM_DEBUG( |
| llvm::dbgs() |
| << "FIRToMemRef: unable to create convert for derived-type " |
| "memref:\n"; |
| firMemref.dump()); |
| return failure(); |
| } |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| |
| if (auto convertOp = dyn_cast<fir::ConvertOp>(memrefOp)) { |
| Type fromTy = convertOp->getOperand(0).getType(); |
| Type toTy = firMemref.getType(); |
| if (isa<fir::ReferenceType>(fromTy) && isa<fir::ReferenceType>(toTy)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, convertOp, rewriter, typeConverter); |
| if (failed(converted)) { |
| LLVM_DEBUG( |
| llvm::dbgs() |
| << "FIRToMemRef: unable to create convert for conversion " |
| "op:\n"; |
| firMemref.dump()); |
| return failure(); |
| } |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| } |
| |
| if (auto boxAddrOp = dyn_cast<fir::BoxAddrOp>(memrefOp)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, boxAddrOp, rewriter, typeConverter); |
| if (failed(converted)) { |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: unable to create convert for box_addr " |
| "op:\n"; |
| firMemref.dump()); |
| return failure(); |
| } |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| |
| if (memrefIsDeviceData(memrefOp)) { |
| FailureOr<Value> converted = |
| getFIRConvert(memOp, memrefOp, rewriter, typeConverter); |
| if (failed(converted)) |
| return failure(); |
| SmallVector<Value> indices; |
| return std::pair{*converted, indices}; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: unable to create convert for memref value:\n"; |
| firMemref.dump()); |
| |
| return failure(); |
| } |
| |
| void FIRToMemRef::replaceFIRMemrefs(Value firMemref, Value converted, |
| PatternRewriter &rewriter) const { |
| Operation *op = firMemref.getDefiningOp(); |
| if (op && (isa<fir::ArrayCoorOp>(op) || isMarshalLike(op))) |
| return; |
| |
| SmallPtrSet<Operation *, 4> worklist; |
| for (auto user : firMemref.getUsers()) { |
| if (isMarshalLike(user) || isa<fir::LoadOp, fir::StoreOp>(user)) |
| continue; |
| if (!domInfo->dominates(converted, user)) |
| continue; |
| if (!(isa<omp::AtomicCaptureOp>(user->getParentOp()) || |
| isa<acc::AtomicCaptureOp>(user->getParentOp()))) |
| worklist.insert(user); |
| } |
| |
| Type ty = firMemref.getType(); |
| |
| for (auto op : worklist) { |
| rewriter.setInsertionPoint(op); |
| Location loc = op->getLoc(); |
| Value replaceConvert = fir::ConvertOp::create(rewriter, loc, ty, converted); |
| op->replaceUsesOfWith(firMemref, replaceConvert); |
| } |
| |
| worklist.clear(); |
| |
| for (auto user : firMemref.getUsers()) { |
| if (isMarshalLike(user) || isa<fir::LoadOp, fir::StoreOp>(user)) |
| continue; |
| if (isa<omp::AtomicCaptureOp>(user->getParentOp()) || |
| isa<acc::AtomicCaptureOp>(user->getParentOp())) |
| if (domInfo->dominates(converted, user)) |
| worklist.insert(user); |
| } |
| |
| if (worklist.empty()) |
| return; |
| |
| while (!worklist.empty()) { |
| Operation *parentOp = (*worklist.begin())->getParentOp(); |
| |
| Value replaceConvert; |
| SmallVector<Operation *> erase; |
| for (auto op : worklist) { |
| if (op->getParentOp() != parentOp) |
| continue; |
| if (!replaceConvert) { |
| rewriter.setInsertionPoint(parentOp); |
| replaceConvert = |
| fir::ConvertOp::create(rewriter, op->getLoc(), ty, converted); |
| } |
| op->replaceUsesOfWith(firMemref, replaceConvert); |
| erase.push_back(op); |
| } |
| |
| for (auto op : erase) |
| worklist.erase(op); |
| } |
| } |
| |
| void FIRToMemRef::rewriteLoadOp(fir::LoadOp load, PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| Value firMemref = load.getMemref(); |
| if (!typeConverter.convertibleType(firMemref.getType())) |
| return; |
| |
| LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: attempting to convert FIR load:\n"; |
| load.dump(); firMemref.dump()); |
| |
| MemRefInfo memrefInfo = |
| getMemRefInfo(firMemref, rewriter, typeConverter, load.getOperation()); |
| if (failed(memrefInfo)) |
| return; |
| |
| Type originalType = load.getResult().getType(); |
| Value converted = memrefInfo->first; |
| SmallVector<Value> indices = memrefInfo->second; |
| |
| LLVM_DEBUG(llvm::dbgs() |
| << "FIRToMemRef: convert for FIR load created successfully:\n"; |
| converted.dump()); |
| |
| rewriter.setInsertionPointAfter(load); |
| |
| Attribute attr = (load.getOperation())->getAttr("tbaa"); |
| memref::LoadOp loadOp = |
| rewriter.replaceOpWithNewOp<memref::LoadOp>(load, converted, indices); |
| if (attr) |
| loadOp.getOperation()->setAttr("tbaa", attr); |
| |
| LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: new memref.load op:\n"; |
| loadOp.dump(); assert(succeeded(verify(loadOp)))); |
| |
| if (loadOp.getType() != originalType) { |
| Value castVal = |
| createTypeConversion(rewriter, loadOp.getLoc(), originalType, loadOp); |
| loadOp.getResult().replaceAllUsesExcept(castVal, castVal.getDefiningOp()); |
| } |
| |
| if (!isa<fir::LogicalType>(originalType)) |
| replaceFIRMemrefs(firMemref, converted, rewriter); |
| } |
| |
| void FIRToMemRef::rewriteStoreOp(fir::StoreOp store, PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| Value firMemref = store.getMemref(); |
| |
| if (!typeConverter.convertibleType(firMemref.getType())) |
| return; |
| |
| LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: attempting to convert FIR store:\n"; |
| store.dump(); firMemref.dump()); |
| |
| MemRefInfo memrefInfo = |
| getMemRefInfo(firMemref, rewriter, typeConverter, store.getOperation()); |
| if (failed(memrefInfo)) |
| return; |
| |
| Value converted = memrefInfo->first; |
| SmallVector<Value> indices = memrefInfo->second; |
| LLVM_DEBUG( |
| llvm::dbgs() |
| << "FIRToMemRef: convert for FIR store created successfully:\n"; |
| converted.dump()); |
| |
| Value value = store.getValue(); |
| rewriter.setInsertionPointAfter(store); |
| |
| Type convertedType = typeConverter.convertType(value.getType()); |
| if (convertedType != value.getType()) |
| value = |
| createTypeConversion(rewriter, store.getLoc(), convertedType, value); |
| |
| Attribute attr = (store.getOperation())->getAttr("tbaa"); |
| memref::StoreOp storeOp = rewriter.replaceOpWithNewOp<memref::StoreOp>( |
| store, value, converted, indices); |
| if (attr) |
| storeOp.getOperation()->setAttr("tbaa", attr); |
| |
| LLVM_DEBUG(llvm::dbgs() << "FIRToMemRef: new memref.store op:\n"; |
| storeOp.dump(); assert(succeeded(verify(storeOp)))); |
| |
| bool isLogicalRef = false; |
| if (fir::ReferenceType refTy = |
| llvm::dyn_cast<fir::ReferenceType>(firMemref.getType())) |
| isLogicalRef = llvm::isa<fir::LogicalType>(refTy.getEleTy()); |
| if (!isLogicalRef) |
| replaceFIRMemrefs(firMemref, converted, rewriter); |
| } |
| |
| // Lower operand and result type of FIR logical operation to get rid |
| // of bitcast after loads from memref and before store to memref |
| // storing arrays of logicals as integers. |
| template <typename Op> |
| static void rewriteLogicalOperation(Op op, PatternRewriter &rewriter, |
| FIRToMemRefTypeConverter &typeConverter) { |
| mlir::Type oldType = op.getResult().getType(); |
| mlir::Type convertedTy = typeConverter.convertType(oldType); |
| if (convertedTy == oldType) |
| return; |
| rewriter.setInsertionPoint(op); |
| mlir::Location loc = op.getLoc(); |
| // Inserted bitcast from/to logical will be folded with the one created |
| // around load/store. |
| mlir::Value lhs = |
| createTypeConversion(rewriter, loc, convertedTy, op.getLhs()); |
| mlir::Value rhs = |
| createTypeConversion(rewriter, loc, convertedTy, op.getRhs()); |
| auto newOp = Op::create(rewriter, loc, convertedTy, lhs, rhs); |
| mlir::Value result = createTypeConversion(rewriter, loc, oldType, newOp); |
| rewriter.replaceOp(op, result); |
| } |
| |
| void FIRToMemRef::runOnOperation() { |
| LLVM_DEBUG(llvm::dbgs() << "Enter FIRToMemRef()\n"); |
| |
| func::FuncOp op = getOperation(); |
| MLIRContext *context = op.getContext(); |
| ModuleOp mod = op->getParentOfType<ModuleOp>(); |
| FIRToMemRefTypeConverter typeConverter(mod); |
| |
| typeConverter.setConvertComplexTypes(true); |
| |
| PatternRewriter rewriter(context); |
| domInfo = new DominanceInfo(op); |
| |
| op.walk([&](fir::AllocaOp alloca) { |
| rewriteAlloca(alloca, rewriter, typeConverter); |
| }); |
| |
| op.walk([&](Operation *op) { |
| llvm::TypeSwitch<Operation *>(op) |
| .Case<fir::LoadOp>([&](auto loadOp) { |
| rewriteLoadOp(loadOp, rewriter, typeConverter); |
| }) |
| .Case<fir::StoreOp>([&](auto storeOp) { |
| rewriteStoreOp(storeOp, rewriter, typeConverter); |
| }) |
| .Case<fir::LogicalAndOp, fir::LogicalOrOp, fir::EqvOp, fir::NeqvOp>( |
| [&](auto logicalOp) { |
| rewriteLogicalOperation(logicalOp, rewriter, typeConverter); |
| }) |
| .Default([](Operation *) {}); |
| }); |
| |
| for (auto eraseOp : eraseOps) |
| rewriter.eraseOp(eraseOp); |
| eraseOps.clear(); |
| |
| if (domInfo) |
| delete domInfo; |
| |
| LLVM_DEBUG(llvm::dbgs() << "After FIRToMemRef()\n"; op.dump(); |
| llvm::dbgs() << "Exit FIRToMemRef()\n";); |
| } |
| |
| } // namespace fir |