diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -33,10 +33,27 @@ class FirOpBuilder : public mlir::OpBuilder { public: explicit FirOpBuilder(mlir::Operation *op, const fir::KindMapping &kindMap) - : OpBuilder{op} {} + : OpBuilder{op}, kindMap{kindMap} {} explicit FirOpBuilder(mlir::OpBuilder &builder, const fir::KindMapping &kindMap) - : OpBuilder{builder} {} + : OpBuilder{builder}, kindMap{kindMap} {} + + /// Get the current Region of the insertion point. + mlir::Region &getRegion() { return *getBlock()->getParent(); } + + /// Get the current Module + mlir::ModuleOp getModule() { + return getRegion().getParentOfType(); + } + + /// Get a reference to the kind map. + const fir::KindMapping &getKindMap() { return kindMap; } + + /// Safely create a reference type to the type `eleTy`. + mlir::Type getRefType(mlir::Type eleTy); + + /// Create a sequence of `eleTy` with `rank` dimensions of unknown size. + mlir::Type getVarLenSeqTy(mlir::Type eleTy, unsigned rank = 1); /// Get the integer type whose bit width corresponds to the width of pointer /// types, or is bigger. @@ -46,19 +63,133 @@ return getI64Type(); } + /// Get the mlir real type that implements fortran REAL(kind). + mlir::Type getRealType(int kind); + + /// Create a null constant memory reference of type \p ptrType. + /// If \p ptrType is not provided, !fir.ref type will be used. + mlir::Value createNullConstant(mlir::Location loc, mlir::Type ptrType = {}); + /// Create an integer constant of type \p type and value \p i. mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType, std::int64_t i); - /// Lazy creation of fir.convert op. - mlir::Value createConvert(mlir::Location loc, mlir::Type toTy, - mlir::Value val); + /// Create a real constant from an integer value. + mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType, + llvm::APFloat::integerPart val); + + /// Create a real constant from an APFloat value. + mlir::Value createRealConstant(mlir::Location loc, mlir::Type realType, + const llvm::APFloat &val); + + /// Create a real constant of type \p realType with a value zero. + mlir::Value createRealZeroConstant(mlir::Location loc, mlir::Type realType) { + return createRealConstant(loc, realType, 0u); + } + + /// Create a global value. + fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type, + llvm::StringRef name, + mlir::StringAttr linkage = {}, + mlir::Attribute value = {}, bool isConst = false); + + fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type, + llvm::StringRef name, bool isConst, + std::function bodyBuilder, + mlir::StringAttr linkage = {}); + + /// Create a global constant (read-only) value. + fir::GlobalOp createGlobalConstant(mlir::Location loc, mlir::Type type, + llvm::StringRef name, + mlir::StringAttr linkage = {}, + mlir::Attribute value = {}) { + return createGlobal(loc, type, name, linkage, value, /*isConst=*/true); + } + + fir::GlobalOp + createGlobalConstant(mlir::Location loc, mlir::Type type, + llvm::StringRef name, + std::function bodyBuilder, + mlir::StringAttr linkage = {}) { + return createGlobal(loc, type, name, /*isConst=*/true, bodyBuilder, + linkage); + } + + /// Convert a StringRef string into a fir::StringLitOp. + fir::StringLitOp createStringLitOp(mlir::Location loc, + llvm::StringRef string); + + //===--------------------------------------------------------------------===// + // Linkage helpers (inline). The default linkage is external. + //===--------------------------------------------------------------------===// + + mlir::StringAttr createCommonLinkage() { return getStringAttr("common"); } + + mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); } + + mlir::StringAttr createLinkOnceLinkage() { return getStringAttr("linkonce"); } + + mlir::StringAttr createWeakLinkage() { return getStringAttr("weak"); } /// Cast the input value to IndexType. mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) { return createConvert(loc, getIndexType(), val); } + /// Get a function by name. If the function exists in the current module, it + /// is returned. Otherwise, a null FuncOp is returned. + mlir::FuncOp getNamedFunction(llvm::StringRef name) { + return getNamedFunction(getModule(), name); + } + + static mlir::FuncOp getNamedFunction(mlir::ModuleOp module, + llvm::StringRef name); + + fir::GlobalOp getNamedGlobal(llvm::StringRef name) { + return getNamedGlobal(getModule(), name); + } + + static fir::GlobalOp getNamedGlobal(mlir::ModuleOp module, + llvm::StringRef name); + + /// Lazy creation of fir.convert op. + mlir::Value createConvert(mlir::Location loc, mlir::Type toTy, + mlir::Value val); + + /// Create a new FuncOp. If the function may have already been created, use + /// `addNamedFunction` instead. + mlir::FuncOp createFunction(mlir::Location loc, llvm::StringRef name, + mlir::FunctionType ty) { + return createFunction(loc, getModule(), name, ty); + } + + static mlir::FuncOp createFunction(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, + mlir::FunctionType ty); + + /// Determine if the named function is already in the module. Return the + /// instance if found, otherwise add a new named function to the module. + mlir::FuncOp addNamedFunction(mlir::Location loc, llvm::StringRef name, + mlir::FunctionType ty) { + if (auto func = getNamedFunction(name)) + return func; + return createFunction(loc, name, ty); + } + + static mlir::FuncOp addNamedFunction(mlir::Location loc, + mlir::ModuleOp module, + llvm::StringRef name, + mlir::FunctionType ty) { + if (auto func = getNamedFunction(module, name)) + return func; + return createFunction(loc, module, name, ty); + } + + /// Create constant i1 with value 1. if \p b is true or 0. otherwise + mlir::Value createBool(mlir::Location loc, bool b) { + return createIntegerConstant(loc, getIntegerType(1), b ? 1 : 0); + } + //===--------------------------------------------------------------------===// // If-Then-Else generation helper //===--------------------------------------------------------------------===// @@ -126,8 +257,30 @@ /// Generate code testing \p addr is a null address. mlir::Value genIsNull(mlir::Location loc, mlir::Value addr); + +private: + const KindMapping &kindMap; }; } // namespace fir +namespace fir::factory { + +//===--------------------------------------------------------------------===// +// String literal helper helpers +//===--------------------------------------------------------------------===// + +/// Unique a compiler generated identifier. A short prefix should be provided +/// to hint at the origin of the identifier. +std::string uniqueCGIdent(llvm::StringRef prefix, llvm::StringRef name); + +//===----------------------------------------------------------------------===// +// Location helpers +//===----------------------------------------------------------------------===// + +/// Generate a constant of the given type with the location line number +mlir::Value locationToLineNo(fir::FirOpBuilder &, mlir::Location, mlir::Type); + +} // namespace fir::factory + #endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp --- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp +++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp @@ -7,6 +7,70 @@ //===----------------------------------------------------------------------===// #include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Dialect/FIROpsSupport.h" +#include "flang/Optimizer/Support/FatalError.h" +#include "flang/Optimizer/Support/InternalNames.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/MD5.h" + +static llvm::cl::opt + 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(name); +} + +fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, + llvm::StringRef name) { + return modOp.lookupSymbol(name); +} + +mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) { + assert(!eleTy.isa() && "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"); + } +} + +mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, + mlir::Type ptrType) { + auto ty = ptrType ? ptrType : getRefType(getNoneType()); + return create(loc, ty); +} mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, mlir::Type ty, @@ -14,6 +78,74 @@ return create(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()) + 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()) { + auto attr = getFloatAttr(fltTy, value); + return create(loc, fltTy, attr); + } + llvm_unreachable("should use builtin floating-point type"); +} + +/// 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(name)) + return glob; + setInsertionPoint(module.getBody(), module.getBody()->end()); + auto glob = create(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 bodyBuilder, mlir::StringAttr linkage) { + auto module = getModule(); + auto insertPt = saveInsertionPoint(); + if (auto glob = module.lookupSymbol(name)) + return glob; + setInsertionPoint(module.getBody(), module.getBody()->end()); + auto glob = create(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::createConvert(mlir::Location loc, mlir::Type toTy, mlir::Value val) { if (val.getType() != toTy) { @@ -23,6 +155,19 @@ 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 attrs{dataAttr, sizeAttr}; + return create(loc, llvm::ArrayRef{type}, + llvm::None, attrs); +} + static mlir::Value genNullPointerComparison(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value addr, @@ -41,3 +186,31 @@ mlir::Value fir::FirOpBuilder::genIsNull(mlir::Location loc, mlir::Value addr) { return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::eq); } + +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::locationToLineNo(fir::FirOpBuilder &builder, + mlir::Location loc, + mlir::Type type) { + if (auto flc = loc.dyn_cast()) + return builder.createIntegerConstant(loc, type, flc.getLine()); + return builder.createIntegerConstant(loc, type, 0); +} diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -3176,7 +3176,7 @@ if (auto f = module.lookupSymbol(name)) return f; mlir::OpBuilder modBuilder(module.getBodyRegion()); - modBuilder.setInsertionPoint(module.getBody()->getTerminator()); + modBuilder.setInsertionPointToEnd(module.getBody()); auto result = modBuilder.create(loc, name, type, attrs); result.setVisibility(mlir::SymbolTable::Visibility::Private); return result; diff --git a/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp b/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp --- a/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp +++ b/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp @@ -16,8 +16,19 @@ void SetUp() override { fir::KindMapping kindMap(&context); mlir::OpBuilder builder(&context); - firBuilder = std::make_unique(builder, kindMap); + auto loc = builder.getUnknownLoc(); + + // Set up a Module with a dummy function operation inside. + // Set the insertion point in the function entry block. + mlir::ModuleOp mod = builder.create(loc); + mlir::FuncOp func = mlir::FuncOp::create( + loc, "func1", builder.getFunctionType(llvm::None, llvm::None)); + auto *entryBlock = func.addEntryBlock(); + mod.push_back(mod); + builder.setInsertionPointToStart(entryBlock); + fir::support::loadDialects(context); + firBuilder = std::make_unique(mod, kindMap); } fir::FirOpBuilder &getBuilder() { return *firBuilder; } @@ -34,6 +45,14 @@ loc, arith::CmpIPredicate::eq, zero1, zero2); } +static void checkIntegerConstant(mlir::Value value, mlir::Type ty, int64_t v) { + EXPECT_TRUE(mlir::isa(value.getDefiningOp())); + auto cstOp = dyn_cast(value.getDefiningOp()); + EXPECT_EQ(ty, cstOp.getType()); + auto valueAttr = cstOp.getValue().dyn_cast_or_null(); + EXPECT_EQ(v, valueAttr.getInt()); +} + //===----------------------------------------------------------------------===// // IfBuilder tests //===----------------------------------------------------------------------===// @@ -99,3 +118,128 @@ auto cmpOp = dyn_cast(res.getDefiningOp()); EXPECT_EQ(arith::CmpIPredicate::eq, cmpOp.predicate()); } + +TEST_F(FIRBuilderTest, createZeroConstant) { + auto builder = getBuilder(); + auto loc = builder.getUnknownLoc(); + + auto cst = builder.createNullConstant(loc); + EXPECT_TRUE(mlir::isa(cst.getDefiningOp())); + auto zeroOp = dyn_cast(cst.getDefiningOp()); + EXPECT_EQ(fir::ReferenceType::get(builder.getNoneType()), + zeroOp.getResult().getType()); + auto idxTy = builder.getIndexType(); + + cst = builder.createNullConstant(loc, idxTy); + EXPECT_TRUE(mlir::isa(cst.getDefiningOp())); + zeroOp = dyn_cast(cst.getDefiningOp()); + EXPECT_EQ(builder.getIndexType(), zeroOp.getResult().getType()); +} + +TEST_F(FIRBuilderTest, createRealZeroConstant) { + auto builder = getBuilder(); + auto ctx = builder.getContext(); + auto loc = builder.getUnknownLoc(); + auto realTy = mlir::FloatType::getF64(ctx); + auto cst = builder.createRealZeroConstant(loc, realTy); + EXPECT_TRUE(mlir::isa(cst.getDefiningOp())); + auto cstOp = dyn_cast(cst.getDefiningOp()); + EXPECT_EQ(realTy, cstOp.getType()); + EXPECT_EQ(0u, cstOp.value().cast().getValue().convertToDouble()); +} + +TEST_F(FIRBuilderTest, createBool) { + auto builder = getBuilder(); + auto loc = builder.getUnknownLoc(); + auto b = builder.createBool(loc, false); + checkIntegerConstant(b, builder.getIntegerType(1), 0); +} + +TEST_F(FIRBuilderTest, getVarLenSeqTy) { + auto builder = getBuilder(); + auto ty = builder.getVarLenSeqTy(builder.getI64Type()); + EXPECT_TRUE(ty.isa()); + fir::SequenceType seqTy = ty.dyn_cast(); + EXPECT_EQ(1u, seqTy.getDimension()); + EXPECT_TRUE(fir::unwrapSequenceType(ty).isInteger(64)); +} + +TEST_F(FIRBuilderTest, getNamedFunction) { + auto builder = getBuilder(); + auto func2 = builder.getNamedFunction("func2"); + EXPECT_EQ(nullptr, func2); + auto loc = builder.getUnknownLoc(); + func2 = builder.createFunction( + loc, "func2", builder.getFunctionType(llvm::None, llvm::None)); + auto func2query = builder.getNamedFunction("func2"); + EXPECT_EQ(func2, func2query); +} + +TEST_F(FIRBuilderTest, createGlobal1) { + auto builder = getBuilder(); + auto loc = builder.getUnknownLoc(); + auto i64Type = IntegerType::get(builder.getContext(), 64); + auto global = builder.createGlobal( + loc, i64Type, "global1", builder.createInternalLinkage(), {}, true); + EXPECT_TRUE(mlir::isa(global)); + EXPECT_EQ("global1", global.sym_name()); + EXPECT_TRUE(global.constant().hasValue()); + EXPECT_EQ(i64Type, global.type()); + EXPECT_TRUE(global.linkName().hasValue()); + EXPECT_EQ( + builder.createInternalLinkage().getValue(), global.linkName().getValue()); + EXPECT_FALSE(global.initVal().hasValue()); + + auto g1 = builder.getNamedGlobal("global1"); + EXPECT_EQ(global, g1); + auto g2 = builder.getNamedGlobal("global7"); + EXPECT_EQ(nullptr, g2); + auto g3 = builder.getNamedGlobal(""); + EXPECT_EQ(nullptr, g3); +} + +TEST_F(FIRBuilderTest, createGlobal2) { + auto builder = getBuilder(); + auto loc = builder.getUnknownLoc(); + auto i32Type = IntegerType::get(builder.getContext(), 32); + auto attr = builder.getIntegerAttr(i32Type, 16); + auto global = builder.createGlobal( + loc, i32Type, "global2", builder.createLinkOnceLinkage(), attr, false); + EXPECT_TRUE(mlir::isa(global)); + EXPECT_EQ("global2", global.sym_name()); + EXPECT_FALSE(global.constant().hasValue()); + EXPECT_EQ(i32Type, global.type()); + EXPECT_TRUE(global.initVal().hasValue()); + EXPECT_TRUE(global.initVal().getValue().isa()); + EXPECT_EQ( + 16, global.initVal().getValue().cast().getValue()); + EXPECT_TRUE(global.linkName().hasValue()); + EXPECT_EQ( + builder.createLinkOnceLinkage().getValue(), global.linkName().getValue()); +} + +TEST_F(FIRBuilderTest, uniqueCFIdent) { + auto str1 = fir::factory::uniqueCGIdent("", "func1"); + EXPECT_EQ("_QQ.66756E6331", str1); + str1 = fir::factory::uniqueCGIdent("", ""); + EXPECT_EQ("_QQ.", str1); + str1 = fir::factory::uniqueCGIdent("pr", "func1"); + EXPECT_EQ("_QQpr.66756E6331", str1); + str1 = fir::factory::uniqueCGIdent( + "", "longnamemorethan32characterneedshashing"); + EXPECT_EQ("_QQ.c22a886b2f30ea8c064ef1178377fc31", str1); + str1 = fir::factory::uniqueCGIdent( + "pr", "longnamemorethan32characterneedshashing"); + EXPECT_EQ("_QQpr.c22a886b2f30ea8c064ef1178377fc31", str1); +} + +TEST_F(FIRBuilderTest, locationToLineNo) { + auto builder = getBuilder(); + auto loc = mlir::FileLineColLoc::get(builder.getIdentifier("file1"), 10, 5); + mlir::Value line = + fir::factory::locationToLineNo(builder, loc, builder.getI64Type()); + checkIntegerConstant(line, builder.getI64Type(), 10); + line = fir::factory::locationToLineNo( + builder, builder.getUnknownLoc(), builder.getI64Type()); + checkIntegerConstant(line, builder.getI64Type(), 0); +}