Skip to content

Commit 74eba97

Browse files
authored
[flang] Definitions of fir.pack/unpack_array operations. (llvm#130698)
As defined in llvm#127147.
1 parent d578148 commit 74eba97

File tree

7 files changed

+356
-5
lines changed

7 files changed

+356
-5
lines changed

flang/include/flang/Optimizer/Dialect/FIRAttr.td

+22
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,26 @@ def fir_LocationKindAttr : EnumAttr<FIROpsDialect, fir_LocationKind, "loc_kind">
156156
def LocationKindArrayAttr : ArrayOfAttr<FIROpsDialect, "LocationKindArray",
157157
"loc_kind_array", "LocationKindAttr">;
158158

159+
/// Optimization heuristics for fir.pack_array operation.
160+
def fir_PackArrayHeuristics
161+
: I32BitEnumAttr<"PackArrayHeuristics", "",
162+
[
163+
/// fir.pack_array cannot be optimized based on the
164+
/// array usage pattern.
165+
I32BitEnumAttrCaseNone<"None", "none">,
166+
/// fir.pack_array can be optimized away, if the array
167+
/// is not used in a loop.
168+
I32BitEnumAttrCaseBit<"LoopOnly", 0, "loop_only">,
169+
]> {
170+
let separator = ", ";
171+
let cppNamespace = "::fir";
172+
let genSpecializedAttr = 0;
173+
}
174+
175+
def fir_PackArrayHeuristicsAttr
176+
: EnumAttr<FIROpsDialect, fir_PackArrayHeuristics,
177+
"pack_array_heuristics"> {
178+
let assemblyFormat = "`<` $value `>`";
179+
}
180+
159181
#endif // FIR_DIALECT_FIR_ATTRS

flang/include/flang/Optimizer/Dialect/FIROps.td

+96
Original file line numberDiff line numberDiff line change
@@ -3320,4 +3320,100 @@ def fir_DummyScopeOp : fir_Op<"dummy_scope",
33203320
let assemblyFormat = "attr-dict `:` type(results)";
33213321
}
33223322

