Skip to content

Commit

Permalink
[flang] Add support for dense complex constants
Browse files Browse the repository at this point in the history
Add support for representing complex array constants with MLIR
dense attribute. This improves compile time and greatly reduces
memory usage of programs with large complex array constants.

Fixes #63610

Reviewed By: vzakhari

Differential Revision: https://reviews.llvm.org/D155951
  • Loading branch information
luporl committed Aug 30, 2023
1 parent 296d5cb commit c8517f1
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 15 deletions.
39 changes: 28 additions & 11 deletions flang/lib/Lower/ConvertConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,24 @@ static mlir::Attribute convertToAttribute(
} else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
return builder.getIntegerAttr(type, value.IsTrue());
} else {
static_assert(TC == Fortran::common::TypeCategory::Real,
"type values cannot be converted to attributes");
std::string str = value.DumpHexadecimal();
auto floatVal =
consAPFloat(builder.getKindMap().getFloatSemantics(KIND), str);
return builder.getFloatAttr(type, floatVal);
auto getFloatAttr = [&](const auto &value, mlir::Type type) {
std::string str = value.DumpHexadecimal();
auto floatVal =
consAPFloat(builder.getKindMap().getFloatSemantics(KIND), str);
return builder.getFloatAttr(type, floatVal);
};

if constexpr (TC == Fortran::common::TypeCategory::Real) {
return getFloatAttr(value, type);
} else {
static_assert(TC == Fortran::common::TypeCategory::Complex,
"type values cannot be converted to attributes");
mlir::Type eleTy = mlir::cast<mlir::ComplexType>(type).getElementType();
llvm::SmallVector<mlir::Attribute, 2> attrs = {
getFloatAttr(value.REAL(), eleTy),
getFloatAttr(value.AIMAG(), eleTy)};
return builder.getArrayAttr(attrs);
}
}
return {};
}
Expand All @@ -75,12 +87,11 @@ namespace {
/// Helper class to lower an array constant to a global with an MLIR dense
/// attribute.
///
/// If we have an array of integer, real, or logical, then we can
/// If we have an array of integer, real, complex, or logical, then we can
/// create a global array with the dense attribute.
///
/// The mlir tensor type can only handle integer, real, or logical. It
/// does not currently support nested structures which is required for
/// complex.
/// The mlir tensor type can only handle integer, real, complex, or logical.
/// It does not currently support nested structures.
class DenseGlobalBuilder {
public:
static fir::GlobalOp tryCreating(fir::FirOpBuilder &builder,
Expand All @@ -98,6 +109,8 @@ class DenseGlobalBuilder {
[&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeReal> &x) {
globalBuilder.tryConvertingToAttributes(builder, x);
},
[&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeComplex> &
x) { globalBuilder.tryConvertingToAttributes(builder, x); },
[](const auto &) {},
},
initExpr.u);
Expand Down Expand Up @@ -133,6 +146,9 @@ class DenseGlobalBuilder {
: TC;
attributeElementType = Fortran::lower::getFIRType(
builder.getContext(), attrTc, KIND, std::nullopt);
if (auto firCTy = mlir::dyn_cast<fir::ComplexType>(attributeElementType))
attributeElementType =
mlir::ComplexType::get(firCTy.getEleType(builder.getKindMap()));
for (auto element : constant.values())
attributes.push_back(
convertToAttribute<TC, KIND>(builder, element, attributeElementType));
Expand Down Expand Up @@ -544,7 +560,8 @@ genOutlineArrayLit(Fortran::lower::AbstractConverter &converter,
// always possible.
if constexpr (T::category == Fortran::common::TypeCategory::Logical ||
T::category == Fortran::common::TypeCategory::Integer ||
T::category == Fortran::common::TypeCategory::Real) {
T::category == Fortran::common::TypeCategory::Real ||
T::category == Fortran::common::TypeCategory::Complex) {
global = DenseGlobalBuilder::tryCreating(
builder, loc, arrayTy, globalName, builder.createInternalLinkage(),
true, constant);
Expand Down
7 changes: 4 additions & 3 deletions flang/lib/Lower/ConvertVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,12 +431,13 @@ static fir::GlobalOp defineGlobal(Fortran::lower::AbstractConverter &converter,

// If this is an array, check to see if we can use a dense attribute
// with a tensor mlir type. This optimization currently only supports
// Fortran arrays of integer, real, or logical. The tensor type does
// not support nested structures which are needed for complex numbers.
// Fortran arrays of integer, real, complex, or logical. The tensor
// type does not support nested structures.
if (symTy.isa<fir::SequenceType>() &&
!Fortran::semantics::IsAllocatableOrPointer(sym)) {
mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy();
if (eleTy.isa<mlir::IntegerType, mlir::FloatType, fir::LogicalType>()) {
if (eleTy.isa<mlir::IntegerType, mlir::FloatType, fir::ComplexType,
fir::LogicalType>()) {
const auto *details =
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
if (details->init()) {
Expand Down
37 changes: 37 additions & 0 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,42 @@ struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
}
};

// Check if attr's type is compatible with ty.
//
// This is done by comparing attr's element type, converted to LLVM type,
// with ty's element type.
//
// Only integer and floating point (including complex) attributes are
// supported. Also, attr is expected to have a TensorType and ty is expected
// to be of LLVMArrayType. If any of the previous conditions is false, then
// the specified attr and ty are not supported by this function and are
// assumed to be compatible.
static inline bool attributeTypeIsCompatible(mlir::MLIRContext *ctx,
mlir::Attribute attr,
mlir::Type ty) {
// Get attr's LLVM element type.
if (!attr)
return true;
auto intOrFpEleAttr = mlir::dyn_cast<mlir::DenseIntOrFPElementsAttr>(attr);
if (!intOrFpEleAttr)
return true;
auto tensorTy = mlir::dyn_cast<mlir::TensorType>(intOrFpEleAttr.getType());
if (!tensorTy)
return true;
mlir::Type attrEleTy =
mlir::LLVMTypeConverter(ctx).convertType(tensorTy.getElementType());

// Get ty's element type.
auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty);
if (!arrTy)
return true;
mlir::Type eleTy = arrTy.getElementType();
while ((arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)))
eleTy = arrTy.getElementType();

return attrEleTy == eleTy;
}

/// Lower `fir.global` operation to `llvm.global` operation.
/// `fir.insert_on_range` operations are replaced with constant dense attribute
/// if they are applied on the full range.
Expand All @@ -2906,6 +2942,7 @@ struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
auto loc = global.getLoc();
mlir::Attribute initAttr = global.getInitVal().value_or(mlir::Attribute());
assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
auto linkage = convertLinkage(global.getLinkName());
auto isConst = global.getConstant().has_value();
auto g = rewriter.create<mlir::LLVM::GlobalOp>(
Expand Down
12 changes: 11 additions & 1 deletion flang/test/Lower/array.f90
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,20 @@ subroutine range()
real, dimension(2,3) :: a1
integer, dimension(3,4) :: a2
integer, dimension(2,3,4) :: a3

complex, dimension(2,3) :: c0, c1

a0 = (/1, 2, 3, 3, 3, 3, 3, 3, 3, 3/)
a1 = reshape((/3.5, 3.5, 3.5, 3.5, 3.5, 3.5/), shape(a1))
a2 = reshape((/1, 3, 3, 5, 3, 3, 3, 3, 9, 9, 9, 8/), shape(a2))
a3 = reshape((/1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12/), shape(a3))

c0 = reshape((/(1.0, 1.5), (2.0, 2.5), (3.0, 3.5), (4.0, 4.5), (5.0, 5.5), (6.0, 6.5)/), shape(c0))
data c1/6 * (0.0, 0.0)/
end subroutine range

! c1 data
! CHECK: fir.global internal @_QFrangeEc1(dense<(0.000000e+00,0.000000e+00)> : tensor<3x2xcomplex<f32>>) : !fir.array<2x3x!fir.complex<4>>

! a0 array constructor
! CHECK: fir.global internal @_QQro.10xi4.{{.*}}(dense<[1, 2, 3, 3, 3, 3, 3, 3, 3, 3]> : tensor<10xi32>) constant : !fir.array<10xi32>

Expand All @@ -122,6 +129,9 @@ end subroutine range
! a3 array constructor
! CHECK: fir.global internal @_QQro.2x3x4xi4.{{.*}}(dense<{{\[\[\[1, 1], \[2, 2], \[3, 3]], \[\[4, 4], \[5, 5], \[6, 6]], \[\[7, 7], \[8, 8], \[9, 9]], \[\[10, 10], \[11, 11], \[12, 12]]]}}> : tensor<4x3x2xi32>) constant : !fir.array<2x3x4xi32>

! c0 array constructor
! CHECK: fir.global internal @_QQro.2x3xz4.{{.*}}(dense<{{\[}}[(1.000000e+00,1.500000e+00), (2.000000e+00,2.500000e+00)], [(3.000000e+00,3.500000e+00), (4.000000e+00,4.500000e+00)], [(5.000000e+00,5.500000e+00), (6.000000e+00,6.500000e+00)]]> : tensor<3x2xcomplex<f32>>) constant : !fir.array<2x3x!fir.complex<4>>

! CHECK-LABEL rangeGlobal
subroutine rangeGlobal()
! CHECK: fir.global internal @_QFrangeglobal{{.*}}(dense<[1, 1, 2, 2, 3, 3]> : tensor<6xi32>) : !fir.array<6xi32>
Expand Down

0 comments on commit c8517f1

Please sign in to comment.