| //===-- FIRBuilder.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/Builder/FIRBuilder.h" |
| #include "flang/Optimizer/Builder/BoxValue.h" |
| #include "flang/Optimizer/Builder/Character.h" |
| #include "flang/Optimizer/Builder/Complex.h" |
| #include "flang/Optimizer/Builder/MutableBox.h" |
| #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
| #include "flang/Optimizer/Support/FatalError.h" |
| #include "flang/Optimizer/Support/InternalNames.h" |
| #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/MD5.h" |
| |
| static llvm::cl::opt<std::size_t> |
| nameLengthHashSize("length-to-hash-string-literal", |
| llvm::cl::desc("string literals that exceed this length" |
| " will use a hash value as their symbol " |
| "name"), |
| llvm::cl::init(32)); |
| |
| mlir::FuncOp fir::FirOpBuilder::createFunction(mlir::Location loc, |
| mlir::ModuleOp module, |
| llvm::StringRef name, |
| mlir::FunctionType ty) { |
| return fir::createFuncOp(loc, module, name, ty); |
| } |
| |
| mlir::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, |
| llvm::StringRef name) { |
| return modOp.lookupSymbol<mlir::FuncOp>(name); |
| } |
| |
| fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, |
| llvm::StringRef name) { |
| return modOp.lookupSymbol<fir::GlobalOp>(name); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) { |
| assert(!eleTy.isa<fir::ReferenceType>() && "cannot be a reference type"); |
| return fir::ReferenceType::get(eleTy); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) { |
| fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent()); |
| return fir::SequenceType::get(shape, eleTy); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getRealType(int kind) { |
| switch (kindMap.getRealTypeID(kind)) { |
| case llvm::Type::TypeID::HalfTyID: |
| return mlir::FloatType::getF16(getContext()); |
| case llvm::Type::TypeID::FloatTyID: |
| return mlir::FloatType::getF32(getContext()); |
| case llvm::Type::TypeID::DoubleTyID: |
| return mlir::FloatType::getF64(getContext()); |
| case llvm::Type::TypeID::X86_FP80TyID: |
| return mlir::FloatType::getF80(getContext()); |
| case llvm::Type::TypeID::FP128TyID: |
| return mlir::FloatType::getF128(getContext()); |
| default: |
| fir::emitFatalError(UnknownLoc::get(getContext()), |
| "unsupported type !fir.real<kind>"); |
| } |
| } |
| |
| mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, |
| mlir::Type ptrType) { |
| auto ty = ptrType ? ptrType : getRefType(getNoneType()); |
| return create<fir::ZeroOp>(loc, ty); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, |
| mlir::Type ty, |
| std::int64_t cst) { |
| return create<mlir::ConstantOp>(loc, ty, getIntegerAttr(ty, cst)); |
| } |
| |
| mlir::Value |
| fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy, |
| llvm::APFloat::integerPart val) { |
| auto apf = [&]() -> llvm::APFloat { |
| if (auto ty = fltTy.dyn_cast<fir::RealType>()) |
| return llvm::APFloat(kindMap.getFloatSemantics(ty.getFKind()), val); |
| if (fltTy.isF16()) |
| return llvm::APFloat(llvm::APFloat::IEEEhalf(), val); |
| if (fltTy.isBF16()) |
| return llvm::APFloat(llvm::APFloat::BFloat(), val); |
| if (fltTy.isF32()) |
| return llvm::APFloat(llvm::APFloat::IEEEsingle(), val); |
| if (fltTy.isF64()) |
| return llvm::APFloat(llvm::APFloat::IEEEdouble(), val); |
| if (fltTy.isF80()) |
| return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val); |
| if (fltTy.isF128()) |
| return llvm::APFloat(llvm::APFloat::IEEEquad(), val); |
| llvm_unreachable("unhandled MLIR floating-point type"); |
| }; |
| return createRealConstant(loc, fltTy, apf()); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc, |
| mlir::Type fltTy, |
| const llvm::APFloat &value) { |
| if (fltTy.isa<mlir::FloatType>()) { |
| auto attr = getFloatAttr(fltTy, value); |
| return create<mlir::arith::ConstantOp>(loc, fltTy, attr); |
| } |
| llvm_unreachable("should use builtin floating-point type"); |
| } |
| |
| static llvm::SmallVector<mlir::Value> |
| elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape) { |
| auto arrTy = type.dyn_cast<fir::SequenceType>(); |
| if (shape.empty() || !arrTy) |
| return {}; |
| // elide the constant dimensions before construction |
| assert(shape.size() == arrTy.getDimension()); |
| llvm::SmallVector<mlir::Value> dynamicShape; |
| auto typeShape = arrTy.getShape(); |
| for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i) |
| if (typeShape[i] == fir::SequenceType::getUnknownExtent()) |
| dynamicShape.push_back(shape[i]); |
| return dynamicShape; |
| } |
| |
| static llvm::SmallVector<mlir::Value> |
| elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams) { |
| if (lenParams.empty()) |
| return {}; |
| if (auto arrTy = type.dyn_cast<fir::SequenceType>()) |
| type = arrTy.getEleTy(); |
| if (fir::hasDynamicSize(type)) |
| return lenParams; |
| return {}; |
| } |
| |
| /// Allocate a local variable. |
| /// A local variable ought to have a name in the source code. |
| mlir::Value fir::FirOpBuilder::allocateLocal( |
| mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape, |
| llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| // Convert the shape extents to `index`, as needed. |
| llvm::SmallVector<mlir::Value> indices; |
| llvm::SmallVector<mlir::Value> elidedShape = |
| elideExtentsAlreadyInType(ty, shape); |
| llvm::SmallVector<mlir::Value> elidedLenParams = |
| elideLengthsAlreadyInType(ty, lenParams); |
| auto idxTy = getIndexType(); |
| llvm::for_each(elidedShape, [&](mlir::Value sh) { |
| indices.push_back(createConvert(loc, idxTy, sh)); |
| }); |
| // Add a target attribute, if needed. |
| llvm::SmallVector<mlir::NamedAttribute> attrs; |
| if (asTarget) |
| attrs.emplace_back( |
| mlir::Identifier::get(fir::getTargetAttrName(), getContext()), |
| getUnitAttr()); |
| // Create the local variable. |
| if (name.empty()) { |
| if (uniqName.empty()) |
| return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices, |
| attrs); |
| return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams, |
| indices, attrs); |
| } |
| return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams, |
| indices, attrs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::allocateLocal( |
| mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape, |
| llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape, |
| lenParams, asTarget); |
| } |
| |
| /// Get the block for adding Allocas. |
| mlir::Block *fir::FirOpBuilder::getAllocaBlock() { |
| // auto iface = |
| // getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); |
| // return iface ? iface.getAllocaBlock() : getEntryBlock(); |
| return getEntryBlock(); |
| } |
| |
| /// Create a temporary variable on the stack. Anonymous temporaries have no |
| /// `name` value. Temporaries do not require a uniqued name. |
| mlir::Value |
| fir::FirOpBuilder::createTemporary(mlir::Location loc, mlir::Type type, |
| llvm::StringRef name, mlir::ValueRange shape, |
| mlir::ValueRange lenParams, |
| llvm::ArrayRef<mlir::NamedAttribute> attrs) { |
| llvm::SmallVector<mlir::Value> dynamicShape = |
| elideExtentsAlreadyInType(type, shape); |
| llvm::SmallVector<mlir::Value> dynamicLength = |
| elideLengthsAlreadyInType(type, lenParams); |
| InsertPoint insPt; |
| const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty(); |
| if (hoistAlloc) { |
| insPt = saveInsertionPoint(); |
| setInsertionPointToStart(getAllocaBlock()); |
| } |
| |
| // If the alloca is inside an OpenMP Op which will be outlined then pin the |
| // alloca here. |
| const bool pinned = |
| getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); |
| assert(!type.isa<fir::ReferenceType>() && "cannot be a reference"); |
| auto ae = |
| create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, name, |
| pinned, dynamicLength, dynamicShape, attrs); |
| if (hoistAlloc) |
| restoreInsertionPoint(insPt); |
| return ae; |
| } |
| |
| /// Create a global variable in the (read-only) data section. A global variable |
| /// must have a unique name to identify and reference it. |
| fir::GlobalOp |
| fir::FirOpBuilder::createGlobal(mlir::Location loc, mlir::Type type, |
| llvm::StringRef name, mlir::StringAttr linkage, |
| mlir::Attribute value, bool isConst) { |
| auto module = getModule(); |
| auto insertPt = saveInsertionPoint(); |
| if (auto glob = module.lookupSymbol<fir::GlobalOp>(name)) |
| return glob; |
| setInsertionPoint(module.getBody(), module.getBody()->end()); |
| auto glob = create<fir::GlobalOp>(loc, name, isConst, type, value, linkage); |
| restoreInsertionPoint(insertPt); |
| return glob; |
| } |
| |
| fir::GlobalOp fir::FirOpBuilder::createGlobal( |
| mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst, |
| std::function<void(FirOpBuilder &)> bodyBuilder, mlir::StringAttr linkage) { |
| auto module = getModule(); |
| auto insertPt = saveInsertionPoint(); |
| if (auto glob = module.lookupSymbol<fir::GlobalOp>(name)) |
| return glob; |
| setInsertionPoint(module.getBody(), module.getBody()->end()); |
| auto glob = create<fir::GlobalOp>(loc, name, isConst, type, mlir::Attribute{}, |
| linkage); |
| auto ®ion = glob.getRegion(); |
| region.push_back(new mlir::Block); |
| auto &block = glob.getRegion().back(); |
| setInsertionPointToStart(&block); |
| bodyBuilder(*this); |
| restoreInsertionPoint(insertPt); |
| return glob; |
| } |
| |
| mlir::Value fir::FirOpBuilder::convertWithSemantics(mlir::Location loc, |
| mlir::Type toTy, |
| mlir::Value val) { |
| assert(toTy && "store location must be typed"); |
| auto fromTy = val.getType(); |
| if (fromTy == toTy) |
| return val; |
| fir::factory::Complex helper{*this, loc}; |
| if ((fir::isa_real(fromTy) || fir::isa_integer(fromTy)) && |
| fir::isa_complex(toTy)) { |
| // imaginary part is zero |
| auto eleTy = helper.getComplexPartType(toTy); |
| auto cast = createConvert(loc, eleTy, val); |
| llvm::APFloat zero{ |
| kindMap.getFloatSemantics(toTy.cast<fir::ComplexType>().getFKind()), 0}; |
| auto imag = createRealConstant(loc, eleTy, zero); |
| return helper.createComplex(toTy, cast, imag); |
| } |
| if (fir::isa_complex(fromTy) && |
| (fir::isa_integer(toTy) || fir::isa_real(toTy))) { |
| // drop the imaginary part |
| auto rp = helper.extractComplexPart(val, /*isImagPart=*/false); |
| return createConvert(loc, toTy, rp); |
| } |
| return createConvert(loc, toTy, val); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc, |
| mlir::Type toTy, mlir::Value val) { |
| if (val.getType() != toTy) { |
| assert(!fir::isa_derived(toTy)); |
| return create<fir::ConvertOp>(loc, toTy, val); |
| } |
| return val; |
| } |
| |
| fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc, |
| llvm::StringRef data) { |
| auto type = fir::CharacterType::get(getContext(), 1, data.size()); |
| auto strAttr = mlir::StringAttr::get(getContext(), data); |
| auto valTag = mlir::Identifier::get(fir::StringLitOp::value(), getContext()); |
| mlir::NamedAttribute dataAttr(valTag, strAttr); |
| auto sizeTag = mlir::Identifier::get(fir::StringLitOp::size(), getContext()); |
| mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size())); |
| llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr}; |
| return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type}, |
| llvm::None, attrs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> exts) { |
| auto shapeType = fir::ShapeType::get(getContext(), exts.size()); |
| return create<fir::ShapeOp>(loc, shapeType, exts); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> shift, |
| llvm::ArrayRef<mlir::Value> exts) { |
| auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size()); |
| llvm::SmallVector<mlir::Value> shapeArgs; |
| auto idxTy = getIndexType(); |
| for (auto [lbnd, ext] : llvm::zip(shift, exts)) { |
| auto lb = createConvert(loc, idxTy, lbnd); |
| shapeArgs.push_back(lb); |
| shapeArgs.push_back(ext); |
| } |
| return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| const fir::AbstractArrayBox &arr) { |
| if (arr.lboundsAllOne()) |
| return genShape(loc, arr.getExtents()); |
| return genShape(loc, arr.getLBounds(), arr.getExtents()); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc, |
| const fir::ExtendedValue &exv) { |
| return exv.match( |
| [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); }, |
| [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); }, |
| [&](const fir::BoxValue &box) -> mlir::Value { |
| if (!box.getLBounds().empty()) { |
| auto shiftType = |
| fir::ShiftType::get(getContext(), box.getLBounds().size()); |
| return create<fir::ShiftOp>(loc, shiftType, box.getLBounds()); |
| } |
| return {}; |
| }, |
| [&](const fir::MutableBoxValue &) -> mlir::Value { |
| // MutableBoxValue must be read into another category to work with them |
| // outside of allocation/assignment contexts. |
| fir::emitFatalError(loc, "createShape on MutableBoxValue"); |
| }, |
| [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); }); |
| } |
| |
| static mlir::Value genNullPointerComparison(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value addr, |
| arith::CmpIPredicate condition) { |
| auto intPtrTy = builder.getIntPtrType(); |
| auto ptrToInt = builder.createConvert(loc, intPtrTy, addr); |
| auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0); |
| return builder.create<arith::CmpIOp>(loc, condition, ptrToInt, c0); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genIsNotNull(mlir::Location loc, |
| mlir::Value addr) { |
| return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::ne); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genIsNull(mlir::Location loc, mlir::Value addr) { |
| return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::eq); |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // ExtendedValue inquiry helper implementation |
| //===--------------------------------------------------------------------===// |
| |
| mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &box) { |
| return box.match( |
| [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); }, |
| [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| return x.getLen(); |
| }, |
| [&](const fir::BoxValue &x) -> mlir::Value { |
| assert(x.isCharacter()); |
| if (!x.getExplicitParameters().empty()) |
| return x.getExplicitParameters()[0]; |
| return fir::factory::CharacterExprHelper{builder, loc} |
| .readLengthFromBox(x.getAddr()); |
| }, |
| [&](const fir::MutableBoxValue &) -> mlir::Value { |
| // MutableBoxValue must be read into another category to work with them |
| // outside of allocation/assignment contexts. |
| fir::emitFatalError(loc, "readCharLen on MutableBoxValue"); |
| }, |
| [&](const auto &) -> mlir::Value { |
| fir::emitFatalError( |
| loc, "Character length inquiry on a non-character entity"); |
| }); |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| const fir::BoxValue &box) { |
| llvm::SmallVector<mlir::Value> result; |
| auto explicitExtents = box.getExplicitExtents(); |
| if (!explicitExtents.empty()) { |
| result.append(explicitExtents.begin(), explicitExtents.end()); |
| return result; |
| } |
| auto rank = box.rank(); |
| auto idxTy = builder.getIndexType(); |
| for (decltype(rank) dim = 0; dim < rank; ++dim) { |
| auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); |
| auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, |
| box.getAddr(), dimVal); |
| result.emplace_back(dimInfo.getResult(1)); |
| } |
| return result; |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| const fir::ExtendedValue &box) { |
| return box.match( |
| [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return {x.getExtents().begin(), x.getExtents().end()}; |
| }, |
| [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return {x.getExtents().begin(), x.getExtents().end()}; |
| }, |
| [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return fir::factory::readExtents(builder, loc, x); |
| }, |
| [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| auto load = fir::factory::genMutableBoxRead(builder, loc, x); |
| return fir::factory::getExtents(builder, loc, load); |
| }, |
| [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| } |
| |
| std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix, |
| llvm::StringRef name) { |
| // For "long" identifiers use a hash value |
| if (name.size() > nameLengthHashSize) { |
| llvm::MD5 hash; |
| hash.update(name); |
| llvm::MD5::MD5Result result; |
| hash.final(result); |
| llvm::SmallString<32> str; |
| llvm::MD5::stringifyResult(result, str); |
| std::string hashName = prefix.str(); |
| hashName.append(".").append(str.c_str()); |
| return fir::NameUniquer::doGenerated(hashName); |
| } |
| // "Short" identifiers use a reversible hex string |
| std::string nm = prefix.str(); |
| return fir::NameUniquer::doGenerated( |
| nm.append(".").append(llvm::toHex(name))); |
| } |
| |
| mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder, |
| mlir::Location loc) { |
| if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) { |
| // must be encoded as asciiz, C string |
| auto fn = flc.getFilename().str() + '\0'; |
| return fir::getBase(createStringLiteral(builder, loc, fn)); |
| } |
| return builder.createNullConstant(loc); |
| } |
| |
| mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Type type) { |
| if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) |
| return builder.createIntegerConstant(loc, type, flc.getLine()); |
| return builder.createIntegerConstant(loc, type, 0); |
| } |
| |
| fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| llvm::StringRef str) { |
| std::string globalName = fir::factory::uniqueCGIdent("cl", str); |
| auto type = fir::CharacterType::get(builder.getContext(), 1, str.size()); |
| auto global = builder.getNamedGlobal(globalName); |
| if (!global) |
| global = builder.createGlobalConstant( |
| loc, type, globalName, |
| [&](fir::FirOpBuilder &builder) { |
| auto stringLitOp = builder.createStringLitOp(loc, str); |
| builder.create<fir::HasValueOp>(loc, stringLitOp); |
| }, |
| builder.createLinkOnceLinkage()); |
| auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(), |
| global.getSymbol()); |
| auto len = builder.createIntegerConstant( |
| loc, builder.getCharacterLengthType(), str.size()); |
| return fir::CharBoxValue{addr, len}; |
| } |