3323+
def fir_PackArrayOp
3324+
: fir_Op<"pack_array", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
3325+
AllTypesMatch<["array", "result"]>]> {
3326+
let summary = "Pack non-contiguous array into a temporary";
3327+
3328+
let description = [{
3329+
The operation creates a new !fir.box/class<!fir.array<>> value
3330+
to represent either the original array or a newly allocated
3331+
temporary array, maybe identical to the original array by value.
3332+
3333+
Arguments:
3334+
- array is the original array.
3335+
It must have !fir.box/class<!fir.array<>> type.
3336+
- stack/heap attribute indicates where the temporary array
3337+
needs to be allocated.
3338+
- innermost/whole attribute identifies the contiguity mode.
3339+
innermost means that the repacking has to be done iff the original
3340+
array is not contiguous in the leading dimension.
3341+
whole means that the repacking has to be done iff the original
3342+
array is not contiguous in any dimension.
3343+
innermost is disallowed for 1D arrays in favor of whole.
3344+
- no_copy attribute indicates that the original array
3345+
is not copied into the temporary.
3346+
- typeparams specify the length parameters of the original array.
3347+
Even though the array is fully represented with a box, the explicit
3348+
length parameters might be specified to simplify computing
3349+
the size of the array's element in compilation time (e.g. constant
3350+
length parameters might be propagated after MLIR inlining).
3351+
- optional constraints attributes:
3352+
* max_size is an unsigned integer attribute specifying the maximum
3353+
byte size of an array that is eligible for repacking.
3354+
* max_element_size is an unsigned integer attribute specifying
3355+
the maximum byte element-size of an array that is eligible
3356+
for repacking.
3357+
* min_stride is an unsigned integer attribute specifying
3358+
the minimum byte stride of the innermost dimension of an array
3359+
that is eligible for repacking.
3360+
- heuristics attribute specifies conditions when the array repacking
3361+
may be optimized.
3362+
}];
3363+
3364+
let arguments = (ins AnyBoxedArray:$array, UnitAttr:$stack,
3365+
UnitAttr:$innermost, UnitAttr:$no_copy, OptionalAttr<UI64Attr>:$max_size,
3366+
OptionalAttr<UI64Attr>:$max_element_size,
3367+
OptionalAttr<UI64Attr>:$min_stride,
3368+
DefaultValuedAttr<fir_PackArrayHeuristicsAttr,
3369+
"::fir::PackArrayHeuristics::None">:$heuristics,
3370+
Variadic<AnyIntegerType>:$typeparams);
3371+
3372+
let results = (outs AnyBoxedArray:$result);
3373+
let assemblyFormat = [{
3374+
$array (`stack` $stack^):(`heap`)?
3375+
(`innermost` $innermost^):(`whole`)?
3376+
(`no_copy` $no_copy^)?
3377+
(`constraints` custom<PackArrayConstraints>($max_size, $max_element_size, $min_stride)^)?
3378+
(`heuristics` $heuristics^)?
3379+
(`typeparams` $typeparams^)?
3380+
attr-dict `:` functional-type(operands, results)
3381+
}];
3382+
3383+
let hasVerifier = 1;
3384+
}
3385+
3386+
def fir_UnpackArrayOp
3387+
: fir_Op<"unpack_array", [SameTypeOperands,
3388+
DeclareOpInterfaceMethods<
3389+
MemoryEffectsOpInterface>]> {
3390+
let summary = "Unpack values from temporary array into original array";
3391+
3392+
let description = [{
3393+
The operation is either a no-op or deallocates the temporary array,
3394+
and maybe copies the temporary array into the original array.
3395+
3396+
Arguments:
3397+
- temp is a fir.box/fir.class value produced by fir.pack_array.
3398+
It describes either the original array or the temporary array.
3399+
- original is the original array descriptor.
3400+
- stack/heap attribute indicates where the temporary array
3401+
was allocated.
3402+
- no_copy attribute indicates that the temporary array
3403+
is not copied into the original temporary array.
3404+
}];
3405+
3406+
let arguments = (ins AnyBoxedArray:$temp, AnyBoxedArray:$original,
3407+
UnitAttr:$stack, UnitAttr:$no_copy);
3408+
3409+
let assemblyFormat = [{
3410+
$temp `to` $original
3411+
(`stack` $stack^):(`heap`)?
3412+
(`no_copy` $no_copy^)?
3413+
attr-dict `:` type($original)
3414+
}];
3415+
3416+
let hasVerifier = 1;
3417+
}
3418+
33233419
#endif

flang/include/flang/Optimizer/Dialect/FIRTypes.td

+7
Original file line numberDiff line numberDiff line change
@@ -658,5 +658,12 @@ def ArrayOrBoxOrRecord : TypeConstraint<Or<[fir_SequenceType.predicate,
658658
IsBaseBoxTypePred, fir_RecordType.predicate]>,
659659
"fir.box, fir.array or fir.type">;
660660

661+
// Returns true iff the type is an array box or a reference to such type.
662+
def IsArrayBoxPred : CPred<"::fir::getBoxRank($_self) != 0">;
663+
664+
// Any boxed array type (not a reference to a boxed array type).
665+
def AnyBoxedArray
666+
: TypeConstraint<And<[BoxOrClassType.predicate, IsArrayBoxPred]>,
667+
"any boxed array">;
661668

662669
#endif // FIR_DIALECT_FIR_TYPES

flang/lib/Optimizer/Dialect/FIRAttr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,5 +300,5 @@ void FIROpsDialect::registerAttributes() {
300300
FortranProcedureFlagsEnumAttr, FortranVariableFlagsAttr,
301301
LowerBoundAttr, PointIntervalAttr, RealAttr, ReduceAttr,
302302
SubclassAttr, UpperBoundAttr, LocationKindAttr,
303-
LocationKindArrayAttr>();
303+
LocationKindArrayAttr, PackArrayHeuristicsAttr>();
304304
}

flang/lib/Optimizer/Dialect/FIROps.cpp

