[fir] Add fir.array_access op
The `array_access` provides a reference to a single element from an array value. This is *not* a view in the immutable array, otherwise it couldn't be stored to. It can be see as a logical copy of the element and its position in the array. This reference can be written to and modified without changing the original array. The `array_access` operation is used to fetch the memory reference of an element in an array value. ```fortran real :: a(n,m) ... ... a ... ... a(r,s+1) ... ``` One can use `fir.array_access` to recover the implied memory reference to the element `a(i,j)` in an array expression `a` as shown above. It can also be used to recover the reference element `a(r,s+1)` in the second expression. ```mlir %s = fir.shape %n, %m : (index, index) -> !fir.shape<2> // load the entire array 'a' %v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32> // fetch the value of one of the array value's elements %1 = fir.array_access %v, %i, %j : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32> ``` More information about `array_access` and other array operations can be found in flang/docs/FIRArrayOperations.md. This patch is part of the upstreaming effort from fir-dev branch. Reviewed By: kiranchandramohan, schweitz Differential Revision: https://reviews.llvm.org/D112445 Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
This commit is contained in:
parent
ca53e049e0
commit
f254a8eff6
|
@ -1533,6 +1533,68 @@ def fir_ArrayModifyOp : fir_Op<"array_modify", [AttrSizedOperandSegments,
|
|||
let verifier = "return ::verify(*this);";
|
||||
}
|
||||
|
||||
def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
|
||||
NoSideEffect]> {
|
||||
let summary = "Fetch the reference of an element of an array value";
|
||||
|
||||
let description = [{
|
||||
The `array_access` provides a reference to a single element from an array
|
||||
value. This is *not* a view in the immutable array, otherwise it couldn't
|
||||
be stored to. It can be see as a logical copy of the element and its
|
||||
position in the array. This reference can be written to and modified without
|
||||
changing the original array.
|
||||
|
||||
The `array_access` operation is used to fetch the memory reference of an
|
||||
element in an array value.
|
||||
|
||||
```fortran
|
||||
real :: a(n,m)
|
||||
...
|
||||
... a ...
|
||||
... a(r,s+1) ...
|
||||
```
|
||||
|
||||
One can use `fir.array_access` to recover the implied memory reference to
|
||||
the element `a(i,j)` in an array expression `a` as shown above. It can also
|
||||
be used to recover the reference element `a(r,s+1)` in the second
|
||||
expression.
|
||||
|
||||
```mlir
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
// fetch the value of one of the array value's elements
|
||||
%1 = fir.array_access %v, %i, %j : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32>
|
||||
```
|
||||
|
||||
It is only possible to use `array_access` on an `array_load` result value or
|
||||
a value that can be trace back transitively to an `array_load` as the
|
||||
dominating source. Other array operation such as `array_amend` can be in
|
||||
between.
|
||||
|
||||
TODO: The above restriction is not enforced. The design of the operation
|
||||
might need to be revisited to avoid such restructions.
|
||||
|
||||
More information about `array_access` and other array operations can be
|
||||
found in flang/docs/FIRArrayOperations.md.
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
fir_SequenceType:$sequence,
|
||||
Variadic<AnyCoordinateType>:$indices,
|
||||
Variadic<AnyIntegerType>:$typeparams
|
||||
);
|
||||
|
||||
let results = (outs fir_ReferenceType:$element);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$sequence `,` $indices (`typeparams` $typeparams^)? attr-dict `:`
|
||||
functional-type(operands, results)
|
||||
}];
|
||||
|
||||
let verifier = "return ::verify(*this);";
|
||||
}
|
||||
|
||||
def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
|
||||
[AttrSizedOperandSegments]> {
|
||||
|
||||
|
|
|
@ -496,6 +496,24 @@ static mlir::LogicalResult verify(fir::ArrayFetchOp op) {
|
|||
return mlir::success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArrayAccessOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static mlir::LogicalResult verify(fir::ArrayAccessOp op) {
|
||||
auto arrTy = op.sequence().getType().cast<fir::SequenceType>();
|
||||
std::size_t indSize = op.indices().size();
|
||||
if (indSize < arrTy.getDimension())
|
||||
return op.emitOpError("number of indices != dimension of array");
|
||||
if (indSize == arrTy.getDimension() &&
|
||||
op.element().getType() != fir::ReferenceType::get(arrTy.getEleTy()))
|
||||
return op.emitOpError("return type does not match array");
|
||||
mlir::Type ty = validArraySubobject(op);
|
||||
if (!ty || fir::ReferenceType::get(ty) != op.getType())
|
||||
return op.emitOpError("return type and/or indices do not type check");
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ArrayUpdateOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -747,3 +747,14 @@ func @llvm_ptr_load_store_coordinate(%arg0: !fir.ref<tuple<!fir.ref<!fir.box<!fi
|
|||
%1 = fir.load %0 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.ptr<f32>>>>
|
||||
return %1 : !fir.ref<!fir.box<!fir.ptr<f32>>>
|
||||
}
|
||||
|
||||
func @array_access_ops(%a : !fir.ref<!fir.array<?x?xf32>>) {
|
||||
%c1 = arith.constant 1 : index
|
||||
%n = arith.constant 0 : index
|
||||
%m = arith.constant 50 : index
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
%p = fir.array_access %v, %c1, %c1 : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32>
|
||||
// CHECK: %{{.*}} = fir.array_access %{{.*}}, %{{.*}}, %{{.*}} : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32>
|
||||
return
|
||||
}
|
||||
|
|
|
@ -694,3 +694,55 @@ func @array_merge_store_no_slice_substr(%arr1 : !fir.ref<!fir.array<?x?xf32>>, %
|
|||
fir.array_merge_store %av1, %av2 to %arr1[%slice] : !fir.array<?x?xf32>, !fir.array<?x?xf32>, !fir.ref<!fir.array<?x?xf32>>, !fir.slice<1>
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
func @array_access(%a : !fir.ref<!fir.array<?x?xf32>>) {
|
||||
%c1 = arith.constant 1 : index
|
||||
%n = arith.constant 0 : index
|
||||
%m = arith.constant 50 : index
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
// expected-error@+1 {{'fir.array_access' op number of indices != dimension of array}}
|
||||
%p = fir.array_access %v, %c1 : (!fir.array<?x?xf32>, index) -> !fir.ref<f32>
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
func @array_access(%a : !fir.ref<!fir.array<?x?xf32>>) {
|
||||
%c1 = arith.constant 1 : index
|
||||
%n = arith.constant 0 : index
|
||||
%m = arith.constant 50 : index
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
// expected-error@+1 {{'fir.array_access' op return type does not match array}}
|
||||
%p = fir.array_access %v, %c1, %c1 : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f64>
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
func @foo(%arg0: !fir.ref<!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>>) {
|
||||
%c1 = arith.constant 1 : index
|
||||
%c0 = arith.constant 0 : index
|
||||
%c9 = arith.constant 9 : index
|
||||
%c19 = arith.constant 19 : index
|
||||
%c30 = arith.constant 30 : index
|
||||
%0 = fir.shape %c30 : (index) -> !fir.shape<1>
|
||||
%1 = fir.array_load %arg0(%0) : (!fir.ref<!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>>, !fir.shape<1>) -> !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>
|
||||
%2 = fir.do_loop %arg1 = %c1 to %c9 step %c1 unordered iter_args(%arg2 = %1) -> (!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>) {
|
||||
%3 = fir.field_index c, !fir.type<t{c:!fir.array<20xi32>}>
|
||||
%4 = fir.do_loop %arg3 = %c0 to %c19 step %c1 unordered iter_args(%arg4 = %arg2) -> (!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>) {
|
||||
// expected-error@+1 {{'fir.array_access' op return type and/or indices do not type check}}
|
||||
%5 = fir.array_access %1, %arg1, %3, %arg3 : (!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>, index, !fir.field, index) -> !fir.ref<f32>
|
||||
%6 = fir.call @ifoo(%5) : (!fir.ref<f32>) -> i32
|
||||
%7 = fir.array_update %arg4, %6, %arg1, %3, %arg3 : (!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>, i32, index, !fir.field, index) -> !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>
|
||||
fir.result %7 : !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>
|
||||
}
|
||||
fir.result %4 : !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>
|
||||
}
|
||||
fir.array_merge_store %1, %2 to %arg0 : !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>, !fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>, !fir.ref<!fir.array<30x!fir.type<t{c:!fir.array<20xi32>}>>>
|
||||
return
|
||||
}
|
||||
func private @ifoo(!fir.ref<f32>) -> i32
|
||||
|
|
Loading…
Reference in New Issue