llvm-project/flang/lib/Optimizer/Builder/TemporaryStorage.cpp
Jean Perier b361e1c025 [flang][hlfir] address post-commit comments from D151247 and D151251
Addresses comments not addressed in https://reviews.llvm.org/D151251
and https://reviews.llvm.org/D151247

- Fix typo in comments.
- Update an expected test output to include the fir.allocmem argument.
- Make a more generic type comparisons and cast when fetching value
  back from the AnyValueStack temporary storage.

Differential Revision: https://reviews.llvm.org/D151428
2023-05-25 16:34:54 +02:00

234 lines
10 KiB
C++

//===-- Optimizer/Builder/TemporaryStorage.cpp ------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
// Implementation of utility data structures to create and manipulate temporary
// storages to stack Fortran values or pointers in HLFIR.
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Builder/TemporaryStorage.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/Runtime/TemporaryStack.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
//===----------------------------------------------------------------------===//
// fir::factory::Counter implementation.
//===----------------------------------------------------------------------===//
fir::factory::Counter::Counter(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Value initialValue,
bool canCountThroughLoops)
: canCountThroughLoops{canCountThroughLoops}, initialValue{initialValue} {
mlir::Type type = initialValue.getType();
one = builder.createIntegerConstant(loc, type, 1);
if (canCountThroughLoops) {
index = builder.createTemporary(loc, type);
builder.create<fir::StoreOp>(loc, initialValue, index);
} else {
index = initialValue;
}
}
mlir::Value
fir::factory::Counter::getAndIncrementIndex(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (canCountThroughLoops) {
mlir::Value indexValue = builder.create<fir::LoadOp>(loc, index);
mlir::Value newValue =
builder.create<mlir::arith::AddIOp>(loc, indexValue, one);
builder.create<fir::StoreOp>(loc, newValue, index);
return indexValue;
}
mlir::Value indexValue = index;
index = builder.create<mlir::arith::AddIOp>(loc, indexValue, one);
return indexValue;
}
void fir::factory::Counter::reset(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (canCountThroughLoops)
builder.create<fir::StoreOp>(loc, initialValue, index);
else
index = initialValue;
}
//===----------------------------------------------------------------------===//
// fir::factory::HomogeneousScalarStack implementation.
//===----------------------------------------------------------------------===//
fir::factory::HomogeneousScalarStack::HomogeneousScalarStack(
mlir::Location loc, fir::FirOpBuilder &builder,
fir::SequenceType declaredType, mlir::Value extent,
llvm::ArrayRef<mlir::Value> lengths, bool allocateOnHeap,
bool stackThroughLoops, llvm::StringRef tempName)
: allocateOnHeap{allocateOnHeap},
counter{loc, builder,
builder.createIntegerConstant(loc, builder.getIndexType(), 1),
stackThroughLoops} {
// Allocate the temporary storage.
llvm::SmallVector<mlir::Value, 1> extents{extent};
mlir::Value tempStorage;
if (allocateOnHeap)
tempStorage = builder.createHeapTemporary(loc, declaredType, tempName,
extents, lengths);
else
tempStorage =
builder.createTemporary(loc, declaredType, tempName, extents, lengths);
mlir::Value shape = builder.genShape(loc, extents);
temp = builder
.create<hlfir::DeclareOp>(loc, tempStorage, tempName, shape,
lengths, fir::FortranVariableFlagsAttr{})
.getBase();
}
void fir::factory::HomogeneousScalarStack::pushValue(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Value value) {
hlfir::Entity entity{value};
assert(entity.isScalar() && "cannot use inlined temp with array");
mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);
hlfir::Entity tempElement = hlfir::getElementAt(
loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue});
// TODO: "copy" would probably be better than assign to ensure there are no
// side effects (user assignments, temp, lhs finalization)?
// This only makes a difference for derived types, and for now derived types
// will use the runtime strategy to avoid any bad behaviors. So the todo
// below should not get hit but is added as a remainder/safety.
if (!entity.hasIntrinsicType())
TODO(loc, "creating inlined temporary stack for derived types");
builder.create<hlfir::AssignOp>(loc, value, tempElement);
}
void fir::factory::HomogeneousScalarStack::resetFetchPosition(
mlir::Location loc, fir::FirOpBuilder &builder) {
counter.reset(loc, builder);
}
mlir::Value
fir::factory::HomogeneousScalarStack::fetch(mlir::Location loc,
fir::FirOpBuilder &builder) {
mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);
hlfir::Entity tempElement = hlfir::getElementAt(
loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue});
return hlfir::loadTrivialScalar(loc, builder, tempElement);
}
void fir::factory::HomogeneousScalarStack::destroy(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (allocateOnHeap) {
auto declare = temp.getDefiningOp<hlfir::DeclareOp>();
assert(declare && "temp must have been declared");
builder.create<fir::FreeMemOp>(loc, declare.getMemref());
}
}
hlfir::Entity fir::factory::HomogeneousScalarStack::moveStackAsArrayExpr(
mlir::Location loc, fir::FirOpBuilder &builder) {
mlir::Value mustFree = builder.createBool(loc, allocateOnHeap);
auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree);
return hlfir::Entity{hlfirExpr};
}
//===----------------------------------------------------------------------===//
// fir::factory::SimpleCopy implementation.
//===----------------------------------------------------------------------===//
fir::factory::SimpleCopy::SimpleCopy(mlir::Location loc,
fir::FirOpBuilder &builder,
hlfir::Entity source,
llvm::StringRef tempName) {
// Use hlfir.as_expr and hlfir.associate to create a copy and leave
// bufferization deals with how best to make the copy.
if (source.isVariable())
source = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, source)};
copy = hlfir::genAssociateExpr(loc, builder, source,
source.getFortranElementType(), tempName);
}
void fir::factory::SimpleCopy::destroy(mlir::Location loc,
fir::FirOpBuilder &builder) {
builder.create<hlfir::EndAssociateOp>(loc, copy);
}
//===----------------------------------------------------------------------===//
// fir::factory::AnyValueStack implementation.
//===----------------------------------------------------------------------===//
fir::factory::AnyValueStack::AnyValueStack(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type valueStaticType)
: valueStaticType{valueStaticType},
counter{loc, builder,
builder.createIntegerConstant(loc, builder.getI64Type(), 0),
/*stackThroughLoops=*/true} {
opaquePtr = fir::runtime::genCreateValueStack(loc, builder);
// Compute the storage type. I1 are stored as fir.logical<1>. This is required
// to use descriptor.
mlir::Type storageType =
hlfir::getFortranElementOrSequenceType(valueStaticType);
mlir::Type i1Type = builder.getI1Type();
if (storageType == i1Type)
storageType = fir::LogicalType::get(builder.getContext(), 1);
assert(hlfir::getFortranElementType(storageType) != i1Type &&
"array of i1 should not be used");
mlir::Type heapType = fir::HeapType::get(storageType);
mlir::Type boxType;
if (hlfir::isPolymorphicType(valueStaticType))
boxType = fir::ClassType::get(heapType);
else
boxType = fir::BoxType::get(heapType);
retValueBox = builder.createTemporary(loc, boxType);
}
void fir::factory::AnyValueStack::pushValue(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Value value) {
hlfir::Entity entity{value};
mlir::Type storageElementType =
hlfir::getFortranElementType(retValueBox.getType());
auto [box, maybeCleanUp] =
hlfir::convertToBox(loc, builder, entity, storageElementType);
fir::runtime::genPushValue(loc, builder, opaquePtr, fir::getBase(box));
if (maybeCleanUp)
(*maybeCleanUp)();
}
void fir::factory::AnyValueStack::resetFetchPosition(
mlir::Location loc, fir::FirOpBuilder &builder) {
counter.reset(loc, builder);
}
mlir::Value fir::factory::AnyValueStack::fetch(mlir::Location loc,
fir::FirOpBuilder &builder) {
mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder);
fir::runtime::genValueAt(loc, builder, opaquePtr, indexValue, retValueBox);
// Dereference the allocatable "retValueBox", and load if trivial scalar
// value.
mlir::Value result =
hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{retValueBox});
if (valueStaticType != result.getType()) {
// Cast back saved simple scalars stored with another type to their original
// type (like i1).
if (fir::isa_trivial(valueStaticType))
return builder.createConvert(loc, valueStaticType, result);
// Memory type mismatches (e.g. fir.ref vs fir.heap) or hlfir.expr vs
// variable type mismatches are OK, but the base Fortran type must be the
// same.
assert(hlfir::getFortranElementOrSequenceType(valueStaticType) ==
hlfir::getFortranElementOrSequenceType(result.getType()) &&
"non trivial values must be saved with their original type");
}
return result;
}
void fir::factory::AnyValueStack::destroy(mlir::Location loc,
fir::FirOpBuilder &builder) {
fir::runtime::genDestroyValueStack(loc, builder, opaquePtr);
}