| //===-- MIFOpConversion.cpp -----------------------------------------------===// |
| // |
| // 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 "flang/Optimizer/Transforms/MIFOpConversion.h" |
| #include "flang/Optimizer/Builder/BoxValue.h" |
| #include "flang/Optimizer/Builder/Character.h" |
| #include "flang/Optimizer/Builder/MIFCommon.h" |
| #include "flang/Optimizer/Builder/MutableBox.h" |
| #include "flang/Optimizer/Builder/Runtime/Inquiry.h" |
| #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" |
| #include "flang/Optimizer/CodeGen/TypeConverter.h" |
| #include "flang/Optimizer/Dialect/FIRDialect.h" |
| #include "flang/Optimizer/Dialect/FIROps.h" |
| #include "flang/Optimizer/Dialect/MIF/MIFOps.h" |
| #include "flang/Optimizer/Support/DataLayout.h" |
| #include "flang/Optimizer/Support/InternalNames.h" |
| #include "flang/Runtime/stop.h" |
| #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" |
| #include "mlir/Dialect/DLTI/DLTI.h" |
| #include "mlir/Transforms/DialectConversion.h" |
| |
| namespace fir { |
| #define GEN_PASS_DEF_MIFOPCONVERSION |
| #include "flang/Optimizer/Transforms/Passes.h.inc" |
| } // namespace fir |
| |
| using namespace mlir; |
| using namespace Fortran::runtime; |
| |
| namespace { |
| |
| // Default prefix for subroutines of PRIF compiled with LLVM |
| static std::string getPRIFProcName(std::string fmt) { |
| std::ostringstream oss; |
| oss << "prif_" << fmt; |
| return fir::NameUniquer::doProcedure({"prif"}, {}, oss.str()); |
| } |
| |
| static mlir::Type getPRIFStatType(fir::FirOpBuilder &builder) { |
| return builder.getRefType(builder.getI32Type()); |
| } |
| |
| static mlir::Type getPRIFErrmsgType(fir::FirOpBuilder &builder) { |
| return fir::BoxType::get(fir::CharacterType::get( |
| builder.getContext(), 1, fir::CharacterType::unknownLen())); |
| } |
| |
| static mlir::Type |
| genBoxedSequenceType(mlir::Type eleTy, |
| std::optional<int64_t> rank = std::nullopt) { |
| if (rank.has_value()) |
| return fir::BoxType::get(fir::SequenceType::get({rank.value()}, eleTy)); |
| return fir::BoxType::get( |
| fir::SequenceType::get({fir::SequenceType::getUnknownExtent()}, eleTy)); |
| } |
| |
| static mlir::Type getCoarrayHandleType(fir::FirOpBuilder &builder, |
| mlir::Location loc) { |
| // Defining the coarray handle type |
| std::string handleDTName = |
| fir::NameUniquer::doType({"prif"}, {}, 0, "prif_coarray_handle", {}); |
| fir::RecordType handleTy = |
| fir::RecordType::get(builder.getContext(), handleDTName); |
| mlir::Type infoTy = |
| fir::BoxType::get(fir::PointerType::get(builder.getNoneType())); |
| handleTy.finalize({}, {{"info", infoTy}}); |
| |
| // Checking if the type information was generated |
| fir::TypeInfoOp dt; |
| fir::RecordType parentType{}; |
| mlir::OpBuilder::InsertPoint insertPointIfCreated; |
| std::tie(dt, insertPointIfCreated) = |
| builder.createTypeInfoOp(loc, handleTy, parentType); |
| if (insertPointIfCreated.isSet()) { |
| // fir.type_info wasn't built in a previous call. |
| dt->setAttr(dt.getNoInitAttrName(), builder.getUnitAttr()); |
| dt->setAttr(dt.getNoDestroyAttrName(), builder.getUnitAttr()); |
| dt->setAttr(dt.getNoFinalAttrName(), builder.getUnitAttr()); |
| builder.restoreInsertionPoint(insertPointIfCreated); |
| // Create global op |
| // FIXME: replace handleTy by the Derived type that describe handleTy |
| std::string globalName = |
| fir::NameUniquer::getTypeDescriptorName(handleDTName); |
| auto linkage = builder.createLinkOnceODRLinkage(); |
| builder.createGlobal(loc, handleTy, globalName, linkage); |
| } |
| return handleTy; |
| } |
| |
| mlir::Value getCoarrayHandle(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value coarray) { |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| std::string uniqName = mif::getFullUniqName(coarray); |
| if (!uniqName.empty()) { |
| std::string globalName = uniqName + coarrayHandleSuffix.str(); |
| mlir::SymbolRefAttr symAttr = |
| mlir::SymbolRefAttr::get(builder.getContext(), globalName); |
| mlir::Value coarrayHandle = |
| fir::AddrOfOp::create(builder, loc, builder.getRefType(boxTy), symAttr); |
| return fir::LoadOp::create(builder, loc, coarrayHandle); |
| } |
| mlir::emitError(coarray.getLoc(), |
| "Unable to locate the coarray handle for this argument."); |
| return mlir::Value{}; |
| } |
| |
| // Storing the coarray descriptor as a global variable |
| void storeCoarrayHandle(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value coarrayHandle, std::string uniqName) { |
| std::string globalName = uniqName + coarrayHandleSuffix.str(); |
| fir::GlobalOp global = builder.getNamedGlobal(globalName); |
| if (!global) { |
| global = builder.createGlobal(loc, coarrayHandle.getType(), globalName, |
| builder.createLinkOnceLinkage()); |
| mlir::Region ®ion = global.getRegion(); |
| region.push_back(new mlir::Block); |
| mlir::Block &block = region.back(); |
| auto insertPt = builder.saveInsertionPoint(); |
| builder.setInsertionPointToStart(&block); |
| auto box = fir::factory::createUnallocatedBox(builder, loc, |
| coarrayHandle.getType(), {}); |
| fir::HasValueOp::create(builder, loc, box); |
| builder.restoreInsertionPoint(insertPt); |
| } |
| |
| mlir::SymbolRefAttr symAttr = |
| mlir::SymbolRefAttr::get(builder.getContext(), globalName); |
| auto addrOf = fir::AddrOfOp::create( |
| builder, loc, builder.getRefType(coarrayHandle.getType()), symAttr); |
| fir::StoreOp::create(builder, loc, coarrayHandle, addrOf); |
| } |
| |
| std::int64_t getCorank(mlir::Value coarray) { |
| mlir::Type coarrayType = fir::unwrapRefType(coarray.getType()); |
| return fir::getBoxCorank(coarrayType); |
| } |
| |
| static int computeElementByteSize(mlir::Location loc, mlir::Type type, |
| fir::KindMapping &kindMap, |
| bool emitErrorOnFailure = true) { |
| auto eleTy = fir::unwrapSequenceType(type); |
| if (auto t{mlir::dyn_cast<mlir::IntegerType>(eleTy)}) |
| return t.getWidth() / 8; |
| if (auto t{mlir::dyn_cast<mlir::FloatType>(eleTy)}) |
| return t.getWidth() / 8; |
| if (auto t{mlir::dyn_cast<fir::LogicalType>(eleTy)}) |
| return kindMap.getLogicalBitsize(t.getFKind()) / 8; |
| if (auto t{mlir::dyn_cast<mlir::ComplexType>(eleTy)}) { |
| int elemSize = |
| mlir::cast<mlir::FloatType>(t.getElementType()).getWidth() / 8; |
| return 2 * elemSize; |
| } |
| if (auto t{mlir::dyn_cast<fir::CharacterType>(eleTy)}) |
| return kindMap.getCharacterBitsize(t.getFKind()) / 8; |
| if (emitErrorOnFailure) |
| mlir::emitError(loc, "unsupported type"); |
| return 0; |
| } |
| |
| // Function used to compute the size in bytes of an entity. This function |
| // is used during an allocation of a coarray (or a component of a coarray), |
| // as it's a required argument in some PRIF procedures. |
| static mlir::Value getSizeInBytes(fir::FirOpBuilder &builder, |
| mlir::Location loc, mlir::ModuleOp module, |
| mlir::DataLayout *dl, |
| const fir::LLVMTypeConverter *typeConverter, |
| mlir::Value box) { |
| fir::KindMapping kindMap{fir::getKindMapping(module)}; |
| mlir::Type baseTy = fir::unwrapPassByRefType(box.getType()); |
| |
| mlir::Value sizeInBytes = builder.createTemporary(loc, builder.getI64Type()); |
| mlir::Value bytes; |
| if (!mlir::dyn_cast_or_null<fir::BaseBoxType>(baseTy)) { |
| if (fir::isa_trivial(baseTy)) { |
| int width = computeElementByteSize(loc, baseTy, kindMap); |
| bytes = builder.createIntegerConstant(loc, builder.getI64Type(), width); |
| } else if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(baseTy)) { |
| std::size_t size = 0; |
| if (fir::isa_derived(seqTy.getEleTy())) { |
| mlir::Type structTy = typeConverter->convertType(seqTy.getEleTy()); |
| size = dl->getTypeSizeInBits(structTy) / 8; |
| } else { |
| size = computeElementByteSize(loc, seqTy.getEleTy(), kindMap); |
| } |
| mlir::Value width = |
| builder.createIntegerConstant(loc, builder.getI64Type(), size); |
| mlir::Value nbElem; |
| if (fir::sequenceWithNonConstantShape(seqTy)) { |
| // TODO: Not handle for now, but will be do it later. |
| mlir::emitError(loc, |
| "unsupported sequence type with non constant shape"); |
| } else { |
| nbElem = builder.createIntegerConstant(loc, builder.getI64Type(), |
| seqTy.getConstantArraySize()); |
| } |
| bytes = mlir::arith::MulIOp::create(builder, loc, nbElem, width); |
| } else if (fir::isa_derived(baseTy)) { |
| mlir::Type structTy = typeConverter->convertType(baseTy); |
| std::size_t structSize = dl->getTypeSizeInBits(structTy) / 8; |
| bytes = |
| builder.createIntegerConstant(loc, builder.getI64Type(), structSize); |
| } else if (fir::isa_char(baseTy)) { |
| mlir::Type charTy = typeConverter->convertType(baseTy); |
| std::size_t charSize = dl->getTypeSizeInBits(charTy) / 8; |
| bytes = |
| builder.createIntegerConstant(loc, builder.getI64Type(), charSize); |
| } else { |
| mlir::emitError(loc, "unsupported type in mif allocation\n"); |
| } |
| } else { |
| if (fir::isa_ref_type(box.getType())) |
| box = fir::LoadOp::create(builder, loc, box); |
| bytes = fir::BoxEleSizeOp::create(builder, loc, builder.getI64Type(), box); |
| auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(baseTy); |
| if (fir::extractSequenceType(boxTy)) { |
| mlir::Value extent = builder.createConvert( |
| loc, builder.getI64Type(), fir::runtime::genSize(builder, loc, box)); |
| bytes = mlir::arith::MulIOp::create(builder, loc, bytes, extent); |
| } |
| } |
| fir::StoreOp::create(builder, loc, bytes, sizeInBytes); |
| return sizeInBytes; |
| } |
| |
| // Most PRIF functions take `errmsg` and `errmsg_alloc` as two optional |
| // arguments of intent (out). One is allocatable, the other is not. |
| // It is the responsibility of the compiler to ensure that the appropriate |
| // optional argument is passed, and at most one must be provided in a given |
| // call. |
| // Depending on the type of `errmsg`, this function will return the pair |
| // corresponding to (`errmsg`, `errmsg_alloc`). |
| static std::pair<mlir::Value, mlir::Value> |
| genErrmsgPRIF(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value errmsg) { |
| mlir::Value absent = |
| fir::AbsentOp::create(builder, loc, getPRIFErrmsgType(builder)); |
| if (!errmsg) |
| return {absent, absent}; |
| |
| bool isAllocatableErrmsg = fir::isAllocatableType(errmsg.getType()); |
| mlir::Value errMsg = isAllocatableErrmsg ? absent : errmsg; |
| mlir::Value errMsgAlloc = isAllocatableErrmsg ? errmsg : absent; |
| return {errMsg, errMsgAlloc}; |
| } |
| |
| static mlir::Value genStatPRIF(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value stat) { |
| if (!stat) |
| return fir::AbsentOp::create(builder, loc, getPRIFStatType(builder)); |
| return stat; |
| } |
| |
| static fir::CallOp genPRIFStopErrorStop(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value stopCode, |
| bool isError = false) { |
| mlir::Type stopCharTy = fir::BoxCharType::get(builder.getContext(), 1); |
| mlir::Type i1Ty = builder.getI1Type(); |
| mlir::Type i32Ty = builder.getI32Type(); |
| |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {builder.getRefType(i1Ty), builder.getRefType(i32Ty), stopCharTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| isError |
| ? builder.createFunction(loc, getPRIFProcName("error_stop"), ftype) |
| : builder.createFunction(loc, getPRIFProcName("stop"), ftype); |
| |
| // QUIET is managed in flang-rt, so its value is set to TRUE here. |
| mlir::Value q = builder.createBool(loc, true); |
| mlir::Value quiet = builder.createTemporary(loc, i1Ty); |
| fir::StoreOp::create(builder, loc, q, quiet); |
| |
| mlir::Value stopCodeInt, stopCodeChar; |
| if (!stopCode) { |
| stopCodeChar = fir::AbsentOp::create(builder, loc, stopCharTy); |
| stopCodeInt = |
| fir::AbsentOp::create(builder, loc, builder.getRefType(i32Ty)); |
| } else if (fir::isa_integer(stopCode.getType())) { |
| stopCodeChar = fir::AbsentOp::create(builder, loc, stopCharTy); |
| stopCodeInt = builder.createTemporary(loc, i32Ty); |
| if (stopCode.getType() != i32Ty) |
| stopCode = fir::ConvertOp::create(builder, loc, i32Ty, stopCode); |
| fir::StoreOp::create(builder, loc, stopCode, stopCodeInt); |
| } else { |
| stopCodeChar = stopCode; |
| if (!mlir::isa<fir::BoxCharType>(stopCodeChar.getType())) { |
| auto len = |
| fir::UndefOp::create(builder, loc, builder.getCharacterLengthType()); |
| stopCodeChar = |
| fir::EmboxCharOp::create(builder, loc, stopCharTy, stopCodeChar, len); |
| } |
| stopCodeInt = |
| fir::AbsentOp::create(builder, loc, builder.getRefType(i32Ty)); |
| } |
| |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, quiet, stopCodeInt, stopCodeChar); |
| return fir::CallOp::create(builder, loc, funcOp, args); |
| } |
| |
| enum class TerminationKind { Normal = 0, Error = 1, FailImage = 2 }; |
| // Generates a wrapper function for the different kind of termination in PRIF. |
| // This function will be used to register wrappers on PRIF runtime termination |
| // functions into the Fortran runtime. |
| mlir::Value genTerminationOperationWrapper(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::ModuleOp module, |
| TerminationKind termKind) { |
| std::string funcName; |
| mlir::FunctionType funcType = |
| mlir::FunctionType::get(builder.getContext(), {}, {}); |
| mlir::Type i32Ty = builder.getI32Type(); |
| if (termKind == TerminationKind::Normal) { |
| funcName = getPRIFProcName("stop"); |
| funcType = mlir::FunctionType::get(builder.getContext(), {i32Ty}, {}); |
| } else if (termKind == TerminationKind::Error) { |
| funcName = getPRIFProcName("error_stop"); |
| funcType = mlir::FunctionType::get(builder.getContext(), {i32Ty}, {}); |
| } else { |
| funcName = getPRIFProcName("fail_image"); |
| } |
| funcName += "_termination_wrapper"; |
| mlir::func::FuncOp funcWrapperOp = |
| module.lookupSymbol<mlir::func::FuncOp>(funcName); |
| |
| if (!funcWrapperOp) { |
| funcWrapperOp = builder.createFunction(loc, funcName, funcType); |
| |
| // generating the body of the function. |
| mlir::OpBuilder::InsertPoint saveInsertPoint = builder.saveInsertionPoint(); |
| builder.setInsertionPointToStart(funcWrapperOp.addEntryBlock()); |
| |
| if (termKind == TerminationKind::Normal) { |
| genPRIFStopErrorStop(builder, loc, funcWrapperOp.getArgument(0), |
| /*isError*/ false); |
| } else if (termKind == TerminationKind::Error) { |
| genPRIFStopErrorStop(builder, loc, funcWrapperOp.getArgument(0), |
| /*isError*/ true); |
| } else { |
| mlir::func::FuncOp fOp = builder.createFunction( |
| loc, getPRIFProcName("fail_image"), |
| mlir::FunctionType::get(builder.getContext(), {}, {})); |
| fir::CallOp::create(builder, loc, fOp); |
| } |
| |
| mlir::func::ReturnOp::create(builder, loc); |
| builder.restoreInsertionPoint(saveInsertPoint); |
| } |
| |
| mlir::SymbolRefAttr symbolRef = mlir::SymbolRefAttr::get( |
| builder.getContext(), funcWrapperOp.getSymNameAttr()); |
| return fir::AddrOfOp::create(builder, loc, funcType, symbolRef); |
| } |
| |
| /// Convert mif.init operation to runtime call of 'prif_init' |
| struct MIFInitOpConversion : public mlir::OpRewritePattern<mif::InitOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::InitOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::Value result = builder.createTemporary(loc, i32Ty); |
| |
| // Registering PRIF runtime termination to the Fortran runtime |
| // STOP |
| mlir::Value funcStopOp = genTerminationOperationWrapper( |
| builder, loc, mod, TerminationKind::Normal); |
| mlir::func::FuncOp normalEndFunc = |
| fir::runtime::getRuntimeFunc<mkRTKey(RegisterImagesNormalEndCallback)>( |
| loc, builder); |
| llvm::SmallVector<mlir::Value> args1 = fir::runtime::createArguments( |
| builder, loc, normalEndFunc.getFunctionType(), funcStopOp); |
| fir::CallOp::create(builder, loc, normalEndFunc, args1); |
| |
| // ERROR STOP |
| mlir::Value funcErrorStopOp = genTerminationOperationWrapper( |
| builder, loc, mod, TerminationKind::Error); |
| mlir::func::FuncOp errorFunc = |
| fir::runtime::getRuntimeFunc<mkRTKey(RegisterImagesErrorCallback)>( |
| loc, builder); |
| llvm::SmallVector<mlir::Value> args2 = fir::runtime::createArguments( |
| builder, loc, errorFunc.getFunctionType(), funcErrorStopOp); |
| fir::CallOp::create(builder, loc, errorFunc, args2); |
| |
| // FAIL IMAGE |
| mlir::Value failImageOp = genTerminationOperationWrapper( |
| builder, loc, mod, TerminationKind::FailImage); |
| mlir::func::FuncOp failImageFunc = |
| fir::runtime::getRuntimeFunc<mkRTKey(RegisterFailImageCallback)>( |
| loc, builder); |
| llvm::SmallVector<mlir::Value> args3 = fir::runtime::createArguments( |
| builder, loc, errorFunc.getFunctionType(), failImageOp); |
| fir::CallOp::create(builder, loc, failImageFunc, args3); |
| |
| // Initialize the multi-image parallel environment |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {builder.getRefType(i32Ty)}, /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("init"), ftype); |
| llvm::SmallVector<mlir::Value> args = |
| fir::runtime::createArguments(builder, loc, ftype, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOpWithNewOp<fir::LoadOp>(op, result); |
| return mlir::success(); |
| } |
| }; |
| |
| /// After a PRIF call that always writes i64 cosubscripts into a sequence |
| /// buffer, convert the buffer to the declared Fortran element type when it |
| /// is narrower than i64. Returns \p i64Result unchanged when eleTy == i64Ty; |
| /// otherwise allocates a typed buffer, copies with element-wise conversion, |
| /// and returns the result widened to \p declaredBoxTy. |
| static mlir::Value convertI64SeqToEleTy(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value i64Result, mlir::Type i64Ty, |
| mlir::Type eleTy, std::int64_t corank, |
| mlir::Type declaredBoxTy) { |
| if (eleTy == i64Ty) |
| return i64Result; |
| mlir::Value i64Buf = fir::BoxAddrOp::create( |
| builder, loc, |
| builder.getRefType( |
| mlir::cast<fir::BoxType>(i64Result.getType()).getEleTy()), |
| i64Result); |
| mlir::Type declaredSeqType = fir::SequenceType::get( |
| static_cast<fir::SequenceType::Extent>(corank), eleTy); |
| mlir::Value declaredBuf = builder.createTemporary(loc, declaredSeqType); |
| mlir::Type indexTy = builder.getIndexType(); |
| mlir::Type i64RefTy = builder.getRefType(i64Ty); |
| mlir::Type eleRefTy = builder.getRefType(eleTy); |
| for (std::int64_t i = 0; i < corank; ++i) { |
| mlir::Value idx = builder.createIntegerConstant(loc, indexTy, i); |
| mlir::Value srcAddr = |
| fir::CoordinateOp::create(builder, loc, i64RefTy, i64Buf, idx); |
| mlir::Value i64Val = fir::LoadOp::create(builder, loc, srcAddr); |
| mlir::Value eleVal = builder.createConvert(loc, eleTy, i64Val); |
| mlir::Value dstAddr = |
| fir::CoordinateOp::create(builder, loc, eleRefTy, declaredBuf, idx); |
| fir::StoreOp::create(builder, loc, eleVal, dstAddr); |
| } |
| mlir::Value result = builder.createBox(loc, declaredBuf); |
| return fir::ConvertOp::create(builder, loc, declaredBoxTy, result); |
| } |
| |
| /// Convert mif.this_image operation to PRIF runtime call |
| struct MIFThisImageOpConversion |
| : public mlir::OpRewritePattern<mif::ThisImageOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::ThisImageOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::Type boxTy = fir::BoxType::get(rewriter.getNoneType()); |
| |
| mlir::Value teamArg = op.getTeam(); |
| if (!op.getTeam()) |
| teamArg = fir::AbsentOp::create(builder, loc, boxTy); |
| else |
| teamArg = builder.createBox(loc, teamArg); |
| |
| if (op.getCoarray()) { |
| llvm::SmallVector<mlir::Value> args; |
| mlir::FunctionType ftype; |
| mlir::func::FuncOp funcOp; |
| mlir::Value result; |
| mlir::Value coarrayHandle = |
| getCoarrayHandle(builder, loc, op.getCoarray()); |
| if (mlir::Value d = op.getDim()) { |
| mlir::Value dim = builder.createTemporary(loc, i32Ty); |
| d = builder.createConvert(loc, i32Ty, d); |
| fir::StoreOp::create(builder, loc, d, dim); |
| result = builder.createTemporary(loc, i64Ty); |
| ftype = mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ |
| {boxTy, builder.getRefType(i32Ty), |
| boxTy, builder.getRefType(i64Ty)}, |
| /*results*/ {}); |
| funcOp = builder.createFunction( |
| loc, getPRIFProcName("this_image_with_dim"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, coarrayHandle, |
| dim, teamArg, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| result = fir::LoadOp::create(builder, loc, result).getResult(); |
| result = builder.createConvert(loc, op.getType(), result); |
| } else { |
| std::int64_t corank = getCorank(op.getCoarray()); |
| mlir::Type resTy = fir::SequenceType::get({corank}, i64Ty); |
| // Need to embox the array |
| result = builder.createBox(loc, builder.createTemporary(loc, resTy)); |
| ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, boxTy, fir::BoxType::get(resTy)}, |
| /*results*/ {}); |
| funcOp = builder.createFunction( |
| loc, getPRIFProcName("this_image_with_coarray"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, coarrayHandle, |
| teamArg, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| result = fir::ConvertOp::create(builder, loc, |
| genBoxedSequenceType(i64Ty), result); |
| auto boxResultTy = mlir::cast<fir::BoxType>(op.getResult().getType()); |
| mlir::Type eleTy = fir::unwrapSequenceType(boxResultTy.getEleTy()); |
| result = convertI64SeqToEleTy(builder, loc, result, i64Ty, eleTy, |
| corank, op.getResult().getType()); |
| } |
| rewriter.replaceOp(op, result); |
| } else { |
| mlir::Value result = builder.createTemporary(loc, i32Ty); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, builder.getRefType(i32Ty)}, /*results*/ {}); |
| mlir::func::FuncOp funcOp = builder.createFunction( |
| loc, getPRIFProcName("this_image_no_coarray"), ftype); |
| |
| llvm::SmallVector<mlir::Value> args = |
| fir::runtime::createArguments(builder, loc, ftype, teamArg, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOpWithNewOp<fir::LoadOp>(op, result); |
| } |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.num_images operation to runtime call of |
| /// prif_num_images_with_{team|team_number} |
| struct MIFNumImagesOpConversion |
| : public mlir::OpRewritePattern<mif::NumImagesOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::NumImagesOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type boxTy = fir::BoxType::get(rewriter.getNoneType()); |
| mlir::Value result = builder.createTemporary(loc, i32Ty); |
| |
| mlir::func::FuncOp funcOp; |
| llvm::SmallVector<mlir::Value> args; |
| if (!op.getTeam() && !op.getTeamNumber()) { |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {builder.getRefType(i32Ty)}, /*results*/ {}); |
| funcOp = |
| builder.createFunction(loc, getPRIFProcName("num_images"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, result); |
| } else { |
| if (op.getTeam()) { |
| mlir::FunctionType ftype = |
| mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ |
| {boxTy, builder.getRefType(i32Ty)}, |
| /*results*/ {}); |
| funcOp = builder.createFunction( |
| loc, getPRIFProcName("num_images_with_team"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, op.getTeam(), |
| result); |
| } else { |
| mlir::Value teamNumber = builder.createTemporary(loc, i64Ty); |
| mlir::Value cst = op.getTeamNumber(); |
| if (op.getTeamNumber().getType() != i64Ty) |
| cst = fir::ConvertOp::create(builder, loc, i64Ty, op.getTeamNumber()); |
| fir::StoreOp::create(builder, loc, cst, teamNumber); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {builder.getRefType(i64Ty), builder.getRefType(i32Ty)}, |
| /*results*/ {}); |
| funcOp = builder.createFunction( |
| loc, getPRIFProcName("num_images_with_team_number"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, teamNumber, |
| result); |
| } |
| } |
| fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOpWithNewOp<fir::LoadOp>(op, result); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.sync_all operation to runtime call of 'prif_sync_all' |
| struct MIFSyncAllOpConversion : public mlir::OpRewritePattern<mif::SyncAllOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::SyncAllOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("sync_all"), ftype); |
| |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, stat, errmsgArg, errmsgAllocArg); |
| rewriter.replaceOpWithNewOp<fir::CallOp>(op, funcOp, args); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.sync_images operation to runtime call of 'prif_sync_images' |
| struct MIFSyncImagesOpConversion |
| : public mlir::OpRewritePattern<mif::SyncImagesOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::SyncImagesOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type imgSetTy = fir::BoxType::get(fir::SequenceType::get( |
| {fir::SequenceType::getUnknownExtent()}, builder.getI32Type())); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {imgSetTy, getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("sync_images"), ftype); |
| |
| // If imageSet is scalar, PRIF require to pass an array of size 1. |
| mlir::Value imageSet = op.getImageSet(); |
| if (!imageSet) |
| imageSet = fir::AbsentOp::create(builder, loc, imgSetTy); |
| else if (auto boxTy = mlir::dyn_cast<fir::BoxType>(imageSet.getType())) { |
| if (!mlir::isa<fir::SequenceType>(boxTy.getEleTy())) { |
| mlir::Value one = |
| builder.createIntegerConstant(loc, builder.getI32Type(), 1); |
| mlir::Value shape = fir::ShapeOp::create(builder, loc, one); |
| imageSet = |
| fir::ReboxOp::create(builder, loc, |
| fir::BoxType::get(fir::SequenceType::get( |
| {1}, builder.getI32Type())), |
| imageSet, shape, mlir::Value{}); |
| } |
| } |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, imageSet, stat, errmsgArg, errmsgAllocArg); |
| rewriter.replaceOpWithNewOp<fir::CallOp>(op, funcOp, args); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.sync_memory operation to runtime call of 'prif_sync_memory' |
| struct MIFSyncMemoryOpConversion |
| : public mlir::OpRewritePattern<mif::SyncMemoryOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::SyncMemoryOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("sync_memory"), ftype); |
| |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, stat, errmsgArg, errmsgAllocArg); |
| rewriter.replaceOpWithNewOp<fir::CallOp>(op, funcOp, args); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.sync_team operation to runtime call of 'prif_sync_team' |
| struct MIFSyncTeamOpConversion |
| : public mlir::OpRewritePattern<mif::SyncTeamOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::SyncTeamOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("sync_team"), ftype); |
| |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, op.getTeam(), stat, errmsgArg, errmsgAllocArg); |
| rewriter.replaceOpWithNewOp<fir::CallOp>(op, funcOp, args); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Generate call to collective subroutines except co_reduce |
| /// A must be lowered as a box |
| static fir::CallOp genCollectiveSubroutine(fir::FirOpBuilder &builder, |
| mlir::Location loc, mlir::Value A, |
| mlir::Value image, mlir::Value stat, |
| mlir::Value errmsg, |
| std::string coName) { |
| mlir::Value rootImage; |
| mlir::Type i32Ty = builder.getI32Type(); |
| if (!image) |
| rootImage = fir::AbsentOp::create(builder, loc, builder.getRefType(i32Ty)); |
| else { |
| rootImage = builder.createTemporary(loc, i32Ty); |
| if (image.getType() != i32Ty) |
| image = fir::ConvertOp::create(builder, loc, i32Ty, image); |
| fir::StoreOp::create(builder, loc, image, rootImage); |
| } |
| |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::FunctionType ftype = |
| mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ |
| {boxTy, builder.getRefType(builder.getI32Type()), |
| getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = builder.createFunction(loc, coName, ftype); |
| |
| auto [errmsgArg, errmsgAllocArg] = genErrmsgPRIF(builder, loc, errmsg); |
| if (!stat) |
| stat = fir::AbsentOp::create(builder, loc, getPRIFStatType(builder)); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, A, rootImage, stat, errmsgArg, errmsgAllocArg); |
| return fir::CallOp::create(builder, loc, funcOp, args); |
| } |
| |
| /// Convert mif.co_broadcast operation to runtime call of 'prif_co_broadcast' |
| struct MIFCoBroadcastOpConversion |
| : public mlir::OpRewritePattern<mif::CoBroadcastOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::CoBroadcastOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| fir::CallOp callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getSourceImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_broadcast")); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.co_max operation to runtime call of 'prif_co_max' |
| struct MIFCoMaxOpConversion : public mlir::OpRewritePattern<mif::CoMaxOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::CoMaxOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| fir::CallOp callOp; |
| mlir::Type argTy = |
| fir::unwrapSequenceType(fir::unwrapPassByRefType(op.getA().getType())); |
| if (mlir::isa<fir::CharacterType>(argTy)) |
| callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getResultImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_max_character")); |
| else |
| callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getResultImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_max")); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.co_min operation to runtime call of 'prif_co_min' |
| struct MIFCoMinOpConversion : public mlir::OpRewritePattern<mif::CoMinOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::CoMinOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| fir::CallOp callOp; |
| mlir::Type argTy = |
| fir::unwrapSequenceType(fir::unwrapPassByRefType(op.getA().getType())); |
| if (mlir::isa<fir::CharacterType>(argTy)) |
| callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getResultImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_min_character")); |
| else |
| callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getResultImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_min")); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.co_sum operation to runtime call of 'prif_co_sum' |
| struct MIFCoSumOpConversion : public mlir::OpRewritePattern<mif::CoSumOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::CoSumOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| fir::CallOp callOp = genCollectiveSubroutine( |
| builder, loc, op.getA(), op.getResultImage(), op.getStat(), |
| op.getErrmsg(), getPRIFProcName("co_sum")); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.form_team operation to runtime call of 'prif_form_team' |
| struct MIFFormTeamOpConversion |
| : public mlir::OpRewritePattern<mif::FormTeamOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::FormTeamOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {builder.getRefType(builder.getI64Type()), boxTy, |
| builder.getRefType(builder.getI32Type()), getPRIFStatType(builder), |
| errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("form_team"), ftype); |
| |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Value teamNumber = builder.createTemporary(loc, i64Ty); |
| mlir::Value t = |
| (op.getTeamNumber().getType() == i64Ty) |
| ? op.getTeamNumber() |
| : fir::ConvertOp::create(builder, loc, i64Ty, op.getTeamNumber()); |
| fir::StoreOp::create(builder, loc, t, teamNumber); |
| |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::Value newIndex; |
| if (op.getNewIndex()) { |
| newIndex = builder.createTemporary(loc, i32Ty); |
| mlir::Value ni = |
| (op.getNewIndex().getType() == i32Ty) |
| ? op.getNewIndex() |
| : fir::ConvertOp::create(builder, loc, i32Ty, op.getNewIndex()); |
| fir::StoreOp::create(builder, loc, ni, newIndex); |
| } else |
| newIndex = fir::AbsentOp::create(builder, loc, builder.getRefType(i32Ty)); |
| |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, teamNumber, op.getTeamVar(), newIndex, stat, |
| errmsgArg, errmsgAllocArg); |
| fir::CallOp callOp = fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Generate runtime call to 'prif_end_team' from mif.end_team operation. |
| fir::CallOp genPrifEndTeamCallOp(mif::EndTeamOp op, |
| mlir::PatternRewriter &rewriter) { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("end_team"), ftype); |
| |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, stat, errmsgArg, errmsgAllocArg); |
| return fir::CallOp::create(rewriter, loc, funcOp, args); |
| } |
| |
| /// Convert mif.change_team operation to runtime call of 'prif_change_team' |
| struct MIFChangeTeamOpConversion |
| : public mlir::OpRewritePattern<mif::ChangeTeamOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::ChangeTeamOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| |
| mlir::Location loc = op.getLoc(); |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("change_team"), ftype); |
| |
| mlir::Value stat = genStatPRIF(builder, loc, op.getStat()); |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, op.getTeam(), stat, errmsgArg, errmsgAllocArg); |
| |
| mlir::Operation *changeTeamOp = op.getOperation(); |
| mlir::Block *currentBlock = changeTeamOp->getBlock(); |
| |
| mlir::Block *newBlock = rewriter.splitBlock( |
| currentBlock, std::next(changeTeamOp->getIterator())); |
| rewriter.setInsertionPoint(changeTeamOp); |
| // Creation of the call to prif_change_team |
| fir::CallOp::create(rewriter, loc, funcOp, args); |
| |
| // Inlining all the region into the new block |
| mlir::Region &teamRegion = op.getRegion(); |
| mlir::Block *firstBlock = &teamRegion.front(); |
| mlir::Block *lastBlock = &teamRegion.back(); |
| rewriter.inlineRegionBefore(teamRegion, newBlock); |
| |
| rewriter.setInsertionPointToEnd(currentBlock); |
| mlir::cf::BranchOp::create(rewriter, loc, firstBlock); |
| |
| // Removing mif.end_team operation and add the call to prif_end_team. |
| if (auto endTeamOp = |
| mlir::dyn_cast<mif::EndTeamOp>(lastBlock->getTerminator())) { |
| rewriter.setInsertionPoint(endTeamOp); |
| genPrifEndTeamCallOp(endTeamOp, rewriter); |
| mlir::cf::BranchOp::create(rewriter, loc, newBlock); |
| rewriter.eraseOp(endTeamOp); |
| } else |
| fir::emitFatalError(loc, |
| "internal error: missing expected mif::EndTeamOp"); |
| |
| rewriter.eraseOp(op); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.get_team operation to runtime call of 'prif_get_team' |
| struct MIFGetTeamOpConversion : public mlir::OpRewritePattern<mif::GetTeamOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::GetTeamOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::Type lvlTy = builder.getRefType(builder.getI32Type()); |
| mlir::FunctionType ftype = |
| mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ {lvlTy, boxTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("get_team"), ftype); |
| |
| mlir::Value level = op.getLevel(); |
| if (!level) |
| level = fir::AbsentOp::create(builder, loc, lvlTy); |
| else { |
| mlir::Value cst = op.getLevel(); |
| mlir::Type i32Ty = builder.getI32Type(); |
| level = builder.createTemporary(loc, i32Ty); |
| if (cst.getType() != i32Ty) |
| cst = builder.createConvert(loc, i32Ty, cst); |
| fir::StoreOp::create(builder, loc, cst, level); |
| } |
| mlir::Type resultType = op.getResult().getType(); |
| mlir::Type baseTy = fir::unwrapRefType(resultType); |
| mlir::Value team = builder.createTemporary(loc, baseTy); |
| fir::EmboxOp box = fir::EmboxOp::create(builder, loc, resultType, team); |
| |
| llvm::SmallVector<mlir::Value> args = |
| fir::runtime::createArguments(builder, loc, ftype, level, box); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| |
| rewriter.replaceOp(op, box); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.team_number operation to runtime call of 'prif_team_number' |
| struct MIFTeamNumberOpConversion |
| : public mlir::OpRewritePattern<mif::TeamNumberOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::TeamNumberOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::FunctionType ftype = |
| mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ {boxTy, builder.getRefType(i64Ty)}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("team_number"), ftype); |
| |
| mlir::Value team = op.getTeam(); |
| if (!team) |
| team = fir::AbsentOp::create(builder, loc, boxTy); |
| |
| mlir::Value result = builder.createTemporary(loc, i64Ty); |
| llvm::SmallVector<mlir::Value> args = |
| fir::runtime::createArguments(builder, loc, ftype, team, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| fir::LoadOp load = fir::LoadOp::create(builder, loc, result); |
| rewriter.replaceOp(op, load); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.alloca_coarray operation to runtime call of |
| /// 'prif_allocate_coarray' |
| struct MIFAllocCoarrayOpConversion |
| : public mlir::OpRewritePattern<mif::AllocCoarrayOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| MIFAllocCoarrayOpConversion(mlir::MLIRContext *context, mlir::DataLayout *dl, |
| const fir::LLVMTypeConverter *typeConverter) |
| : OpRewritePattern(context), dl{dl}, typeConverter{typeConverter} {} |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::AllocCoarrayOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type ptrTy = fir::PointerType::get(builder.getNoneType()); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type coboundsTy = genBoxedSequenceType(i64Ty); |
| // Type of the procedure pointed by final_func will be the following : |
| mlir::Type procTypePtr = fir::BoxProcType::get( |
| builder.getContext(), |
| mlir::FunctionType::get(builder.getContext(), |
| {boxTy, getPRIFStatType(builder), errmsgTy}, |
| {})); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {coboundsTy, coboundsTy, builder.getRefType(i64Ty), |
| builder.getRefType(builder.getNoneType()), boxTy, ptrTy, |
| getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("allocate_coarray"), ftype); |
| |
| // TODO: Handle final_func if needed |
| mlir::Value finalFunc = builder.createTemporary(loc, procTypePtr); |
| mlir::Value nullBoxProc = |
| fir::factory::createNullBoxProc(builder, loc, procTypePtr); |
| fir::StoreOp::create(builder, loc, nullBoxProc, finalFunc); |
| // Allocate instance of prif_coarray_handle type based on the PRIF |
| // specification. |
| mlir::Type handleTy = getCoarrayHandleType(builder, loc); |
| mlir::Value coarrayHandle = |
| builder.createBox(loc, builder.createTemporary(loc, handleTy)); |
| |
| mlir::Value allocMem = |
| fir::ConvertOp::create(builder, loc, ptrTy, op.getBox()); |
| |
| mlir::Value sizeInBytes = |
| getSizeInBytes(builder, loc, mod, dl, typeConverter, op.getBox()); |
| mlir::Value stat = op.getStat(); |
| if (!stat) |
| stat = fir::AbsentOp::create(builder, loc, getPRIFStatType(builder)); |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, op.getLcobounds(), op.getUcobounds(), sizeInBytes, |
| finalFunc, coarrayHandle, allocMem, stat, errmsgArg, errmsgAllocArg); |
| fir::CallOp callOp = fir::CallOp::create(builder, loc, funcOp, args); |
| |
| storeCoarrayHandle(builder, loc, coarrayHandle, op.getUniqName().str()); |
| |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| |
| private: |
| mlir::DataLayout *dl; |
| const fir::LLVMTypeConverter *typeConverter; |
| }; |
| |
| /// Convert mif.dealloca_coarray operation to runtime call of |
| /// 'prif_deallocate_coarray' |
| struct MIFDeallocCoarrayOpConversion |
| : public mlir::OpRewritePattern<mif::DeallocCoarrayOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::DeallocCoarrayOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| |
| mlir::Type errmsgTy = getPRIFErrmsgType(builder); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {boxTy, getPRIFStatType(builder), errmsgTy, errmsgTy}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = builder.createFunction( |
| loc, getPRIFProcName("deallocate_coarray"), ftype); |
| |
| mlir::Value coarrayHandle = getCoarrayHandle(builder, loc, op.getCoarray()); |
| mlir::Value stat = op.getStat(); |
| if (!stat) |
| stat = fir::AbsentOp::create(builder, loc, getPRIFStatType(builder)); |
| auto [errmsgArg, errmsgAllocArg] = |
| genErrmsgPRIF(builder, loc, op.getErrmsg()); |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, coarrayHandle, stat, errmsgArg, errmsgAllocArg); |
| fir::CallOp callOp = fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOp(op, callOp); |
| return mlir::success(); |
| } |
| }; |
| |
| /// Convert mif.coshape operation to runtime call of 'prif_coshape' |
| struct MIFCoshapeOpConversion : public mlir::OpRewritePattern<mif::CoshapeOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::CoshapeOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| |
| mlir::FunctionType ftype = |
| mlir::FunctionType::get(builder.getContext(), |
| /*inputs*/ {boxTy, genBoxedSequenceType(i64Ty)}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName("coshape"), ftype); |
| |
| mlir::Value coarrayHandle = getCoarrayHandle(builder, loc, op.getCoarray()); |
| std::int64_t corank = getCorank(op.getCoarray()); |
| mlir::Type resultType = fir::SequenceType::get( |
| static_cast<fir::SequenceType::Extent>(corank), i64Ty); |
| mlir::Value result = |
| builder.createBox(loc, builder.createTemporary(loc, resultType)); |
| |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, coarrayHandle, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| result = fir::ConvertOp::create(builder, loc, genBoxedSequenceType(i64Ty), |
| result); |
| |
| auto boxResultTy = mlir::cast<fir::BoxType>(op.getResult().getType()); |
| mlir::Type eleTy = fir::unwrapSequenceType(boxResultTy.getEleTy()); |
| result = convertI64SeqToEleTy(builder, loc, result, i64Ty, eleTy, corank, |
| op.getResult().getType()); |
| rewriter.replaceOp(op, result); |
| return mlir::success(); |
| } |
| }; |
| |
| template <class T> |
| mlir::LogicalResult CoboundOpConversion(T op, mlir::PatternRewriter &rewriter, |
| const std::string &prefix) { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| |
| mlir::Value coarrayHandle = getCoarrayHandle(builder, loc, op.getCoarray()); |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ |
| {boxTy, builder.getRefType(i32Ty), builder.getRefType(i64Ty)}, |
| /*results*/ {}); |
| mlir::func::FuncOp funcOp = |
| builder.createFunction(loc, getPRIFProcName(prefix + "_with_dim"), ftype); |
| |
| mlir::Value result = builder.createTemporary(loc, i64Ty); |
| mlir::Value dim = builder.createTemporary(loc, i32Ty); |
| mlir::Value d = fir::ConvertOp::create(builder, loc, i32Ty, op.getDim()); |
| fir::StoreOp::create(builder, loc, d, dim); |
| |
| llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments( |
| builder, loc, ftype, coarrayHandle, dim, result); |
| fir::CallOp::create(builder, loc, funcOp, args); |
| result = fir::LoadOp::create(builder, loc, result).getResult(); |
| result = builder.createConvert(loc, op.getType(), result); |
| rewriter.replaceOp(op, result); |
| return mlir::success(); |
| } |
| |
| /// Convert mif.lcobound operation to runtime call of |
| /// 'prif_lcobound_{with|no}_dim' |
| struct MIFLcoboundOpConversion |
| : public mlir::OpRewritePattern<mif::LcoboundOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::LcoboundOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| return CoboundOpConversion(op, rewriter, "lcobound"); |
| } |
| }; |
| |
| /// Convert mif.ucobound operation to runtime call of |
| /// 'prif_ucobound_{with|no}_dim' |
| struct MIFUcoboundOpConversion |
| : public mlir::OpRewritePattern<mif::UcoboundOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::UcoboundOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| return CoboundOpConversion(op, rewriter, "ucobound"); |
| } |
| }; |
| |
| /// Convert mif.image_index operation to runtime call of |
| /// 'prif_image_index[_with_team[_number]]' |
| struct MIFImageIndexOpConversion |
| : public mlir::OpRewritePattern<mif::ImageIndexOp> { |
| using OpRewritePattern::OpRewritePattern; |
| |
| mlir::LogicalResult |
| matchAndRewrite(mif::ImageIndexOp op, |
| mlir::PatternRewriter &rewriter) const override { |
| auto mod = op->template getParentOfType<mlir::ModuleOp>(); |
| fir::FirOpBuilder builder(rewriter, mod); |
| mlir::Location loc = op.getLoc(); |
| mlir::Type i64Ty = builder.getI64Type(); |
| mlir::Type i32Ty = builder.getI32Type(); |
| mlir::Type resTy = builder.getRefType(i32Ty); |
| mlir::Type boxTy = fir::BoxType::get(builder.getNoneType()); |
| mlir::Value result = builder.createTemporary(loc, i32Ty); |
| |
| mlir::func::FuncOp funcOp; |
| llvm::SmallVector<mlir::Value> args; |
| mlir::Value coarrayHandle = getCoarrayHandle(builder, loc, op.getCoarray()); |
| if (!op.getTeam() && !op.getTeamNumber()) { |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, genBoxedSequenceType(i64Ty), resTy}, |
| /*results*/ {}); |
| funcOp = |
| builder.createFunction(loc, getPRIFProcName("image_index"), ftype); |
| args = fir::runtime::createArguments(builder, loc, ftype, coarrayHandle, |
| op.getSub(), result); |
| } else { |
| mlir::Value team = op.getTeam() ? op.getTeam() : op.getTeamNumber(); |
| std::string imageIndexName = |
| op.getTeamNumber() ? getPRIFProcName("image_index_with_team_number") |
| : getPRIFProcName("image_index_with_team"); |
| mlir::Type teamTy = boxTy; |
| if (op.getTeamNumber()) { |
| teamTy = builder.getRefType(i64Ty); |
| mlir::Value t = builder.createConvert(loc, i64Ty, team); |
| team = builder.createTemporary(loc, i64Ty); |
| fir::StoreOp::create(builder, loc, t, team); |
| } else { |
| team = builder.createBox(loc, team); |
| } |
| mlir::FunctionType ftype = mlir::FunctionType::get( |
| builder.getContext(), |
| /*inputs*/ {boxTy, genBoxedSequenceType(i64Ty), teamTy, resTy}, |
| /*results*/ {}); |
| funcOp = builder.createFunction(loc, imageIndexName, ftype); |
| |
| args = fir::runtime::createArguments(builder, loc, ftype, coarrayHandle, |
| op.getSub(), team, result); |
| } |
| fir::CallOp::create(builder, loc, funcOp, args); |
| rewriter.replaceOpWithNewOp<fir::LoadOp>(op, result); |
| return mlir::success(); |
| } |
| }; |
| |
| class MIFOpConversion : public fir::impl::MIFOpConversionBase<MIFOpConversion> { |
| public: |
| void runOnOperation() override { |
| auto *ctx = &getContext(); |
| mlir::RewritePatternSet patterns(ctx); |
| mlir::ConversionTarget target(*ctx); |
| |
| mlir::Operation *op = getOperation(); |
| mlir::ModuleOp module = mlir::dyn_cast<mlir::ModuleOp>(op); |
| if (!module) |
| return signalPassFailure(); |
| mlir::SymbolTable symtab(module); |
| |
| std::optional<mlir::DataLayout> dl = fir::support::getOrSetMLIRDataLayout( |
| module, /*allowDefaultLayout=*/true); |
| if (!dl.has_value()) { |
| mlir::emitError( |
| module.getLoc(), |
| "data layout attribute is required to perform MIFOpConversion pass"); |
| return signalPassFailure(); |
| } |
| |
| fir::LLVMTypeConverter typeConverter(module, /*applyTBAA=*/false, |
| /*forceUnifiedTBAATree=*/false, *dl); |
| mif::populateMIFOpConversionPatterns(typeConverter, *dl, patterns); |
| |
| target.addLegalDialect<fir::FIROpsDialect, mlir::cf::ControlFlowDialect>(); |
| target.addLegalOp<mlir::ModuleOp>(); |
| |
| if (mlir::failed(mlir::applyPartialConversion(getOperation(), target, |
| std::move(patterns)))) { |
| mlir::emitError(mlir::UnknownLoc::get(ctx), |
| "error in MIF op conversion\n"); |
| return signalPassFailure(); |
| } |
| } |
| }; |
| } // namespace |
| |
| void mif::populateMIFOpConversionPatterns( |
| const fir::LLVMTypeConverter &converter, mlir::DataLayout &dl, |
| mlir::RewritePatternSet &patterns) { |
| patterns.insert<MIFAllocCoarrayOpConversion>(patterns.getContext(), &dl, |
| &converter); |
| patterns.insert<MIFInitOpConversion, MIFThisImageOpConversion, |
| MIFNumImagesOpConversion, MIFSyncAllOpConversion, |
| MIFSyncImagesOpConversion, MIFSyncMemoryOpConversion, |
| MIFSyncTeamOpConversion, MIFCoBroadcastOpConversion, |
| MIFCoMaxOpConversion, MIFCoMinOpConversion, |
| MIFCoSumOpConversion, MIFFormTeamOpConversion, |
| MIFChangeTeamOpConversion, MIFGetTeamOpConversion, |
| MIFTeamNumberOpConversion, MIFDeallocCoarrayOpConversion, |
| MIFCoshapeOpConversion, MIFLcoboundOpConversion, |
| MIFUcoboundOpConversion, MIFImageIndexOpConversion>( |
| patterns.getContext()); |
| } |