[flang][fir] Add remaining Ops. Updates to pre-existing Ops.

- add ops: rebox, insert_on_range, absent, is_present
  - embox, coordinate_of: replace old hand-written parser/pretty-printer with assembly format
  - remove dead floating point ops, since buitlins work for all types
  - update call op
  - update documentation
  - misc. NFC to formatting
  - add op round trip tests

Authors: Eric Schweitz, Jean Perier, Zachary Selk, Kiran Chandramohan, et.al.

Differential Revision: https://reviews.llvm.org/D97500
This commit is contained in:
Eric Schweitz 2021-02-25 14:01:45 -08:00
parent aa097ef8d4
commit 0b785a46b7
6 changed files with 904 additions and 423 deletions

View file

@ -170,6 +170,7 @@ class fir_AllocatableOp<string mnemonic, Resource resource,
mlir::Type getAllocatedType();
bool hasLenParams() { return bool{(*this)->getAttr(lenpName())}; }
bool hasShapeOperands() { return numShapeOperands() > 0; }
unsigned numLenParams() {
if (auto val = (*this)->getAttrOfType<mlir::IntegerAttr>(lenpName()))
@ -985,9 +986,11 @@ def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> {
let assemblyFormat = "$resval attr-dict `:` type($resval)";
}
//===------------------------------------------------------------------------===//
// Operations on !fir.box<T> type objects
//===------------------------------------------------------------------------===//
def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]> {
def fir_EmboxOp : fir_Op<"embox", [NoSideEffect, AttrSizedOperandSegments]> {
let summary = "boxes a given reference and (optional) dimension information";
let description = [{
@ -999,83 +1002,105 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]> {
```mlir
%c1 = constant 1 : index
%c10 = constant 10 : index
%4 = fir.dims(%c1, %c10, %c1) : (index, index, index) -> !fir.dims<1>
%5 = ... : !fir.ref<!fir.array<10 x i32>>
%6 = fir.embox %5, %4 : (!fir.ref<!fir.array<10 x i32>>, !fir.dims<1>) -> !fir.box<!fir.array<10 x i32>>
%6 = fir.embox %5 : (!fir.ref<!fir.array<10 x i32>>) -> !fir.box<!fir.array<10 x i32>>
```
The descriptor tuple may contain additional implementation-specific
information through the use of additional attributes.
Specifically,
- shape: emboxing an array may require shape information (an array's
lower bounds and extents may not be known until runtime),
- slice: an array section can be described with a slice triple,
- lenParams: for emboxing a derived type with LEN type parameters,
- accessMap: unused/experimental.
}];
let arguments = (ins AnyReferenceLike:$memref, Variadic<AnyEmboxArg>:$args);
let arguments = (ins
AnyReferenceLike:$memref,
Optional<AnyShapeType>:$shape,
Optional<fir_SliceType>:$slice,
Variadic<AnyIntegerType>:$lenParams,
OptionalAttr<AffineMapAttr>:$accessMap
);
let results = (outs fir_BoxType);
let parser = "return parseEmboxOp(parser, result);";
let builders = [
OpBuilderDAG<(ins "llvm::ArrayRef<mlir::Type>":$resultTypes,
"mlir::Value":$memref, CArg<"mlir::Value", "{}">:$shape,
CArg<"mlir::Value", "{}">:$slice,
CArg<"mlir::ValueRange", "{}">:$lenParams),
[{ return build($_builder, $_state, resultTypes, memref, shape, slice,
lenParams, mlir::AffineMapAttr{}); }]>
];
let printer = [{
p << getOperationName() << ' ';
p.printOperand(memref());
if (hasLenParams()) {
p << '(';
p.printOperands(getLenParams());
p << ')';
}
if (getNumOperands() == 2) {
p << ", ";
p.printOperands(dims());
} else if (auto map = (*this)->getAttr(layoutName())) {
p << " [" << map << ']';
}
p.printOptionalAttrDict((*this)->getAttrs(), {layoutName(), lenpName()});
p << " : ";
p.printFunctionalType(getOperation());
let assemblyFormat = [{
$memref (`(` $shape^ `)`)? (`[` $slice^ `]`)? (`typeparams` $lenParams^)?
(`map` $accessMap^)? attr-dict `:` functional-type(operands, results)
}];
let verifier = [{
if (hasLenParams()) {
auto lenParams = numLenParams();
auto eleTy = fir::dyn_cast_ptrEleTy(memref().getType());
if (!eleTy)
return emitOpError("must embox a memory reference type");
if (auto rt = eleTy.dyn_cast<fir::RecordType>()) {
if (lenParams != rt.getNumLenParams())
return emitOpError("number of LEN params does not correspond"
" to the !fir.type type");
} else {
return emitOpError("LEN parameters require !fir.type type");
}
}
if (dims().size() == 0) {
// Ok. If there is no dims and no layout map, then emboxing a scalar.
// TODO: Should the type be enforced? It already must agree.
} else if (dims().size() == 1) {
//auto d = *dims().begin();
} else {
return emitOpError("embox can only have one !fir.dim argument");
}
return mlir::success();
}];
let verifier = [{ return ::verify(*this); }];
let extraClassDeclaration = [{
static constexpr llvm::StringRef layoutName() { return "layout_map"; }
static constexpr llvm::StringRef lenpName() { return "len_param_count"; }
bool hasLenParams() { return bool{(*this)->getAttr(lenpName())}; }
unsigned numLenParams() {
if (auto x = (*this)->getAttrOfType<mlir::IntegerAttr>(lenpName()))
return x.getInt();
return 0;
}
operand_range getLenParams() {
return {operand_begin(), operand_begin() + numLenParams()};
}
operand_range dims() {
return {operand_begin() + numLenParams() + 1, operand_end()};
}
mlir::Value getShape() { return shape(); }
mlir::Value getSlice() { return slice(); }
bool hasLenParams() { return !lenParams().empty(); }
unsigned numLenParams() { return lenParams().size(); }
}];
}
def fir_ReboxOp : fir_Op<"rebox", [NoSideEffect, AttrSizedOperandSegments]> {
let summary = "create a box given another box and (optional) dimension information";
let description = [{
Create a new boxed reference value from another box. This is meant to be used
when the taking a reference to part of a boxed value, or to an entire boxed value with
new shape or type information.
The new extra information can be:
- new shape information (new lower bounds, new rank, or new extents.
New rank/extents can only be provided if the original fir.box is
contiguous in all dimension but maybe the first one). The shape
operand must be provided to set new shape information.
- new type (only for derived types). It is possible to set the dynamic type
of the new box to one of the parent types of the input box dynamic type.
Type parameters cannot be changed. This change is reflected in the requested
result type of the new box.
A slice argument can be provided to build a reference to part of a boxed value.
In this case, the shape operand must be absent or be a fir.shift that can be
used to provide a non default origin for the slice.
The following example illustrates creating a fir.box for x(10:33:2)
where x is described by a fir.box and has non default lower bounds,
and then applying a new 2-dimension shape to this fir.box.
```mlir
%0 = fir.slice %c10, %c33, %c2 : (index, index, index) -> !fir.slice<1>
%1 = fir.shift %c0 : (index) -> !fir.shift<1>
%2 = fir.rebox %x(%1) [%0] : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>, !fir.slice<1>) -> !fir.box<!fir.array<?xf32>>
%3 = fir.shape %c3, %c4 : (index, index) -> !fir.shape<2>
%4 = fir.rebox %2(%3) : (!fir.box<!fir.array<?xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?xf32>>
```
}];
let arguments = (ins
fir_BoxType:$box,
Optional<AnyShapeOrShiftType>:$shape,
Optional<fir_SliceType>:$slice
);
let results = (outs fir_BoxType);
let assemblyFormat = [{
$box (`(` $shape^ `)`)? (`[` $slice^ `]`)? attr-dict `:` functional-type(operands, results)
}];
let verifier = [{ return ::verify(*this); }];
}
def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> {
let summary = "boxes a given CHARACTER reference and its LEN parameter";
@ -1774,58 +1799,28 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> {
array `%h`.
}];
let arguments = (ins AnyRefOrBox:$ref, Variadic<AnyCoordinateType>:$coor);
let arguments = (ins
AnyRefOrBox:$ref,
Variadic<AnyCoordinateType>:$coor,
TypeAttr:$baseType
);
let results = (outs fir_ReferenceType);
let parser = "return parseCoordinateOp(parser, result);";
let parser = [{ return parseCoordinateCustom(parser, result); }];
let printer = [{ ::print(p, *this); }];
let verifier = [{ return ::verify(*this); }];
let printer = [{
p << getOperationName() << ' ' << (*this)->getOperands();
p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{baseType()});
p << " : ";
p.printFunctionalType((*this)->getOperandTypes(),
(*this)->getResultTypes());
}];
let verifier = [{
auto refTy = ref().getType();
if (fir::isa_ref_type(refTy)) {
auto eleTy = fir::dyn_cast_ptrEleTy(refTy);
if (auto arrTy = eleTy.dyn_cast<fir::SequenceType>()) {
if (arrTy.hasUnknownShape())
return emitOpError("cannot find coordinate in unknown shape");
if (arrTy.getConstantRows() < arrTy.getDimension() - 1)
return emitOpError("cannot find coordinate with unknown extents");
}
}
// Recovering a LEN type parameter only makes sense from a boxed value
for (auto co : coor())
if (dyn_cast_or_null<LenParamIndexOp>(co.getDefiningOp())) {
if (getNumOperands() != 2)
return emitOpError("len_param_index must be last argument");
if (!ref().getType().dyn_cast<fir::BoxType>())
return emitOpError("len_param_index must be used on box type");
}
if (auto attr = (*this)->getAttr(CoordinateOp::baseType())) {
if (!attr.isa<mlir::TypeAttr>())
return emitOpError("improperly constructed");
} else {
return emitOpError("must have base type");
}
return mlir::success();
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "Type":$type, "Value":$ref, "ValueRange":$coor,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "Type":$type, "ValueRange":$operands,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>];
OpBuilderDAG<(ins "mlir::Type":$resultType,
"mlir::Value":$ref, "mlir::ValueRange":$coor),
[{ return build($_builder, $_state, resultType, ref, coor,
mlir::TypeAttr::get(ref.getType())); }]>,
];
let extraClassDeclaration = [{
static constexpr llvm::StringRef baseType() { return "base_type"; }
mlir::Type getBaseType();
/// Get the type of the base object.
mlir::Type getBaseType() { return baseType(); }
}];
}
@ -1836,6 +1831,8 @@ def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]> {
Extract a value from an entity with a type composed of tuples, arrays,
and/or derived types. Returns the value from entity with the type of the
specified component. Cannot be used on values of `!fir.box` type.
It can also be used to access complex parts and elements of a character
string.
Note that the entity ssa-value must be of compile-time known size in order
to use this operation.
@ -1897,8 +1894,8 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> {
auto loc = parser.getNameLoc();
if (parser.parseOperandList(operands,
mlir::OpAsmParser::Delimiter::None) ||
parser.parseRParen() ||
parser.parseColonTypeList(types) ||
parser.parseRParen() ||
parser.resolveOperands(operands, types, loc, result.operands))
return mlir::failure();
}
@ -2146,9 +2143,11 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]> {
let summary = "insert a new sub-value into a copy of an existing aggregate";
let description = [{
Insert a value from an entity with a type composed of tuples, arrays,
Insert a value into an entity with a type composed of tuples, arrays,
and/or derived types. Returns a new ssa value with the same type as the
original entity. Cannot be used on values of `!fir.box` type.
It can also be used to set complex parts and elements of a character
string.
Note that the entity ssa-value must be of compile-time known size in order
to use this operation.
@ -2169,6 +2168,26 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]> {
let assemblyFormat = [{
operands attr-dict `:` functional-type(operands, results)
}];
let hasCanonicalizer = 1;
}
def fir_InsertOnRangeOp : fir_OneResultOp<"insert_on_range", [NoSideEffect]> {
let summary = "insert sub-value into a range on an existing sequence";
let description = [{
Insert a constant value into an entity with an array type. Returns a
new ssa value where the range of offsets from the original array have been
replaced with the constant. The result is an array type entity.
}];
let arguments = (ins fir_SequenceType:$seq, AnyType:$val,
Variadic<Index>:$coor);
let results = (outs fir_SequenceType);
let assemblyFormat = [{
operands attr-dict `:` functional-type(operands, results)
}];
}
def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> {
@ -2214,12 +2233,13 @@ def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> {
<< ", " << (*this)->getAttr(typeAttrName());
}];
let builders = [
OpBuilderDAG<(ins "StringRef":$fieldName, "Type":$recTy),
let builders = [OpBuilderDAG<(ins "llvm::StringRef":$fieldName,
"mlir::Type":$recTy),
[{
$_state.addAttribute(fieldAttrName(), $_builder.getStringAttr(fieldName));
$_state.addAttribute(typeAttrName(), TypeAttr::get(recTy));
}]>];
}]
>];
let extraClassDeclaration = [{
static constexpr llvm::StringRef fieldAttrName() { return "field_id"; }
@ -2247,10 +2267,7 @@ def fir_ResultOp : fir_Op<"result",
}];
let arguments = (ins Variadic<AnyType>:$results);
let builders = [
OpBuilderDAG<(ins),
[{/* do nothing */}]>
];
let builders = [OpBuilderDAG<(ins), [{ /* do nothing */ }]>];
let assemblyFormat = "($results^ `:` type($results))? attr-dict";
@ -2272,7 +2289,7 @@ def fir_DoLoopOp : region_Op<"do_loop",
let summary = "generalized loop operation";
let description = [{
Generalized high-level looping construct. This operation is similar to
MLIR's `loop.for`.
MLIR's `scf.for`.
```mlir
%l = constant 0 : index
@ -2281,7 +2298,7 @@ def fir_DoLoopOp : region_Op<"do_loop",
fir.do_loop %i = %l to %u step %s unordered {
%x = fir.convert %i : (index) -> i32
%v = fir.call @compute(%x) : (i32) -> f32
%p = fir.coordinate_of %A, %i : (!fir.ref<f32>, index) -> !fir.ref<f32>
%p = fir.coordinate_of %A, %i : (!fir.ref<!fir.array<?xf32>>, index) -> !fir.ref<f32>
fir.store %v to %p : !fir.ref<f32>
}
```
@ -2295,23 +2312,26 @@ def fir_DoLoopOp : region_Op<"do_loop",
Index:$upperBound,
Index:$step,
Variadic<AnyType>:$initArgs,
OptionalAttr<UnitAttr>:$unordered
);
let results = (outs
Variadic<AnyType>:$results
OptionalAttr<UnitAttr>:$unordered,
OptionalAttr<UnitAttr>:$finalValue
);
let results = (outs Variadic<AnyType>:$results);
let regions = (region SizedRegion<1>:$region);
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "mlir::Value":$lowerBound, "mlir::Value":$upperBound,
"mlir::Value":$step, CArg<"bool", "false">:$unordered,
CArg<"ValueRange", "llvm::None">:$iterArgs,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
CArg<"bool", "false">:$finalCountValue,
CArg<"mlir::ValueRange", "llvm::None">:$iterArgs,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attributes)>
];
let extraClassDeclaration = [{
static constexpr llvm::StringRef unorderedAttrName() { return "unordered"; }
static constexpr llvm::StringRef finalValueAttrName() {
return "finalValue";
}
mlir::Value getInductionVar() { return getBody()->getArgument(0); }
mlir::OpBuilder getBodyBuilder() {
@ -2350,6 +2370,11 @@ def fir_DoLoopOp : region_Op<"do_loop",
(*this)->setAttr(unorderedAttrName(),
mlir::UnitAttr::get(getContext()));
}
mlir::BlockArgument iterArgToBlockArg(mlir::Value iterArg);
void resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
unsigned resultNum);
mlir::Value blockArgToSourceOp(unsigned blockArgNum);
}];
}
@ -2374,28 +2399,31 @@ def fir_IfOp : region_Op<"if", [NoRegionArguments]> {
let results = (outs Variadic<AnyType>:$results);
let regions = (region
SizedRegion<1>:$whereRegion,
AnyRegion:$otherRegion
SizedRegion<1>:$thenRegion,
AnyRegion:$elseRegion
);
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "Value":$cond, "bool":$withOtherRegion)>,
OpBuilderDAG<(ins "TypeRange":$resultTypes, "Value":$cond,
"bool":$withOtherRegion)>
OpBuilderDAG<(ins "mlir::Value":$cond, "bool":$withElseRegion)>,
OpBuilderDAG<(ins "mlir::TypeRange":$resultTypes, "mlir::Value":$cond,
"bool":$withElseRegion)>
];
let extraClassDeclaration = [{
mlir::OpBuilder getWhereBodyBuilder() {
assert(!whereRegion().empty() && "Unexpected empty 'where' region.");
mlir::Block &body = whereRegion().front();
mlir::OpBuilder getThenBodyBuilder() {
assert(!thenRegion().empty() && "Unexpected empty 'where' region.");
mlir::Block &body = thenRegion().front();
return mlir::OpBuilder(&body, std::prev(body.end()));
}
mlir::OpBuilder getOtherBodyBuilder() {
assert(!otherRegion().empty() && "Unexpected empty 'other' region.");
mlir::Block &body = otherRegion().front();
mlir::OpBuilder getElseBodyBuilder() {
assert(!elseRegion().empty() && "Unexpected empty 'other' region.");
mlir::Block &body = elseRegion().front();
return mlir::OpBuilder(&body, std::prev(body.end()));
}
void resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
unsigned resultNum);
}];
}
@ -2403,12 +2431,30 @@ def fir_IterWhileOp : region_Op<"iterate_while",
[DeclareOpInterfaceMethods<LoopLikeOpInterface>]> {
let summary = "DO loop with early exit condition";
let description = [{
This construct is useful for lowering implied-DO loops. It is very similar
to `fir::DoLoopOp` with the addition that it requires a single loop-carried
bool value that signals an early exit condition to the operation. A `true`
disposition means the next loop iteration should proceed. A `false`
indicates that the `fir.iterate_while` operation should terminate and
return its iteration arguments.
This single-entry, single-exit looping construct is useful for lowering
counted loops that can exit early such as, for instance, implied-DO loops.
It is very similar to `fir::DoLoopOp` with the addition that it requires
a single loop-carried bool value that signals an early exit condition to
the operation. A `true` disposition means the next loop iteration should
proceed. A `false` indicates that the `fir.iterate_while` operation should
terminate and return its iteration arguments. This is a degenerate counted
loop in that the loop is not guaranteed to execute all iterations.
An example iterate_while that returns the counter value, the early
termination condition, and an extra loop-carried value is shown here. This
loop counts from %lo to %up (inclusive), stepping by %c1, so long as the
early exit (%ok) is true. The iter_args %sh value is also carried by the
loop. The result triple is the values of %i=phi(%lo,%i+%c1),
%ok=phi(%okIn,%okNew), and %sh=phi(%shIn,%shNew) from the last executed
iteration.
```mlir
%v:3 = fir.iterate_while (%i = %lo to %up step %c1) and (%ok = %okIn) iter_args(%sh = %shIn) -> (index, i1, i16) {
%shNew = fir.call @bar(%sh) : (i16) -> i16
%okNew = fir.call @foo(%sh) : (i16) -> i1
fir.result %i, %okNew, %shNew : index, i1, i16
}
```
}];
let arguments = (ins
@ -2416,23 +2462,25 @@ def fir_IterWhileOp : region_Op<"iterate_while",
Index:$upperBound,
Index:$step,
I1:$iterateIn,
Variadic<AnyType>:$initArgs
);
let results = (outs
I1:$iterateResult,
Variadic<AnyType>:$results
Variadic<AnyType>:$initArgs,
OptionalAttr<UnitAttr>:$finalValue
);
let results = (outs Variadic<AnyType>:$results);
let regions = (region SizedRegion<1>:$region);
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "mlir::Value":$lowerBound, "mlir::Value":$upperBound,
"mlir::Value":$step, "mlir::Value":$iterate,
CArg<"ValueRange", "llvm::None">:$iterArgs,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
CArg<"bool", "false">:$finalCountValue,
CArg<"mlir::ValueRange", "llvm::None">:$iterArgs,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attributes)>
];
let extraClassDeclaration = [{
static constexpr llvm::StringRef finalValueAttrName() {
return "finalValue";
}
mlir::Block *getBody() { return &region().front(); }
mlir::Value getIterateVar() { return getBody()->getArgument(1); }
mlir::Value getInductionVar() { return getBody()->getArgument(0); }
@ -2464,6 +2512,11 @@ def fir_IterWhileOp : region_Op<"iterate_while",
unsigned getNumIterOperands() {
return (*this)->getNumOperands() - getNumControlOperands();
}
mlir::BlockArgument iterArgToBlockArg(mlir::Value iterArg);
void resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
unsigned resultNum);
mlir::Value blockArgToSourceOp(unsigned blockArgNum);
}];
}
@ -2471,8 +2524,7 @@ def fir_IterWhileOp : region_Op<"iterate_while",
// Procedure call operations
//===----------------------------------------------------------------------===//
def fir_CallOp : fir_Op<"call",
[MemoryEffects<[MemAlloc, MemFree, MemRead, MemWrite]>]> {
def fir_CallOp : fir_Op<"call", [CallOpInterface]> {
let summary = "call a procedure";
let description = [{
@ -2491,19 +2543,63 @@ def fir_CallOp : fir_Op<"call",
OptionalAttr<SymbolRefAttr>:$callee,
Variadic<AnyType>:$args
);
let results = (outs Variadic<AnyType>);
let parser = "return parseCallOp(parser, result);";
let printer = "printCallOp(p, *this);";
let builders = [
OpBuilderDAG<(ins "mlir::FuncOp":$callee,
CArg<"mlir::ValueRange", "{}">:$operands),
[{
$_state.addOperands(operands);
$_state.addAttribute(calleeAttrName(),
$_builder.getSymbolRefAttr(callee));
$_state.addTypes(callee.getType().getResults());
}]>,
OpBuilderDAG<(ins "mlir::SymbolRefAttr":$callee,
"llvm::ArrayRef<mlir::Type>":$results,
CArg<"mlir::ValueRange", "{}">:$operands),
[{
$_state.addOperands(operands);
$_state.addAttribute(calleeAttrName(), callee);
$_state.addTypes(results);
}]>,
OpBuilderDAG<(ins "llvm::StringRef":$callee,
"llvm::ArrayRef<mlir::Type>":$results,
CArg<"mlir::ValueRange", "{}">:$operands),
[{
build($_builder, $_state, $_builder.getSymbolRefAttr(callee), results,
operands);
}]>];
let extraClassDeclaration = [{
static constexpr StringRef calleeAttrName() { return "callee"; }
mlir::FunctionType getFunctionType();
/// Get the argument operands to the called function.
operand_range getArgOperands() {
if (auto calling =
(*this)->getAttrOfType<SymbolRefAttr>(calleeAttrName()))
return {arg_operand_begin(), arg_operand_end()};
return {arg_operand_begin() + 1, arg_operand_end()};
}
operand_iterator arg_operand_begin() { return operand_begin(); }
operand_iterator arg_operand_end() { return operand_end(); }
/// Return the callee of this operation.
CallInterfaceCallable getCallableForCallee() {
if (auto calling =
(*this)->getAttrOfType<SymbolRefAttr>(calleeAttrName()))
return calling;
return getOperand(0);
}
}];
}
def fir_DispatchOp : fir_Op<"dispatch",
[MemoryEffects<[MemAlloc, MemFree, MemRead, MemWrite]>]> {
def fir_DispatchOp : fir_Op<"dispatch", []> {
let summary = "call a type-bound procedure";
let description = [{
@ -2531,10 +2627,11 @@ def fir_DispatchOp : fir_Op<"dispatch",
llvm::StringRef calleeName;
if (failed(parser.parseOptionalKeyword(&calleeName))) {
mlir::StringAttr calleeAttr;
if (parser.parseAttribute(calleeAttr, "method", result.attributes))
if (parser.parseAttribute(calleeAttr, methodAttrName(),
result.attributes))
return mlir::failure();
} else {
result.addAttribute("method",
result.addAttribute(methodAttrName(),
parser.getBuilder().getStringAttr(calleeName));
}
if (parser.parseOperandList(operands,
@ -2545,22 +2642,19 @@ def fir_DispatchOp : fir_Op<"dispatch",
parser.resolveOperands(
operands, calleeType.getInputs(), calleeLoc, result.operands))
return mlir::failure();
result.addAttribute("fn_type", mlir::TypeAttr::get(calleeType));
return mlir::success();
}];
let printer = [{
p << getOperationName() << ' ' << (*this)->getAttr("method") << '(';
p << getOperationName() << ' ' << (*this)->getAttr(methodAttrName()) << '(';
p.printOperand(object());
if (arg_operand_begin() != arg_operand_end()) {
if (!args().empty()) {
p << ", ";
p.printOperands(args());
}
p << ')';
p.printOptionalAttrDict((*this)->getAttrs(), {"fn_type", "method"});
auto resTy{getResultTypes()};
llvm::SmallVector<mlir::Type, 8> argTy(getOperandTypes());
p << " : " << mlir::FunctionType::get(getContext(), argTy, resTy);
p << ") : ";
p.printFunctionalType((*this)->getOperandTypes(),
(*this)->getResultTypes());
}];
let extraClassDeclaration = [{
@ -2568,9 +2662,13 @@ def fir_DispatchOp : fir_Op<"dispatch",
operand_range getArgOperands() {
return {arg_operand_begin(), arg_operand_end()};
}
// operand[0] is the object (of box type)
operand_iterator arg_operand_begin() { return operand_begin() + 1; }
operand_iterator arg_operand_end() { return operand_end(); }
llvm::StringRef passArgAttrName() { return "pass_arg_pos"; }
static constexpr llvm::StringRef passArgAttrName() {
return "pass_arg_pos";
}
static constexpr llvm::StringRef methodAttrName() { return "method"; }
unsigned passArgPos();
}];
}
@ -2591,12 +2689,13 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
```
}];
let results = (outs fir_SequenceType);
let results = (outs fir_CharacterType);
let parser = [{
auto &builder = parser.getBuilder();
mlir::Attribute val;
mlir::NamedAttrList attrs;
llvm::SMLoc trailingTypeLoc;
if (parser.parseAttribute(val, "fake", attrs))
return mlir::failure();
if (auto v = val.dyn_cast<mlir::StringAttr>())
@ -2611,12 +2710,15 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
if (parser.parseLParen() ||
parser.parseAttribute(sz, size(), result.attributes) ||
parser.parseRParen() ||
parser.getCurrentLocation(&trailingTypeLoc) ||
parser.parseColonType(type))
return mlir::failure();
if (!(type.isa<fir::CharacterType>() || type.isa<mlir::IntegerType>()))
return parser.emitError(parser.getCurrentLocation(),
auto charTy = type.dyn_cast<fir::CharacterType>();
if (!charTy)
return parser.emitError(trailingTypeLoc,
"must have character type");
type = fir::SequenceType::get(type.getContext(), {sz.getInt()}, type, {});
type = fir::CharacterType::get(builder.getContext(), charTy.getFKind(),
sz.getInt());
if (!type || parser.addTypesToList(type, result.types))
return mlir::failure();
return mlir::success();
@ -2625,20 +2727,17 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
let printer = [{
p << getOperationName() << ' ' << getValue() << '(';
p << getSize().cast<mlir::IntegerAttr>().getValue() << ") : ";
p.printType(getType().cast<fir::SequenceType>().getEleTy());
p.printType(getType());
}];
let verifier = [{
if (getSize().cast<mlir::IntegerAttr>().getValue().isNegative())
return emitOpError("size must be non-negative");
auto eleTy = getType().cast<fir::SequenceType>().getEleTy();
if (!eleTy.isa<fir::CharacterType>())
return emitOpError("must have !fir.char type");
if (auto xl = (*this)->getAttr(xlist())) {
auto xList = xl.cast<mlir::ArrayAttr>();
for (auto a : xList)
if (!a.isa<mlir::IntegerAttr>())
return emitOpError("values in list must be integers");
return emitOpError("values in list must be integers");
}
return mlir::success();
}];
@ -2686,32 +2785,6 @@ class fir_UnaryArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
let printer = [{ return printUnaryOp(this->getOperation(), p); }];
}
def FirRealAttr : Attr<CPred<"$_self.isa<fir::RealAttr>()">, "FIR real attr"> {
let storageType = [{ fir::RealAttr }];
let returnType = [{ llvm::APFloat }];
}
def fir_ConstfOp : fir_Op<"constf", [NoSideEffect]> {
let summary = "create a floating point constant";
let description = [{
A floating-point constant. This operation is to augment MLIR to be able
to represent APFloat values that are not supported in the standard dialect.
}];
let arguments = (ins FirRealAttr:$constant);
let results = (outs fir_RealType:$res);
let assemblyFormat = "`(` $constant `)` attr-dict `:` type($res)";
let verifier = [{
if (!getType().isa<fir::RealType>())
return emitOpError("must be a !fir.real type");
return mlir::success();
}];
}
class RealUnaryArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
fir_UnaryArithmeticOp<mnemonic, traits>,
Arguments<(ins AnyRealLike:$operand)>;
@ -2748,9 +2821,8 @@ def fir_CmpfOp : fir_Op<"cmpf",
let results = (outs AnyLogicalLike);
let builders = [
OpBuilderDAG<(ins "CmpFPredicate":$predicate, "Value":$lhs, "Value":$rhs),
[{
let builders = [OpBuilderDAG<(ins "mlir::CmpFPredicate":$predicate,
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
buildCmpFOp($_builder, $_state, predicate, lhs, rhs);
}]>];
@ -2857,9 +2929,8 @@ def fir_CmpcOp : fir_Op<"cmpc",
let printer = "printCmpcOp(p, *this);";
let builders = [
OpBuilderDAG<(ins "CmpFPredicate":$predicate, "Value":$lhs, "Value":$rhs),
[{
let builders = [OpBuilderDAG<(ins "mlir::CmpFPredicate":$predicate,
"mlir::Value":$lhs, "mlir::Value":$rhs), [{
buildCmpCOp($_builder, $_state, predicate, lhs, rhs);
}]>];
@ -2882,7 +2953,8 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> {
let description = [{
Convert a symbol (a function or global reference) to an SSA-value to be
used in other Operations.
used in other Operations. References to Fortran symbols are distinguished
via this operation from other arbitrary constant values.
```mlir
%p = fir.address_of(@symbol) : !fir.ref<f64>
@ -2891,7 +2963,7 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> {
let arguments = (ins SymbolRefAttr:$symbol);
let results = (outs fir_ReferenceType:$resTy);
let results = (outs AnyAddressableLike:$resTy);
let assemblyFormat = "`(` $symbol `)` attr-dict `:` type($resTy)";
}
@ -2943,6 +3015,7 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]> {
static bool isFloatCompatible(mlir::Type ty);
static bool isPointerCompatible(mlir::Type ty);
}];
let hasCanonicalizer = 1;
}
def FortranTypeAttr : Attr<And<[CPred<"$_self.isa<TypeAttr>()">,
@ -2989,9 +3062,7 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> {
p.printOptionalAttrDict((*this)->getAttrs(), {"in_type"});
}];
let builders = [
OpBuilderDAG<(ins "mlir::TypeAttr":$inty)>
];
let builders = [OpBuilderDAG<(ins "mlir::TypeAttr":$inty)>];
let verifier = [{
mlir::Type resultTy = getType();
@ -3095,22 +3166,24 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "StringRef":$name, "Type":$type,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "StringRef":$name, "Type":$type,
CArg<"StringAttr", "{}">:$linkage,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type,
CArg<"StringAttr", "{}">:$linkage,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "StringRef":$name, "Type":$type, "Attribute":$initVal,
CArg<"StringAttr", "{}">:$linkage,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type,
"Attribute":$initVal, CArg<"StringAttr", "{}">:$linkage,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant,
"mlir::Type":$type,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type,
CArg<"mlir::StringAttr", "{}">:$linkage,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant,
"mlir::Type":$type, CArg<"mlir::StringAttr", "{}">:$linkage,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type,
"mlir::Attribute":$initVal, CArg<"mlir::StringAttr", "{}">:$linkage,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant,
"mlir::Type":$type, "mlir::Attribute":$initVal,
CArg<"mlir::StringAttr", "{}">:$linkage,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
];
let extraClassDeclaration = [{
@ -3271,8 +3344,8 @@ def fir_DispatchTableOp : fir_Op<"dispatch_table",
let skipDefaultBuilders = 1;
let builders = [
OpBuilderDAG<(ins "StringRef":$name, "Type":$type,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs),
OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs),
[{
$_state.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
$_builder.getStringAttr(name));
@ -3341,4 +3414,42 @@ def fir_DTEntryOp : fir_Op<"dt_entry", []> {
}];
}
def fir_AbsentOp : fir_OneResultOp<"absent", [NoSideEffect]> {
let summary = "create value to be passed for absent optional function argument";
let description = [{
Given the type of a function argument, create a value that will signal that
an optional argument is absent in the call. On the caller side, fir.is_present
can be used to query if the value of an optional argument was created with
a fir.absent operation.
It is undefined to use a value that was created by a fir.absent op in any other
operation than fir.call and fir.is_present.
```mlir
%1 = fir.absent fir.box<fir.array<?xf32>>
fir.call @_QPfoo(%1) : (fir.box<fir.array<?xf32>>) -> ()
```
}];
let results = (outs AnyRefOrBoxLike:$intype);
let assemblyFormat = "type($intype) attr-dict";
}
def fir_IsPresentOp : fir_SimpleOp<"is_present", [NoSideEffect]> {
let summary = "is this optional function argument present?";
let description = [{
Determine if an optional function argument is PRESENT (i.e. that it was not
created by a fir.absent op on the caller side).
```mlir
func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>) {
%0 = fir.is_present %arg0 : (!fir.box<!fir.array<?xf32>>) -> i1
...
```
}];
let arguments = (ins AnyRefOrBoxLike:$val);
let results = (outs BoolLike);
}
#endif

View file

@ -341,36 +341,7 @@ fir::CharBoxValue Fortran::lower::CharacterExprHelper::createSubstring(
mlir::Value Fortran::lower::CharacterExprHelper::createLenTrim(
const fir::CharBoxValue &str) {
// Note: Runtime for LEN_TRIM should also be available at some
// point. For now use an inlined implementation.
auto indexType = builder.getIndexType();
auto len = builder.createConvert(loc, indexType, str.getLen());
auto one = builder.createIntegerConstant(loc, indexType, 1);
auto minusOne = builder.createIntegerConstant(loc, indexType, -1);
auto zero = builder.createIntegerConstant(loc, indexType, 0);
auto trueVal = builder.createIntegerConstant(loc, builder.getI1Type(), 1);
auto blank = createBlankConstantCode(getCharacterType(str));
mlir::Value lastChar = builder.create<mlir::SubIOp>(loc, len, one);
auto iterWhile = builder.create<fir::IterWhileOp>(
loc, lastChar, zero, minusOne, trueVal, lastChar);
auto insPt = builder.saveInsertionPoint();
builder.setInsertionPointToStart(iterWhile.getBody());
auto index = iterWhile.getInductionVar();
// Look for first non-blank from the right of the character.
auto c = createLoadCharAt(str, index);
c = builder.createConvert(loc, blank.getType(), c);
auto isBlank =
builder.create<mlir::CmpIOp>(loc, mlir::CmpIPredicate::eq, blank, c);
llvm::SmallVector<mlir::Value, 2> results = {isBlank, index};
builder.create<fir::ResultOp>(loc, results);
builder.restoreInsertionPoint(insPt);
// Compute length after iteration (zero if all blanks)
mlir::Value newLen =
builder.create<mlir::AddIOp>(loc, iterWhile.getResult(1), one);
auto result =
builder.create<SelectOp>(loc, iterWhile.getResult(0), zero, newLen);
return builder.createConvert(loc, getLengthType(), result);
return {};
}
mlir::Value Fortran::lower::CharacterExprHelper::createTemp(mlir::Type type,

View file

@ -182,7 +182,7 @@ static void makeNextConditionalOn(Fortran::lower::FirOpBuilder &builder,
: builder.create<fir::IfOp>(loc, ok, /*withOtherwise=*/false);
if (!insertPt.isSet())
insertPt = builder.saveInsertionPoint();
builder.setInsertionPointToStart(&whereOp.whereRegion().front());
builder.setInsertionPointToStart(&whereOp.thenRegion().front());
}
template <typename D>
@ -414,10 +414,10 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter,
for (auto *op = builder.getBlock()->getParentOp(); isa<fir::IfOp>(op);
op = op->getBlock()->getParentOp()) {
auto whereOp = dyn_cast<fir::IfOp>(op);
auto *lastOp = &whereOp.whereRegion().front().back();
auto *lastOp = &whereOp.thenRegion().front().back();
builder.setInsertionPointAfter(lastOp);
builder.create<fir::ResultOp>(loc, lastOp->getResult(0)); // runtime result
builder.setInsertionPointToStart(&whereOp.otherRegion().front());
builder.setInsertionPointToStart(&whereOp.elseRegion().front());
builder.create<fir::ResultOp>(loc, falseValue); // known false result
}
builder.restoreInsertionPoint(insertPt);

View file

@ -22,7 +22,7 @@ fir::FIROpsDialect::FIROpsDialect(mlir::MLIRContext *ctx)
addTypes<BoxType, BoxCharType, BoxProcType, CharacterType, fir::ComplexType,
FieldType, HeapType, fir::IntegerType, LenType, LogicalType,
PointerType, RealType, RecordType, ReferenceType, SequenceType,
ShapeType, ShapeShiftType, SliceType, TypeDescType,
ShapeType, ShapeShiftType, ShiftType, SliceType, TypeDescType,
fir::VectorType>();
addAttributes<ClosedIntervalAttr, ExactTypeAttr, LowerBoundAttr,
PointIntervalAttr, RealAttr, SubclassAttr, UpperBoundAttr>();

View file

@ -19,6 +19,7 @@
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Matchers.h"
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/TypeSwitch.h"
@ -246,6 +247,11 @@ mlir::Type fir::BoxDimsOp::getTupleType() {
// CallOp
//===----------------------------------------------------------------------===//
mlir::FunctionType fir::CallOp::getFunctionType() {
return mlir::FunctionType::get(getContext(), getOperandTypes(),
getResultTypes());
}
static void printCallOp(mlir::OpAsmPrinter &p, fir::CallOp &op) {
auto callee = op.callee();
bool isDirect = callee.hasValue();
@ -291,12 +297,9 @@ static mlir::ParseResult parseCallOp(mlir::OpAsmParser &parser,
} else {
auto funcArgs =
llvm::ArrayRef<mlir::OpAsmParser::OperandType>(operands).drop_front();
llvm::SmallVector<mlir::Value, 8> resultArgs(
result.operands.begin() + (result.operands.empty() ? 0 : 1),
result.operands.end());
if (parser.resolveOperand(operands[0], funcType, result.operands) ||
parser.resolveOperands(funcArgs, funcType.getInputs(),
parser.getNameLoc(), resultArgs))
parser.getNameLoc(), result.operands))
return mlir::failure();
}
result.addTypes(funcType.getResults());
@ -403,6 +406,10 @@ mlir::ParseResult fir::parseCmpcOp(mlir::OpAsmParser &parser,
// ConvertOp
//===----------------------------------------------------------------------===//
void fir::ConvertOp::getCanonicalizationPatterns(
OwningRewritePatternList &results, MLIRContext *context) {
}
mlir::OpFoldResult fir::ConvertOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
if (value().getType() == getType())
return value();
@ -425,8 +432,7 @@ mlir::OpFoldResult fir::ConvertOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
bool fir::ConvertOp::isIntegerCompatible(mlir::Type ty) {
return ty.isa<mlir::IntegerType>() || ty.isa<mlir::IndexType>() ||
ty.isa<fir::IntegerType>() || ty.isa<fir::LogicalType>() ||
ty.isa<fir::CharacterType>();
ty.isa<fir::IntegerType>() || ty.isa<fir::LogicalType>();
}
bool fir::ConvertOp::isFloatCompatible(mlir::Type ty) {
@ -436,67 +442,68 @@ bool fir::ConvertOp::isFloatCompatible(mlir::Type ty) {
bool fir::ConvertOp::isPointerCompatible(mlir::Type ty) {
return ty.isa<fir::ReferenceType>() || ty.isa<fir::PointerType>() ||
ty.isa<fir::HeapType>() || ty.isa<mlir::MemRefType>() ||
ty.isa<fir::TypeDescType>();
ty.isa<mlir::FunctionType>() || ty.isa<fir::TypeDescType>();
}
//===----------------------------------------------------------------------===//
// CoordinateOp
//===----------------------------------------------------------------------===//
static mlir::ParseResult parseCoordinateOp(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
llvm::ArrayRef<mlir::Type> allOperandTypes;
llvm::ArrayRef<mlir::Type> allResultTypes;
llvm::SMLoc allOperandLoc = parser.getCurrentLocation();
llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> allOperands;
if (parser.parseOperandList(allOperands))
return failure();
if (parser.parseOptionalAttrDict(result.attributes))
return failure();
if (parser.parseColon())
return failure();
static void print(mlir::OpAsmPrinter &p, fir::CoordinateOp op) {
p << op.getOperationName() << ' ' << op.ref() << ", " << op.coor();
p.printOptionalAttrDict(op->getAttrs(), /*elideAttrs=*/{"baseType"});
p << " : ";
p.printFunctionalType(op.getOperandTypes(), op->getResultTypes());
}
static mlir::ParseResult parseCoordinateCustom(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
mlir::OpAsmParser::OperandType memref;
if (parser.parseOperand(memref) || parser.parseComma())
return mlir::failure();
llvm::SmallVector<mlir::OpAsmParser::OperandType, 8> coorOperands;
if (parser.parseOperandList(coorOperands))
return mlir::failure();
llvm::SmallVector<mlir::OpAsmParser::OperandType, 16> allOperands;
allOperands.push_back(memref);
allOperands.append(coorOperands.begin(), coorOperands.end());
mlir::FunctionType funcTy;
if (parser.parseType(funcTy))
return failure();
allOperandTypes = funcTy.getInputs();
allResultTypes = funcTy.getResults();
result.addTypes(allResultTypes);
if (parser.resolveOperands(allOperands, allOperandTypes, allOperandLoc,
auto loc = parser.getCurrentLocation();
if (parser.parseOptionalAttrDict(result.attributes) ||
parser.parseColonType(funcTy) ||
parser.resolveOperands(allOperands, funcTy.getInputs(), loc,
result.operands))
return failure();
if (funcTy.getNumInputs()) {
// No inputs handled by verify
result.addAttribute(fir::CoordinateOp::baseType(),
mlir::TypeAttr::get(funcTy.getInput(0)));
parser.addTypesToList(funcTy.getResults(), result.types);
result.addAttribute("baseType", mlir::TypeAttr::get(funcTy.getInput(0)));
return mlir::success();
}
static mlir::LogicalResult verify(fir::CoordinateOp op) {
auto refTy = op.ref().getType();
if (fir::isa_ref_type(refTy)) {
auto eleTy = fir::dyn_cast_ptrEleTy(refTy);
if (auto arrTy = eleTy.dyn_cast<fir::SequenceType>()) {
if (arrTy.hasUnknownShape())
return op.emitOpError("cannot find coordinate in unknown shape");
if (arrTy.getConstantRows() < arrTy.getDimension() - 1)
return op.emitOpError("cannot find coordinate with unknown extents");
}
if (!(fir::isa_aggregate(eleTy) || fir::isa_complex(eleTy) ||
fir::isa_char_string(eleTy)))
return op.emitOpError("cannot apply coordinate_of to this type");
}
return success();
}
mlir::Type fir::CoordinateOp::getBaseType() {
return (*this)
->getAttr(CoordinateOp::baseType())
.cast<mlir::TypeAttr>()
.getValue();
}
void fir::CoordinateOp::build(OpBuilder &, OperationState &result,
mlir::Type resType, ValueRange operands,
ArrayRef<NamedAttribute> attrs) {
assert(operands.size() >= 1u && "mismatched number of parameters");
result.addOperands(operands);
result.addAttribute(fir::CoordinateOp::baseType(),
mlir::TypeAttr::get(operands[0].getType()));
result.attributes.append(attrs.begin(), attrs.end());
result.addTypes({resType});
}
void fir::CoordinateOp::build(OpBuilder &builder, OperationState &result,
mlir::Type resType, mlir::Value ref,
ValueRange coor, ArrayRef<NamedAttribute> attrs) {
llvm::SmallVector<mlir::Value, 16> operands{ref};
operands.append(coor.begin(), coor.end());
build(builder, result, resType, operands, attrs);
// Recovering a LEN type parameter only makes sense from a boxed value. For a
// bare reference, the LEN type parameters must be passed as additional
// arguments to `op`.
for (auto co : op.coor())
if (dyn_cast_or_null<fir::LenParamIndexOp>(co.getDefiningOp())) {
if (op.getNumOperands() != 2)
return op.emitOpError("len_param_index must be last argument");
if (!op.ref().getType().isa<BoxType>())
return op.emitOpError("len_param_index must be used on box type");
}
return mlir::success();
}
//===----------------------------------------------------------------------===//
@ -504,8 +511,8 @@ void fir::CoordinateOp::build(OpBuilder &builder, OperationState &result,
//===----------------------------------------------------------------------===//
mlir::FunctionType fir::DispatchOp::getFunctionType() {
auto attr = (*this)->getAttr("fn_type").cast<mlir::TypeAttr>();
return attr.getValue().cast<mlir::FunctionType>();
return mlir::FunctionType::get(getContext(), getOperandTypes(),
getResultTypes());
}
//===----------------------------------------------------------------------===//
@ -522,40 +529,45 @@ void fir::DispatchTableOp::appendTableEntry(mlir::Operation *op) {
// EmboxOp
//===----------------------------------------------------------------------===//
static mlir::ParseResult parseEmboxOp(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
mlir::FunctionType type;
llvm::SmallVector<mlir::OpAsmParser::OperandType, 8> operands;
mlir::OpAsmParser::OperandType memref;
if (parser.parseOperand(memref))
return mlir::failure();
operands.push_back(memref);
auto &builder = parser.getBuilder();
if (!parser.parseOptionalLParen()) {
if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None) ||
parser.parseRParen())
return mlir::failure();
auto lens = builder.getI32IntegerAttr(operands.size());
result.addAttribute(fir::EmboxOp::lenpName(), lens);
static mlir::LogicalResult verify(fir::EmboxOp op) {
auto eleTy = fir::dyn_cast_ptrEleTy(op.memref().getType());
if (!eleTy)
return op.emitOpError("must embox a memory reference type");
bool isArray = false;
if (auto seqTy = eleTy.dyn_cast<fir::SequenceType>()) {
eleTy = seqTy.getEleTy();
isArray = true;
}
if (!parser.parseOptionalComma()) {
mlir::OpAsmParser::OperandType dims;
if (parser.parseOperand(dims))
return mlir::failure();
operands.push_back(dims);
} else if (!parser.parseOptionalLSquare()) {
mlir::AffineMapAttr map;
if (parser.parseAttribute(map, fir::EmboxOp::layoutName(),
result.attributes) ||
parser.parseRSquare())
return mlir::failure();
if (op.hasLenParams()) {
auto lenPs = op.numLenParams();
if (auto rt = eleTy.dyn_cast<fir::RecordType>()) {
if (lenPs != rt.getNumLenParams())
return op.emitOpError("number of LEN params does not correspond"
" to the !fir.type type");
} else if (auto strTy = eleTy.dyn_cast<fir::CharacterType>()) {
if (strTy.getLen() != fir::CharacterType::unknownLen())
return op.emitOpError("CHARACTER already has static LEN");
} else {
return op.emitOpError("LEN parameters require CHARACTER or derived type");
}
for (auto lp : op.lenParams())
if (!fir::isa_integer(lp.getType()))
return op.emitOpError("LEN parameters must be integral type");
}
if (op.getShape()) {
auto shapeTy = op.getShape().getType();
if (!(shapeTy.isa<fir::ShapeType>() || shapeTy.isa<ShapeShiftType>()))
return op.emitOpError("must be shape or shapeshift type");
if (!isArray)
return op.emitOpError("shape must not be provided for a scalar");
}
if (op.getSlice()) {
auto sliceTy = op.getSlice().getType();
if (!sliceTy.isa<fir::SliceType>())
return op.emitOpError("must be a slice type");
if (!isArray)
return op.emitOpError("slice must not be provided for a scalar");
}
if (parser.parseOptionalAttrDict(result.attributes) ||
parser.parseColonType(type) ||
parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(),
result.operands) ||
parser.addTypesToList(type.getResults(), result.types))
return mlir::failure();
return mlir::success();
}
@ -579,7 +591,7 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) {
auto &builder = parser.getBuilder();
if (mlir::succeeded(parser.parseOptionalKeyword(&linkage))) {
if (fir::GlobalOp::verifyValidLinkage(linkage))
return failure();
return mlir::failure();
mlir::StringAttr linkAttr = builder.getStringAttr(linkage);
result.addAttribute(fir::GlobalOp::linkageAttrName(), linkAttr);
}
@ -588,7 +600,7 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) {
mlir::SymbolRefAttr nameAttr;
if (parser.parseAttribute(nameAttr, fir::GlobalOp::symbolAttrName(),
result.attributes))
return failure();
return mlir::failure();
result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
builder.getStringAttr(nameAttr.getRootReference()));
@ -598,7 +610,7 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) {
if (parser.parseAttribute(attr, fir::GlobalOp::initValAttrName(),
result.attributes) ||
parser.parseRParen())
return failure();
return mlir::failure();
simpleInitializer = true;
}
@ -610,7 +622,7 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) {
mlir::Type globalType;
if (parser.parseColonType(globalType))
return failure();
return mlir::failure();
result.addAttribute(fir::GlobalOp::typeAttrName(),
mlir::TypeAttr::get(globalType));
@ -619,11 +631,13 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) {
result.addRegion();
} else {
// Parse the optional initializer body.
if (parser.parseRegion(*result.addRegion(), llvm::None, llvm::None))
return failure();
auto parseResult = parser.parseOptionalRegion(
*result.addRegion(), /*arguments=*/llvm::None, /*argTypes=*/llvm::None);
if (parseResult.hasValue() && mlir::failed(*parseResult))
return mlir::failure();
}
return success();
return mlir::success();
}
void fir::GlobalOp::appendInitialValue(mlir::Operation *op) {
@ -680,11 +694,74 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder, OperationState &result,
mlir::ParseResult fir::GlobalOp::verifyValidLinkage(StringRef linkage) {
// Supporting only a subset of the LLVM linkage types for now
static const llvm::SmallVector<const char *, 3> validNames = {
"internal", "common", "weak"};
static const char *validNames[] = {"common", "internal", "linkonce", "weak"};
return mlir::success(llvm::is_contained(validNames, linkage));
}
//===----------------------------------------------------------------------===//
// InsertValueOp
//===----------------------------------------------------------------------===//
static bool checkIsIntegerConstant(mlir::Value v, int64_t conVal) {
if (auto c = dyn_cast_or_null<mlir::ConstantOp>(v.getDefiningOp())) {
auto attr = c.getValue();
if (auto iattr = attr.dyn_cast<mlir::IntegerAttr>())
return iattr.getInt() == conVal;
}
return false;
}
static bool isZero(mlir::Value v) { return checkIsIntegerConstant(v, 0); }
static bool isOne(mlir::Value v) { return checkIsIntegerConstant(v, 1); }
// Undo some complex patterns created in the front-end and turn them back into
// complex ops.
template <typename FltOp, typename CpxOp>
struct UndoComplexPattern : public mlir::RewritePattern {
UndoComplexPattern(mlir::MLIRContext *ctx)
: mlir::RewritePattern("fir.insert_value", {}, 2, ctx) {}
mlir::LogicalResult
matchAndRewrite(mlir::Operation *op,
mlir::PatternRewriter &rewriter) const override {
auto insval = dyn_cast_or_null<fir::InsertValueOp>(op);
if (!insval || !insval.getType().isa<fir::ComplexType>())
return mlir::failure();
auto insval2 =
dyn_cast_or_null<fir::InsertValueOp>(insval.adt().getDefiningOp());
if (!insval2 || !isa<fir::UndefOp>(insval2.adt().getDefiningOp()))
return mlir::failure();
auto binf = dyn_cast_or_null<FltOp>(insval.val().getDefiningOp());
auto binf2 = dyn_cast_or_null<FltOp>(insval2.val().getDefiningOp());
if (!binf || !binf2 || insval.coor().size() != 1 ||
!isOne(insval.coor()[0]) || insval2.coor().size() != 1 ||
!isZero(insval2.coor()[0]))
return mlir::failure();
auto eai =
dyn_cast_or_null<fir::ExtractValueOp>(binf.lhs().getDefiningOp());
auto ebi =
dyn_cast_or_null<fir::ExtractValueOp>(binf.rhs().getDefiningOp());
auto ear =
dyn_cast_or_null<fir::ExtractValueOp>(binf2.lhs().getDefiningOp());
auto ebr =
dyn_cast_or_null<fir::ExtractValueOp>(binf2.rhs().getDefiningOp());
if (!eai || !ebi || !ear || !ebr || ear.adt() != eai.adt() ||
ebr.adt() != ebi.adt() || eai.coor().size() != 1 ||
!isOne(eai.coor()[0]) || ebi.coor().size() != 1 ||
!isOne(ebi.coor()[0]) || ear.coor().size() != 1 ||
!isZero(ear.coor()[0]) || ebr.coor().size() != 1 ||
!isZero(ebr.coor()[0]))
return mlir::failure();
rewriter.replaceOpWithNewOp<CpxOp>(op, ear.adt(), ebr.adt());
return mlir::success();
}
};
void fir::InsertValueOp::getCanonicalizationPatterns(
mlir::OwningRewritePatternList &results, mlir::MLIRContext *context) {
results.insert<UndoComplexPattern<fir::AddfOp, fir::AddcOp>,
UndoComplexPattern<fir::SubfOp, fir::SubcOp>>(context);
}
//===----------------------------------------------------------------------===//
// IterWhileOp
//===----------------------------------------------------------------------===//
@ -692,9 +769,14 @@ mlir::ParseResult fir::GlobalOp::verifyValidLinkage(StringRef linkage) {
void fir::IterWhileOp::build(mlir::OpBuilder &builder,
mlir::OperationState &result, mlir::Value lb,
mlir::Value ub, mlir::Value step,
mlir::Value iterate, mlir::ValueRange iterArgs,
mlir::Value iterate, bool finalCountValue,
mlir::ValueRange iterArgs,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
result.addOperands({lb, ub, step, iterate});
if (finalCountValue) {
result.addTypes(builder.getIndexType());
result.addAttribute(finalValueAttrName(), builder.getUnitAttr());
}
result.addTypes(iterate.getType());
result.addOperands(iterArgs);
for (auto v : iterArgs)
@ -736,24 +818,50 @@ static mlir::ParseResult parseIterWhileOp(mlir::OpAsmParser &parser,
// Parse the initial iteration arguments.
llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> regionArgs;
auto prependCount = false;
// Induction variable.
regionArgs.push_back(inductionVariable);
regionArgs.push_back(iterateVar);
result.addTypes(i1Type);
if (mlir::succeeded(parser.parseOptionalKeyword("iter_args"))) {
if (succeeded(parser.parseOptionalKeyword("iter_args"))) {
llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> operands;
llvm::SmallVector<mlir::Type, 4> regionTypes;
// Parse assignment list and results type list.
if (parser.parseAssignmentList(regionArgs, operands) ||
parser.parseArrowTypeList(regionTypes))
return mlir::failure();
return failure();
if (regionTypes.size() == operands.size() + 2)
prependCount = true;
llvm::ArrayRef<mlir::Type> resTypes = regionTypes;
resTypes = prependCount ? resTypes.drop_front(2) : resTypes;
// Resolve input operands.
for (auto operand_type : llvm::zip(operands, regionTypes))
for (auto operand_type : llvm::zip(operands, resTypes))
if (parser.resolveOperand(std::get<0>(operand_type),
std::get<1>(operand_type), result.operands))
return mlir::failure();
result.addTypes(regionTypes);
return failure();
if (prependCount) {
// This is an assert here, because these types are verified.
assert(regionTypes[0].isa<mlir::IndexType>() &&
regionTypes[1].isSignlessInteger(1));
result.addTypes(regionTypes);
} else {
result.addTypes(i1Type);
result.addTypes(resTypes);
}
} else if (succeeded(parser.parseOptionalArrow())) {
llvm::SmallVector<mlir::Type, 4> typeList;
if (parser.parseLParen() || parser.parseTypeList(typeList) ||
parser.parseRParen())
return failure();
// Type list must be "(index, i1)".
if (typeList.size() != 2 || !typeList[0].isa<mlir::IndexType>() ||
!typeList[1].isSignlessInteger(1))
return failure();
result.addTypes(typeList);
prependCount = true;
} else {
result.addTypes(i1Type);
}
if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
@ -761,7 +869,11 @@ static mlir::ParseResult parseIterWhileOp(mlir::OpAsmParser &parser,
llvm::SmallVector<mlir::Type, 4> argTypes;
// Induction variable (hidden)
argTypes.push_back(indexType);
if (prependCount)
result.addAttribute(IterWhileOp::finalValueAttrName(),
builder.getUnitAttr());
else
argTypes.push_back(indexType);
// Loop carried variables (including iterate)
argTypes.append(result.types.begin(), result.types.end());
// Parse the body region.
@ -780,10 +892,6 @@ static mlir::ParseResult parseIterWhileOp(mlir::OpAsmParser &parser,
}
static mlir::LogicalResult verify(fir::IterWhileOp op) {
if (auto cst = dyn_cast_or_null<ConstantIndexOp>(op.step().getDefiningOp()))
if (cst.getValue() <= 0)
return op.emitOpError("constant step operand must be positive");
// Check that the body defines as single block argument for the induction
// variable.
auto *body = op.getBody();
@ -797,6 +905,19 @@ static mlir::LogicalResult verify(fir::IterWhileOp op) {
"the induction variable");
auto opNumResults = op.getNumResults();
if (op.finalValue()) {
// Result type must be "(index, i1, ...)".
if (!op.getResult(0).getType().isa<mlir::IndexType>())
return op.emitOpError("result #0 expected to be index");
if (!op.getResult(1).getType().isSignlessInteger(1))
return op.emitOpError("result #1 expected to be i1");
opNumResults--;
} else {
// iterate_while always returns the early exit induction value.
// Result type must be "(i1, ...)"
if (!op.getResult(0).getType().isSignlessInteger(1))
return op.emitOpError("result #0 expected to be i1");
}
if (opNumResults == 0)
return mlir::failure();
if (op.getNumIterOperands() != opNumResults)
@ -807,7 +928,8 @@ static mlir::LogicalResult verify(fir::IterWhileOp op) {
"mismatch in number of basic block args and defined values");
auto iterOperands = op.getIterOperands();
auto iterArgs = op.getRegionIterArgs();
auto opResults = op.getResults();
auto opResults =
op.finalValue() ? op.getResults().drop_front() : op.getResults();
unsigned i = 0;
for (auto e : llvm::zip(iterOperands, iterArgs, opResults)) {
if (std::get<0>(e).getType() != std::get<2>(e).getType())
@ -835,9 +957,14 @@ static void print(mlir::OpAsmPrinter &p, fir::IterWhileOp op) {
llvm::interleaveComma(
llvm::zip(regionArgs.drop_front(), operands.drop_front()), p,
[&](auto it) { p << std::get<0>(it) << " = " << std::get<1>(it); });
p << ") -> (" << op.getResultTypes().drop_front() << ')';
auto resTypes = op.finalValue() ? op.getResultTypes()
: op.getResultTypes().drop_front();
p << ") -> (" << resTypes << ')';
} else if (op.finalValue()) {
p << " -> (" << op.getResultTypes() << ')';
}
p.printOptionalAttrDictWithKeyword(op->getAttrs(), {});
p.printOptionalAttrDictWithKeyword(op->getAttrs(),
{IterWhileOp::finalValueAttrName()});
p.printRegion(op.region(), /*printEntryBlockArgs=*/false,
/*printBlockTerminators=*/true);
}
@ -855,6 +982,27 @@ fir::IterWhileOp::moveOutOfLoop(llvm::ArrayRef<mlir::Operation *> ops) {
return success();
}
mlir::BlockArgument fir::IterWhileOp::iterArgToBlockArg(mlir::Value iterArg) {
for (auto i : llvm::enumerate(initArgs()))
if (iterArg == i.value())
return region().front().getArgument(i.index() + 1);
return {};
}
void fir::IterWhileOp::resultToSourceOps(
llvm::SmallVectorImpl<mlir::Value> &results, unsigned resultNum) {
auto oper = finalValue() ? resultNum + 1 : resultNum;
auto *term = region().front().getTerminator();
if (oper < term->getNumOperands())
results.push_back(term->getOperand(oper));
}
mlir::Value fir::IterWhileOp::blockArgToSourceOp(unsigned blockArgNum) {
if (blockArgNum > 0 && blockArgNum <= initArgs().size())
return initArgs()[blockArgNum - 1];
return {};
}
//===----------------------------------------------------------------------===//
// LoadOp
//===----------------------------------------------------------------------===//
@ -880,15 +1028,19 @@ mlir::ParseResult fir::LoadOp::getElementOf(mlir::Type &ele, mlir::Type ref) {
void fir::DoLoopOp::build(mlir::OpBuilder &builder,
mlir::OperationState &result, mlir::Value lb,
mlir::Value ub, mlir::Value step, bool unordered,
mlir::ValueRange iterArgs,
bool finalCountValue, mlir::ValueRange iterArgs,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
result.addOperands({lb, ub, step});
result.addOperands(iterArgs);
if (finalCountValue) {
result.addTypes(builder.getIndexType());
result.addAttribute(finalValueAttrName(), builder.getUnitAttr());
}
for (auto v : iterArgs)
result.addTypes(v.getType());
mlir::Region *bodyRegion = result.addRegion();
bodyRegion->push_back(new Block{});
if (iterArgs.empty())
if (iterArgs.empty() && !finalCountValue)
DoLoopOp::ensureTerminator(*bodyRegion, builder, result.location);
bodyRegion->front().addArgument(builder.getIndexType());
bodyRegion->front().addArguments(iterArgs.getTypes());
@ -922,6 +1074,7 @@ static mlir::ParseResult parseDoLoopOp(mlir::OpAsmParser &parser,
// Parse the optional initial iteration arguments.
llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> regionArgs, operands;
llvm::SmallVector<mlir::Type, 4> argTypes;
auto prependCount = false;
regionArgs.push_back(inductionVariable);
if (succeeded(parser.parseOptionalKeyword("iter_args"))) {
@ -929,18 +1082,30 @@ static mlir::ParseResult parseDoLoopOp(mlir::OpAsmParser &parser,
if (parser.parseAssignmentList(regionArgs, operands) ||
parser.parseArrowTypeList(result.types))
return failure();
if (result.types.size() == operands.size() + 1)
prependCount = true;
// Resolve input operands.
for (auto operand_type : llvm::zip(operands, result.types))
llvm::ArrayRef<mlir::Type> resTypes = result.types;
for (auto operand_type :
llvm::zip(operands, prependCount ? resTypes.drop_front() : resTypes))
if (parser.resolveOperand(std::get<0>(operand_type),
std::get<1>(operand_type), result.operands))
return failure();
} else if (succeeded(parser.parseOptionalArrow())) {
if (parser.parseKeyword("index"))
return failure();
result.types.push_back(indexType);
prependCount = true;
}
if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
return mlir::failure();
// Induction variable.
argTypes.push_back(indexType);
if (prependCount)
result.addAttribute(DoLoopOp::finalValueAttrName(), builder.getUnitAttr());
else
argTypes.push_back(indexType);
// Loop carried variables
argTypes.append(result.types.begin(), result.types.end());
// Parse the body region.
@ -953,7 +1118,7 @@ static mlir::ParseResult parseDoLoopOp(mlir::OpAsmParser &parser,
if (parser.parseRegion(*body, regionArgs, argTypes))
return failure();
fir::DoLoopOp::ensureTerminator(*body, builder, result.location);
DoLoopOp::ensureTerminator(*body, builder, result.location);
return mlir::success();
}
@ -969,10 +1134,6 @@ fir::DoLoopOp fir::getForInductionVarOwner(mlir::Value val) {
// Lifted from loop.loop
static mlir::LogicalResult verify(fir::DoLoopOp op) {
if (auto cst = dyn_cast_or_null<ConstantIndexOp>(op.step().getDefiningOp()))
if (cst.getValue() <= 0)
return op.emitOpError("constant step operand must be positive");
// Check that the body defines as single block argument for the induction
// variable.
auto *body = op.getBody();
@ -984,6 +1145,12 @@ static mlir::LogicalResult verify(fir::DoLoopOp op) {
auto opNumResults = op.getNumResults();
if (opNumResults == 0)
return success();
if (op.finalValue()) {
if (op.unordered())
return op.emitOpError("unordered loop has no final value");
opNumResults--;
}
if (op.getNumIterOperands() != opNumResults)
return op.emitOpError(
"mismatch in number of loop-carried values and defined values");
@ -992,7 +1159,8 @@ static mlir::LogicalResult verify(fir::DoLoopOp op) {
"mismatch in number of basic block args and defined values");
auto iterOperands = op.getIterOperands();
auto iterArgs = op.getRegionIterArgs();
auto opResults = op.getResults();
auto opResults =
op.finalValue() ? op.getResults().drop_front() : op.getResults();
unsigned i = 0;
for (auto e : llvm::zip(iterOperands, iterArgs, opResults)) {
if (std::get<0>(e).getType() != std::get<2>(e).getType())
@ -1022,9 +1190,13 @@ static void print(mlir::OpAsmPrinter &p, fir::DoLoopOp op) {
});
p << ") -> (" << op.getResultTypes() << ')';
printBlockTerminators = true;
} else if (op.finalValue()) {
p << " -> " << op.getResultTypes();
printBlockTerminators = true;
}
p.printOptionalAttrDictWithKeyword(op->getAttrs(),
{fir::DoLoopOp::unorderedAttrName()});
{fir::DoLoopOp::unorderedAttrName(),
fir::DoLoopOp::finalValueAttrName()});
p.printRegion(op.region(), /*printEntryBlockArgs=*/false,
printBlockTerminators);
}
@ -1042,6 +1214,33 @@ fir::DoLoopOp::moveOutOfLoop(llvm::ArrayRef<mlir::Operation *> ops) {
return success();
}
/// Translate a value passed as an iter_arg to the corresponding block
/// argument in the body of the loop.
mlir::BlockArgument fir::DoLoopOp::iterArgToBlockArg(mlir::Value iterArg) {
for (auto i : llvm::enumerate(initArgs()))
if (iterArg == i.value())
return region().front().getArgument(i.index() + 1);
return {};
}
/// Translate the result vector (by index number) to the corresponding value
/// to the `fir.result` Op.
void fir::DoLoopOp::resultToSourceOps(
llvm::SmallVectorImpl<mlir::Value> &results, unsigned resultNum) {
auto oper = finalValue() ? resultNum + 1 : resultNum;
auto *term = region().front().getTerminator();
if (oper < term->getNumOperands())
results.push_back(term->getOperand(oper));
}
/// Translate the block argument (by index number) to the corresponding value
/// passed as an iter_arg to the parent DoLoopOp.
mlir::Value fir::DoLoopOp::blockArgToSourceOp(unsigned blockArgNum) {
if (blockArgNum > 0 && blockArgNum <= initArgs().size())
return initArgs()[blockArgNum - 1];
return {};
}
//===----------------------------------------------------------------------===//
// MulfOp
//===----------------------------------------------------------------------===//
@ -1051,6 +1250,89 @@ mlir::OpFoldResult fir::MulfOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
opnds, [](APFloat a, APFloat b) { return a * b; });
}
//===----------------------------------------------------------------------===//
// ReboxOp
//===----------------------------------------------------------------------===//
/// Get the scalar type related to a fir.box type.
/// Example: return f32 for !fir.box<!fir.heap<!fir.array<?x?xf32>>.
static mlir::Type getBoxScalarEleTy(mlir::Type boxTy) {
auto eleTy = fir::dyn_cast_ptrOrBoxEleTy(boxTy);
if (auto seqTy = eleTy.dyn_cast<fir::SequenceType>())
return seqTy.getEleTy();
return eleTy;
}
/// Get the rank from a !fir.box type
static unsigned getBoxRank(mlir::Type boxTy) {
auto eleTy = fir::dyn_cast_ptrOrBoxEleTy(boxTy);
if (auto seqTy = eleTy.dyn_cast<fir::SequenceType>())
return seqTy.getDimension();
return 0;
}
static mlir::LogicalResult verify(fir::ReboxOp op) {
auto inputBoxTy = op.box().getType();
if (fir::isa_unknown_size_box(inputBoxTy))
return op.emitOpError("box operand must not have unknown rank or type");
auto outBoxTy = op.getType();
if (fir::isa_unknown_size_box(outBoxTy))
return op.emitOpError("result type must not have unknown rank or type");
auto inputRank = getBoxRank(inputBoxTy);
auto inputEleTy = getBoxScalarEleTy(inputBoxTy);
auto outRank = getBoxRank(outBoxTy);
auto outEleTy = getBoxScalarEleTy(outBoxTy);
if (auto slice = op.slice()) {
// Slicing case
if (slice.getType().cast<fir::SliceType>().getRank() != inputRank)
return op.emitOpError("slice operand rank must match box operand rank");
if (auto shape = op.shape()) {
if (auto shiftTy = shape.getType().dyn_cast<fir::ShiftType>()) {
if (shiftTy.getRank() != inputRank)
return op.emitOpError("shape operand and input box ranks must match "
"when there is a slice");
} else {
return op.emitOpError("shape operand must absent or be a fir.shift "
"when there is a slice");
}
}
if (auto sliceOp = slice.getDefiningOp()) {
auto slicedRank = mlir::cast<fir::SliceOp>(sliceOp).getOutRank();
if (slicedRank != outRank)
return op.emitOpError("result type rank and rank after applying slice "
"operand must match");
}
} else {
// Reshaping case
unsigned shapeRank = inputRank;
if (auto shape = op.shape()) {
auto ty = shape.getType();
if (auto shapeTy = ty.dyn_cast<fir::ShapeType>()) {
shapeRank = shapeTy.getRank();
} else if (auto shapeShiftTy = ty.dyn_cast<fir::ShapeShiftType>()) {
shapeRank = shapeShiftTy.getRank();
} else {
auto shiftTy = ty.cast<fir::ShiftType>();
shapeRank = shiftTy.getRank();
if (shapeRank != inputRank)
return op.emitOpError("shape operand and input box ranks must match "
"when the shape is a fir.shift");
}
}
if (shapeRank != outRank)
return op.emitOpError("result type and shape operand ranks must match");
}
if (inputEleTy != outEleTy)
// TODO: check that outBoxTy is a parent type of inputBoxTy for derived
// types.
if (!inputEleTy.isa<fir::RecordType>())
return op.emitOpError(
"op input and output element types must match for intrinsic types");
return mlir::success();
}
//===----------------------------------------------------------------------===//
// ResultOp
//===----------------------------------------------------------------------===//
@ -1084,7 +1366,7 @@ static constexpr llvm::StringRef getTargetOffsetAttr() {
template <typename A, typename... AdditionalArgs>
static A getSubOperands(unsigned pos, A allArgs,
mlir::DenseIntElementsAttr ranges,
AdditionalArgs &&...additionalArgs) {
AdditionalArgs &&... additionalArgs) {
unsigned start = 0;
for (unsigned i = 0; i < pos; ++i)
start += (*(ranges.begin() + i)).getZExtValue();
@ -1221,7 +1503,7 @@ static mlir::ParseResult parseSelectCase(mlir::OpAsmParser &parser,
return mlir::failure();
dests.push_back(dest);
destArgs.push_back(destArg);
if (!parser.parseOptionalRSquare())
if (mlir::succeeded(parser.parseOptionalRSquare()))
break;
if (parser.parseComma())
return mlir::failure();
@ -1422,7 +1704,7 @@ static ParseResult parseSelectType(OpAsmParser &parser,
attrs.push_back(attr);
dests.push_back(dest);
destArgs.push_back(destArg);
if (!parser.parseOptionalRSquare())
if (mlir::succeeded(parser.parseOptionalRSquare()))
break;
if (parser.parseComma())
return mlir::failure();
@ -1451,6 +1733,25 @@ unsigned fir::SelectTypeOp::targetOffsetSize() {
getTargetOffsetAttr()));
}
//===----------------------------------------------------------------------===//
// SliceOp
//===----------------------------------------------------------------------===//
/// Return the output rank of a slice op. The output rank must be between 1 and
/// the rank of the array being sliced (inclusive).
unsigned fir::SliceOp::getOutputRank(mlir::ValueRange triples) {
unsigned rank = 0;
if (!triples.empty()) {
for (unsigned i = 1, end = triples.size(); i < end; i += 3) {
auto op = triples[i].getDefiningOp();
if (!mlir::isa_and_nonnull<fir::UndefOp>(op))
++rank;
}
assert(rank > 0);
}
return rank;
}
//===----------------------------------------------------------------------===//
// StoreOp
//===----------------------------------------------------------------------===//
@ -1486,6 +1787,7 @@ mlir::OpFoldResult fir::SubfOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
//===----------------------------------------------------------------------===//
// IfOp
//===----------------------------------------------------------------------===//
void fir::IfOp::build(mlir::OpBuilder &builder, OperationState &result,
mlir::Value cond, bool withElseRegion) {
build(builder, result, llvm::None, cond, withElseRegion);
@ -1500,13 +1802,13 @@ void fir::IfOp::build(mlir::OpBuilder &builder, OperationState &result,
mlir::Region *thenRegion = result.addRegion();
thenRegion->push_back(new mlir::Block());
if (resultTypes.empty())
fir::IfOp::ensureTerminator(*thenRegion, builder, result.location);
IfOp::ensureTerminator(*thenRegion, builder, result.location);
mlir::Region *elseRegion = result.addRegion();
if (withElseRegion) {
elseRegion->push_back(new mlir::Block());
if (resultTypes.empty())
fir::IfOp::ensureTerminator(*elseRegion, builder, result.location);
IfOp::ensureTerminator(*elseRegion, builder, result.location);
}
}
@ -1523,28 +1825,27 @@ static mlir::ParseResult parseIfOp(OpAsmParser &parser,
parser.resolveOperand(cond, i1Type, result.operands))
return mlir::failure();
if (parser.parseRegion(*thenRegion, {}, {}))
if (parser.parseOptionalArrowTypeList(result.types))
return mlir::failure();
fir::IfOp::ensureTerminator(*thenRegion, parser.getBuilder(),
result.location);
if (parser.parseRegion(*thenRegion, {}, {}))
return mlir::failure();
IfOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location);
if (!parser.parseOptionalKeyword("else")) {
if (mlir::succeeded(parser.parseOptionalKeyword("else"))) {
if (parser.parseRegion(*elseRegion, {}, {}))
return mlir::failure();
fir::IfOp::ensureTerminator(*elseRegion, parser.getBuilder(),
result.location);
IfOp::ensureTerminator(*elseRegion, parser.getBuilder(), result.location);
}
// Parse the optional attribute list.
if (parser.parseOptionalAttrDict(result.attributes))
return mlir::failure();
return mlir::success();
}
static LogicalResult verify(fir::IfOp op) {
if (op.getNumResults() != 0 && op.otherRegion().empty())
if (op.getNumResults() != 0 && op.elseRegion().empty())
return op.emitOpError("must have an else block if defining values");
return mlir::success();
@ -1557,11 +1858,11 @@ static void print(mlir::OpAsmPrinter &p, fir::IfOp op) {
p << " -> (" << op.getResultTypes() << ')';
printBlockTerminators = true;
}
p.printRegion(op.whereRegion(), /*printEntryBlockArgs=*/false,
p.printRegion(op.thenRegion(), /*printEntryBlockArgs=*/false,
printBlockTerminators);
// Print the 'else' regions if it exists and has a block.
auto &otherReg = op.otherRegion();
auto &otherReg = op.elseRegion();
if (!otherReg.empty()) {
p << " else";
p.printRegion(otherReg, /*printEntryBlockArgs=*/false,
@ -1570,6 +1871,16 @@ static void print(mlir::OpAsmPrinter &p, fir::IfOp op) {
p.printOptionalAttrDict(op->getAttrs());
}
void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
unsigned resultNum) {
auto *term = thenRegion().front().getTerminator();
if (resultNum < term->getNumOperands())
results.push_back(term->getOperand(resultNum));
term = elseRegion().front().getTerminator();
if (resultNum < term->getNumOperands())
results.push_back(term->getOperand(resultNum));
}
//===----------------------------------------------------------------------===//
mlir::ParseResult fir::isValidCaseAttr(mlir::Attribute attr) {
@ -1639,7 +1950,9 @@ mlir::FuncOp fir::createFuncOp(mlir::Location loc, mlir::ModuleOp module,
return f;
mlir::OpBuilder modBuilder(module.getBodyRegion());
modBuilder.setInsertionPoint(module.getBody()->getTerminator());
return modBuilder.create<mlir::FuncOp>(loc, name, type, attrs);
auto result = modBuilder.create<mlir::FuncOp>(loc, name, type, attrs);
result.setVisibility(mlir::SymbolTable::Visibility::Private);
return result;
}
fir::GlobalOp fir::createGlobalOp(mlir::Location loc, mlir::ModuleOp module,
@ -1648,7 +1961,9 @@ fir::GlobalOp fir::createGlobalOp(mlir::Location loc, mlir::ModuleOp module,
if (auto g = module.lookupSymbol<fir::GlobalOp>(name))
return g;
mlir::OpBuilder modBuilder(module.getBodyRegion());
return modBuilder.create<fir::GlobalOp>(loc, name, type, attrs);
auto result = modBuilder.create<fir::GlobalOp>(loc, name, type, attrs);
result.setVisibility(mlir::SymbolTable::Visibility::Private);
return result;
}
// Tablegen operators

View file

@ -1,5 +1,5 @@
// Test the FIR operations
// Parse operations and check that we can reparse what we print.
// RUN: fir-opt %s | fir-opt | FileCheck %s
// CHECK-LABEL: func private @it1() -> !fir.int<4>
@ -97,10 +97,12 @@ func @instructions() {
%23 = fir.extract_value %22, %21 : (!fir.type<derived{f:f32}>, !fir.field) -> f32
// CHECK: [[VAL_26:%.*]] = constant 1 : i32
// CHECK: [[VAL_27:%.*]] = fir.shape [[VAL_21]] : (i32) -> !fir.shape<1>
// CHECK: [[VAL_28:%.*]] = constant 1.0
// CHECK: [[VAL_29:%.*]] = fir.insert_value [[VAL_24]], [[VAL_28]], [[VAL_23]] : (!fir.type<derived{f:f32}>, f32, !fir.field) -> !fir.type<derived{f:f32}>
// CHECK: [[VAL_30:%.*]] = fir.len_param_index f, !fir.type<derived3{f:f32}>
%c1 = constant 1 : i32
%24 = fir.shape %19 : (i32) -> !fir.shape<1>
%cf1 = constant 1.0 : f32
%25 = fir.insert_value %22, %cf1, %21 : (!fir.type<derived{f:f32}>, f32, !fir.field) -> !fir.type<derived{f:f32}>
%26 = fir.len_param_index f, !fir.type<derived3{f:f32}>
@ -539,6 +541,7 @@ func @arith_real(%a : !fir.real<16>, %b : !fir.real<16>) -> !fir.real<16> {
// CHECK: [[VAL_175:%.*]] = fir.subf [[VAL_174]], [[VAL_170]] : !fir.real<16>
// CHECK: [[VAL_176:%.*]] = fir.mulf [[VAL_173]], [[VAL_175]] : !fir.real<16>
// CHECK: [[VAL_177:%.*]] = fir.divf [[VAL_176]], [[VAL_169]] : !fir.real<16>
// CHECK: [[VAL_178:%.*]] = fir.modf [[VAL_177]], [[VAL_170]] : !fir.real<16>
%c1 = constant 1.0 : f32
%0 = fir.convert %c1 : (f32) -> !fir.real<16>
%1 = fir.negf %a : !fir.real<16>
@ -546,35 +549,36 @@ func @arith_real(%a : !fir.real<16>, %b : !fir.real<16>) -> !fir.real<16> {
%3 = fir.subf %2, %b : !fir.real<16>
%4 = fir.mulf %1, %3 : !fir.real<16>
%5 = fir.divf %4, %a : !fir.real<16>
// CHECK: return [[VAL_177]] : !fir.real<16>
%6 = fir.modf %5, %b : !fir.real<16>
// CHECK: return [[VAL_178]] : !fir.real<16>
// CHECK: }
return %5 : !fir.real<16>
return %6 : !fir.real<16>
}
// CHECK-LABEL: func @arith_complex(
// CHECK-SAME: [[VAL_178:%.*]]: !fir.complex<16>, [[VAL_179:%.*]]: !fir.complex<16>) -> !fir.complex<16> {
// CHECK-SAME: [[VAL_179:%.*]]: !fir.complex<16>, [[VAL_180:%.*]]: !fir.complex<16>) -> !fir.complex<16> {
func @arith_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) -> !fir.complex<16> {
// CHECK: [[VAL_180:%.*]] = fir.negc [[VAL_178]] : !fir.complex<16>
// CHECK: [[VAL_181:%.*]] = fir.addc [[VAL_179]], [[VAL_180]] : !fir.complex<16>
// CHECK: [[VAL_182:%.*]] = fir.subc [[VAL_181]], [[VAL_179]] : !fir.complex<16>
// CHECK: [[VAL_183:%.*]] = fir.mulc [[VAL_180]], [[VAL_182]] : !fir.complex<16>
// CHECK: [[VAL_184:%.*]] = fir.divc [[VAL_183]], [[VAL_178]] : !fir.complex<16>
// CHECK: [[VAL_181:%.*]] = fir.negc [[VAL_179]] : !fir.complex<16>
// CHECK: [[VAL_182:%.*]] = fir.addc [[VAL_180]], [[VAL_181]] : !fir.complex<16>
// CHECK: [[VAL_183:%.*]] = fir.subc [[VAL_182]], [[VAL_180]] : !fir.complex<16>
// CHECK: [[VAL_184:%.*]] = fir.mulc [[VAL_181]], [[VAL_183]] : !fir.complex<16>
// CHECK: [[VAL_185:%.*]] = fir.divc [[VAL_184]], [[VAL_179]] : !fir.complex<16>
%1 = fir.negc %a : !fir.complex<16>
%2 = fir.addc %b, %1 : !fir.complex<16>
%3 = fir.subc %2, %b : !fir.complex<16>
%4 = fir.mulc %1, %3 : !fir.complex<16>
%5 = fir.divc %4, %a : !fir.complex<16>
// CHECK: return [[VAL_184]] : !fir.complex<16>
// CHECK: return [[VAL_185]] : !fir.complex<16>
// CHECK: }
return %5 : !fir.complex<16>
}
// CHECK-LABEL: func @character_literal() -> !fir.array<13x!fir.char<1>> {
func @character_literal() -> !fir.array<13 x !fir.char<1>> {
// CHECK: [[VAL_185:%.*]] = fir.string_lit "Hello, World!"(13) : !fir.char<1>
%0 = fir.string_lit "Hello, World!"(13) : !fir.char<1>
// CHECK: return [[VAL_185]] : !fir.array<13x!fir.char<1>>
return %0 : !fir.array<13 x !fir.char<1>>
// CHECK-LABEL: func @character_literal() -> !fir.char<1,13> {
func @character_literal() -> !fir.char<1,13> {
// CHECK: [[VAL_186:%.*]] = fir.string_lit "Hello, World!"(13) : !fir.char<1,13>
%0 = fir.string_lit "Hello, World!"(13) : !fir.char<1,13>
// CHECK: return [[VAL_186]] : !fir.char<1,13>
return %0 : !fir.char<1,13>
// CHECK: }
}
@ -582,14 +586,14 @@ func @character_literal() -> !fir.array<13 x !fir.char<1>> {
func private @earlyexit2(%a : i32) -> i1
// CHECK-LABEL: func @early_exit(
// CHECK-SAME: [[VAL_186:%.*]]: i1, [[VAL_187:%.*]]: i32) -> i1 {
// CHECK-SAME: [[VAL_187:%.*]]: i1, [[VAL_188:%.*]]: i32) -> i1 {
func @early_exit(%ok : i1, %k : i32) -> i1 {
// CHECK: [[VAL_188:%.*]] = constant 1 : index
// CHECK: [[VAL_189:%.*]] = constant 100 : index
// CHECK: [[VAL_189:%.*]] = constant 1 : index
// CHECK: [[VAL_190:%.*]] = constant 100 : index
%c1 = constant 1 : index
%c100 = constant 100 : index
// CHECK: [[VAL_190:%.*]], [[VAL_191:%.*]] = fir.iterate_while ([[VAL_192:%.*]] = [[VAL_188]] to [[VAL_189]] step [[VAL_188]]) and ([[VAL_193:%.*]] = [[VAL_186]]) iter_args([[VAL_194:%.*]] = [[VAL_187]]) -> (i32) {
// CHECK: %[[VAL_191:.*]]:2 = fir.iterate_while ([[VAL_192:%.*]] = [[VAL_189]] to [[VAL_190]] step [[VAL_189]]) and ([[VAL_193:%.*]] = [[VAL_187]]) iter_args([[VAL_194:%.*]] = [[VAL_188]]) -> (i32) {
// CHECK: [[VAL_195:%.*]] = call @earlyexit2([[VAL_194]]) : (i32) -> i1
// CHECK: fir.result [[VAL_195]], [[VAL_194]] : i1, i32
// CHECK: }
@ -597,11 +601,56 @@ func @early_exit(%ok : i1, %k : i32) -> i1 {
%stop = call @earlyexit2(%v) : (i32) -> i1
fir.result %stop, %v : i1, i32
}
// CHECK: return [[VAL_190]] : i1
// CHECK: return %[[VAL_191]]#0 : i1
// CHECK: }
return %newOk#0 : i1
}
// CHECK-LABEL: @array_access
func @array_access(%arr : !fir.ref<!fir.array<?x?xf32>>) {
// CHECK-DAG: %[[c1:.*]] = constant 100
// CHECK-DAG: %[[c2:.*]] = constant 50
%c100 = constant 100 : index
%c50 = constant 50 : index
// CHECK: %[[sh:.*]] = fir.shape %[[c1]], %[[c2]] : {{.*}} -> !fir.shape<2>
%shape = fir.shape %c100, %c50 : (index, index) -> !fir.shape<2>
%c47 = constant 47 : index
%c78 = constant 78 : index
%c3 = constant 3 : index
%c18 = constant 18 : index
%c36 = constant 36 : index
%c4 = constant 4 : index
// CHECK: %[[sl:.*]] = fir.slice {{.*}} -> !fir.slice<2>
%slice = fir.slice %c47, %c78, %c3, %c18, %c36, %c4 : (index,index,index,index,index,index) -> !fir.slice<2>
%c0 = constant 0 : index
%c99 = constant 99 : index
%c1 = constant 1 : index
fir.do_loop %i = %c0 to %c99 step %c1 {
%c49 = constant 49 : index
fir.do_loop %j = %c0 to %c49 step %c1 {
// CHECK: fir.array_coor %{{.*}}(%[[sh]]) [%[[sl]]] %{{.*}}, %{{.*}} :
%p = fir.array_coor %arr(%shape)[%slice] %i, %j : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>, !fir.slice<2>, index, index) -> !fir.ref<f32>
%x = constant 42.0 : f32
fir.store %x to %p : !fir.ref<f32>
}
}
return
}
// CHECK-LABEL: @test_is_present
func @test_is_present(%arg0: !fir.box<!fir.array<?xf32>>) -> i1 {
// CHECK: fir.is_present %{{.*}} : (!fir.box<!fir.array<?xf32>>) -> i1
%0 = fir.is_present %arg0 : (!fir.box<!fir.array<?xf32>>) -> i1
return %0 : i1
}
// CHECK-LABEL: @test_absent
func @test_absent() -> i1 {
// CHECK: fir.absent !fir.box<!fir.array<?xf32>>
%0 = fir.absent !fir.box<!fir.array<?xf32>>
%1 = fir.call @_QPfoo(%0) : (!fir.box<!fir.array<?xf32>>) -> i1
return %1 : i1
}
// CHECK-LABEL: @test_misc_ops(
// CHECK-SAME: [[ARR1:%.*]]: !fir.ref<!fir.array<?x?xf32>>, [[INDXM:%.*]]: index, [[INDXN:%.*]]: index, [[INDXO:%.*]]: index, [[INDXP:%.*]]: index)
func @test_misc_ops(%arr1 : !fir.ref<!fir.array<?x?xf32>>, %m : index, %n : index, %o : index, %p : index) {
@ -617,7 +666,11 @@ func @test_misc_ops(%arr1 : !fir.ref<!fir.array<?x?xf32>>, %m : index, %n : inde
%c1_i32 = constant 9 : i32
// CHECK: [[ARR2:%.*]] = fir.zero_bits !fir.array<10xi32>
// CHECK: [[ARR3:%.*]] = fir.insert_on_range [[ARR2]], [[C1_I32]], [[C2]], [[C9]] : (!fir.array<10xi32>, i32, index, index) -> !fir.array<10xi32>
// CHECK: fir.call @noret1([[ARR3]]) : (!fir.array<10xi32>) -> ()
%arr2 = fir.zero_bits !fir.array<10xi32>
%arr3 = fir.insert_on_range %arr2, %c1_i32, %c2, %c9 : (!fir.array<10xi32>, i32, index, index) -> !fir.array<10xi32>
fir.call @noret1(%arr3) : (!fir.array<10xi32>) -> ()
// CHECK: [[SHAPE:%.*]] = fir.shape_shift [[INDXM:%.*]], [[INDXN:%.*]], [[INDXO:%.*]], [[INDXP:%.*]] : (index, index, index, index) -> !fir.shapeshift<2>
// CHECK: [[AV1:%.*]] = fir.array_load [[ARR1]]([[SHAPE]]) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<2>) -> !fir.array<?x?xf32>
@ -632,3 +685,34 @@ func @test_misc_ops(%arr1 : !fir.ref<!fir.array<?x?xf32>>, %m : index, %n : inde
return
}
// CHECK-LABEL: @test_shift
func @test_shift(%arg0: !fir.box<!fir.array<?xf32>>) -> !fir.ref<f32> {
%c4 = constant 4 : index
%c100 = constant 100 : index
// CHECK: fir.shift %{{.*}} : (index) -> !fir.shift<1>
%0 = fir.shift %c4 : (index) -> !fir.shift<1>
%1 = fir.array_coor %arg0(%0) %c100 : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>, index) -> !fir.ref<f32>
return %1 : !fir.ref<f32>
}
func private @bar_rebox_test(!fir.box<!fir.array<?x?xf32>>)
// CHECK-LABEL: @test_rebox(
func @test_rebox(%arg0: !fir.box<!fir.array<?xf32>>) {
%c0 = constant 0 : index
%c1 = constant 1 : index
%c2 = constant 2 : index
%c3 = constant 3 : index
%c4 = constant 4 : index
%c10 = constant 10 : index
%c33 = constant 33 : index
%0 = fir.slice %c10, %c33, %c2 : (index, index, index) -> !fir.slice<1>
%1 = fir.shift %c0 : (index) -> !fir.shift<1>
// CHECK: fir.rebox %{{.*}}(%{{.*}}) [%{{.*}}] : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>, !fir.slice<1>) -> !fir.box<!fir.array<?xf32>>
%2 = fir.rebox %arg0(%1) [%0] : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>, !fir.slice<1>) -> !fir.box<!fir.array<?xf32>>
%3 = fir.shape %c3, %c4 : (index, index) -> !fir.shape<2>
// CHECK: fir.rebox %{{.*}}(%{{.*}}) : (!fir.box<!fir.array<?xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?xf32>>
%4 = fir.rebox %2(%3) : (!fir.box<!fir.array<?xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?xf32>>
fir.call @bar_rebox_test(%4) : (!fir.box<!fir.array<?x?xf32>>) -> ()
return
}