Index: flang/include/flang/Optimizer/Dialect/FIROps.td =================================================================== --- flang/include/flang/Optimizer/Dialect/FIROps.td +++ flang/include/flang/Optimizer/Dialect/FIROps.td @@ -170,6 +170,7 @@ mlir::Type getAllocatedType(); bool hasLenParams() { return bool{(*this)->getAttr(lenpName())}; } + bool hasShapeOperands() { return numShapeOperands() > 0; } unsigned numLenParams() { if (auto val = (*this)->getAttrOfType(lenpName())) @@ -985,9 +986,11 @@ let assemblyFormat = "$resval attr-dict `:` type($resval)"; } +//===------------------------------------------------------------------------===// // Operations on !fir.box 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,99 @@ ```mlir %c1 = constant 1 : index %c10 = constant 10 : index - %4 = fir.dims(%c1, %c10, %c1) : (index, index, index) -> !fir.dims<1> %5 = ... : !fir.ref> - %6 = fir.embox %5, %4 : (!fir.ref>, !fir.dims<1>) -> !fir.box> + %6 = fir.embox %5 : (!fir.ref>) -> !fir.box> ``` The descriptor tuple may contain additional implementation-specific information through the use of additional attributes. }]; - let arguments = (ins AnyReferenceLike:$memref, Variadic:$args); + let arguments = (ins + AnyReferenceLike:$memref, + Optional:$shape, + Optional:$slice, + Variadic:$lenParams, + OptionalAttr:$accessMap + ); let results = (outs fir_BoxType); - let parser = "return parseEmboxOp(parser, result);"; + let builders = [ + OpBuilderDAG<(ins "llvm::ArrayRef":$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(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()) { - 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(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.shift<1>, !fir.slice<1>) -> !fir.box> + %3 = fir.shape %c3, %c4 : (index, index) -> !fir.shape<2> + %4 = fir.rebox %2(%3) : (!fir.box>, !fir.shape<2>) -> !fir.box> + ``` + + }]; + + let arguments = (ins + fir_BoxType:$box, + Optional:$shape, + Optional:$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 +1793,28 @@ array `%h`. }]; - let arguments = (ins AnyRefOrBox:$ref, Variadic:$coor); + let arguments = (ins + AnyRefOrBox:$ref, + Variadic:$coor, + TypeAttr:$baseType + ); let results = (outs fir_ReferenceType); - let parser = "return parseCoordinateOp(parser, result);"; - - let printer = [{ - p << getOperationName() << ' ' << (*this)->getOperands(); - p.printOptionalAttrDict(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()) { - 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(co.getDefiningOp())) { - if (getNumOperands() != 2) - return emitOpError("len_param_index must be last argument"); - if (!ref().getType().dyn_cast()) - return emitOpError("len_param_index must be used on box type"); - } - if (auto attr = (*this)->getAttr(CoordinateOp::baseType())) { - if (!attr.isa()) - return emitOpError("improperly constructed"); - } else { - return emitOpError("must have base type"); - } - return mlir::success(); - }]; + let parser = [{ return parseCoordinateCustom(parser, result); }]; + let printer = [{ ::print(p, *this); }]; + let verifier = [{ return ::verify(*this); }]; - let skipDefaultBuilders = 1; let builders = [ - OpBuilderDAG<(ins "Type":$type, "Value":$ref, "ValueRange":$coor, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "Type":$type, "ValueRange":$operands, - CArg<"ArrayRef", "{}">:$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 +1825,8 @@ 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 +1888,8 @@ 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 +2137,11 @@ 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 +2162,26 @@ 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:$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 +2227,13 @@ << ", " << (*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 +2261,7 @@ }]; let arguments = (ins Variadic:$results); - let builders = [ - OpBuilderDAG<(ins), - [{/* do nothing */}]> - ]; + let builders = [OpBuilderDAG<(ins), [{ /* do nothing */ }]>]; let assemblyFormat = "($results^ `:` type($results))? attr-dict"; @@ -2281,7 +2292,7 @@ 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, index) -> !fir.ref + %p = fir.coordinate_of %A, %i : (!fir.ref>, index) -> !fir.ref fir.store %v to %p : !fir.ref } ``` @@ -2295,23 +2306,26 @@ Index:$upperBound, Index:$step, Variadic:$initArgs, - OptionalAttr:$unordered - ); - let results = (outs - Variadic:$results + OptionalAttr:$unordered, + OptionalAttr:$finalValue ); + let results = (outs Variadic:$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", "{}">:$attributes)> + CArg<"bool", "false">:$finalCountValue, + CArg<"mlir::ValueRange", "llvm::None">:$iterArgs, + CArg<"llvm::ArrayRef", "{}">:$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 +2364,11 @@ (*this)->setAttr(unorderedAttrName(), mlir::UnitAttr::get(getContext())); } + + mlir::BlockArgument iterArgToBlockArg(mlir::Value iterArg); + void resultToSourceOps(llvm::SmallVectorImpl &results, + unsigned resultNum); + mlir::Value blockArgToSourceOp(unsigned blockArgNum); }]; } @@ -2374,28 +2393,31 @@ let results = (outs Variadic:$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 &results, + unsigned resultNum); }]; } @@ -2403,12 +2425,30 @@ [DeclareOpInterfaceMethods]> { 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 +2456,25 @@ Index:$upperBound, Index:$step, I1:$iterateIn, - Variadic:$initArgs - ); - let results = (outs - I1:$iterateResult, - Variadic:$results + Variadic:$initArgs, + OptionalAttr:$finalValue ); + let results = (outs Variadic:$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", "{}">:$attributes)> + CArg<"bool", "false">:$finalCountValue, + CArg<"mlir::ValueRange", "llvm::None">:$iterArgs, + CArg<"llvm::ArrayRef", "{}">:$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 +2506,11 @@ unsigned getNumIterOperands() { return (*this)->getNumOperands() - getNumControlOperands(); } + + mlir::BlockArgument iterArgToBlockArg(mlir::Value iterArg); + void resultToSourceOps(llvm::SmallVectorImpl &results, + unsigned resultNum); + mlir::Value blockArgToSourceOp(unsigned blockArgNum); }]; } @@ -2471,8 +2518,7 @@ // 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 +2537,63 @@ OptionalAttr:$callee, Variadic:$args ); - let results = (outs Variadic); 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":$results, + CArg<"mlir::ValueRange", "{}">:$operands), + [{ + $_state.addOperands(operands); + $_state.addAttribute(calleeAttrName(), callee); + $_state.addTypes(results); + }]>, + OpBuilderDAG<(ins "llvm::StringRef":$callee, + "llvm::ArrayRef":$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(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(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 +2621,11 @@ 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 +2636,19 @@ 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(getAttrs(), {"fn_type", "method"}); - auto resTy{getResultTypes()}; - llvm::SmallVector argTy(getOperandTypes()); - p << " : " << mlir::FunctionType::get(getContext(), argTy, resTy); + p << ") : "; + p.printFunctionalType((*this)->getOperandTypes(), + (*this)->getResultTypes()); }]; let extraClassDeclaration = [{ @@ -2568,9 +2656,13 @@ 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 +2683,13 @@ ``` }]; - 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()) @@ -2611,12 +2704,15 @@ if (parser.parseLParen() || parser.parseAttribute(sz, size(), result.attributes) || parser.parseRParen() || + parser.getCurrentLocation(&trailingTypeLoc) || parser.parseColonType(type)) return mlir::failure(); - if (!(type.isa() || type.isa())) - return parser.emitError(parser.getCurrentLocation(), + auto charTy = type.dyn_cast(); + 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 +2721,17 @@ let printer = [{ p << getOperationName() << ' ' << getValue() << '('; p << getSize().cast().getValue() << ") : "; - p.printType(getType().cast().getEleTy()); + p.printType(getType()); }]; let verifier = [{ if (getSize().cast().getValue().isNegative()) return emitOpError("size must be non-negative"); - auto eleTy = getType().cast().getEleTy(); - if (!eleTy.isa()) - return emitOpError("must have !fir.char type"); if (auto xl = (*this)->getAttr(xlist())) { auto xList = xl.cast(); for (auto a : xList) if (!a.isa()) - return emitOpError("values in list must be integers"); + return emitOpError("values in list must be integers"); } return mlir::success(); }]; @@ -2686,32 +2779,6 @@ let printer = [{ return printUnaryOp(this->getOperation(), p); }]; } -def FirRealAttr : Attr()">, "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()) - return emitOpError("must be a !fir.real type"); - return mlir::success(); - }]; -} - class RealUnaryArithmeticOp traits = []> : fir_UnaryArithmeticOp, Arguments<(ins AnyRealLike:$operand)>; @@ -2748,9 +2815,8 @@ 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 +2923,8 @@ 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 +2947,8 @@ 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 @@ -2891,7 +2957,7 @@ 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 +3009,7 @@ static bool isFloatCompatible(mlir::Type ty); static bool isPointerCompatible(mlir::Type ty); }]; + let hasCanonicalizer = 1; } def FortranTypeAttr : Attr()">, @@ -2989,9 +3056,7 @@ p.printOptionalAttrDict(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 +3160,24 @@ let skipDefaultBuilders = 1; let builders = [ - OpBuilderDAG<(ins "StringRef":$name, "Type":$type, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "StringRef":$name, "Type":$type, - CArg<"StringAttr", "{}">:$linkage, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type, - CArg<"StringAttr", "{}">:$linkage, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "StringRef":$name, "Type":$type, "Attribute":$initVal, - CArg<"StringAttr", "{}">:$linkage, - CArg<"ArrayRef", "{}">:$attrs)>, - OpBuilderDAG<(ins "StringRef":$name, "bool":$isConstant, "Type":$type, - "Attribute":$initVal, CArg<"StringAttr", "{}">:$linkage, - CArg<"ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant, + "mlir::Type":$type, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type, + CArg<"mlir::StringAttr", "{}">:$linkage, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant, + "mlir::Type":$type, CArg<"mlir::StringAttr", "{}">:$linkage, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type, + "mlir::Attribute":$initVal, CArg<"mlir::StringAttr", "{}">:$linkage, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, + OpBuilderDAG<(ins "llvm::StringRef":$name, "bool":$isConstant, + "mlir::Type":$type, "mlir::Attribute":$initVal, + CArg<"mlir::StringAttr", "{}">:$linkage, + CArg<"llvm::ArrayRef", "{}">:$attrs)>, ]; let extraClassDeclaration = [{ @@ -3271,8 +3338,8 @@ let skipDefaultBuilders = 1; let builders = [ - OpBuilderDAG<(ins "StringRef":$name, "Type":$type, - CArg<"ArrayRef", "{}">:$attrs), + OpBuilderDAG<(ins "llvm::StringRef":$name, "mlir::Type":$type, + CArg<"llvm::ArrayRef", "{}">:$attrs), [{ $_state.addAttribute(mlir::SymbolTable::getSymbolAttrName(), $_builder.getStringAttr(name)); @@ -3341,4 +3408,42 @@ }]; } +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.call @_QPfoo(%1) : (fir.box>) -> () + ``` + }]; + + 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>) { + %0 = fir.is_present %arg0 : (!fir.box>) -> i1 + ... + ``` + }]; + + let arguments = (ins AnyRefOrBoxLike:$val); + + let results = (outs BoolLike); +} + #endif Index: flang/lib/Lower/CharacterExpr.cpp =================================================================== --- flang/lib/Lower/CharacterExpr.cpp +++ flang/lib/Lower/CharacterExpr.cpp @@ -341,36 +341,7 @@ 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(loc, len, one); - - auto iterWhile = builder.create( - 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(loc, mlir::CmpIPredicate::eq, blank, c); - llvm::SmallVector results = {isBlank, index}; - builder.create(loc, results); - builder.restoreInsertionPoint(insPt); - // Compute length after iteration (zero if all blanks) - mlir::Value newLen = - builder.create(loc, iterWhile.getResult(1), one); - auto result = - builder.create(loc, iterWhile.getResult(0), zero, newLen); - return builder.createConvert(loc, getLengthType(), result); + return {}; } mlir::Value Fortran::lower::CharacterExprHelper::createTemp(mlir::Type type, Index: flang/lib/Lower/IO.cpp =================================================================== --- flang/lib/Lower/IO.cpp +++ flang/lib/Lower/IO.cpp @@ -182,7 +182,7 @@ : builder.create(loc, ok, /*withOtherwise=*/false); if (!insertPt.isSet()) insertPt = builder.saveInsertionPoint(); - builder.setInsertionPointToStart(&whereOp.whereRegion().front()); + builder.setInsertionPointToStart(&whereOp.thenRegion().front()); } template @@ -414,10 +414,10 @@ for (auto *op = builder.getBlock()->getParentOp(); isa(op); op = op->getBlock()->getParentOp()) { auto whereOp = dyn_cast(op); - auto *lastOp = &whereOp.whereRegion().front().back(); + auto *lastOp = &whereOp.thenRegion().front().back(); builder.setInsertionPointAfter(lastOp); builder.create(loc, lastOp->getResult(0)); // runtime result - builder.setInsertionPointToStart(&whereOp.otherRegion().front()); + builder.setInsertionPointToStart(&whereOp.elseRegion().front()); builder.create(loc, falseValue); // known false result } builder.restoreInsertionPoint(insertPt); Index: flang/lib/Optimizer/Dialect/FIROps.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIROps.cpp +++ flang/lib/Optimizer/Dialect/FIROps.cpp @@ -19,6 +19,7 @@ #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/Matchers.h" +#include "mlir/IR/PatternMatch.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/TypeSwitch.h" @@ -250,6 +251,11 @@ // 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(); @@ -295,12 +301,9 @@ } else { auto funcArgs = llvm::ArrayRef(operands).drop_front(); - llvm::SmallVector 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()); @@ -407,6 +410,10 @@ // ConvertOp //===----------------------------------------------------------------------===// +void fir::ConvertOp::getCanonicalizationPatterns( + OwningRewritePatternList &results, MLIRContext *context) { +} + mlir::OpFoldResult fir::ConvertOp::fold(llvm::ArrayRef opnds) { if (value().getType() == getType()) return value(); @@ -429,8 +436,7 @@ bool fir::ConvertOp::isIntegerCompatible(mlir::Type ty) { return ty.isa() || ty.isa() || - ty.isa() || ty.isa() || - ty.isa(); + ty.isa() || ty.isa(); } bool fir::ConvertOp::isFloatCompatible(mlir::Type ty) { @@ -440,67 +446,68 @@ bool fir::ConvertOp::isPointerCompatible(mlir::Type ty) { return ty.isa() || ty.isa() || ty.isa() || ty.isa() || - ty.isa(); + ty.isa() || ty.isa(); } //===----------------------------------------------------------------------===// // CoordinateOp //===----------------------------------------------------------------------===// -static mlir::ParseResult parseCoordinateOp(mlir::OpAsmParser &parser, - mlir::OperationState &result) { - llvm::ArrayRef allOperandTypes; - llvm::ArrayRef allResultTypes; - llvm::SMLoc allOperandLoc = parser.getCurrentLocation(); - llvm::SmallVector 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 coorOperands; + if (parser.parseOperandList(coorOperands)) + return mlir::failure(); + llvm::SmallVector allOperands; + allOperands.push_back(memref); + allOperands.append(coorOperands.begin(), coorOperands.end()); mlir::FunctionType funcTy; - if (parser.parseType(funcTy)) - return failure(); - allOperandTypes = funcTy.getInputs(); - allResultTypes = funcTy.getResults(); - result.addTypes(allResultTypes); - if (parser.resolveOperands(allOperands, allOperandTypes, allOperandLoc, + auto loc = parser.getCurrentLocation(); + if (parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(funcTy) || + parser.resolveOperands(allOperands, funcTy.getInputs(), loc, result.operands)) return failure(); - if (funcTy.getNumInputs()) { - // No inputs handled by verify - result.addAttribute(fir::CoordinateOp::baseType(), - mlir::TypeAttr::get(funcTy.getInput(0))); - } - return success(); -} - -mlir::Type fir::CoordinateOp::getBaseType() { - return (*this) - ->getAttr(CoordinateOp::baseType()) - .cast() - .getValue(); -} - -void fir::CoordinateOp::build(OpBuilder &, OperationState &result, - mlir::Type resType, ValueRange operands, - ArrayRef attrs) { - assert(operands.size() >= 1u && "mismatched number of parameters"); - result.addOperands(operands); - result.addAttribute(fir::CoordinateOp::baseType(), - mlir::TypeAttr::get(operands[0].getType())); - result.attributes.append(attrs.begin(), attrs.end()); - result.addTypes({resType}); + parser.addTypesToList(funcTy.getResults(), result.types); + result.addAttribute("baseType", mlir::TypeAttr::get(funcTy.getInput(0))); + return mlir::success(); } -void fir::CoordinateOp::build(OpBuilder &builder, OperationState &result, - mlir::Type resType, mlir::Value ref, - ValueRange coor, ArrayRef attrs) { - llvm::SmallVector operands{ref}; - operands.append(coor.begin(), coor.end()); - build(builder, result, resType, operands, attrs); +static mlir::LogicalResult verify(fir::CoordinateOp op) { + auto refTy = op.ref().getType(); + if (fir::isa_ref_type(refTy)) { + auto eleTy = fir::dyn_cast_ptrEleTy(refTy); + if (auto arrTy = eleTy.dyn_cast()) { + if (arrTy.hasUnknownShape()) + return op.emitOpError("cannot find coordinate in unknown shape"); + if (arrTy.getConstantRows() < arrTy.getDimension() - 1) + return op.emitOpError("cannot find coordinate with unknown extents"); + } + if (!(fir::isa_aggregate(eleTy) || fir::isa_complex(eleTy) || + fir::isa_char_string(eleTy))) + return op.emitOpError("cannot apply coordinate_of to this type"); + } + // Recovering a LEN type parameter only makes sense from a boxed value. For a + // bare reference, the LEN type parameters must be passed as additional + // arguments to `op`. + for (auto co : op.coor()) + if (dyn_cast_or_null(co.getDefiningOp())) { + if (op.getNumOperands() != 2) + return op.emitOpError("len_param_index must be last argument"); + if (!op.ref().getType().isa()) + return op.emitOpError("len_param_index must be used on box type"); + } + return mlir::success(); } //===----------------------------------------------------------------------===// @@ -508,8 +515,8 @@ //===----------------------------------------------------------------------===// mlir::FunctionType fir::DispatchOp::getFunctionType() { - auto attr = (*this)->getAttr("fn_type").cast(); - return attr.getValue().cast(); + return mlir::FunctionType::get(getContext(), getOperandTypes(), + getResultTypes()); } //===----------------------------------------------------------------------===// @@ -526,40 +533,45 @@ // EmboxOp //===----------------------------------------------------------------------===// -static mlir::ParseResult parseEmboxOp(mlir::OpAsmParser &parser, - mlir::OperationState &result) { - mlir::FunctionType type; - llvm::SmallVector 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()) { + 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()) { + 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()) { + 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() || shapeTy.isa())) + 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()) + 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(); } @@ -583,7 +595,7 @@ 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); } @@ -592,7 +604,7 @@ 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())); @@ -602,7 +614,7 @@ if (parser.parseAttribute(attr, fir::GlobalOp::initValAttrName(), result.attributes) || parser.parseRParen()) - return failure(); + return mlir::failure(); simpleInitializer = true; } @@ -614,7 +626,7 @@ mlir::Type globalType; if (parser.parseColonType(globalType)) - return failure(); + return mlir::failure(); result.addAttribute(fir::GlobalOp::typeAttrName(), mlir::TypeAttr::get(globalType)); @@ -623,11 +635,13 @@ 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) { @@ -684,11 +698,74 @@ mlir::ParseResult fir::GlobalOp::verifyValidLinkage(StringRef linkage) { // Supporting only a subset of the LLVM linkage types for now - static const llvm::SmallVector 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(v.getDefiningOp())) { + auto attr = c.getValue(); + if (auto iattr = attr.dyn_cast()) + 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 +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(op); + if (!insval || !insval.getType().isa()) + return mlir::failure(); + auto insval2 = + dyn_cast_or_null(insval.adt().getDefiningOp()); + if (!insval2 || !isa(insval2.adt().getDefiningOp())) + return mlir::failure(); + auto binf = dyn_cast_or_null(insval.val().getDefiningOp()); + auto binf2 = dyn_cast_or_null(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(binf.lhs().getDefiningOp()); + auto ebi = + dyn_cast_or_null(binf.rhs().getDefiningOp()); + auto ear = + dyn_cast_or_null(binf2.lhs().getDefiningOp()); + auto ebr = + dyn_cast_or_null(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(op, ear.adt(), ebr.adt()); + return mlir::success(); + } +}; + +void fir::InsertValueOp::getCanonicalizationPatterns( + mlir::OwningRewritePatternList &results, mlir::MLIRContext *context) { + results.insert, + UndoComplexPattern>(context); +} + //===----------------------------------------------------------------------===// // IterWhileOp //===----------------------------------------------------------------------===// @@ -696,9 +773,14 @@ 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 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) @@ -740,24 +822,50 @@ // Parse the initial iteration arguments. llvm::SmallVector 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 operands; llvm::SmallVector 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 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() && + regionTypes[1].isSignlessInteger(1)); + result.addTypes(regionTypes); + } else { + result.addTypes(i1Type); + result.addTypes(resTypes); + } + } else if (succeeded(parser.parseOptionalArrow())) { + llvm::SmallVector typeList; + if (parser.parseLParen() || parser.parseTypeList(typeList) || + parser.parseRParen()) + return failure(); + // Type list must be "(index, i1)". + if (typeList.size() != 2 || !typeList[0].isa() || + !typeList[1].isSignlessInteger(1)) + return failure(); + result.addTypes(typeList); + prependCount = true; + } else { + result.addTypes(i1Type); } if (parser.parseOptionalAttrDictWithKeyword(result.attributes)) @@ -765,7 +873,11 @@ llvm::SmallVector 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. @@ -784,10 +896,6 @@ } static mlir::LogicalResult verify(fir::IterWhileOp op) { - if (auto cst = dyn_cast_or_null(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(); @@ -801,6 +909,19 @@ "the induction variable"); auto opNumResults = op.getNumResults(); + if (op.finalValue()) { + // Result type must be "(index, i1, ...)". + if (!op.getResult(0).getType().isa()) + 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) @@ -811,7 +932,8 @@ "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()) @@ -839,9 +961,14 @@ 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); } @@ -859,6 +986,27 @@ 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 &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 //===----------------------------------------------------------------------===// @@ -884,15 +1032,19 @@ 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 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()); @@ -926,6 +1078,7 @@ // Parse the optional initial iteration arguments. llvm::SmallVector regionArgs, operands; llvm::SmallVector argTypes; + auto prependCount = false; regionArgs.push_back(inductionVariable); if (succeeded(parser.parseOptionalKeyword("iter_args"))) { @@ -933,18 +1086,30 @@ 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 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. @@ -957,7 +1122,7 @@ if (parser.parseRegion(*body, regionArgs, argTypes)) return failure(); - fir::DoLoopOp::ensureTerminator(*body, builder, result.location); + DoLoopOp::ensureTerminator(*body, builder, result.location); return mlir::success(); } @@ -973,10 +1138,6 @@ // Lifted from loop.loop static mlir::LogicalResult verify(fir::DoLoopOp op) { - if (auto cst = dyn_cast_or_null(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(); @@ -988,6 +1149,12 @@ 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"); @@ -996,7 +1163,8 @@ "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()) @@ -1026,9 +1194,13 @@ }); 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); } @@ -1046,6 +1218,33 @@ 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 &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 //===----------------------------------------------------------------------===// @@ -1055,6 +1254,89 @@ 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>. +static mlir::Type getBoxScalarEleTy(mlir::Type boxTy) { + auto eleTy = fir::dyn_cast_ptrOrBoxEleTy(boxTy); + if (auto seqTy = eleTy.dyn_cast()) + 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()) + 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().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()) { + 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(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()) { + shapeRank = shapeTy.getRank(); + } else if (auto shapeShiftTy = ty.dyn_cast()) { + shapeRank = shapeShiftTy.getRank(); + } else { + auto shiftTy = ty.cast(); + 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()) + return op.emitOpError( + "op input and output element types must match for intrinsic types"); + return mlir::success(); +} + //===----------------------------------------------------------------------===// // ResultOp //===----------------------------------------------------------------------===// @@ -1088,7 +1370,7 @@ template 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(); @@ -1225,7 +1507,7 @@ 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(); @@ -1426,7 +1708,7 @@ 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(); @@ -1455,6 +1737,25 @@ 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(op)) + ++rank; + } + assert(rank > 0); + } + return rank; +} + //===----------------------------------------------------------------------===// // StoreOp //===----------------------------------------------------------------------===// @@ -1490,6 +1791,7 @@ //===----------------------------------------------------------------------===// // IfOp //===----------------------------------------------------------------------===// + void fir::IfOp::build(mlir::OpBuilder &builder, OperationState &result, mlir::Value cond, bool withElseRegion) { build(builder, result, llvm::None, cond, withElseRegion); @@ -1504,13 +1806,13 @@ 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); } } @@ -1527,28 +1829,27 @@ 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(); @@ -1561,11 +1862,11 @@ 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, @@ -1574,6 +1875,16 @@ p.printOptionalAttrDict(op->getAttrs()); } +void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl &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) { @@ -1643,7 +1954,9 @@ return f; mlir::OpBuilder modBuilder(module.getBodyRegion()); modBuilder.setInsertionPoint(module.getBody()->getTerminator()); - return modBuilder.create(loc, name, type, attrs); + auto result = modBuilder.create(loc, name, type, attrs); + result.setVisibility(mlir::SymbolTable::Visibility::Private); + return result; } fir::GlobalOp fir::createGlobalOp(mlir::Location loc, mlir::ModuleOp module, @@ -1652,7 +1965,9 @@ if (auto g = module.lookupSymbol(name)) return g; mlir::OpBuilder modBuilder(module.getBodyRegion()); - return modBuilder.create(loc, name, type, attrs); + auto result = modBuilder.create(loc, name, type, attrs); + result.setVisibility(mlir::SymbolTable::Visibility::Private); + return result; } // Tablegen operators Index: flang/test/Fir/fir-ops.fir =================================================================== --- flang/test/Fir/fir-ops.fir +++ flang/test/Fir/fir-ops.fir @@ -569,12 +569,12 @@ 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 +582,14 @@ 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,7 +597,7 @@ %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 }