1 //===-- FIRBuilder.cpp ----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "flang/Optimizer/Builder/FIRBuilder.h" 10 #include "flang/Optimizer/Builder/BoxValue.h" 11 #include "flang/Optimizer/Builder/Character.h" 12 #include "flang/Optimizer/Builder/MutableBox.h" 13 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 14 #include "flang/Optimizer/Support/FatalError.h" 15 #include "flang/Optimizer/Support/InternalNames.h" 16 #include "mlir/Dialect/OpenMP/OpenMPDialect.h" 17 #include "llvm/ADT/StringExtras.h" 18 #include "llvm/Support/CommandLine.h" 19 #include "llvm/Support/MD5.h" 20 21 static llvm::cl::opt<std::size_t> 22 nameLengthHashSize("length-to-hash-string-literal", 23 llvm::cl::desc("string literals that exceed this length" 24 " will use a hash value as their symbol " 25 "name"), 26 llvm::cl::init(32)); 27 28 mlir::FuncOp fir::FirOpBuilder::createFunction(mlir::Location loc, 29 mlir::ModuleOp module, 30 llvm::StringRef name, 31 mlir::FunctionType ty) { 32 return fir::createFuncOp(loc, module, name, ty); 33 } 34 35 mlir::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, 36 llvm::StringRef name) { 37 return modOp.lookupSymbol<mlir::FuncOp>(name); 38 } 39 40 fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, 41 llvm::StringRef name) { 42 return modOp.lookupSymbol<fir::GlobalOp>(name); 43 } 44 45 mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) { 46 assert(!eleTy.isa<fir::ReferenceType>() && "cannot be a reference type"); 47 return fir::ReferenceType::get(eleTy); 48 } 49 50 mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) { 51 fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent()); 52 return fir::SequenceType::get(shape, eleTy); 53 } 54 55 mlir::Type fir::FirOpBuilder::getRealType(int kind) { 56 switch (kindMap.getRealTypeID(kind)) { 57 case llvm::Type::TypeID::HalfTyID: 58 return mlir::FloatType::getF16(getContext()); 59 case llvm::Type::TypeID::FloatTyID: 60 return mlir::FloatType::getF32(getContext()); 61 case llvm::Type::TypeID::DoubleTyID: 62 return mlir::FloatType::getF64(getContext()); 63 case llvm::Type::TypeID::X86_FP80TyID: 64 return mlir::FloatType::getF80(getContext()); 65 case llvm::Type::TypeID::FP128TyID: 66 return mlir::FloatType::getF128(getContext()); 67 default: 68 fir::emitFatalError(UnknownLoc::get(getContext()), 69 "unsupported type !fir.real<kind>"); 70 } 71 } 72 73 mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, 74 mlir::Type ptrType) { 75 auto ty = ptrType ? ptrType : getRefType(getNoneType()); 76 return create<fir::ZeroOp>(loc, ty); 77 } 78 79 mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, 80 mlir::Type ty, 81 std::int64_t cst) { 82 return create<mlir::ConstantOp>(loc, ty, getIntegerAttr(ty, cst)); 83 } 84 85 mlir::Value 86 fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy, 87 llvm::APFloat::integerPart val) { 88 auto apf = [&]() -> llvm::APFloat { 89 if (auto ty = fltTy.dyn_cast<fir::RealType>()) 90 return llvm::APFloat(kindMap.getFloatSemantics(ty.getFKind()), val); 91 if (fltTy.isF16()) 92 return llvm::APFloat(llvm::APFloat::IEEEhalf(), val); 93 if (fltTy.isBF16()) 94 return llvm::APFloat(llvm::APFloat::BFloat(), val); 95 if (fltTy.isF32()) 96 return llvm::APFloat(llvm::APFloat::IEEEsingle(), val); 97 if (fltTy.isF64()) 98 return llvm::APFloat(llvm::APFloat::IEEEdouble(), val); 99 if (fltTy.isF80()) 100 return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val); 101 if (fltTy.isF128()) 102 return llvm::APFloat(llvm::APFloat::IEEEquad(), val); 103 llvm_unreachable("unhandled MLIR floating-point type"); 104 }; 105 return createRealConstant(loc, fltTy, apf()); 106 } 107 108 mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc, 109 mlir::Type fltTy, 110 const llvm::APFloat &value) { 111 if (fltTy.isa<mlir::FloatType>()) { 112 auto attr = getFloatAttr(fltTy, value); 113 return create<mlir::arith::ConstantOp>(loc, fltTy, attr); 114 } 115 llvm_unreachable("should use builtin floating-point type"); 116 } 117 118 static llvm::SmallVector<mlir::Value> 119 elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape) { 120 auto arrTy = type.dyn_cast<fir::SequenceType>(); 121 if (shape.empty() || !arrTy) 122 return {}; 123 // elide the constant dimensions before construction 124 assert(shape.size() == arrTy.getDimension()); 125 llvm::SmallVector<mlir::Value> dynamicShape; 126 auto typeShape = arrTy.getShape(); 127 for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i) 128 if (typeShape[i] == fir::SequenceType::getUnknownExtent()) 129 dynamicShape.push_back(shape[i]); 130 return dynamicShape; 131 } 132 133 static llvm::SmallVector<mlir::Value> 134 elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams) { 135 if (lenParams.empty()) 136 return {}; 137 if (auto arrTy = type.dyn_cast<fir::SequenceType>()) 138 type = arrTy.getEleTy(); 139 if (fir::hasDynamicSize(type)) 140 return lenParams; 141 return {}; 142 } 143 144 /// Allocate a local variable. 145 /// A local variable ought to have a name in the source code. 146 mlir::Value fir::FirOpBuilder::allocateLocal( 147 mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, 148 llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape, 149 llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { 150 // Convert the shape extents to `index`, as needed. 151 llvm::SmallVector<mlir::Value> indices; 152 llvm::SmallVector<mlir::Value> elidedShape = 153 elideExtentsAlreadyInType(ty, shape); 154 llvm::SmallVector<mlir::Value> elidedLenParams = 155 elideLengthsAlreadyInType(ty, lenParams); 156 auto idxTy = getIndexType(); 157 llvm::for_each(elidedShape, [&](mlir::Value sh) { 158 indices.push_back(createConvert(loc, idxTy, sh)); 159 }); 160 // Add a target attribute, if needed. 161 llvm::SmallVector<mlir::NamedAttribute> attrs; 162 if (asTarget) 163 attrs.emplace_back( 164 mlir::Identifier::get(fir::getTargetAttrName(), getContext()), 165 getUnitAttr()); 166 // Create the local variable. 167 if (name.empty()) { 168 if (uniqName.empty()) 169 return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices, 170 attrs); 171 return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams, 172 indices, attrs); 173 } 174 return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams, 175 indices, attrs); 176 } 177 178 mlir::Value fir::FirOpBuilder::allocateLocal( 179 mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, 180 llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape, 181 llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { 182 return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape, 183 lenParams, asTarget); 184 } 185 186 /// Get the block for adding Allocas. 187 mlir::Block *fir::FirOpBuilder::getAllocaBlock() { 188 // auto iface = 189 // getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); 190 // return iface ? iface.getAllocaBlock() : getEntryBlock(); 191 return getEntryBlock(); 192 } 193 194 /// Create a temporary variable on the stack. Anonymous temporaries have no 195 /// `name` value. Temporaries do not require a uniqued name. 196 mlir::Value 197 fir::FirOpBuilder::createTemporary(mlir::Location loc, mlir::Type type, 198 llvm::StringRef name, mlir::ValueRange shape, 199 mlir::ValueRange lenParams, 200 llvm::ArrayRef<mlir::NamedAttribute> attrs) { 201 llvm::SmallVector<mlir::Value> dynamicShape = 202 elideExtentsAlreadyInType(type, shape); 203 llvm::SmallVector<mlir::Value> dynamicLength = 204 elideLengthsAlreadyInType(type, lenParams); 205 InsertPoint insPt; 206 const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty(); 207 if (hoistAlloc) { 208 insPt = saveInsertionPoint(); 209 setInsertionPointToStart(getAllocaBlock()); 210 } 211 212 // If the alloca is inside an OpenMP Op which will be outlined then pin the 213 // alloca here. 214 const bool pinned = 215 getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); 216 assert(!type.isa<fir::ReferenceType>() && "cannot be a reference"); 217 auto ae = 218 create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, name, 219 pinned, dynamicLength, dynamicShape, attrs); 220 if (hoistAlloc) 221 restoreInsertionPoint(insPt); 222 return ae; 223 } 224 225 /// Create a global variable in the (read-only) data section. A global variable 226 /// must have a unique name to identify and reference it. 227 fir::GlobalOp 228 fir::FirOpBuilder::createGlobal(mlir::Location loc, mlir::Type type, 229 llvm::StringRef name, mlir::StringAttr linkage, 230 mlir::Attribute value, bool isConst) { 231 auto module = getModule(); 232 auto insertPt = saveInsertionPoint(); 233 if (auto glob = module.lookupSymbol<fir::GlobalOp>(name)) 234 return glob; 235 setInsertionPoint(module.getBody(), module.getBody()->end()); 236 auto glob = create<fir::GlobalOp>(loc, name, isConst, type, value, linkage); 237 restoreInsertionPoint(insertPt); 238 return glob; 239 } 240 241 fir::GlobalOp fir::FirOpBuilder::createGlobal( 242 mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst, 243 std::function<void(FirOpBuilder &)> bodyBuilder, mlir::StringAttr linkage) { 244 auto module = getModule(); 245 auto insertPt = saveInsertionPoint(); 246 if (auto glob = module.lookupSymbol<fir::GlobalOp>(name)) 247 return glob; 248 setInsertionPoint(module.getBody(), module.getBody()->end()); 249 auto glob = create<fir::GlobalOp>(loc, name, isConst, type, mlir::Attribute{}, 250 linkage); 251 auto ®ion = glob.getRegion(); 252 region.push_back(new mlir::Block); 253 auto &block = glob.getRegion().back(); 254 setInsertionPointToStart(&block); 255 bodyBuilder(*this); 256 restoreInsertionPoint(insertPt); 257 return glob; 258 } 259 260 mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc, 261 mlir::Type toTy, mlir::Value val) { 262 if (val.getType() != toTy) { 263 assert(!fir::isa_derived(toTy)); 264 return create<fir::ConvertOp>(loc, toTy, val); 265 } 266 return val; 267 } 268 269 fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc, 270 llvm::StringRef data) { 271 auto type = fir::CharacterType::get(getContext(), 1, data.size()); 272 auto strAttr = mlir::StringAttr::get(getContext(), data); 273 auto valTag = mlir::Identifier::get(fir::StringLitOp::value(), getContext()); 274 mlir::NamedAttribute dataAttr(valTag, strAttr); 275 auto sizeTag = mlir::Identifier::get(fir::StringLitOp::size(), getContext()); 276 mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size())); 277 llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr}; 278 return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type}, 279 llvm::None, attrs); 280 } 281 282 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, 283 llvm::ArrayRef<mlir::Value> exts) { 284 auto shapeType = fir::ShapeType::get(getContext(), exts.size()); 285 return create<fir::ShapeOp>(loc, shapeType, exts); 286 } 287 288 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, 289 llvm::ArrayRef<mlir::Value> shift, 290 llvm::ArrayRef<mlir::Value> exts) { 291 auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size()); 292 llvm::SmallVector<mlir::Value> shapeArgs; 293 auto idxTy = getIndexType(); 294 for (auto [lbnd, ext] : llvm::zip(shift, exts)) { 295 auto lb = createConvert(loc, idxTy, lbnd); 296 shapeArgs.push_back(lb); 297 shapeArgs.push_back(ext); 298 } 299 return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs); 300 } 301 302 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, 303 const fir::AbstractArrayBox &arr) { 304 if (arr.lboundsAllOne()) 305 return genShape(loc, arr.getExtents()); 306 return genShape(loc, arr.getLBounds(), arr.getExtents()); 307 } 308 309 mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc, 310 const fir::ExtendedValue &exv) { 311 return exv.match( 312 [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); }, 313 [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); }, 314 [&](const fir::BoxValue &box) -> mlir::Value { 315 if (!box.getLBounds().empty()) { 316 auto shiftType = 317 fir::ShiftType::get(getContext(), box.getLBounds().size()); 318 return create<fir::ShiftOp>(loc, shiftType, box.getLBounds()); 319 } 320 return {}; 321 }, 322 [&](const fir::MutableBoxValue &) -> mlir::Value { 323 // MutableBoxValue must be read into another category to work with them 324 // outside of allocation/assignment contexts. 325 fir::emitFatalError(loc, "createShape on MutableBoxValue"); 326 }, 327 [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); }); 328 } 329 330 static mlir::Value genNullPointerComparison(fir::FirOpBuilder &builder, 331 mlir::Location loc, 332 mlir::Value addr, 333 arith::CmpIPredicate condition) { 334 auto intPtrTy = builder.getIntPtrType(); 335 auto ptrToInt = builder.createConvert(loc, intPtrTy, addr); 336 auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0); 337 return builder.create<arith::CmpIOp>(loc, condition, ptrToInt, c0); 338 } 339 340 mlir::Value fir::FirOpBuilder::genIsNotNull(mlir::Location loc, 341 mlir::Value addr) { 342 return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::ne); 343 } 344 345 mlir::Value fir::FirOpBuilder::genIsNull(mlir::Location loc, mlir::Value addr) { 346 return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::eq); 347 } 348 349 //===--------------------------------------------------------------------===// 350 // ExtendedValue inquiry helper implementation 351 //===--------------------------------------------------------------------===// 352 353 mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder, 354 mlir::Location loc, 355 const fir::ExtendedValue &box) { 356 return box.match( 357 [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); }, 358 [&](const fir::CharArrayBoxValue &x) -> mlir::Value { 359 return x.getLen(); 360 }, 361 [&](const fir::BoxValue &x) -> mlir::Value { 362 assert(x.isCharacter()); 363 if (!x.getExplicitParameters().empty()) 364 return x.getExplicitParameters()[0]; 365 return fir::factory::CharacterExprHelper{builder, loc} 366 .readLengthFromBox(x.getAddr()); 367 }, 368 [&](const fir::MutableBoxValue &) -> mlir::Value { 369 // MutableBoxValue must be read into another category to work with them 370 // outside of allocation/assignment contexts. 371 fir::emitFatalError(loc, "readCharLen on MutableBoxValue"); 372 }, 373 [&](const auto &) -> mlir::Value { 374 fir::emitFatalError( 375 loc, "Character length inquiry on a non-character entity"); 376 }); 377 } 378 379 llvm::SmallVector<mlir::Value> 380 fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc, 381 const fir::BoxValue &box) { 382 llvm::SmallVector<mlir::Value> result; 383 auto explicitExtents = box.getExplicitExtents(); 384 if (!explicitExtents.empty()) { 385 result.append(explicitExtents.begin(), explicitExtents.end()); 386 return result; 387 } 388 auto rank = box.rank(); 389 auto idxTy = builder.getIndexType(); 390 for (decltype(rank) dim = 0; dim < rank; ++dim) { 391 auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); 392 auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, 393 box.getAddr(), dimVal); 394 result.emplace_back(dimInfo.getResult(1)); 395 } 396 return result; 397 } 398 399 llvm::SmallVector<mlir::Value> 400 fir::factory::getExtents(fir::FirOpBuilder &builder, mlir::Location loc, 401 const fir::ExtendedValue &box) { 402 return box.match( 403 [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { 404 return {x.getExtents().begin(), x.getExtents().end()}; 405 }, 406 [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { 407 return {x.getExtents().begin(), x.getExtents().end()}; 408 }, 409 [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> { 410 return fir::factory::readExtents(builder, loc, x); 411 }, 412 [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> { 413 auto load = fir::factory::genMutableBoxRead(builder, loc, x); 414 return fir::factory::getExtents(builder, loc, load); 415 }, 416 [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); 417 } 418 419 std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix, 420 llvm::StringRef name) { 421 // For "long" identifiers use a hash value 422 if (name.size() > nameLengthHashSize) { 423 llvm::MD5 hash; 424 hash.update(name); 425 llvm::MD5::MD5Result result; 426 hash.final(result); 427 llvm::SmallString<32> str; 428 llvm::MD5::stringifyResult(result, str); 429 std::string hashName = prefix.str(); 430 hashName.append(".").append(str.c_str()); 431 return fir::NameUniquer::doGenerated(hashName); 432 } 433 // "Short" identifiers use a reversible hex string 434 std::string nm = prefix.str(); 435 return fir::NameUniquer::doGenerated( 436 nm.append(".").append(llvm::toHex(name))); 437 } 438 439 mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder, 440 mlir::Location loc) { 441 if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) { 442 // must be encoded as asciiz, C string 443 auto fn = flc.getFilename().str() + '\0'; 444 return fir::getBase(createStringLiteral(builder, loc, fn)); 445 } 446 return builder.createNullConstant(loc); 447 } 448 449 mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder, 450 mlir::Location loc, 451 mlir::Type type) { 452 if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) 453 return builder.createIntegerConstant(loc, type, flc.getLine()); 454 return builder.createIntegerConstant(loc, type, 0); 455 } 456 457 fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder, 458 mlir::Location loc, 459 llvm::StringRef str) { 460 std::string globalName = fir::factory::uniqueCGIdent("cl", str); 461 auto type = fir::CharacterType::get(builder.getContext(), 1, str.size()); 462 auto global = builder.getNamedGlobal(globalName); 463 if (!global) 464 global = builder.createGlobalConstant( 465 loc, type, globalName, 466 [&](fir::FirOpBuilder &builder) { 467 auto stringLitOp = builder.createStringLitOp(loc, str); 468 builder.create<fir::HasValueOp>(loc, stringLitOp); 469 }, 470 builder.createLinkOnceLinkage()); 471 auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(), 472 global.getSymbol()); 473 auto len = builder.createIntegerConstant( 474 loc, builder.getCharacterLengthType(), str.size()); 475 return fir::CharBoxValue{addr, len}; 476 } 477