Skip to content

Commit 7792a63

Browse files
committed
[flang] Definitions of fir.pack/unpack_array operations.
As defined in llvm#127147.
1 parent 9d7ca6c commit 7792a63

File tree

7 files changed

+356
-5
lines changed

7 files changed

+356
-5
lines changed

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

Lines changed: 22 additions & 0 deletions
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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3276,4 +3276,100 @@ def fir_DummyScopeOp : fir_Op<"dummy_scope",
32763276
let assemblyFormat = "attr-dict `:` type(results)";
32773277
}
32783278

3279+
def fir_PackArrayOp
3280+
: fir_Op<"pack_array", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
3281+
AllTypesMatch<["array", "result"]>]> {
3282+
let summary = "Pack non-contiguous array into a temporary";
3283+
3284+
let description = [{
3285+
The operation creates a new !fir.box/class<!fir.array<>> value
3286+
to represent either the original array or a newly allocated
3287+
temporary array, maybe identical to the original array by value.
3288+
3289+
Arguments:
3290+
- array is the original array.
3291+
It must have !fir.box/class<!fir.array<>> type.
3292+
- stack/heap attribute indicates where the temporary array
3293+
needs to be allocated.
3294+
- innermost/whole attribute identifies the contiguity mode.
3295+
innermost means that the repacking has to be done iff the original
3296+
array is not contiguous in the leading dimension.
3297+
whole means that the repacking has to be done iff the original
3298+
array is not contiguous in any dimension.
3299+
innermost is disallowed for 1D arrays in favor of whole.
3300+
- no_copy attribute indicates that the original array
3301+
is not copied into the temporary.
3302+
- typeparams specify the length parameters of the original array.
3303+
Even though the array is fully represented with a box, the explicit
3304+
length parameters might be specified to simplify computing
3305+
the size of the array's element in compilation time (e.g. constant
3306+
length parameters might be propagated after MLIR inlining).
3307+
- optional constraints attributes:
3308+
* max_size is an unsigned integer attribute specifying the maximum
3309+
byte size of an array that is eligible for repacking.
3310+
* max_element_size is an unsigned integer attribute specifying
3311+
the maximum byte element-size of an array that is eligible
3312+
for repacking.
3313+
* min_stride is an unsigned integer attribute specifying
3314+
the minimum byte stride of the innermost dimension of an array
3315+
that is eligible for repacking.
3316+
- heuristics attribute specifies conditions when the array repacking
3317+
may be optimized.
3318+
}];
3319+
3320+
let arguments = (ins AnyBoxedArray:$array, UnitAttr:$stack,
3321+
UnitAttr:$innermost, UnitAttr:$no_copy, OptionalAttr<UI64Attr>:$max_size,
3322+
OptionalAttr<UI64Attr>:$max_element_size,
3323+
OptionalAttr<UI64Attr>:$min_stride,
3324+
DefaultValuedAttr<fir_PackArrayHeuristicsAttr,
3325+
"::fir::PackArrayHeuristics::None">:$heuristics,
3326+
Variadic<AnyIntegerType>:$typeparams);
3327+
3328+
let results = (outs AnyBoxedArray:$result);
3329+
let assemblyFormat = [{
3330+
$array (`stack` $stack^):(`heap`)?
3331+
(`innermost` $innermost^):(`whole`)?
3332+
(`no_copy` $no_copy^)?
3333+
(`constraints` custom<PackArrayConstraints>($max_size, $max_element_size, $min_stride)^)?
3334+
(`heuristics` $heuristics^)?
3335+
(`typeparams` $typeparams^)?
3336+
attr-dict `:` functional-type(operands, results)
3337+
}];
3338+
3339+
let hasVerifier = 1;
3340+
}
3341+
3342+
def fir_UnpackArrayOp
3343+
: fir_Op<"unpack_array", [SameTypeOperands,
3344+
DeclareOpInterfaceMethods<
3345+
MemoryEffectsOpInterface>]> {
3346+
let summary = "Unpack values from temporary array into original array";
3347+
3348+
let description = [{
3349+
The operation is either a no-op or deallocates the temporary array,
3350+
and maybe copies the temporary array into the original array.
3351+
3352+
Arguments:
3353+
- temp is a fir.box/fir.class value produced by fir.pack_array.
3354+
It describes either the original array or the temporary array.
3355+
- original is the original array descriptor.
3356+
- stack/heap attribute indicates where the temporary array
3357+
was allocated.
3358+
- no_copy attribute indicates that the temporary array
3359+
is not copied into the original temporary array.
3360+
}];
3361+
3362+
let arguments = (ins AnyBoxedArray:$temp, AnyBoxedArray:$original,
3363+
UnitAttr:$stack, UnitAttr:$no_copy);
3364+
3365+
let assemblyFormat = [{
3366+
$temp `to` $original
3367+
(`stack` $stack^):(`heap`)?
3368+
(`no_copy` $no_copy^)?
3369+
attr-dict `:` type($original)
3370+
}];
3371+
3372+
let hasVerifier = 1;
3373+
}
3374+
32793375
#endif

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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 114 additions & 4 deletions
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();
@@ -4541,6 +4546,111 @@ llvm::LogicalResult fir::DeclareOp::verify() {
45414546
return fortranVar.verifyDeclareLikeOpImpl(getMemref());
45424547
}
45434548

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

flang/test/Fir/fir-ops.fir

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,3 +933,28 @@ func.func @test_call_arg_attrs_indirect(%arg0: i16, %arg1: (i16)-> i16) -> i16 {
933933
%0 = fir.call %arg1(%arg0) : (i16 {llvm.noundef, llvm.signext}) -> (i16 {llvm.signext})
934934
return %0 : i16
935935
}
936+
937+
// CHECK-LABEL: func.func @test_pack_unpack_array(
938+
// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<!fir.box<none>>,
939+
// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?xi32>>) {
940+
func.func @test_pack_unpack_array(%arg0: !fir.ref<!fir.box<none>>, %arg1: !fir.box<!fir.array<?xi32>>) {
941+
// CHECK: %[[VAL_2:.*]] = fir.pack_array %[[VAL_1]] heap whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
942+
%0 = fir.pack_array %arg1 heap whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
943+
%1 = fir.convert %0 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
944+
fir.store %1 to %arg0 : !fir.ref<!fir.box<none>>
945+
// CHECK: %[[VAL_4:.*]] = fir.pack_array %[[VAL_1]] stack whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
946+
%2 = fir.pack_array %arg1 stack whole : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
947+
%3 = fir.convert %2 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
948+
fir.store %3 to %arg0 : !fir.ref<!fir.box<none>>
949+
// CHECK: %[[VAL_6:.*]] = fir.pack_array %[[VAL_1]] heap whole no_copy : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
950+
%4 = fir.pack_array %arg1 heap whole no_copy constraints {} heuristics <none> : (!fir.box<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
951+
%5 = fir.convert %4 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
952+
fir.store %5 to %arg0 : !fir.ref<!fir.box<none>>
953+
// 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>>
954+
%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>>
955+
%7 = fir.convert %6 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
956+
fir.store %7 to %arg0 : !fir.ref<!fir.box<none>>
957+
// CHECK: fir.unpack_array %[[VAL_8]] to %[[VAL_1]] stack no_copy : !fir.box<!fir.array<?xi32>>
958+
fir.unpack_array %6 to %arg1 stack no_copy : !fir.box<!fir.array<?xi32>>
959+
return
960+
}

0 commit comments

Comments
 (0)