+114-4
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,16 @@ llvm::LogicalResult fir::AllocMemOp::verify() {
380380

381381
// CHARACTERs and derived types with LEN PARAMETERs are dependent types that
382382
// require runtime values to fully define the type of an object.
383-
static bool validTypeParams(mlir::Type dynTy, mlir::ValueRange typeParams) {
383+
static bool validTypeParams(mlir::Type dynTy, mlir::ValueRange typeParams,
384+
bool allowParamsForBox = false) {
384385
dynTy = fir::unwrapAllRefAndSeqType(dynTy);
385-
// A box value will contain type parameter values itself.
386-
if (mlir::isa<fir::BoxType>(dynTy))
387-
return typeParams.size() == 0;
386+
if (mlir::isa<fir::BaseBoxType>(dynTy)) {
387+
// A box value will contain type parameter values itself.
388+
if (!allowParamsForBox)
389+
return typeParams.size() == 0;
390+
391+
dynTy = fir::getFortranElementType(dynTy);
392+
}
388393
// Derived type must have all type parameters satisfied.
389394
if (auto recTy = mlir::dyn_cast<fir::RecordType>(dynTy))
390395
return typeParams.size() == recTy.getNumLenParams();
@@ -4561,6 +4566,111 @@ llvm::LogicalResult fir::DeclareOp::verify() {
45614566
return fortranVar.verifyDeclareLikeOpImpl(getMemref());
45624567
}
45634568

4569+
//===----------------------------------------------------------------------===//
4570+
// PackArrayOp
4571+
//===----------------------------------------------------------------------===//
4572+
4573+
llvm::LogicalResult fir::PackArrayOp::verify() {
4574+
mlir::Type arrayType = getArray().getType();
4575+
if (!validTypeParams(arrayType, getTypeparams(), /*allowParamsForBox=*/true))
4576+
return emitOpError("invalid type parameters");
4577+
4578+
if (getInnermost() && fir::getBoxRank(arrayType) == 1)
4579+
return emitOpError(
4580+
"'innermost' is invalid for 1D arrays, use 'whole' instead");
4581+
return mlir::success();
4582+
}
4583+
4584+
void fir::PackArrayOp::getEffects(
4585+
llvm::SmallVectorImpl<
4586+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
4587+
&effects) {
4588+
if (getStack())
4589+
effects.emplace_back(
4590+
mlir::MemoryEffects::Allocate::get(),
4591+
mlir::SideEffects::AutomaticAllocationScopeResource::get());
4592+
else
4593+
effects.emplace_back(mlir::MemoryEffects::Allocate::get(),
4594+
mlir::SideEffects::DefaultResource::get());
4595+
4596+
if (!getNoCopy())
4597+
effects.emplace_back(mlir::MemoryEffects::Read::get(),
4598+
mlir::SideEffects::DefaultResource::get());
4599+
}
4600+
4601+
static mlir::ParseResult
4602+
parsePackArrayConstraints(mlir::OpAsmParser &parser, mlir::IntegerAttr &maxSize,
4603+
mlir::IntegerAttr &maxElementSize,
4604+
mlir::IntegerAttr &minStride) {
4605+
mlir::OperationName opName = mlir::OperationName(
4606+
fir::PackArrayOp::getOperationName(), parser.getContext());
4607+
struct {
4608+
llvm::StringRef name;
4609+
mlir::IntegerAttr &ref;
4610+
} attributes[] = {
4611+
{fir::PackArrayOp::getMaxSizeAttrName(opName), maxSize},
4612+
{fir::PackArrayOp::getMaxElementSizeAttrName(opName), maxElementSize},
4613+
{fir::PackArrayOp::getMinStrideAttrName(opName), minStride}};
4614+
4615+
mlir::NamedAttrList parsedAttrs;
4616+
if (succeeded(parser.parseOptionalAttrDict(parsedAttrs))) {
4617+
for (auto parsedAttr : parsedAttrs) {
4618+
for (auto opAttr : attributes) {
4619+
if (parsedAttr.getName() == opAttr.name)
4620+
opAttr.ref = mlir::cast<mlir::IntegerAttr>(parsedAttr.getValue());
4621+
}
4622+
}
4623+
return mlir::success();
4624+
}
4625+
return mlir::failure();
4626+
}
4627+
4628+
static void printPackArrayConstraints(mlir::OpAsmPrinter &p,
4629+
fir::PackArrayOp &op,
4630+
const mlir::IntegerAttr &maxSize,
4631+
const mlir::IntegerAttr &maxElementSize,
4632+
const mlir::IntegerAttr &minStride) {
4633+
llvm::SmallVector<mlir::NamedAttribute> attributes;
4634+
if (maxSize)
4635+
attributes.emplace_back(op.getMaxSizeAttrName(), maxSize);
4636+
if (maxElementSize)
4637+
attributes.emplace_back(op.getMaxElementSizeAttrName(), maxElementSize);
4638+
if (minStride)
4639+
attributes.emplace_back(op.getMinStrideAttrName(), minStride);
4640+
4641+
p.printOptionalAttrDict(attributes);
4642+
}
4643+
4644+
//===----------------------------------------------------------------------===//
4645+
// UnpackArrayOp
4646+
//===----------------------------------------------------------------------===//
4647+
4648+
llvm::LogicalResult fir::UnpackArrayOp::verify() {
4649+
if (auto packOp = getTemp().getDefiningOp<fir::PackArrayOp>())
4650+
if (getStack() != packOp.getStack())
4651+
return emitOpError() << "the pack operation uses different memory for "
4652+
"the temporary (stack vs heap): "
4653+
<< *packOp.getOperation() << "\n";
4654+
return mlir::success();
4655+
}
4656+
4657+
void fir::UnpackArrayOp::getEffects(
4658+
llvm::SmallVectorImpl<
4659+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
4660+
&effects) {
4661+
if (getStack())
4662+
effects.emplace_back(
4663+
mlir::MemoryEffects::Free::get(),
4664+
mlir::SideEffects::AutomaticAllocationScopeResource::get());
4665+
else
4666+
effects.emplace_back(mlir::MemoryEffects::Free::get(),
4667+
mlir::SideEffects::DefaultResource::get());
4668+
4669+
if (!getNoCopy())
4670+
effects.emplace_back(mlir::MemoryEffects::Write::get(),
4671+
mlir::SideEffects::DefaultResource::get());
4672+
}
4673+
45644674
//===----------------------------------------------------------------------===//
45654675
// FIROpsDialect
45664676
//===----------------------------------------------------------------------===//

flang/test/Fir/fir-ops.fir

+25
Original file line numberDiff line numberDiff line change
@@ -942,3 +942,28 @@ func.func @test_copy(%arg0: !fir.ref<!fir.type<sometype{i:i32}>>, %arg1: !fir.pt
942942
fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!fir.type<sometype{i:i32}>>, !fir.ptr<!fir.type<sometype{i:i32}>>
943943
return
944944
}
945+
946+
// CHECK-LABEL: func.func @test_pack_unpack_array(
947+
// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<!fir.box<none>>,
948+
// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xi32>>) {
949+
func.func @test_pack_unpack_array(%arg0: !fir.ref<!fir.box<none>>, %arg1: !fir.box<!fir.array<?xi32>>) {
950+
// CHECK: %[[VAL_2:.*]] = fir.pack_array %[[VAL_1]] heap whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
951+
%0 = fir.pack_array %arg1 heap whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
952+
%1 = fir.convert %0 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
953+
fir.store %1 to %arg0 : !fir.ref<!fir.box<none>>
954+
// CHECK: %[[VAL_4:.*]] = fir.pack_array %[[VAL_1]] stack whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
955+
%2 = fir.pack_array %arg1 stack whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
956+
%3 = fir.convert %2 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
957+
fir.store %3 to %arg0 : !fir.ref<!fir.box<none>>
958+
// CHECK: %[[VAL_6:.*]] = fir.pack_array %[[VAL_1]] heap whole no_copy : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
959+
%4 = fir.pack_array %arg1 heap whole no_copy constraints {} heuristics <none> : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
960+
%5 = fir.convert %4 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
961+
fir.store %5 to %arg0 : !fir.ref<!fir.box<none>>
962+
// CHECK: %[[VAL_8:.*]] = fir.pack_array %[[VAL_1]] stack whole constraints {max_size = 100 : ui64, max_element_size = 1 : ui64, min_stride = 10 : ui64} heuristics <loop_only> : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
963+
%6 = fir.pack_array %arg1 stack whole constraints {max_size = 100 : ui64, max_element_size = 1 : ui64, min_stride = 10 : ui64} heuristics <loop_only> : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
964+
%7 = fir.convert %6 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
965+
fir.store %7 to %arg0 : !fir.ref<!fir.box<none>>
966+
// CHECK: fir.unpack_array %[[VAL_8]] to %[[VAL_1]] stack no_copy : !fir.box<!fir.array<?xi32>>
967+
fir.unpack_array %6 to %arg1 stack no_copy : !fir.box<!fir.array<?xi32>>
968+
return
969+
}

0 commit comments

Comments
 (0)