[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:
parent
aa097ef8d4
commit
0b785a46b7
|
@ -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 ®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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue