384 lines · cpp
1//===-- Optimizer/Builder/TemporaryStorage.cpp ------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8// Implementation of utility data structures to create and manipulate temporary9// storages to stack Fortran values or pointers in HLFIR.10//===----------------------------------------------------------------------===//11 12#include "flang/Optimizer/Builder/TemporaryStorage.h"13#include "flang/Optimizer/Builder/FIRBuilder.h"14#include "flang/Optimizer/Builder/HLFIRTools.h"15#include "flang/Optimizer/Builder/Runtime/TemporaryStack.h"16#include "flang/Optimizer/Builder/Todo.h"17#include "flang/Optimizer/HLFIR/HLFIROps.h"18 19//===----------------------------------------------------------------------===//20// fir::factory::Counter implementation.21//===----------------------------------------------------------------------===//22 23fir::factory::Counter::Counter(mlir::Location loc, fir::FirOpBuilder &builder,24 mlir::Value initialValue,25 bool canCountThroughLoops)26 : canCountThroughLoops{canCountThroughLoops}, initialValue{initialValue} {27 mlir::Type type = initialValue.getType();28 one = builder.createIntegerConstant(loc, type, 1);29 if (canCountThroughLoops) {30 index = builder.createTemporary(loc, type);31 fir::StoreOp::create(builder, loc, initialValue, index);32 } else {33 index = initialValue;34 }35}36 37mlir::Value38fir::factory::Counter::getAndIncrementIndex(mlir::Location loc,39 fir::FirOpBuilder &builder) {40 if (canCountThroughLoops) {41 mlir::Value indexValue = fir::LoadOp::create(builder, loc, index);42 mlir::Value newValue =43 mlir::arith::AddIOp::create(builder, loc, indexValue, one);44 fir::StoreOp::create(builder, loc, newValue, index);45 return indexValue;46 }47 mlir::Value indexValue = index;48 index = mlir::arith::AddIOp::create(builder, loc, indexValue, one);49 return indexValue;50}51 52void fir::factory::Counter::reset(mlir::Location loc,53 fir::FirOpBuilder &builder) {54 if (canCountThroughLoops)55 fir::StoreOp::create(builder, loc, initialValue, index);56 else57 index = initialValue;58}59 60//===----------------------------------------------------------------------===//61// fir::factory::HomogeneousScalarStack implementation.62//===----------------------------------------------------------------------===//63 64fir::factory::HomogeneousScalarStack::HomogeneousScalarStack(65 mlir::Location loc, fir::FirOpBuilder &builder,66 fir::SequenceType declaredType, mlir::Value extent,67 llvm::ArrayRef<mlir::Value> lengths, bool allocateOnHeap,68 bool stackThroughLoops, llvm::StringRef tempName)69 : allocateOnHeap{allocateOnHeap},70 counter{loc, builder,71 builder.createIntegerConstant(loc, builder.getIndexType(), 1),72 stackThroughLoops} {73 // Allocate the temporary storage.74 llvm::SmallVector<mlir::Value, 1> extents{extent};75 mlir::Value tempStorage;76 if (allocateOnHeap)77 tempStorage = builder.createHeapTemporary(loc, declaredType, tempName,78 extents, lengths);79 else80 tempStorage =81 builder.createTemporary(loc, declaredType, tempName, extents, lengths);82 83 mlir::Value shape = builder.genShape(loc, extents);84 temp = hlfir::DeclareOp::create(builder, loc, tempStorage, tempName, shape,85 lengths)86 .getBase();87}88 89void fir::factory::HomogeneousScalarStack::pushValue(mlir::Location loc,90 fir::FirOpBuilder &builder,91 mlir::Value value) {92 hlfir::Entity entity{value};93 assert(entity.isScalar() && "cannot use inlined temp with array");94 mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);95 hlfir::Entity tempElement = hlfir::getElementAt(96 loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue});97 // TODO: "copy" would probably be better than assign to ensure there are no98 // side effects (user assignments, temp, lhs finalization)?99 // This only makes a difference for derived types, and for now derived types100 // will use the runtime strategy to avoid any bad behaviors. So the todo101 // below should not get hit but is added as a remainder/safety.102 if (!entity.hasIntrinsicType())103 TODO(loc, "creating inlined temporary stack for derived types");104 hlfir::AssignOp::create(builder, loc, value, tempElement);105}106 107void fir::factory::HomogeneousScalarStack::resetFetchPosition(108 mlir::Location loc, fir::FirOpBuilder &builder) {109 counter.reset(loc, builder);110}111 112mlir::Value113fir::factory::HomogeneousScalarStack::fetch(mlir::Location loc,114 fir::FirOpBuilder &builder) {115 mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);116 hlfir::Entity tempElement = hlfir::getElementAt(117 loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue});118 return hlfir::loadTrivialScalar(loc, builder, tempElement);119}120 121void fir::factory::HomogeneousScalarStack::destroy(mlir::Location loc,122 fir::FirOpBuilder &builder) {123 if (allocateOnHeap) {124 auto declare = temp.getDefiningOp<hlfir::DeclareOp>();125 assert(declare && "temp must have been declared");126 fir::FreeMemOp::create(builder, loc, declare.getMemref());127 }128}129 130hlfir::Entity fir::factory::HomogeneousScalarStack::moveStackAsArrayExpr(131 mlir::Location loc, fir::FirOpBuilder &builder) {132 mlir::Value mustFree = builder.createBool(loc, allocateOnHeap);133 auto hlfirExpr = hlfir::AsExprOp::create(builder, loc, temp, mustFree);134 return hlfir::Entity{hlfirExpr};135}136 137//===----------------------------------------------------------------------===//138// fir::factory::SimpleCopy implementation.139//===----------------------------------------------------------------------===//140 141fir::factory::SimpleCopy::SimpleCopy(mlir::Location loc,142 fir::FirOpBuilder &builder,143 hlfir::Entity source,144 llvm::StringRef tempName) {145 // Use hlfir.as_expr and hlfir.associate to create a copy and leave146 // bufferization deals with how best to make the copy.147 if (source.isVariable())148 source = hlfir::Entity{hlfir::AsExprOp::create(builder, loc, source)};149 copy = hlfir::genAssociateExpr(loc, builder, source,150 source.getFortranElementType(), tempName);151}152 153void fir::factory::SimpleCopy::destroy(mlir::Location loc,154 fir::FirOpBuilder &builder) {155 hlfir::EndAssociateOp::create(builder, loc, copy);156}157 158//===----------------------------------------------------------------------===//159// fir::factory::AnyValueStack implementation.160//===----------------------------------------------------------------------===//161 162fir::factory::AnyValueStack::AnyValueStack(mlir::Location loc,163 fir::FirOpBuilder &builder,164 mlir::Type valueStaticType)165 : valueStaticType{valueStaticType},166 counter{loc, builder,167 builder.createIntegerConstant(loc, builder.getI64Type(), 0),168 /*stackThroughLoops=*/true} {169 opaquePtr = fir::runtime::genCreateValueStack(loc, builder);170 // Compute the storage type. I1 are stored as fir.logical<1>. This is required171 // to use descriptor.172 mlir::Type storageType =173 hlfir::getFortranElementOrSequenceType(valueStaticType);174 mlir::Type i1Type = builder.getI1Type();175 if (storageType == i1Type)176 storageType = fir::LogicalType::get(builder.getContext(), 1);177 assert(hlfir::getFortranElementType(storageType) != i1Type &&178 "array of i1 should not be used");179 mlir::Type heapType = fir::HeapType::get(storageType);180 mlir::Type boxType;181 if (hlfir::isPolymorphicType(valueStaticType))182 boxType = fir::ClassType::get(heapType);183 else184 boxType = fir::BoxType::get(heapType);185 retValueBox = builder.createTemporary(loc, boxType);186}187 188void fir::factory::AnyValueStack::pushValue(mlir::Location loc,189 fir::FirOpBuilder &builder,190 mlir::Value value) {191 hlfir::Entity entity{value};192 mlir::Type storageElementType =193 hlfir::getFortranElementType(retValueBox.getType());194 auto [box, maybeCleanUp] =195 hlfir::convertToBox(loc, builder, entity, storageElementType);196 fir::runtime::genPushValue(loc, builder, opaquePtr, fir::getBase(box));197 if (maybeCleanUp)198 (*maybeCleanUp)();199}200 201void fir::factory::AnyValueStack::resetFetchPosition(202 mlir::Location loc, fir::FirOpBuilder &builder) {203 counter.reset(loc, builder);204}205 206mlir::Value fir::factory::AnyValueStack::fetch(mlir::Location loc,207 fir::FirOpBuilder &builder) {208 mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);209 fir::runtime::genValueAt(loc, builder, opaquePtr, indexValue, retValueBox);210 // Dereference the allocatable "retValueBox", and load if trivial scalar211 // value.212 mlir::Value result =213 hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{retValueBox});214 if (valueStaticType != result.getType()) {215 // Cast back saved simple scalars stored with another type to their original216 // type (like i1).217 if (fir::isa_trivial(valueStaticType))218 return builder.createConvert(loc, valueStaticType, result);219 // Memory type mismatches (e.g. fir.ref vs fir.heap) or hlfir.expr vs220 // variable type mismatches are OK, but the base Fortran type must be the221 // same.222 assert(hlfir::getFortranElementOrSequenceType(valueStaticType) ==223 hlfir::getFortranElementOrSequenceType(result.getType()) &&224 "non trivial values must be saved with their original type");225 }226 return result;227}228 229void fir::factory::AnyValueStack::destroy(mlir::Location loc,230 fir::FirOpBuilder &builder) {231 fir::runtime::genDestroyValueStack(loc, builder, opaquePtr);232}233 234//===----------------------------------------------------------------------===//235// fir::factory::AnyVariableStack implementation.236//===----------------------------------------------------------------------===//237 238fir::factory::AnyVariableStack::AnyVariableStack(mlir::Location loc,239 fir::FirOpBuilder &builder,240 mlir::Type variableStaticType)241 : variableStaticType{variableStaticType},242 counter{loc, builder,243 builder.createIntegerConstant(loc, builder.getI64Type(), 0),244 /*stackThroughLoops=*/true} {245 opaquePtr = fir::runtime::genCreateDescriptorStack(loc, builder);246 mlir::Type storageType =247 hlfir::getFortranElementOrSequenceType(variableStaticType);248 mlir::Type ptrType = fir::PointerType::get(storageType);249 mlir::Type boxType;250 if (hlfir::isPolymorphicType(variableStaticType))251 boxType = fir::ClassType::get(ptrType);252 else253 boxType = fir::BoxType::get(ptrType);254 retValueBox = builder.createTemporary(loc, boxType);255}256 257void fir::factory::AnyVariableStack::pushValue(mlir::Location loc,258 fir::FirOpBuilder &builder,259 mlir::Value variable) {260 hlfir::Entity entity{variable};261 mlir::Value box =262 hlfir::genVariableBox(loc, builder, entity, entity.getBoxType());263 fir::runtime::genPushDescriptor(loc, builder, opaquePtr, fir::getBase(box));264}265 266void fir::factory::AnyVariableStack::resetFetchPosition(267 mlir::Location loc, fir::FirOpBuilder &builder) {268 counter.reset(loc, builder);269}270 271mlir::Value fir::factory::AnyVariableStack::fetch(mlir::Location loc,272 fir::FirOpBuilder &builder) {273 mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);274 fir::runtime::genDescriptorAt(loc, builder, opaquePtr, indexValue,275 retValueBox);276 hlfir::Entity retBox{fir::LoadOp::create(builder, loc, retValueBox)};277 // The runtime always tracks variable as address, but the form of the variable278 // that was saved may be different (raw address, fir.boxchar), ensure279 // the returned variable has the same form of the one that was saved.280 if (mlir::isa<fir::BaseBoxType>(variableStaticType))281 return builder.createConvert(loc, variableStaticType, retBox);282 if (mlir::isa<fir::BoxCharType>(variableStaticType))283 return hlfir::genVariableBoxChar(loc, builder, retBox);284 mlir::Value rawAddr = genVariableRawAddress(loc, builder, retBox);285 return builder.createConvert(loc, variableStaticType, rawAddr);286}287 288void fir::factory::AnyVariableStack::destroy(mlir::Location loc,289 fir::FirOpBuilder &builder) {290 fir::runtime::genDestroyDescriptorStack(loc, builder, opaquePtr);291}292 293//===----------------------------------------------------------------------===//294// fir::factory::AnyVectorSubscriptStack implementation.295//===----------------------------------------------------------------------===//296 297fir::factory::AnyVectorSubscriptStack::AnyVectorSubscriptStack(298 mlir::Location loc, fir::FirOpBuilder &builder,299 mlir::Type variableStaticType, bool shapeCanBeSavedAsRegister, int rank)300 : AnyVariableStack{loc, builder, variableStaticType} {301 if (shapeCanBeSavedAsRegister) {302 shapeTemp = std::make_unique<TemporaryStorage>(SSARegister{});303 return;304 }305 // The shape will be tracked as the dimension inside a descriptor because306 // that is the easiest from a lowering point of view, and this is an307 // edge case situation that will probably not very well be exercised.308 mlir::Type type =309 fir::BoxType::get(builder.getVarLenSeqTy(builder.getI32Type(), rank));310 boxType = type;311 shapeTemp =312 std::make_unique<TemporaryStorage>(AnyVariableStack{loc, builder, type});313}314 315void fir::factory::AnyVectorSubscriptStack::pushShape(316 mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value shape) {317 if (boxType) {318 // The shape is saved as a dimensions inside a descriptors.319 mlir::Type refType = fir::ReferenceType::get(320 hlfir::getFortranElementOrSequenceType(*boxType));321 mlir::Value null = builder.createNullConstant(loc, refType);322 mlir::Value descriptor =323 fir::EmboxOp::create(builder, loc, *boxType, null, shape);324 shapeTemp->pushValue(loc, builder, descriptor);325 return;326 }327 // Otherwise, simply keep track of the fir.shape itself, it is invariant.328 shapeTemp->cast<SSARegister>().pushValue(loc, builder, shape);329}330 331void fir::factory::AnyVectorSubscriptStack::resetFetchPosition(332 mlir::Location loc, fir::FirOpBuilder &builder) {333 static_cast<AnyVariableStack *>(this)->resetFetchPosition(loc, builder);334 shapeTemp->resetFetchPosition(loc, builder);335}336 337mlir::Value338fir::factory::AnyVectorSubscriptStack::fetchShape(mlir::Location loc,339 fir::FirOpBuilder &builder) {340 if (boxType) {341 hlfir::Entity descriptor{shapeTemp->fetch(loc, builder)};342 return hlfir::genShape(loc, builder, descriptor);343 }344 return shapeTemp->cast<SSARegister>().fetch(loc, builder);345}346 347void fir::factory::AnyVectorSubscriptStack::destroy(348 mlir::Location loc, fir::FirOpBuilder &builder) {349 static_cast<AnyVariableStack *>(this)->destroy(loc, builder);350 shapeTemp->destroy(loc, builder);351}352 353//===----------------------------------------------------------------------===//354// fir::factory::AnyAddressStack implementation.355//===----------------------------------------------------------------------===//356 357fir::factory::AnyAddressStack::AnyAddressStack(mlir::Location loc,358 fir::FirOpBuilder &builder,359 mlir::Type addressType)360 : AnyValueStack(loc, builder, builder.getIntPtrType()),361 addressType{addressType} {}362 363void fir::factory::AnyAddressStack::pushValue(mlir::Location loc,364 fir::FirOpBuilder &builder,365 mlir::Value variable) {366 mlir::Value cast = variable;367 if (auto boxProcType = llvm::dyn_cast<fir::BoxProcType>(variable.getType())) {368 cast =369 fir::BoxAddrOp::create(builder, loc, boxProcType.getEleTy(), variable);370 }371 cast = builder.createConvert(loc, builder.getIntPtrType(), cast);372 static_cast<AnyValueStack *>(this)->pushValue(loc, builder, cast);373}374 375mlir::Value fir::factory::AnyAddressStack::fetch(mlir::Location loc,376 fir::FirOpBuilder &builder) {377 mlir::Value addr = static_cast<AnyValueStack *>(this)->fetch(loc, builder);378 if (auto boxProcType = llvm::dyn_cast<fir::BoxProcType>(addressType)) {379 mlir::Value cast = builder.createConvert(loc, boxProcType.getEleTy(), addr);380 return fir::EmboxProcOp::create(builder, loc, boxProcType, cast);381 }382 return builder.createConvert(loc, addressType, addr);383}384