[flang-commits] [flang] 0b785a4 - [flang][fir] Add remaining Ops. Updates to pre-existing Ops.
Eric Schweitz via flang-commits
flang-commits at lists.llvm.org
Fri Feb 26 17:21:47 PST 2021
Author: Eric Schweitz
Date: 2021-02-26T17:21:35-08:00
New Revision: 0b785a46b7eecbfd24394b42814844e02e5fefe6
URL: https://github.com/llvm/llvm-project/commit/0b785a46b7eecbfd24394b42814844e02e5fefe6
DIFF: https://github.com/llvm/llvm-project/commit/0b785a46b7eecbfd24394b42814844e02e5fefe6.diff
LOG: [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
Added:
Modified:
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/lib/Lower/CharacterExpr.cpp
flang/lib/Lower/IO.cpp
flang/lib/Optimizer/Dialect/FIRDialect.cpp
flang/lib/Optimizer/Dialect/FIROps.cpp
flang/test/Fir/fir-ops.fir
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 6500ab2854cb..5b095594158d 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -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 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 parser = [{ return parseCoordinateCustom(parser, result); }];
+ let printer = [{ ::print(p, *this); }];
+ let verifier = [{ return ::verify(*this); }];
- 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 ®ion().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
diff --git a/flang/lib/Lower/CharacterExpr.cpp b/flang/lib/Lower/CharacterExpr.cpp
index eadf93401939..ab720475183c 100644
--- a/flang/lib/Lower/CharacterExpr.cpp
+++ b/flang/lib/Lower/CharacterExpr.cpp
@@ -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,
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 3c23eb24832f..aae12aaf183c 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -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);
diff --git a/flang/lib/Optimizer/Dialect/FIRDialect.cpp b/flang/lib/Optimizer/Dialect/FIRDialect.cpp
index 696b55255938..889b5ef55366 100644
--- a/flang/lib/Optimizer/Dialect/FIRDialect.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRDialect.cpp
@@ -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>();
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 47211e25ec9d..79b1a33369bf 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -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)));
- }
- 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});
+ parser.addTypesToList(funcTy.getResults(), result.types);
+ result.addAttribute("baseType", mlir::TypeAttr::get(funcTy.getInput(0)));
+ return mlir::success();
}
-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);
+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");
+ }
+ // 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
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index cbfe31879030..6b7602513124 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -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
+}
More information about the flang-commits
mailing list