Index: flang/include/flang/Optimizer/Dialect/FIRDialect.h =================================================================== --- flang/include/flang/Optimizer/Dialect/FIRDialect.h +++ flang/include/flang/Optimizer/Dialect/FIRDialect.h @@ -13,20 +13,6 @@ #include "mlir/InitAllDialects.h" #include "mlir/InitAllPasses.h" -namespace llvm { -class raw_ostream; -class StringRef; -} // namespace llvm - -namespace mlir { -class Attribute; -class DialectAsmParser; -class DialectAsmPrinter; -class Location; -class MLIRContext; -class Type; -} // namespace mlir - namespace fir { /// FIR dialect Index: flang/include/flang/Optimizer/Dialect/FIROps.h =================================================================== --- flang/include/flang/Optimizer/Dialect/FIROps.h +++ flang/include/flang/Optimizer/Dialect/FIROps.h @@ -10,6 +10,8 @@ #define OPTIMIZER_DIALECT_FIROPS_H #include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/Interfaces/LoopLikeInterface.h" +#include "mlir/Interfaces/SideEffects.h" using namespace mlir; Index: flang/include/flang/Optimizer/Dialect/FIROps.td =================================================================== --- flang/include/flang/Optimizer/Dialect/FIROps.td +++ flang/include/flang/Optimizer/Dialect/FIROps.td @@ -16,6 +16,7 @@ include "mlir/IR/SymbolInterfaces.td" include "mlir/Interfaces/ControlFlowInterfaces.td" +include "mlir/Interfaces/LoopLikeInterface.td" include "mlir/Interfaces/SideEffects.td" def fir_Dialect : Dialect { @@ -195,8 +196,9 @@ ); } -class fir_AllocatableOp traits =[]> : - fir_AllocatableBaseOp, +class fir_AllocatableOp traits = []> : + fir_AllocatableBaseOp])>, fir_TwoBuilders, Arguments<(ins TypeAttr:$in_type, Variadic:$args)> { @@ -263,18 +265,27 @@ static constexpr llvm::StringRef inType() { return "in_type"; } static constexpr llvm::StringRef lenpName() { return "len_param_count"; } mlir::Type getAllocatedType(); + bool hasLenParams() { return bool{getAttr(lenpName())}; } + unsigned numLenParams() { if (auto val = getAttrOfType(lenpName())) return val.getInt(); return 0; } + operand_range getLenParams() { return {operand_begin(), operand_begin() + numLenParams()}; } + + unsigned numShapeOperands() { + return operand_end() - operand_begin() + numLenParams(); + } + operand_range getShapeOperands() { return {operand_begin() + numLenParams(), operand_end()}; } + static mlir::Type getRefTy(mlir::Type ty); /// Get the input type of the allocation @@ -286,14 +297,16 @@ // Verify checks common to all allocation operations string allocVerify = [{ llvm::SmallVector visited; - if (verifyInType(getInType(), visited)) + if (verifyInType(getInType(), visited, numShapeOperands())) return emitOpError("invalid type for allocation"); if (verifyRecordLenParams(getInType(), numLenParams())) return emitOpError("LEN params do not correspond to type"); }]; } +//===----------------------------------------------------------------------===// // Memory SSA operations +//===----------------------------------------------------------------------===// def fir_AllocaOp : fir_AllocatableOp<"alloca"> { let summary = "allocate storage for a temporary on the stack given a type"; @@ -338,7 +351,7 @@ }]; } -def fir_LoadOp : fir_OneResultOp<"load", []> { +def fir_LoadOp : fir_OneResultOp<"load", [MemoryEffects<[MemRead]>]> { let summary = "load a value from a memory reference"; let description = [{ Load a value from a memory reference into an ssa-value (virtual register). @@ -396,7 +409,7 @@ }]; } -def fir_StoreOp : fir_Op<"store", []> { +def fir_StoreOp : fir_Op<"store", [MemoryEffects<[MemWrite]>]> { let summary = "store an SSA-value to a memory location"; let description = [{ @@ -473,8 +486,7 @@ let assemblyFormat = "type($intype) attr-dict"; let verifier = [{ - if (auto ref = getType().dyn_cast()) - return emitOpError("undefined values of type !fir.ref not allowed"); + // allow `undef : ref` since it is a possible from transformations return mlir::success(); }]; } @@ -508,7 +520,7 @@ }]; } -def fir_FreeMemOp : fir_Op<"freemem", []> { +def fir_FreeMemOp : fir_Op<"freemem", [MemoryEffects<[MemFree]>]> { let summary = "free a heap object"; let description = [{ @@ -530,7 +542,8 @@ let assemblyFormat = "$heapref attr-dict `:` type($heapref)"; } -//===----------------------------------------------------------------------===//// Terminator operations +//===----------------------------------------------------------------------===// +// Terminator operations //===----------------------------------------------------------------------===// class fir_SwitchTerminatorOp traits = []> : @@ -547,29 +560,15 @@ let successors = (successor VariadicSuccessor:$targets); - let builders = [OpBuilder< - "Builder *, OperationState &result, Value selector," - "ValueRange properOperands, ArrayRef destinations," - "ArrayRef operands = {}," - "ArrayRef attributes = {}", - [{ - result.addOperands(selector); - result.addOperands(properOperands); - for (auto kvp : llvm::zip(destinations, operands)) { - result.addSuccessors(std::get<0>(kvp)); - result.addOperands(std::get<1>(kvp)); - } - result.addAttributes(attributes); - }] - >]; - string extraSwitchClassDeclaration = [{ using Conditions = mlir::Value; static constexpr llvm::StringRef getCasesAttr() { return "case_tags"; } // The number of destination conditions that may be tested - unsigned getNumConditions() { return getNumDest(); } + unsigned getNumConditions() { + return getAttrOfType(getCasesAttr()).size(); + } // The selector is the value being tested to determine the destination mlir::Value getSelector() { return selector(); } @@ -596,12 +595,51 @@ else p.printSuccessor(succ); } + + unsigned targetOffsetSize(); }]; } class fir_IntegralSwitchTerminatorOp traits = []> : fir_SwitchTerminatorOp { + let skipDefaultBuilders = 1; + let builders = [OpBuilder< + "Builder *builder, OperationState &result, Value selector," + "ArrayRef compareOperands, ArrayRef destinations," + "ArrayRef destOperands = {}," + "ArrayRef attributes = {}", + [{ + result.addOperands(selector); + llvm::SmallVector ivalues; + for (auto iv : compareOperands) + ivalues.push_back(builder->getI64IntegerAttr(iv)); + ivalues.push_back(builder->getUnitAttr()); + result.addAttribute(getCasesAttr(), builder->getArrayAttr(ivalues)); + const auto count = destinations.size(); + for (auto d : destinations) + result.addSuccessors(d); + const auto opCount = destOperands.size(); + llvm::SmallVector argOffs; + int32_t sumArgs = 0; + for (std::remove_const_t i = 0; i != count; ++i) { + if (i < opCount) { + result.addOperands(destOperands[i]); + const auto argSz = destOperands[i].size(); + argOffs.push_back(argSz); + sumArgs += argSz; + } else { + argOffs.push_back(0); + } + } + result.addAttribute(getOperandSegmentSizeAttr(), + builder->getI32VectorAttr({1, 0, sumArgs})); + result.addAttribute(getTargetOffsetAttr(), + builder->getI32VectorAttr(argOffs)); + result.addAttributes(attributes); + }] + >]; + let parser = [{ mlir::OpAsmParser::OperandType selector; mlir::Type type; @@ -674,14 +712,17 @@ getSelector().getType().isa())) return emitOpError("must be an integer"); auto cases = getAttrOfType(getCasesAttr()).getValue(); - auto count = getNumConditions(); + auto count = getNumDest(); + if (count == 0) + return emitOpError("must have at least one successor"); + if (getNumConditions() != count) + return emitOpError("number of cases and targets don't match"); + if (targetOffsetSize() != count) + return emitOpError("incorrect number of successor operand groups"); for (decltype(count) i = 0; i != count; ++i) { auto &attr = cases[i]; - if (attr.isa() || attr.isa()) { - // ok - } else { + if (!(attr.isa() || attr.isa())) return emitOpError("invalid case alternative"); - } } return mlir::success(); }]; @@ -750,6 +791,17 @@ ``` }]; + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"Builder *builder, OperationState &result, Value selector," + "ArrayRef compareAttrs, ArrayRef cmpOperands," + "ArrayRef destinations, ArrayRef destOperands = {}," + "ArrayRef attributes = {}">, + OpBuilder<"Builder *builder, OperationState &result, Value selector," + "ArrayRef compareAttrs, ArrayRef cmpOpList," + "ArrayRef destinations, ArrayRef destOperands = {}," + "ArrayRef attributes = {}">]; + let parser = "return parseSelectCase(parser, result);"; let printer = [{ @@ -786,23 +838,30 @@ getSelector().getType().isa())) return emitOpError("must be an integer, character, or logical"); auto cases = getAttrOfType(getCasesAttr()).getValue(); - auto count = getNumConditions(); + auto count = getNumDest(); + if (count == 0) + return emitOpError("must have at least one successor"); + if (getNumConditions() != count) + return emitOpError("number of conditions and successors don't match"); + if (compareOffsetSize() != count) + return emitOpError("incorrect number of compare operand groups"); + if (targetOffsetSize() != count) + return emitOpError("incorrect number of successor operand groups"); for (decltype(count) i = 0; i != count; ++i) { auto &attr = cases[i]; - if (attr.isa() || - attr.isa() || - attr.isa() || - attr.isa() || - attr.isa()) { - // ok - } else { + if (!(attr.isa() || + attr.isa() || + attr.isa() || + attr.isa() || + attr.isa())) return emitOpError("incorrect select case attribute type"); - } } return mlir::success(); }]; - let extraClassDeclaration = extraSwitchClassDeclaration; + let extraClassDeclaration = extraSwitchClassDeclaration#[{ + unsigned compareOffsetSize(); + }]; } def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> { @@ -825,6 +884,39 @@ ``` }]; + let skipDefaultBuilders = 1; + let builders = [OpBuilder< + "Builder *builder, OperationState &result, Value selector," + "ArrayRef typeOperands," + "ArrayRef destinations, ArrayRef destOperands = {}," + "ArrayRef attributes = {}", + [{ + result.addOperands(selector); + result.addAttribute(getCasesAttr(), builder->getArrayAttr(typeOperands)); + const auto count = destinations.size(); + for (auto d : destinations) + result.addSuccessors(d); + const auto opCount = destOperands.size(); + llvm::SmallVector argOffs; + int32_t sumArgs = 0; + for (std::remove_const_t i = 0; i != count; ++i) { + if (i < opCount) { + result.addOperands(destOperands[i]); + const auto argSz = destOperands[i].size(); + argOffs.push_back(argSz); + sumArgs += argSz; + } else { + argOffs.push_back(0); + } + } + result.addAttribute(getOperandSegmentSizeAttr(), + builder->getI32VectorAttr({1, 0, sumArgs})); + result.addAttribute(getTargetOffsetAttr(), + builder->getI32VectorAttr(argOffs)); + result.addAttributes(attributes); + }] + >]; + let parser = "return parseSelectType(parser, result);"; let printer = [{ @@ -848,15 +940,18 @@ if (!(getSelector().getType().isa())) return emitOpError("must be a boxed type"); auto cases = getAttrOfType(getCasesAttr()).getValue(); - auto count = getNumConditions(); + auto count = getNumDest(); + if (count == 0) + return emitOpError("must have at least one successor"); + if (getNumConditions() != count) + return emitOpError("number of conditions and successors don't match"); + if (targetOffsetSize() != count) + return emitOpError("incorrect number of successor operand groups"); for (decltype(count) i = 0; i != count; ++i) { auto &attr = cases[i]; - if (attr.isa() || attr.isa() || - attr.isa()) { - // ok - } else { + if (!(attr.isa() || attr.isa() || + attr.isa())) return emitOpError("invalid type-case alternative"); - } } return mlir::success(); }]; @@ -1016,9 +1111,6 @@ } def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> { - let arguments = (ins AnyReferenceLike:$memref, AnyIntegerLike:$len); - let results = (outs fir_BoxCharType); - let summary = "boxes a given CHARACTER reference and its LEN parameter"; let description = [{ @@ -1040,6 +1132,10 @@ This buffer and its LEN value (10) are wrapped into a pair in `%6`. }]; + let arguments = (ins AnyReferenceLike:$memref, AnyIntegerLike:$len); + + let results = (outs fir_BoxCharType); + let assemblyFormat = [{ $memref `,` $len attr-dict `:` functional-type(operands, results) }]; @@ -1053,7 +1149,6 @@ } def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]> { - let summary = "boxes a given procedure and optional host context"; let description = [{ @@ -1232,6 +1327,8 @@ let arguments = (ins fir_BoxType:$val); let results = (outs AnyReferenceLike); + + let hasFolder = 1; } def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]> { @@ -1249,6 +1346,8 @@ let arguments = (ins fir_BoxCharType:$val); let results = (outs AnyIntegerLike); + + let hasFolder = 1; } def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]> { @@ -1445,8 +1544,8 @@ ```mlir %i = ... : index - %h = ... : !fir.heap> - %p = fir.coordinate_of %h, %i : (!fir.heap>, index) -> !fir.ref + %h = ... : !fir.heap> + %p = fir.coordinate_of %h, %i : (!fir.heap>, index) -> !fir.ref ``` In the example, `%p` will be a pointer to the `%i`-th f32 value in the @@ -1457,11 +1556,27 @@ let results = (outs fir_ReferenceType); - let assemblyFormat = [{ - operands attr-dict `:` functional-type(operands, results) - }]; + let parser = "return parseCoordinateOp(parser, result);"; + let printer = [{ + p << getOperationName() << ' ' << getOperation()->getOperands(); + p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{baseType()}); + p << " : "; + p.printFunctionalType(getOperation()->getOperandTypes(), + getOperation()->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())) { @@ -1470,8 +1585,28 @@ if (!ref().getType().dyn_cast()) return emitOpError("len_param_index must be used on box type"); } + if (auto attr = getAttr(CoordinateOp::baseType())) { + if (!attr.isa()) + return emitOpError("improperly constructed"); + } else { + return emitOpError("must have base type"); + } return mlir::success(); }]; + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "Type type, Value ref, ValueRange coor," + "ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "Type type, ValueRange operands," + "ArrayRef attrs = {}">]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef baseType() { return "base_type"; } + mlir::Type getBaseType(); + }]; } def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]> { @@ -1602,7 +1737,7 @@ stride must not be zero. ```mlir - %d = fir.gendims %l, %u, %s : (index, index, index) -> !fir.dims<1> + %d = fir.gendims %lo, %ext, %str : (index, index, index) -> !fir.dims<1> ``` }]; @@ -1713,20 +1848,51 @@ }]; } +//===----------------------------------------------------------------------===// // Fortran loops +//===----------------------------------------------------------------------===// -def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; +def fir_ResultOp : fir_Op<"result", [Terminator]> { + let summary = "special terminator for use in fir region operations"; -def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { + let description = [{ + Result takes a list of ssa-values produced in the block and forwards them + as a result to the operation that owns the region of the block. The + operation can retain the values or return them to its parent block + depending upon its semantics. + }]; + + let arguments = (ins Variadic:$results); + let builders = [ + OpBuilder<"Builder *builder, OperationState &result", "/* do nothing */"> + ]; + let printer = [{ return ::print(p, *this); }]; + let verifier = [{ return ::verify(*this); }]; + let parser = [{ return ::parse$cppClass(parser, result); }]; +} + +def FirRegionTerminator : SingleBlockImplicitTerminator<"ResultOp">; + +class region_Op traits = []> : + fir_Op { + let printer = [{ return ::print(p, *this); }]; + let verifier = [{ return ::verify(*this); }]; + let parser = [{ return ::parse$cppClass(parser, result); }]; +} + +def fir_LoopOp : region_Op<"do_loop", + [DeclareOpInterfaceMethods]> { let summary = "generalized loop operation"; let description = [{ Generalized high-level looping construct. This operation is similar to - MLIR's `loop.for`. An ordered loop will return the final value of `%i`. + MLIR's `loop.for`. ```mlir %l = constant 0 : index %u = constant 9 : index - fir.loop %i = %l to %u unordered { + %s = constant 1 : index + 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 @@ -1741,143 +1907,100 @@ let arguments = (ins Index:$lowerBound, Index:$upperBound, - Variadic:$optStep, - OptionalAttr:$constantStep, + Index:$step, + Variadic:$initArgs, OptionalAttr:$unordered ); - - let results = (outs Variadic:$lastVal); - + let results = (outs + Variadic:$results + ); let regions = (region SizedRegion<1>:$region); let skipDefaultBuilders = 1; let builders = [ OpBuilder<"mlir::Builder *builder, OperationState &result," "mlir::Value lowerBound, mlir::Value upperBound," - "ValueRange step = {}, ArrayRef attributes = {}"> + "mlir::Value step, bool unordered = false," + "ValueRange iterArgs = llvm::None," + "ArrayRef attributes = {}"> ]; - let parser = "return parseLoopOp(parser, result);"; - - let printer = [{ - p << getOperationName() << ' ' << getInductionVar() << " = " - << lowerBound() << " to " << upperBound(); - auto s = optStep(); - if (s.begin() != s.end()) { - p << " step "; - p.printOperand(*s.begin()); - } - if (unordered()) - p << " unordered"; - p.printRegion(region(), /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); - p.printOptionalAttrDict(getAttrs(), {unorderedAttrName(), stepAttrName()}); - }]; - - let verifier = [{ - auto step = optStep(); - if (step.begin() != step.end()) { - // FIXME: size of step must be 1 - auto *s = (*step.begin()).getDefiningOp(); - if (auto cst = dyn_cast_or_null(s)) - if (cst.getValue() == 0) - return emitOpError("constant step operand must be nonzero"); - } - - // Check that the body defines as single block argument for the induction - // variable. - auto *body = getBody(); - if (body->getNumArguments() != 1 || - !body->getArgument(0).getType().isIndex()) - return emitOpError("expected body to have a single index argument for " - "the induction variable"); - if (lastVal().size() > 1) - return emitOpError("can only return one final value of iterator"); - return mlir::success(); - }]; - let extraClassDeclaration = [{ - static constexpr const char *unorderedAttrName() { return "unordered"; } - static constexpr const char *stepAttrName() { return "step"; } - - /// Is this an unordered loop? - bool isUnordered() { return getAttr(unorderedAttrName()).isa(); } - - /// Does loop set (and return) the final value of the control variable? - bool hasLastValue() { return lastVal().size(); } + static constexpr llvm::StringRef unorderedAttrName() { return "unordered"; } - /// Get the block argument corresponding to the loop control value (PHI) mlir::Value getInductionVar() { return getBody()->getArgument(0); } - - void setLowerBound(mlir::Value bound) { - getOperation()->setOperand(0, bound); + mlir::OpBuilder getBodyBuilder() { + return OpBuilder(getBody(), std::prev(getBody()->end())); + } + mlir::Block::BlockArgListType getRegionIterArgs() { + return getBody()->getArguments().drop_front(); + } + mlir::Operation::operand_range getIterOperands() { + return getOperands().drop_front(getNumControlOperands()); } - void setUpperBound(mlir::Value bound) { - getOperation()->setOperand(1, bound); + void setLowerBound(Value bound) { getOperation()->setOperand(0, bound); } + void setUpperBound(Value bound) { getOperation()->setOperand(1, bound); } + void setStep(Value step) { getOperation()->setOperand(2, step); } + + /// Number of region arguments for loop-carried values + unsigned getNumRegionIterArgs() { + return getBody()->getNumArguments() - 1; + } + /// Number of operands controlling the loop: lb, ub, step + unsigned getNumControlOperands() { return 3; } + /// Does the operation hold operands for loop-carried values + bool hasIterOperands() { + return getOperation()->getNumOperands() > getNumControlOperands(); } + /// Get Number of loop-carried values + unsigned getNumIterOperands() { + return getOperation()->getNumOperands() - getNumControlOperands(); + } + + /// Get the body of the loop + mlir::Block *getBody() { return ®ion().front(); } - void setStep(mlir::Value step) { - getOperation()->setOperand(2, step); + void setUnordered() { + getOperation()->setAttr(unorderedAttrName(), + mlir::UnitAttr::get(getContext())); } }]; } -def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { - let summary = "generalized conditional operation"; +def fir_WhereOp : region_Op<"if"> { + let summary = "if-then-else conditional operation"; let description = [{ - To conditionally execute operations (typically) within the body of a - `fir.loop` operation. This operation is similar to `loop.if`. + Used to conditionally execute operations. This operation is the FIR + dialect's version of `loop.if`. ```mlir %56 = ... : i1 %78 = ... : !fir.ref - fir.where %56 { + fir.if %56 { fir.store %76 to %78 : !fir.ref - } otherwise { + } else { fir.store %77 to %78 : !fir.ref } ``` }]; let arguments = (ins I1:$condition); + let results = (outs Variadic:$results); - let regions = (region SizedRegion<1>:$whereRegion, AnyRegion:$otherRegion); + let regions = (region + SizedRegion<1>:$whereRegion, + AnyRegion:$otherRegion + ); let skipDefaultBuilders = 1; let builders = [ OpBuilder<"Builder *builder, OperationState &result, " - "Value cond, bool withOtherRegion"> + "Value cond, bool withOtherRegion">, + OpBuilder<"Builder *builder, OperationState &result, " + "TypeRange resultTypes, Value cond, bool withOtherRegion"> ]; - let parser = [{ return parseWhereOp(parser, result); }]; - - let printer = [{ - p << getOperationName() << ' ' << condition(); - p.printRegion(whereRegion(), /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); - - // Print the 'else' regions if it exists and has a block. - auto &otherReg = otherRegion(); - if (!otherReg.empty()) { - p << " otherwise"; - p.printRegion(otherReg, /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); - } - p.printOptionalAttrDict(getAttrs()); - }]; - - let verifier = [{ - for (auto ®ion : getOperation()->getRegions()) { - if (region.empty()) - continue; - for (auto &b : region) - if (b.getNumArguments() != 0) - return emitOpError("requires that child entry blocks have no args"); - } - return mlir::success(); - }]; - let extraClassDeclaration = [{ mlir::OpBuilder getWhereBodyBuilder() { assert(!whereRegion().empty() && "Unexpected empty 'where' region."); @@ -1892,9 +2015,81 @@ }]; } +def fir_IterWhileOp : region_Op<"iterate_while", + [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::LoopOp` 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. + }]; + + let arguments = (ins + Index:$lowerBound, + Index:$upperBound, + Index:$step, + I1:$iterateIn, + Variadic:$initArgs + ); + let results = (outs + I1:$iterateResult, + Variadic:$results + ); + let regions = (region SizedRegion<1>:$region); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "mlir::Value lowerBound, mlir::Value upperBound," + "mlir::Value step, mlir::Value iterate," + "ValueRange iterArgs = llvm::None," + "ArrayRef attributes = {}"> + ]; + + let extraClassDeclaration = [{ + mlir::Block *getBody() { return ®ion().front(); } + mlir::Value getIterateVar() { return getBody()->getArgument(1); } + mlir::Value getInductionVar() { return getBody()->getArgument(0); } + mlir::OpBuilder getBodyBuilder() { + return mlir::OpBuilder(getBody(), std::prev(getBody()->end())); + } + mlir::Block::BlockArgListType getRegionIterArgs() { + return getBody()->getArguments().drop_front(); + } + mlir::Operation::operand_range getIterOperands() { + return getOperands().drop_front(getNumControlOperands()); + } + + void setLowerBound(Value bound) { getOperation()->setOperand(0, bound); } + void setUpperBound(Value bound) { getOperation()->setOperand(1, bound); } + void setStep(mlir::Value step) { getOperation()->setOperand(2, step); } + + /// Number of region arguments for loop-carried values + unsigned getNumRegionIterArgs() { + return getBody()->getNumArguments() - 1; + } + /// Number of operands controlling the loop + unsigned getNumControlOperands() { return 3; } + /// Does the operation hold operands for loop-carried values + bool hasIterOperands() { + return getOperation()->getNumOperands() > getNumControlOperands(); + } + /// Get Number of loop-carried values + unsigned getNumIterOperands() { + return getOperation()->getNumOperands() - getNumControlOperands(); + } + }]; +} + +//===----------------------------------------------------------------------===// // Procedure call operations +//===----------------------------------------------------------------------===// -def fir_CallOp : fir_Op<"call", []> { +def fir_CallOp : fir_Op<"call", + [MemoryEffects<[MemAlloc, MemFree, MemRead, MemWrite]>]> { let summary = "call a procedure"; let description = [{ @@ -1924,7 +2119,8 @@ }]; } -def fir_DispatchOp : fir_Op<"dispatch", []> { +def fir_DispatchOp : fir_Op<"dispatch", + [MemoryEffects<[MemAlloc, MemFree, MemRead, MemWrite]>]> { let summary = "call a type-bound procedure"; let description = [{ @@ -2025,7 +2221,8 @@ else if (auto v = val.dyn_cast()) result.attributes.push_back(builder.getNamedAttr(xlist(), v)); else - return mlir::failure(); + return parser.emitError(parser.getCurrentLocation(), + "found an invalid constant"); mlir::IntegerAttr sz; mlir::Type type; if (parser.parseLParen() || @@ -2033,9 +2230,11 @@ parser.parseRParen() || parser.parseColonType(type)) return mlir::failure(); + if (!(type.isa() || type.isa())) + return parser.emitError(parser.getCurrentLocation(), + "must have character type"); type = fir::SequenceType::get({sz.getInt()}, type); - if (!type || - parser.addTypesToList(type, result.types)) + if (!type || parser.addTypesToList(type, result.types)) return mlir::failure(); return mlir::success(); }]; @@ -2084,7 +2283,7 @@ class fir_ArithmeticOp traits = []> : fir_Op, + !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>, Results<(outs AnyType)> { let parser = [{ return impl::parseOneResultSameOperandTypeOp(parser, result); @@ -2095,7 +2294,7 @@ class fir_UnaryArithmeticOp traits = []> : fir_Op, + !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>, Results<(outs AnyType)> { let parser = [{ return impl::parseOneResultSameOperandTypeOp(parser, result); @@ -2140,9 +2339,15 @@ fir_ArithmeticOp, Arguments<(ins AnyRealLike:$lhs, AnyRealLike:$rhs)>; -def fir_AddfOp : RealArithmeticOp<"addf", [Commutative]>; -def fir_SubfOp : RealArithmeticOp<"subf">; -def fir_MulfOp : RealArithmeticOp<"mulf", [Commutative]>; +def fir_AddfOp : RealArithmeticOp<"addf", [Commutative]> { + let hasFolder = 1; +} +def fir_SubfOp : RealArithmeticOp<"subf"> { + let hasFolder = 1; +} +def fir_MulfOp : RealArithmeticOp<"mulf", [Commutative]> { + let hasFolder = 1; +} def fir_DivfOp : RealArithmeticOp<"divf">; def fir_ModfOp : RealArithmeticOp<"modf">; // Pow is a builtin call and not a primitive @@ -2183,8 +2388,7 @@ }]; } -def fir_ConstcOp : fir_Op<"constc", [NoSideEffect]>, - Results<(outs fir_ComplexType)> { +def fir_ConstcOp : fir_Op<"constc", [NoSideEffect]> { let summary = "create a complex constant"; let description = [{ @@ -2193,6 +2397,8 @@ the standard dialect. }]; + let results = (outs fir_ComplexType); + let parser = [{ fir::RealAttr realp; fir::RealAttr imagp; @@ -2290,6 +2496,7 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { let summary = "convert a symbol to an SSA value"; + let description = [{ Convert a symbol (a function or global reference) to an SSA-value to be used in other Operations. @@ -2308,6 +2515,7 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]> { let summary = "encapsulates all Fortran scalar type conversions"; + let description = [{ Generalized type conversion. Convert the ssa value from type T to type U. Not all pairs of types have conversions. When types T and U are the same @@ -2326,6 +2534,32 @@ let assemblyFormat = [{ $value attr-dict `:` functional-type($value, results) }]; + + let hasFolder = 1; + + let verifier = [{ + auto inType = value().getType(); + auto outType = getType(); + if (inType == outType) + return mlir::success(); + if ((isPointerCompatible(inType) && isPointerCompatible(outType)) || + (isIntegerCompatible(inType) && isIntegerCompatible(outType)) || + (isIntegerCompatible(inType) && isFloatCompatible(outType)) || + (isFloatCompatible(inType) && isIntegerCompatible(outType)) || + (isFloatCompatible(inType) && isFloatCompatible(outType)) || + (isIntegerCompatible(inType) && isPointerCompatible(outType)) || + (isPointerCompatible(inType) && isIntegerCompatible(outType)) || + (inType.isa() && outType.isa()) || + (fir::isa_complex(inType) && fir::isa_complex(outType))) + return mlir::success(); + return emitOpError("invalid type conversion"); + }]; + + let extraClassDeclaration = [{ + static bool isIntegerCompatible(mlir::Type ty); + static bool isFloatCompatible(mlir::Type ty); + static bool isPointerCompatible(mlir::Type ty); + }]; } def FortranTypeAttr : Attr()">, @@ -2396,7 +2630,7 @@ } def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", - [SameOperandsAndResultType]> { + [NoSideEffect, SameOperandsAndResultType]> { let summary = "synthetic op to prevent reassociation"; let description = [{ Primitive operation meant to intrusively prevent operator reassociation. @@ -2440,101 +2674,86 @@ %z = constant 0 : index %o = constant 1 : index %4 = fir.insert_value %3, %1, %z : (tuple, i32, index) -> tuple - %5 = fir.insert_value %4, %1, %o : (tuple, f32, index) -> tuple - return %5 + %5 = fir.insert_value %4, %2, %o : (tuple, f32, index) -> tuple + fir.has_value %5 : tuple } ``` }]; let arguments = (ins StrAttr:$sym_name, - OptionalAttr:$initval, - UnitAttr:$constant, - TypeAttr:$type + SymbolRefAttr:$symref, + TypeAttr:$type, + OptionalAttr:$initVal, + OptionalAttr:$constant, + OptionalAttr:$linkName ); let regions = (region AtMostRegion<1>:$region); - let parser = [{ - // Parse the name as a symbol reference attribute. - SymbolRefAttr nameAttr; - if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(), - result.attributes)) - return failure(); - - auto &builder = parser.getBuilder(); - auto name = nameAttr.getRootReference(); - result.attributes.back().second = builder.getStringAttr(name); - - bool simpleInitializer = false; - if (!parser.parseOptionalLParen()) { - Attribute attr; - if (parser.parseAttribute(attr, initValAttrName(), result.attributes) || - parser.parseRParen()) - return failure(); - simpleInitializer = true; - } - - if (succeeded(parser.parseOptionalKeyword(constantAttrName()))) { - // if "constant" keyword then mark this as a constant, not a variable - result.addAttribute(constantAttrName(), builder.getUnitAttr()); - } - - mlir::Type globalType; - if (parser.parseColonType(globalType)) - return failure(); - - result.addAttribute(typeAttrName(), mlir::TypeAttr::get(globalType)); - - if (!simpleInitializer) { - // Parse the optional initializer body. - if (parser.parseRegion(*result.addRegion(), llvm::None, llvm::None)) - return failure(); - } - - return success(); - }]; + let parser = "return parseGlobalOp(parser, result);"; let printer = [{ - auto varName = getAttrOfType( - mlir::SymbolTable::getSymbolAttrName()).getValue(); - p << getOperationName() << " @" << varName; - if (auto iv = initval().getValueOr(Attribute())) { - p << '('; - p.printAttribute(iv); - p << ')'; - } + p << getOperationName(); + if (linkName().hasValue()) + p << ' ' << linkName().getValue(); + p << ' '; + p.printAttributeWithoutType(getAttr(symbolAttrName())); + if (auto val = getValueOrNull()) + p << '(' << val << ')'; if (getAttr(constantAttrName())) - p << ' ' << constantAttrName(); + p << " constant"; p << " : "; p.printType(getType()); - Region &body = getOperation()->getRegion(0); - if (!body.empty()) - p.printRegion(body, /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/true); + if (hasInitializationBody()) + p.printRegion(getOperation()->getRegion(0), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/true); }]; let skipDefaultBuilders = 1; let builders = [ OpBuilder<"mlir::Builder *builder, OperationState &result," - "StringRef name, Type type, ArrayRef attrs = {}", - [{ - result.addAttribute(typeAttrName(), mlir::TypeAttr::get(type)); - result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), - builder->getStringAttr(name)); - result.addAttributes(attrs); - }]> + "StringRef name, Type type, ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, bool isConstant, Type type," + "ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, Type type, StringAttr linkage = {}," + "ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, bool isConstant, Type type," + "StringAttr linkage = {}," + "ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, Type type, Attribute initVal," + "StringAttr linkage = {}," + "ArrayRef attrs = {}">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, bool isConstant, Type type," + "Attribute initVal, StringAttr linkage = {}," + "ArrayRef attrs = {}">, ]; let extraClassDeclaration = [{ + static constexpr llvm::StringRef symbolAttrName() { return "symref"; } static constexpr llvm::StringRef constantAttrName() { return "constant"; } - static constexpr llvm::StringRef initValAttrName() { return "initval"; } + static constexpr llvm::StringRef initValAttrName() { return "initVal"; } + static constexpr llvm::StringRef linkageAttrName() { return "linkName"; } static constexpr llvm::StringRef typeAttrName() { return "type"; } + /// The printable type of the global mlir::Type getType() { return getAttrOfType(typeAttrName()).getValue(); } + /// The semantic type of the global + mlir::Type resultType() { + return fir::AllocaOp::wrapResultType(getType()); + } + + /// Return the initializer attribute if it exists, or a null attribute. + Attribute getValueOrNull() { return initVal().getValueOr(Attribute()); } + /// Append the next initializer value to the `GlobalOp` to construct /// the variable's initial value. void appendInitialValue(mlir::Operation *op); @@ -2544,6 +2763,19 @@ /// A GlobalOp has one block. mlir::Block &getBlock() { return getRegion().front(); } + + /// Determine if `linkage` is a supported keyword + static mlir::ParseResult verifyValidLinkage(StringRef linkage); + + bool hasInitializationBody() { + return (getOperation()->getNumRegions() == 1) && !getRegion().empty() && + !isa(getBlock().front()); + } + + mlir::FlatSymbolRefAttr getSymbol() { + return mlir::FlatSymbolRefAttr::get(getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()).getValue(), getContext()); + } }]; } @@ -2594,8 +2826,10 @@ }]; } -def fir_DispatchTableOp : fir_Op<"dispatch_table", [IsolatedFromAbove, Symbol, - ImplicitFirTerminator]> { +def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; + +def fir_DispatchTableOp : fir_Op<"dispatch_table", + [IsolatedFromAbove, Symbol, ImplicitFirTerminator]> { let summary = "Dispatch table definition"; let description = [{ Index: flang/include/flang/Optimizer/Dialect/FIRType.h =================================================================== --- flang/include/flang/Optimizer/Dialect/FIRType.h +++ flang/include/flang/Optimizer/Dialect/FIRType.h @@ -24,6 +24,8 @@ namespace mlir { class DialectAsmParser; class DialectAsmPrinter; +class ComplexType; +class FloatType; } // namespace mlir namespace fir { @@ -89,6 +91,19 @@ /// Is `t` a FIR dialect type that implies a memory (de)reference? bool isa_ref_type(mlir::Type t); +/// Is `t` a type that is always trivially pass-by-reference? +bool isa_passbyref_type(mlir::Type t); + +/// Is `t` a boxed type? +bool isa_box_type(mlir::Type t); + +/// Is `t` a type that can conform to be pass-by-reference? Depending on the +/// context, these types may simply demote to pass-by-reference or a reference +/// to them may have to be passed instead. +inline bool conformsWithPassByRef(mlir::Type t) { + return isa_ref_type(t) || isa_box_type(t); +} + /// Is `t` a FIR dialect aggregate type? bool isa_aggregate(mlir::Type t); @@ -127,6 +142,10 @@ public: using Base::Base; static CplxType get(mlir::MLIRContext *ctxt, KindTy kind); + + /// Get the corresponding fir.real type. + mlir::Type getElementType() const; + KindTy getFKind() const; }; @@ -324,6 +343,21 @@ /// The number of dimensions of the sequence unsigned getDimension() const { return getShape().size(); } + /// Number of rows of constant extent + unsigned getConstantRows() const; + + /// Is the shape of the sequence constant? + bool hasConstantShape() const { return getConstantRows() == getDimension(); } + + /// Does the sequence have unknown shape? (`array<* x T>`) + bool hasUnknownShape() const { return getShape().empty(); } + + /// Is the interior of the sequence constant? Check if the array is + /// one of constant shape (`array`), unknown shape + /// (`array<*xT>`), or rows with shape and ending with column(s) of + /// unknown extent (`array`). + bool hasConstantInterior() const; + /// The value `-1` represents an unknown extent for a dimension static constexpr Extent getUnknownExtent() { return -1; } @@ -394,6 +428,20 @@ void printFirType(FIROpsDialect *, mlir::Type ty, mlir::DialectAsmPrinter &p); +/// Guarantee `type` is a scalar integral type (standard Integer, standard +/// Index, or FIR Int). Aborts execution if condition is false. +void verifyIntegralType(mlir::Type type); + +/// Is `t` a FIR Real or MLIR Float type? +inline bool isa_real(mlir::Type t) { + return t.isa() || t.isa(); +} + +/// Is `t` a FIR or MLIR Complex type? +inline bool isa_complex(mlir::Type t) { + return t.isa() || t.isa(); +} + } // namespace fir #endif // OPTIMIZER_DIALECT_FIRTYPE_H Index: flang/lib/Optimizer/Dialect/FIRAttr.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIRAttr.cpp +++ flang/lib/Optimizer/Dialect/FIRAttr.cpp @@ -8,16 +8,11 @@ #include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIRDialect.h" -#include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Support/KindMapping.h" #include "mlir/IR/AttributeSupport.h" -#include "mlir/IR/Diagnostics.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/Types.h" -#include "mlir/Parser.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" using namespace fir; Index: flang/lib/Optimizer/Dialect/FIRDialect.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIRDialect.cpp +++ flang/lib/Optimizer/Dialect/FIRDialect.cpp @@ -10,8 +10,6 @@ #include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIRType.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "mlir/IR/StandardTypes.h" using namespace fir; Index: flang/lib/Optimizer/Dialect/FIROps.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIROps.cpp +++ flang/lib/Optimizer/Dialect/FIROps.cpp @@ -10,28 +10,35 @@ #include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/Dialect/CommonFolders.h" #include "mlir/Dialect/StandardOps/IR/Ops.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/Function.h" +#include "mlir/IR/Matchers.h" #include "mlir/IR/Module.h" -#include "mlir/IR/StandardTypes.h" -#include "mlir/IR/SymbolTable.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/TypeSwitch.h" using namespace fir; -/// return true if the sequence type is abstract or the record type is malformed -/// or contains an abstract sequence type +/// Return true if a sequence type is of some incomplete size or a record type +/// is malformed or contains an incomplete sequence type. An incomplete sequence +/// type is one with more unknown extents in the type than have been provided +/// via `dynamicExtents`. Sequence types with an unknown rank are incomplete by +/// definition. static bool verifyInType(mlir::Type inType, - llvm::SmallVectorImpl &visited) { + llvm::SmallVectorImpl &visited, + unsigned dynamicExtents = 0) { if (auto st = inType.dyn_cast()) { auto shape = st.getShape(); if (shape.size() == 0) return true; - for (auto ext : shape) - if (ext < 0) + for (std::size_t i = 0, end{shape.size()}; i < end; ++i) { + if (shape[i] != fir::SequenceType::getUnknownExtent()) + continue; + if (dynamicExtents-- == 0) return true; + } } else if (auto rt = inType.dyn_cast()) { // don't recurse if we're already visiting this one if (llvm::is_contained(visited, rt.getName())) @@ -58,6 +65,15 @@ } //===----------------------------------------------------------------------===// +// AddfOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult fir::AddfOp::fold(llvm::ArrayRef opnds) { + return mlir::constFoldBinaryOp( + opnds, [](APFloat a, APFloat b) { return a + b; }); +} + +//===----------------------------------------------------------------------===// // AllocaOp //===----------------------------------------------------------------------===// @@ -101,6 +117,33 @@ } //===----------------------------------------------------------------------===// +// BoxAddrOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult fir::BoxAddrOp::fold(llvm::ArrayRef opnds) { + if (auto v = val().getDefiningOp()) { + if (auto box = dyn_cast(v)) + return box.memref(); + if (auto box = dyn_cast(v)) + return box.memref(); + } + return {}; +} + +//===----------------------------------------------------------------------===// +// BoxCharLenOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult +fir::BoxCharLenOp::fold(llvm::ArrayRef opnds) { + if (auto v = val().getDefiningOp()) { + if (auto box = dyn_cast(v)) + return box.len(); + } + return {}; +} + +//===----------------------------------------------------------------------===// // BoxDimsOp //===----------------------------------------------------------------------===// @@ -268,6 +311,103 @@ } //===----------------------------------------------------------------------===// +// ConvertOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult fir::ConvertOp::fold(llvm::ArrayRef opnds) { + if (value().getType() == getType()) + return value(); + if (matchPattern(value(), m_Op())) { + auto inner = cast(value().getDefiningOp()); + // (convert (convert 'a : logical -> i1) : i1 -> logical) ==> forward 'a + if (auto toTy = getType().dyn_cast()) + if (auto fromTy = inner.value().getType().dyn_cast()) + if (inner.getType().isa() && (toTy == fromTy)) + return inner.value(); + // (convert (convert 'a : i1 -> logical) : logical -> i1) ==> forward 'a + if (auto toTy = getType().dyn_cast()) + if (auto fromTy = inner.value().getType().dyn_cast()) + if (inner.getType().isa() && (toTy == fromTy) && + (fromTy.getWidth() == 1)) + return inner.value(); + } + return {}; +} + +bool fir::ConvertOp::isIntegerCompatible(mlir::Type ty) { + return ty.isa() || ty.isa() || + ty.isa() || ty.isa() || + ty.isa(); +} + +bool fir::ConvertOp::isFloatCompatible(mlir::Type ty) { + return ty.isa() || ty.isa(); +} + +bool fir::ConvertOp::isPointerCompatible(mlir::Type ty) { + return 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(); + + mlir::FunctionType funcTy; + if (parser.parseType(funcTy)) + return failure(); + allOperandTypes = funcTy.getInputs(); + allResultTypes = funcTy.getResults(); + result.addTypes(allResultTypes); + if (parser.resolveOperands(allOperands, allOperandTypes, allOperandLoc, + 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 getAttr(CoordinateOp::baseType()).cast().getValue(); +} + +void fir::CoordinateOp::build(Builder *, 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}); +} + +void fir::CoordinateOp::build(Builder *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); +} + +//===----------------------------------------------------------------------===// // DispatchOp //===----------------------------------------------------------------------===// @@ -341,10 +481,289 @@ // GlobalOp //===----------------------------------------------------------------------===// +static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) { + // Parse the optional linkage + llvm::StringRef linkage; + auto &builder = parser.getBuilder(); + if (mlir::succeeded(parser.parseOptionalKeyword(&linkage))) { + if (fir::GlobalOp::verifyValidLinkage(linkage)) + return failure(); + mlir::StringAttr linkAttr = builder.getStringAttr(linkage); + result.addAttribute(fir::GlobalOp::linkageAttrName(), linkAttr); + } + + // Parse the name as a symbol reference attribute. + mlir::SymbolRefAttr nameAttr; + if (parser.parseAttribute(nameAttr, fir::GlobalOp::symbolAttrName(), + result.attributes)) + return failure(); + result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder.getStringAttr(nameAttr.getRootReference())); + + bool simpleInitializer = false; + if (mlir::succeeded(parser.parseOptionalLParen())) { + Attribute attr; + if (parser.parseAttribute(attr, fir::GlobalOp::initValAttrName(), + result.attributes) || + parser.parseRParen()) + return failure(); + simpleInitializer = true; + } + + if (succeeded(parser.parseOptionalKeyword("constant"))) { + // if "constant" keyword then mark this as a constant, not a variable + result.addAttribute(fir::GlobalOp::constantAttrName(), + builder.getUnitAttr()); + } + + mlir::Type globalType; + if (parser.parseColonType(globalType)) + return failure(); + + result.addAttribute(fir::GlobalOp::typeAttrName(), + mlir::TypeAttr::get(globalType)); + + if (simpleInitializer) { + result.addRegion(); + } else { + // Parse the optional initializer body. + if (parser.parseRegion(*result.addRegion(), llvm::None, llvm::None)) + return failure(); + } + + return success(); +} + void fir::GlobalOp::appendInitialValue(mlir::Operation *op) { getBlock().getOperations().push_back(op); } +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, bool isConstant, Type type, + Attribute initialVal, StringAttr linkage, + ArrayRef attrs) { + result.addRegion(); + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(type)); + result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); + result.addAttribute(symbolAttrName(), builder->getSymbolRefAttr(name)); + if (isConstant) + result.addAttribute(constantAttrName(), builder->getUnitAttr()); + if (initialVal) + result.addAttribute(initValAttrName(), initialVal); + if (linkage) + result.addAttribute(linkageAttrName(), linkage); + result.attributes.append(attrs.begin(), attrs.end()); +} + +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, Type type, Attribute initialVal, + StringAttr linkage, ArrayRef attrs) { + build(builder, result, name, /*isConstant=*/false, type, {}, linkage, attrs); +} + +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, bool isConstant, Type type, + StringAttr linkage, ArrayRef attrs) { + build(builder, result, name, isConstant, type, {}, linkage, attrs); +} + +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, Type type, StringAttr linkage, + ArrayRef attrs) { + build(builder, result, name, /*isConstant=*/false, type, {}, linkage, attrs); +} + +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, bool isConstant, Type type, + ArrayRef attrs) { + build(builder, result, name, isConstant, type, StringAttr{}, attrs); +} + +void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result, + StringRef name, Type type, + ArrayRef attrs) { + build(builder, result, name, /*isConstant=*/false, type, attrs); +} + +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"}; + return mlir::success(llvm::is_contained(validNames, linkage)); +} + +//===----------------------------------------------------------------------===// +// IterWhileOp +//===----------------------------------------------------------------------===// + +void fir::IterWhileOp::build(mlir::Builder *builder, + mlir::OperationState &result, mlir::Value lb, + mlir::Value ub, mlir::Value step, + mlir::Value iterate, mlir::ValueRange iterArgs, + llvm::ArrayRef attributes) { + result.addOperands({lb, ub, step, iterate}); + result.addTypes(iterate.getType()); + result.addOperands(iterArgs); + for (auto v : iterArgs) + result.addTypes(v.getType()); + mlir::Region *bodyRegion = result.addRegion(); + bodyRegion->push_back(new Block{}); + bodyRegion->front().addArgument(builder->getIndexType()); + bodyRegion->front().addArgument(iterate.getType()); + for (auto v : iterArgs) + bodyRegion->front().addArgument(v.getType()); + result.addAttributes(attributes); +} + +static mlir::ParseResult parseIterWhileOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + auto &builder = parser.getBuilder(); + mlir::OpAsmParser::OperandType inductionVariable, lb, ub, step; + if (parser.parseLParen() || parser.parseRegionArgument(inductionVariable) || + parser.parseEqual()) + return mlir::failure(); + + // Parse loop bounds. + auto indexType = builder.getIndexType(); + auto i1Type = builder.getIntegerType(1); + if (parser.parseOperand(lb) || + parser.resolveOperand(lb, indexType, result.operands) || + parser.parseKeyword("to") || parser.parseOperand(ub) || + parser.resolveOperand(ub, indexType, result.operands) || + parser.parseKeyword("step") || parser.parseOperand(step) || + parser.parseRParen() || + parser.resolveOperand(step, indexType, result.operands)) + return mlir::failure(); + + mlir::OpAsmParser::OperandType iterateVar, iterateInput; + if (parser.parseKeyword("and") || parser.parseLParen() || + parser.parseRegionArgument(iterateVar) || parser.parseEqual() || + parser.parseOperand(iterateInput) || parser.parseRParen() || + parser.resolveOperand(iterateInput, i1Type, result.operands)) + return mlir::failure(); + + // Parse the initial iteration arguments. + llvm::SmallVector regionArgs; + // Induction variable. + regionArgs.push_back(inductionVariable); + regionArgs.push_back(iterateVar); + result.addTypes(i1Type); + + if (mlir::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(); + // Resolve input operands. + for (auto operand_type : llvm::zip(operands, regionTypes)) + if (parser.resolveOperand(std::get<0>(operand_type), + std::get<1>(operand_type), result.operands)) + return mlir::failure(); + result.addTypes(regionTypes); + } + + if (parser.parseOptionalAttrDictWithKeyword(result.attributes)) + return mlir::failure(); + + llvm::SmallVector argTypes; + // Induction variable (hidden) + argTypes.push_back(indexType); + // Loop carried variables (including iterate) + argTypes.append(result.types.begin(), result.types.end()); + // Parse the body region. + auto *body = result.addRegion(); + if (regionArgs.size() != argTypes.size()) + return parser.emitError( + parser.getNameLoc(), + "mismatch in number of loop-carried values and defined values"); + + if (parser.parseRegion(*body, regionArgs, argTypes)) + return failure(); + + fir::IterWhileOp::ensureTerminator(*body, builder, result.location); + + return mlir::success(); +} + +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(); + if (!body->getArgument(1).getType().isInteger(1)) + return op.emitOpError( + "expected body second argument to be an index argument for " + "the induction variable"); + if (!body->getArgument(0).getType().isIndex()) + return op.emitOpError( + "expected body first argument to be an index argument for " + "the induction variable"); + + auto opNumResults = op.getNumResults(); + if (opNumResults == 0) + return mlir::failure(); + if (op.getNumIterOperands() != opNumResults) + return op.emitOpError( + "mismatch in number of loop-carried values and defined values"); + if (op.getNumRegionIterArgs() != opNumResults) + return op.emitOpError( + "mismatch in number of basic block args and defined values"); + auto iterOperands = op.getIterOperands(); + auto iterArgs = op.getRegionIterArgs(); + auto opResults = op.getResults(); + unsigned i = 0; + for (auto e : llvm::zip(iterOperands, iterArgs, opResults)) { + if (std::get<0>(e).getType() != std::get<2>(e).getType()) + return op.emitOpError() << "types mismatch between " << i + << "th iter operand and defined value"; + if (std::get<1>(e).getType() != std::get<2>(e).getType()) + return op.emitOpError() << "types mismatch between " << i + << "th iter region arg and defined value"; + + i++; + } + return mlir::success(); +} + +static void print(mlir::OpAsmPrinter &p, fir::IterWhileOp op) { + p << fir::IterWhileOp::getOperationName() << " (" << op.getInductionVar() + << " = " << op.lowerBound() << " to " << op.upperBound() << " step " + << op.step() << ") and ("; + assert(op.hasIterOperands()); + auto regionArgs = op.getRegionIterArgs(); + auto operands = op.getIterOperands(); + p << regionArgs.front() << " = " << *operands.begin() << ")"; + if (regionArgs.size() > 1) { + p << " iter_args("; + 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() << ')'; + } + p.printOptionalAttrDictWithKeyword(op.getAttrs(), {}); + p.printRegion(op.region(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/true); +} + +mlir::Region &fir::IterWhileOp::getLoopBody() { return region(); } + +bool fir::IterWhileOp::isDefinedOutsideOfLoop(mlir::Value value) { + return !region().isAncestor(value.getParentRegion()); +} + +mlir::LogicalResult +fir::IterWhileOp::moveOutOfLoop(llvm::ArrayRef ops) { + for (auto op : ops) + op->moveBefore(*this); + return success(); +} + //===----------------------------------------------------------------------===// // LoadOp //===----------------------------------------------------------------------===// @@ -367,65 +786,84 @@ // LoopOp //===----------------------------------------------------------------------===// -void fir::LoopOp::build(mlir::Builder *builder, OperationState &result, - mlir::Value lb, mlir::Value ub, ValueRange step, - ArrayRef attributes) { - if (step.empty()) - result.addOperands({lb, ub}); - else - result.addOperands({lb, ub, step[0]}); +void fir::LoopOp::build(mlir::Builder *builder, mlir::OperationState &result, + mlir::Value lb, mlir::Value ub, mlir::Value step, + bool unordered, mlir::ValueRange iterArgs, + llvm::ArrayRef attributes) { + result.addOperands({lb, ub, step}); + result.addOperands(iterArgs); + for (auto v : iterArgs) + result.addTypes(v.getType()); mlir::Region *bodyRegion = result.addRegion(); - LoopOp::ensureTerminator(*bodyRegion, *builder, result.location); + bodyRegion->push_back(new Block{}); + if (iterArgs.empty()) + LoopOp::ensureTerminator(*bodyRegion, *builder, result.location); bodyRegion->front().addArgument(builder->getIndexType()); + for (auto v : iterArgs) + bodyRegion->front().addArgument(v.getType()); + if (unordered) + result.addAttribute(unorderedAttrName(), builder->getUnitAttr()); result.addAttributes(attributes); - NamedAttributeList attrs(attributes); - if (!attrs.get(unorderedAttrName())) - result.addTypes(builder->getIndexType()); } static mlir::ParseResult parseLoopOp(mlir::OpAsmParser &parser, mlir::OperationState &result) { auto &builder = parser.getBuilder(); - OpAsmParser::OperandType inductionVariable, lb, ub, step; + mlir::OpAsmParser::OperandType inductionVariable, lb, ub, step; // Parse the induction variable followed by '='. if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) return mlir::failure(); // Parse loop bounds. - mlir::Type indexType = builder.getIndexType(); + auto indexType = builder.getIndexType(); if (parser.parseOperand(lb) || parser.resolveOperand(lb, indexType, result.operands) || parser.parseKeyword("to") || parser.parseOperand(ub) || - parser.resolveOperand(ub, indexType, result.operands)) - return mlir::failure(); + parser.resolveOperand(ub, indexType, result.operands) || + parser.parseKeyword("step") || parser.parseOperand(step) || + parser.resolveOperand(step, indexType, result.operands)) + return failure(); - if (parser.parseOptionalKeyword(fir::LoopOp::stepAttrName())) { - result.addAttribute(fir::LoopOp::stepAttrName(), - builder.getIntegerAttr(builder.getIndexType(), 1)); - } else if (parser.parseOperand(step) || - parser.resolveOperand(step, indexType, result.operands)) { - return mlir::failure(); + if (mlir::succeeded(parser.parseOptionalKeyword("unordered"))) + result.addAttribute(fir::LoopOp::unorderedAttrName(), + builder.getUnitAttr()); + + // Parse the optional initial iteration arguments. + llvm::SmallVector regionArgs, operands; + llvm::SmallVector argTypes; + regionArgs.push_back(inductionVariable); + + if (succeeded(parser.parseOptionalKeyword("iter_args"))) { + // Parse assignment list and results type list. + if (parser.parseAssignmentList(regionArgs, operands) || + parser.parseArrowTypeList(result.types)) + return failure(); + // Resolve input operands. + for (auto operand_type : llvm::zip(operands, result.types)) + if (parser.resolveOperand(std::get<0>(operand_type), + std::get<1>(operand_type), result.operands)) + return failure(); } - // Parse the optional `unordered` keyword - bool isUnordered = false; - if (!parser.parseOptionalKeyword(LoopOp::unorderedAttrName())) { - result.addAttribute(LoopOp::unorderedAttrName(), builder.getUnitAttr()); - isUnordered = true; - } + if (parser.parseOptionalAttrDictWithKeyword(result.attributes)) + return mlir::failure(); + // Induction variable. + argTypes.push_back(indexType); + // Loop carried variables + argTypes.append(result.types.begin(), result.types.end()); // Parse the body region. - mlir::Region *body = result.addRegion(); - if (parser.parseRegion(*body, inductionVariable, indexType)) - return mlir::failure(); + auto *body = result.addRegion(); + if (regionArgs.size() != argTypes.size()) + return parser.emitError( + parser.getNameLoc(), + "mismatch in number of loop-carried values and defined values"); + + if (parser.parseRegion(*body, regionArgs, argTypes)) + return failure(); fir::LoopOp::ensureTerminator(*body, builder, result.location); - // Parse the optional attribute list. - if (parser.parseOptionalAttrDict(result.attributes)) - return mlir::failure(); - if (!isUnordered) - result.addTypes(builder.getIndexType()); return mlir::success(); } @@ -438,6 +876,133 @@ return dyn_cast_or_null(containingInst); } +// Lifted from loop.loop +static mlir::LogicalResult verify(fir::LoopOp 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(); + if (!body->getArgument(0).getType().isIndex()) + return op.emitOpError( + "expected body first argument to be an index argument for " + "the induction variable"); + + auto opNumResults = op.getNumResults(); + if (opNumResults == 0) + return success(); + if (op.getNumIterOperands() != opNumResults) + return op.emitOpError( + "mismatch in number of loop-carried values and defined values"); + if (op.getNumRegionIterArgs() != opNumResults) + return op.emitOpError( + "mismatch in number of basic block args and defined values"); + auto iterOperands = op.getIterOperands(); + auto iterArgs = op.getRegionIterArgs(); + auto opResults = op.getResults(); + unsigned i = 0; + for (auto e : llvm::zip(iterOperands, iterArgs, opResults)) { + if (std::get<0>(e).getType() != std::get<2>(e).getType()) + return op.emitOpError() << "types mismatch between " << i + << "th iter operand and defined value"; + if (std::get<1>(e).getType() != std::get<2>(e).getType()) + return op.emitOpError() << "types mismatch between " << i + << "th iter region arg and defined value"; + + i++; + } + return success(); +} + +static void print(mlir::OpAsmPrinter &p, fir::LoopOp op) { + bool printBlockTerminators = false; + p << fir::LoopOp::getOperationName() << ' ' << op.getInductionVar() << " = " + << op.lowerBound() << " to " << op.upperBound() << " step " << op.step(); + if (op.unordered()) + p << " unordered"; + if (op.hasIterOperands()) { + p << " iter_args("; + auto regionArgs = op.getRegionIterArgs(); + auto operands = op.getIterOperands(); + llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](auto it) { + p << std::get<0>(it) << " = " << std::get<1>(it); + }); + p << ") -> (" << op.getResultTypes() << ')'; + printBlockTerminators = true; + } + p.printOptionalAttrDictWithKeyword(op.getAttrs(), + {fir::LoopOp::unorderedAttrName()}); + p.printRegion(op.region(), /*printEntryBlockArgs=*/false, + printBlockTerminators); +} + +mlir::Region &fir::LoopOp::getLoopBody() { return region(); } + +bool fir::LoopOp::isDefinedOutsideOfLoop(mlir::Value value) { + return !region().isAncestor(value.getParentRegion()); +} + +mlir::LogicalResult +fir::LoopOp::moveOutOfLoop(llvm::ArrayRef ops) { + for (auto op : ops) + op->moveBefore(*this); + return success(); +} + +//===----------------------------------------------------------------------===// +// MulfOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult fir::MulfOp::fold(llvm::ArrayRef opnds) { + return mlir::constFoldBinaryOp( + opnds, [](APFloat a, APFloat b) { return a * b; }); +} + +//===----------------------------------------------------------------------===// +// ResultOp +//===----------------------------------------------------------------------===// + +static mlir::LogicalResult verify(fir::ResultOp op) { + auto parentOp = op.getParentOp(); + auto results = parentOp->getResults(); + auto operands = op.getOperands(); + + if (isa(parentOp) || isa(parentOp) || + isa(parentOp)) { + if (parentOp->getNumResults() != op.getNumOperands()) + return op.emitOpError() << "parent of result must have same arity"; + for (auto e : llvm::zip(results, operands)) { + if (std::get<0>(e).getType() != std::get<1>(e).getType()) + return op.emitOpError() + << "types mismatch between result op and its parent"; + } + } else { + return op.emitOpError() + << "result only terminates if, do_loop, or iterate_while regions"; + } + return success(); +} + +static mlir::ParseResult parseResultOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + llvm::SmallVector operands; + llvm::SmallVector types; + llvm::SMLoc loc = parser.getCurrentLocation(); + if (parser.parseOperandList(operands) || + parser.parseOptionalColonTypeList(types) || + parser.resolveOperands(operands, types, loc, result.operands)) + return failure(); + return success(); +} + +static void print(mlir::OpAsmPrinter &p, fir::ResultOp op) { + p << op.getOperationName(); + if (op.getNumOperands() != 0) + p << ' ' << op.getOperands() << " : " << op.getOperandTypes(); +} + //===----------------------------------------------------------------------===// // SelectOp //===----------------------------------------------------------------------===// @@ -460,6 +1025,10 @@ return {std::next(allArgs.begin(), start), std::next(allArgs.begin(), end)}; } +static unsigned denseElementsSize(mlir::DenseIntElementsAttr attr) { + return attr.getNumElements(); +} + llvm::Optional fir::SelectOp::getCompareOperands(unsigned) { return {}; } @@ -486,6 +1055,11 @@ bool fir::SelectOp::canEraseSuccessorOperand() { return true; } +unsigned fir::SelectOp::targetOffsetSize() { + return denseElementsSize( + getAttrOfType(getTargetOffsetAttr())); +} + //===----------------------------------------------------------------------===// // SelectCaseOp //===----------------------------------------------------------------------===// @@ -596,6 +1170,95 @@ return mlir::success(); } +unsigned fir::SelectCaseOp::compareOffsetSize() { + return denseElementsSize( + getAttrOfType(getCompareOffsetAttr())); +} + +unsigned fir::SelectCaseOp::targetOffsetSize() { + return denseElementsSize( + getAttrOfType(getTargetOffsetAttr())); +} + +void fir::SelectCaseOp::build(mlir::Builder *builder, + mlir::OperationState &result, + mlir::Value selector, + llvm::ArrayRef compareAttrs, + llvm::ArrayRef cmpOperands, + llvm::ArrayRef destinations, + llvm::ArrayRef destOperands, + llvm::ArrayRef attributes) { + result.addOperands(selector); + result.addAttribute(getCasesAttr(), builder->getArrayAttr(compareAttrs)); + llvm::SmallVector operOffs; + int32_t operSize = 0; + for (auto attr : compareAttrs) { + if (attr.isa()) { + operOffs.push_back(2); + operSize += 2; + } else if (attr.isa()) { + operOffs.push_back(0); + } else { + operOffs.push_back(1); + ++operSize; + } + } + for (auto ops : cmpOperands) + result.addOperands(ops); + result.addAttribute(getCompareOffsetAttr(), + builder->getI32VectorAttr(operOffs)); + const auto count = destinations.size(); + for (auto d : destinations) + result.addSuccessors(d); + const auto opCount = destOperands.size(); + llvm::SmallVector argOffs; + int32_t sumArgs = 0; + for (std::remove_const_t i = 0; i != count; ++i) { + if (i < opCount) { + result.addOperands(destOperands[i]); + const auto argSz = destOperands[i].size(); + argOffs.push_back(argSz); + sumArgs += argSz; + } else { + argOffs.push_back(0); + } + } + result.addAttribute(getOperandSegmentSizeAttr(), + builder->getI32VectorAttr({1, operSize, sumArgs})); + result.addAttribute(getTargetOffsetAttr(), + builder->getI32VectorAttr(argOffs)); + result.addAttributes(attributes); +} + +/// This builder has a slightly simplified interface in that the list of +/// operands need not be partitioned by the builder. Instead the operands are +/// partitioned here, before being passed to the default builder. This +/// partitioning is unchecked, so can go awry on bad input. +void fir::SelectCaseOp::build(mlir::Builder *builder, + mlir::OperationState &result, + mlir::Value selector, + llvm::ArrayRef compareAttrs, + llvm::ArrayRef cmpOpList, + llvm::ArrayRef destinations, + llvm::ArrayRef destOperands, + llvm::ArrayRef attributes) { + llvm::SmallVector cmpOpers; + auto iter = cmpOpList.begin(); + for (auto &attr : compareAttrs) { + if (attr.isa()) { + cmpOpers.push_back(mlir::ValueRange({iter, iter + 2})); + iter += 2; + } else if (attr.isa()) { + cmpOpers.push_back(mlir::ValueRange{}); + } else { + cmpOpers.push_back(mlir::ValueRange({iter, iter + 1})); + ++iter; + } + } + build(builder, result, selector, compareAttrs, cmpOpers, destinations, + destOperands, attributes); +} + //===----------------------------------------------------------------------===// // SelectRankOp //===----------------------------------------------------------------------===// @@ -627,6 +1290,11 @@ bool fir::SelectRankOp::canEraseSuccessorOperand() { return true; } +unsigned fir::SelectRankOp::targetOffsetSize() { + return denseElementsSize( + getAttrOfType(getTargetOffsetAttr())); +} + //===----------------------------------------------------------------------===// // SelectTypeOp //===----------------------------------------------------------------------===// @@ -703,6 +1371,11 @@ return mlir::success(); } +unsigned fir::SelectTypeOp::targetOffsetSize() { + return denseElementsSize( + getAttrOfType(getTargetOffsetAttr())); +} + //===----------------------------------------------------------------------===// // StoreOp //===----------------------------------------------------------------------===// @@ -727,6 +1400,15 @@ } //===----------------------------------------------------------------------===// +// SubfOp +//===----------------------------------------------------------------------===// + +mlir::OpFoldResult fir::SubfOp::fold(llvm::ArrayRef opnds) { + return mlir::constFoldBinaryOp( + opnds, [](APFloat a, APFloat b) { return a - b; }); +} + +//===----------------------------------------------------------------------===// // WhereOp //===----------------------------------------------------------------------===// @@ -758,7 +1440,7 @@ WhereOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location); - if (!parser.parseOptionalKeyword("otherwise")) { + if (!parser.parseOptionalKeyword("else")) { if (parser.parseRegion(*elseRegion, {}, {})) return mlir::failure(); WhereOp::ensureTerminator(*elseRegion, parser.getBuilder(), @@ -772,6 +1454,43 @@ return mlir::success(); } +static LogicalResult verify(fir::WhereOp op) { + // Verify that the entry of each child region does not have arguments. + for (auto ®ion : op.getOperation()->getRegions()) { + if (region.empty()) + continue; + + for (auto &b : region) + if (b.getNumArguments() != 0) + return op.emitOpError( + "requires that child entry blocks have no arguments"); + } + if (op.getNumResults() != 0 && op.otherRegion().empty()) + return op.emitOpError("must have an else block if defining values"); + + return mlir::success(); +} + +static void print(mlir::OpAsmPrinter &p, fir::WhereOp op) { + bool printBlockTerminators = false; + p << fir::WhereOp::getOperationName() << ' ' << op.condition(); + if (!op.results().empty()) { + p << " -> (" << op.getResultTypes() << ')'; + printBlockTerminators = true; + } + p.printRegion(op.whereRegion(), /*printEntryBlockArgs=*/false, + printBlockTerminators); + + // Print the 'else' regions if it exists and has a block. + auto &otherReg = op.otherRegion(); + if (!otherReg.empty()) { + p << " else"; + p.printRegion(otherReg, /*printEntryBlockArgs=*/false, + printBlockTerminators); + } + p.printOptionalAttrDict(op.getAttrs()); +} + //===----------------------------------------------------------------------===// mlir::ParseResult fir::isValidCaseAttr(mlir::Attribute attr) { @@ -840,6 +1559,7 @@ if (auto f = module.lookupSymbol(name)) return f; mlir::OpBuilder modBuilder(module.getBodyRegion()); + modBuilder.setInsertionPoint(module.getBody()->getTerminator()); return modBuilder.create(loc, name, type, attrs); } Index: flang/lib/Optimizer/Dialect/FIRType.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIRType.cpp +++ flang/lib/Optimizer/Dialect/FIRType.cpp @@ -8,15 +8,12 @@ #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Dialect/FIRDialect.h" -#include "mlir/IR/Builders.h" #include "mlir/IR/Diagnostics.h" -#include "mlir/IR/Dialect.h" #include "mlir/IR/DialectImplementation.h" -#include "mlir/IR/StandardTypes.h" -#include "mlir/Parser.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/ErrorHandling.h" using namespace fir; @@ -181,7 +178,7 @@ return SequenceType::get(shape, eleTy, map); } -bool verifyIntegerType(mlir::Type ty) { +static bool verifyIntegerType(mlir::Type ty) { return ty.isa() || ty.isa(); } @@ -842,6 +839,14 @@ return t.isa() || t.isa() || t.isa(); } +bool isa_box_type(mlir::Type t) { + return t.isa() || t.isa() || t.isa(); +} + +bool isa_passbyref_type(mlir::Type t) { + return t.isa() || isa_box_type(t); +} + bool isa_aggregate(mlir::Type t) { return t.isa() || t.isa(); } @@ -905,6 +910,10 @@ return Base::get(ctxt, FIR_COMPLEX, kind); } +mlir::Type fir::CplxType::getElementType() const { + return fir::RealType::get(getContext(), getFKind()); +} + KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind(); } // REAL @@ -1061,6 +1070,34 @@ return getImpl()->getShape(); } +unsigned fir::SequenceType::getConstantRows() const { + auto shape = getShape(); + unsigned count = 0; + for (auto d : shape) { + if (d < 0) + break; + ++count; + } + return count; +} + +// This test helps us determine if we can degenerate an array to a +// pointer to some interior section (possibly a single element) of the +// sequence. This is used to determine if we can lower to the LLVM IR. +bool fir::SequenceType::hasConstantInterior() const { + if (hasUnknownShape()) + return true; + auto rows = getConstantRows(); + auto dim = getDimension(); + if (rows == dim) + return true; + auto shape = getShape(); + for (unsigned i{rows}, size{dim}; i < size; ++i) + if (shape[i] != getUnknownExtent()) + return false; + return true; +} + mlir::LogicalResult fir::SequenceType::verifyConstructionInvariants( mlir::Location loc, const SequenceType::Shape &shape, mlir::Type eleTy, mlir::AffineMapAttr map) { @@ -1178,6 +1215,12 @@ } // namespace +void fir::verifyIntegralType(mlir::Type type) { + if (verifyIntegerType(type) || type.isa()) + return; + llvm_unreachable("expected integral type"); +} + void fir::printFirType(FIROpsDialect *, mlir::Type ty, mlir::DialectAsmPrinter &p) { auto &os = p.getStream(); Index: flang/test/Fir/fir-ops.fir =================================================================== --- flang/test/Fir/fir-ops.fir +++ flang/test/Fir/fir-ops.fir @@ -4,129 +4,174 @@ // UNSUPPORTED: !fir // CHECK-LABEL: func @it1() -> !fir.int<4> +// CHECK: func @box1() -> !fir.boxchar<2> +// CHECK: func @box2() -> !fir.boxproc<(i32, i32) -> i64> +// CHECK: func @box3() -> !fir.box> func @it1() -> !fir.int<4> -// CHECK-LABEL: func @box1() -> !fir.boxchar<2> func @box1() -> !fir.boxchar<2> -// CHECK-LABEL: func @box2() -> !fir.boxproc<(i32, i32) -> i64> func @box2() -> !fir.boxproc<(i32, i32) -> i64> -// CHECK-LABEL: func @box3() -> !fir.box> func @box3() -> !fir.box> // Fortran SUBROUTINE and FUNCTION // CHECK-LABEL: func @print_index3(index, index, index) -// CHECK-LABEL: func @user_i64(i64) -// CHECK-LABEL: func @user_tdesc(!fir.tdesc>) +// CHECK: func @user_i64(i64) +// CHECK: func @user_tdesc(!fir.tdesc>) func @print_index3(index, index, index) func @user_i64(i64) func @user_tdesc(!fir.tdesc>) // expect the void return to be omitted // CHECK-LABEL: func @store_tuple(tuple>) +// CHECK: func @get_method_box() -> !fir.box> +// CHECK: func @method_impl(!fir.box>) func @store_tuple(tuple>) -> () - -// CHECK-LABEL: func @get_method_box() -> !fir.box> -// CHECK-LABEL: func @method_impl(!fir.box>) func @get_method_box() -> !fir.box> func @method_impl(!fir.box>) // CHECK-LABEL: func @nop() -func @nop() - // CHECK-LABEL: func @get_func() -> (() -> ()) +func @nop() func @get_func() -> (() -> ()) -// CHECK-LABEL: @instructions +// CHECK-LABEL: func @instructions() { func @instructions() { - // CHECK: %[[A0:.*]] = fir.alloca !fir.array<10xi32> +// CHECK: [[VAL_0:%.*]] = fir.alloca !fir.array<10xi32> +// CHECK: [[VAL_1:%.*]] = fir.load [[VAL_0]] : !fir.ref> +// CHECK: [[VAL_2:%.*]] = fir.alloca i32 +// CHECK: [[VAL_3:%.*]] = constant 22 : i32 %0 = fir.alloca !fir.array<10xi32> - // CHECK: fir.load %[[A0]] : !fir.ref> %1 = fir.load %0 : !fir.ref> %2 = fir.alloca i32 %3 = constant 22 : i32 - // CHECK: fir.store %{{.*}} to %{{.*}} : !fir.ref + +// CHECK: fir.store [[VAL_3]] to [[VAL_2]] : !fir.ref +// CHECK: [[VAL_4:%.*]] = fir.undefined i32 +// CHECK: [[VAL_5:%.*]] = fir.allocmem !fir.array<100xf32> +// CHECK: [[VAL_6:%.*]] = fir.embox [[VAL_5]] : (!fir.heap>) -> !fir.box> fir.store %3 to %2 : !fir.ref - // CHECK: fir.undefined i32 %4 = fir.undefined i32 - // CHECK: %[[A5:.*]] = fir.allocmem !fir.array<100xf32> %5 = fir.allocmem !fir.array<100xf32> - // CHECK: %[[A6:.*]] = fir.embox %[[A5]] : (!fir.heap>) -> !fir.box> %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> - // CHECK: fir.box_addr %{{.*}} : (!fir.box>) -> !fir.ref> + +// CHECK: [[VAL_7:%.*]] = fir.box_addr [[VAL_6]] : (!fir.box>) -> !fir.ref> +// CHECK: [[VAL_8:%.*]] = constant 0 : index +// CHECK: [[VAL_9:%.*]]:3 = fir.box_dims [[VAL_6]], [[VAL_8]] : (!fir.box>, index) -> (index, index, index) +// CHECK: fir.call @print_index3([[VAL_9]]#0, [[VAL_9]]#1, [[VAL_9]]#2) : (index, index, index) -> () +// CHECK: [[VAL_10:%.*]] = fir.call @it1() : () -> !fir.int<4> %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> %c0 = constant 0 : index - // CHECK: %[[A8:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}} : (!fir.box>, index) -> (index, index, index) %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) - // CHECK: fir.call @print_index3(%[[A8]]#0, %[[A8]]#1, %[[A8]]#2) : (index, index, index) fir.call @print_index3(%d1#0, %d1#1, %d1#2) : (index, index, index) -> () %8 = fir.call @it1() : () -> !fir.int<4> - // CHECK: fir.box_elesize %[[A6]] : (!fir.box>) -> i64 + +// CHECK: [[VAL_11:%.*]] = fir.box_elesize [[VAL_6]] : (!fir.box>) -> i64 +// CHECK: [[VAL_12:%.*]] = fir.box_isalloc [[VAL_6]] : (!fir.box>) -> i1 +// CHECK: [[VAL_13:%.*]] = fir.box_isarray [[VAL_6]] : (!fir.box>) -> i1 +// CHECK: [[VAL_14:%.*]] = fir.box_isptr [[VAL_6]] : (!fir.box>) -> i1 +// CHECK: [[VAL_15:%.*]] = fir.box_rank [[VAL_6]] : (!fir.box>) -> i64 %9 = fir.box_elesize %6 : (!fir.box>) -> i64 - // CHECK: fir.box_isalloc %[[A6]] : (!fir.box>) -> i1 %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 - // CHECK: fir.box_isarray %[[A6]] : (!fir.box>) -> i1 %11 = fir.box_isarray %6 : (!fir.box>) -> i1 - // CHECK: fir.box_isptr %[[A6]] : (!fir.box>) -> i1 %12 = fir.box_isptr %6 : (!fir.box>) -> i1 - // CHECK: fir.box_rank %[[A6]] : (!fir.box>) -> i64 %13 = fir.box_rank %6 : (!fir.box>) -> i64 - // CHECK: fir.box_tdesc %[[A6]] : (!fir.box>) -> !fir.tdesc> + +// CHECK: [[VAL_16:%.*]] = fir.box_tdesc [[VAL_6]] : (!fir.box>) -> !fir.tdesc> +// CHECK: [[VAL_17:%.*]] = fir.call @box1() : () -> !fir.boxchar<2> +// CHECK: [[VAL_18:%.*]] = fir.boxchar_len [[VAL_17]] : (!fir.boxchar<2>) -> i32 +// CHECK: [[VAL_19:%.*]] = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> +// CHECK: [[VAL_20:%.*]] = fir.boxproc_host [[VAL_19]] : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> %15 = fir.call @box1() : () -> !fir.boxchar<2> - // CHECK: fir.boxchar_len %{{.*}} : (!fir.boxchar<2>) -> i32 %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 %17 = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> - // CHECK: fir.boxproc_host %{{.*}} : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + +// CHECK: [[VAL_21:%.*]] = constant 10 : i32 +// CHECK: [[VAL_22:%.*]] = fir.coordinate_of [[VAL_5]], [[VAL_21]] : (!fir.heap>, i32) -> !fir.ref +// CHECK: [[VAL_23:%.*]] = fir.field_index f, !fir.type +// CHECK: [[VAL_24:%.*]] = fir.undefined !fir.type +// CHECK: [[VAL_25:%.*]] = fir.extract_value [[VAL_24]], [[VAL_23]] : (!fir.type, !fir.field) -> f32 %19 = constant 10 : i32 - // CHECK: fir.coordinate_of %{{.*}}, %{{.*}} : (!fir.heap>, i32) -> !fir.ref %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref - // CHECK: fir.field_index f, !fir.type %21 = fir.field_index f, !fir.type - // CHECK: fir.undefined !fir.type %22 = fir.undefined !fir.type - // CHECK: fir.extract_value %{{.*}}, %{{.*}} : (!fir.type, !fir.field) -> f32 %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 + +// CHECK: [[VAL_26:%.*]] = constant 1 : i32 +// CHECK: [[VAL_27:%.*]] = fir.gendims [[VAL_26]], [[VAL_21]], [[VAL_26]] : (i32, i32, i32) -> !fir.dims<1> +// CHECK: [[VAL_28:%.*]] = constant 1.0 +// CHECK: [[VAL_29:%.*]] = fir.insert_value [[VAL_24]], [[VAL_28]], [[VAL_23]] : (!fir.type, f32, !fir.field) -> !fir.type +// CHECK: [[VAL_30:%.*]] = fir.len_param_index f, !fir.type %c1 = constant 1 : i32 - // CHECK: fir.gendims %{{.*}}, %{{.*}}, %{{.*}} : (i32, i32, i32) -> !fir.dims<1> %24 = fir.gendims %c1, %19, %c1 : (i32, i32, i32) -> !fir.dims<1> %cf1 = constant 1.0 : f32 - // CHECK: fir.insert_value %{{.*}}, %{{.*}}, %{{.*}} : (!fir.type, f32, !fir.field) -> !fir.type %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type - // CHECK: fir.len_param_index f, !fir.type %26 = fir.len_param_index f, !fir.type + +// CHECK: [[VAL_31:%.*]] = fir.call @box3() : () -> !fir.box> +// CHECK: [[VAL_32:%.*]] = fir.dispatch "method"([[VAL_31]]) : (!fir.box>) -> i32 +// CHECK: [[VAL_33:%.*]] = fir.convert [[VAL_32]] : (i32) -> i64 +// CHECK: [[VAL_34:%.*]] = fir.gentypedesc !fir.type +// CHECK: fir.call @user_tdesc([[VAL_34]]) : (!fir.tdesc>) -> () +// CHECK: [[VAL_35:%.*]] = fir.no_reassoc [[VAL_33]] : i64 %27 = fir.call @box3() : () -> !fir.box> - // CHECK: fir.dispatch "method"(%{{.*}}) : (!fir.box>) -> i32 %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 - // CHECK: fir.convert %{{.*}} : (i32) -> i64 %29 = fir.convert %28 : (i32) -> i64 - // CHECK: fir.gentypedesc !fir.type %30 = fir.gentypedesc !fir.type fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () - // CHECK: fir.no_reassoc %{{.*}} : i64 %31 = fir.no_reassoc %29 : i64 + +// CHECK: fir.call @user_i64([[VAL_35]]) : (i64) -> () +// CHECK: fir.freemem [[VAL_5]] : !fir.heap> +// CHECK: [[VAL_36:%.*]] = fir.call @get_func() : () -> (() -> ()) +// CHECK: fir.call [[VAL_36]]() : () -> () +// CHECK: [[VAL_37:%.*]] = fir.address_of(@it1) : !fir.ref<() -> !fir.int<4>> +// CHECK: return +// CHECK: } fir.call @user_i64(%31) : (i64) -> () - // CHECK: fir.freemem %{{.*}} : !fir.heap> fir.freemem %5 : !fir.heap> %32 = fir.call @get_func() : () -> (() -> ()) fir.call %32() : () -> () - // CHECK: fir.address_of(@it1) : !fir.ref<() -> !fir.int<4>> %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> return } -// CHECK-LABEL: @boxing_match +// CHECK-LABEL: func @boxing_match() { func @boxing_match() { +// CHECK: [[VAL_38:%.*]] = fir.alloca i32 +// CHECK: [[VAL_39:%.*]] = fir.alloca !fir.type +// CHECK: [[VAL_40:%.*]] = fir.alloca !fir.char<1> +// CHECK: [[VAL_41:%.*]] = fir.alloca tuple +// CHECK: [[VAL_42:%.*]] = fir.embox [[VAL_38]] : (!fir.ref) -> !fir.box +// CHECK: [[VAL_43:%.*]]:6 = fir.unbox [[VAL_42]] : (!fir.box) -> (!fir.ref, i32, i32, !fir.tdesc, i32, !fir.dims<0>) +// CHECK: [[VAL_44:%.*]] = constant 8 : i32 +// CHECK: [[VAL_45:%.*]] = fir.undefined !fir.char<1> +// CHECK: [[VAL_46:%.*]] = fir.emboxchar [[VAL_40]], [[VAL_44]] : (!fir.ref>, i32) -> !fir.boxchar<1> +// CHECK: [[VAL_47:%.*]]:2 = fir.unboxchar [[VAL_46]] : (!fir.boxchar<1>) -> (!fir.ref>, i32) +// CHECK: [[VAL_48:%.*]] = fir.undefined !fir.type +// CHECK: [[VAL_49:%.*]] = constant 0 : i32 +// CHECK: [[VAL_50:%.*]] = constant 12 : i32 +// CHECK: [[VAL_51:%.*]] = fir.insert_value [[VAL_48]], [[VAL_50]], [[VAL_49]] : (!fir.type, i32, i32) -> !fir.type +// CHECK: [[VAL_52:%.*]] = constant 1 : i32 +// CHECK: [[VAL_53:%.*]] = constant 4.213000e+01 : f64 +// CHECK: [[VAL_54:%.*]] = fir.insert_value [[VAL_48]], [[VAL_53]], [[VAL_52]] : (!fir.type, f64, i32) -> !fir.type +// CHECK: fir.store [[VAL_54]] to [[VAL_39]] : !fir.ref> +// CHECK: [[VAL_55:%.*]] = fir.emboxproc @method_impl, [[VAL_41]] : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> +// CHECK: [[VAL_56:%.*]], [[VAL_57:%.*]] = fir.unboxproc [[VAL_55]] : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) +// CHECK: [[VAL_58:%.*]] = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> +// CHECK: [[VAL_59:%.*]], [[VAL_60:%.*]] = fir.unboxproc [[VAL_58]] : (!fir.boxproc<(i32, i32) -> i64>) -> ((i32, i32) -> i64, !fir.ref>>) +// CHECK: [[VAL_61:%.*]] = fir.load [[VAL_60]] : !fir.ref>> +// CHECK: fir.call @store_tuple([[VAL_61]]) : (tuple>) -> () +// CHECK: return +// CHECK: } %0 = fir.alloca i32 %d6 = fir.alloca !fir.type %d3 = fir.alloca !fir.char<1> %e6 = fir.alloca tuple %1 = fir.embox %0 : (!fir.ref) -> !fir.box - // CHECK: fir.unbox %{{.*}} : (!fir.box) -> (!fir.ref, i32, i32, !fir.tdesc, i32, !fir.dims<0>) %2:6 = fir.unbox %1 : (!fir.box) -> (!fir.ref,i32,i32,!fir.tdesc,i32,!fir.dims<0>) %c8 = constant 8 : i32 %3 = fir.undefined !fir.char<1> - // CHECK: fir.emboxchar %{{.*}}, %{{.*}} : (!fir.ref>, i32) -> !fir.boxchar<1> - // CHECK: fir.unboxchar %{{.*}} : (!fir.boxchar<1>) -> (!fir.ref>, i32) %4 = fir.emboxchar %d3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) %6 = fir.undefined !fir.type @@ -139,8 +184,6 @@ fir.store %a3 to %d6 : !fir.ref> %7 = fir.emboxproc @method_impl, %e6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> %8:2 = fir.unboxproc %7 : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) - // CHECK: fir.emboxproc @method_impl, %{{.*}} : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> - // CHECK: fir.unboxproc %{{.*}} : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) %9 = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64> %10:2 = fir.unboxproc %9 : (!fir.boxproc<(i32, i32) -> i64>) -> ((i32, i32) -> i64, !fir.ref>>) %11 = fir.load %10#1 : !fir.ref>> @@ -148,32 +191,61 @@ return } -// CHECK-LABEL: @loop +// CHECK-LABEL: func @loop() { func @loop() { +// CHECK: [[VAL_62:%.*]] = constant 1 : index +// CHECK: [[VAL_63:%.*]] = constant 10 : index +// CHECK: [[VAL_64:%.*]] = constant true %c1 = constant 1 : index %c10 = constant 10 : index %ct = constant true - // CHECK: fir.loop %{{.*}} = %{{.*}} to %{{.*}} { - %i = fir.loop %i = %c1 to %c10 { - // CHECK: fir.where %{{.*}} { - fir.where %ct { + +// CHECK: fir.do_loop [[VAL_65:%.*]] = [[VAL_62]] to [[VAL_63]] step [[VAL_62]] { +// CHECK: fir.if [[VAL_64]] { +// CHECK: fir.call @nop() : () -> () +// CHECK: } else { +// CHECK: fir.call @nop() : () -> () +// CHECK: } +// CHECK: } +// CHECK: fir.unreachable +// CHECK: } + fir.do_loop %i = %c1 to %c10 step %c1 { + fir.if %ct { fir.call @nop() : () -> () - // CHECK: } otherwise { - } otherwise { + } else { fir.call @nop() : () -> () } } - // CHECK: fir.unreachable fir.unreachable } -// CHECK-LABEL: @bar_select +// CHECK: func @bar_select([[VAL_66:%.*]]: i32, [[VAL_67:%.*]]: i32) -> i32 { func @bar_select(%arg : i32, %arg2 : i32) -> i32 { +// CHECK: [[VAL_68:%.*]] = constant 1 : i32 +// CHECK: [[VAL_69:%.*]] = constant 2 : i32 +// CHECK: [[VAL_70:%.*]] = constant 3 : i32 +// CHECK: [[VAL_71:%.*]] = constant 4 : i32 %0 = constant 1 : i32 %1 = constant 2 : i32 %2 = constant 3 : i32 %3 = constant 4 : i32 - // CHECK: fir.select %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), -3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), 4, ^bb4(%{{.*}} : i32), unit, ^bb5] + +// CHECK: fir.select [[VAL_66]] : i32 [1, ^bb1([[VAL_68]] : i32), 2, ^bb2([[VAL_70]], [[VAL_66]], [[VAL_67]] : i32, i32, i32), -3, ^bb3([[VAL_67]], [[VAL_70]] : i32, i32), 4, ^bb4([[VAL_69]] : i32), unit, ^bb5] +// CHECK: ^bb1([[VAL_72:%.*]]: i32): +// CHECK: return [[VAL_72]] : i32 +// CHECK: ^bb2([[VAL_73:%.*]]: i32, [[VAL_74:%.*]]: i32, [[VAL_75:%.*]]: i32): +// CHECK: [[VAL_76:%.*]] = addi [[VAL_73]], [[VAL_74]] : i32 +// CHECK: [[VAL_77:%.*]] = addi [[VAL_76]], [[VAL_75]] : i32 +// CHECK: return [[VAL_77]] : i32 +// CHECK: ^bb3([[VAL_78:%.*]]: i32, [[VAL_79:%.*]]: i32): +// CHECK: [[VAL_80:%.*]] = addi [[VAL_78]], [[VAL_79]] : i32 +// CHECK: return [[VAL_80]] : i32 +// CHECK: ^bb4([[VAL_81:%.*]]: i32): +// CHECK: return [[VAL_81]] : i32 +// CHECK: ^bb5: +// CHECK: [[VAL_82:%.*]] = constant 0 : i32 +// CHECK: return [[VAL_82]] : i32 +// CHECK: } fir.select %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), -3,^bb3(%arg2,%2:i32,i32), 4,^bb4(%1:i32), unit,^bb5 ] ^bb1(%a : i32) : return %a : i32 @@ -191,13 +263,25 @@ return %zero : i32 } -// CHECK-LABEL: @bar_select_rank +// CHECK-LABEL: func @bar_select_rank( +// CHECK-SAME: [[VAL_83:%.*]]: i32, [[VAL_84:%.*]]: i32) -> i32 { func @bar_select_rank(%arg : i32, %arg2 : i32) -> i32 { +// CHECK: [[VAL_85:%.*]] = constant 1 : i32 +// CHECK: [[VAL_86:%.*]] = constant 2 : i32 +// CHECK: [[VAL_87:%.*]] = constant 3 : i32 +// CHECK: [[VAL_88:%.*]] = constant 4 : i32 %0 = constant 1 : i32 %1 = constant 2 : i32 %2 = constant 3 : i32 %3 = constant 4 : i32 - // CHECK: fir.select_rank %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), 3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), -1, ^bb4(%{{.*}} : i32), unit, ^bb5] + +// CHECK: fir.select_rank [[VAL_83]] : i32 [1, ^bb1([[VAL_85]] : i32), 2, ^bb2([[VAL_87]], [[VAL_83]], [[VAL_84]] : i32, i32, i32), 3, ^bb3([[VAL_84]], [[VAL_87]] : i32, i32), -1, ^bb4([[VAL_86]] : i32), unit, ^bb5] +// CHECK: ^bb1([[VAL_89:%.*]]: i32): +// CHECK: return [[VAL_89]] : i32 +// CHECK: ^bb2([[VAL_90:%.*]]: i32, [[VAL_91:%.*]]: i32, [[VAL_92:%.*]]: i32): +// CHECK: [[VAL_93:%.*]] = addi [[VAL_90]], [[VAL_91]] : i32 +// CHECK: [[VAL_94:%.*]] = addi [[VAL_93]], [[VAL_92]] : i32 +// CHECK: return [[VAL_94]] : i32 fir.select_rank %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), 3,^bb3(%arg2,%2:i32,i32), -1,^bb4(%1:i32), unit,^bb5 ] ^bb1(%a : i32) : return %a : i32 @@ -205,26 +289,56 @@ %4 = addi %b, %b2 : i32 %5 = addi %4, %b3 : i32 return %5 : i32 + +// CHECK: ^bb3([[VAL_95:%.*]]: i32, [[VAL_96:%.*]]: i32): +// CHECK: [[VAL_97:%.*]] = addi [[VAL_95]], [[VAL_96]] : i32 +// CHECK: return [[VAL_97]] : i32 +// CHECK: ^bb4([[VAL_98:%.*]]: i32): +// CHECK: return [[VAL_98]] : i32 ^bb3(%c:i32, %c2:i32) : %6 = addi %c, %c2 : i32 return %6 : i32 ^bb4(%d : i32) : return %d : i32 + +// CHECK: ^bb5: +// CHECK: [[VAL_99:%.*]] = constant 0 : i32 +// CHECK: [[VAL_100:%.*]] = fir.call @get_method_box() : () -> !fir.box> +// CHECK: fir.dispatch "method"([[VAL_100]]) : (!fir.box>) -> () ^bb5 : %zero = constant 0 : i32 %7 = fir.call @get_method_box() : () -> !fir.box> fir.dispatch method(%7) : (!fir.box>) -> () + +// CHECK: return [[VAL_99]] : i32 +// CHECK: } return %zero : i32 } -// CHECK-LABEL: @bar_select_type +// CHECK-LABEL: func @bar_select_type( +// CHECK-SAME: [[VAL_101:%.*]]: !fir.box}>>) -> i32 { func @bar_select_type(%arg : !fir.box}>>) -> i32 { + +// CHECK: [[VAL_102:%.*]] = constant 1 : i32 +// CHECK: [[VAL_103:%.*]] = constant 2 : i32 +// CHECK: [[VAL_104:%.*]] = constant 3 : i32 +// CHECK: [[VAL_105:%.*]] = constant 4 : i32 %0 = constant 1 : i32 %1 = constant 2 : i32 %2 = constant 3 : i32 %3 = constant 4 : i32 - // CHECK: fir.select_type %{{.*}} : !fir.box}>> [#fir.instance>, ^bb1(%{{.*}} : i32), #fir.instance>, ^bb2(%{{.*}} : i32), #fir.subsumed>, ^bb3(%{{.*}} : i32), #fir.instance>, ^bb4(%{{.*}} : i32), unit, ^bb5] + +// CHECK: fir.select_type [[VAL_101]] : !fir.box}>> [#fir.instance>, ^bb1([[VAL_102]] : i32), #fir.instance>, ^bb2([[VAL_104]] : i32), #fir.subsumed>, ^bb3([[VAL_104]] : i32), #fir.instance>, ^bb4([[VAL_103]] : i32), unit, ^bb5] fir.select_type %arg : !fir.box}>> [ #fir.instance>,^bb1(%0:i32), #fir.instance>,^bb2(%2:i32), #fir.subsumed>,^bb3(%2:i32), #fir.instance>,^bb4(%1:i32), unit,^bb5 ] + +// CHECK: ^bb1([[VAL_106:%.*]]: i32): +// CHECK: return [[VAL_106]] : i32 +// CHECK: ^bb2([[VAL_107:%.*]]: i32): +// CHECK: return [[VAL_107]] : i32 +// CHECK: ^bb3([[VAL_108:%.*]]: i32): +// CHECK: return [[VAL_108]] : i32 +// CHECK: ^bb4([[VAL_109:%.*]]: i32): +// CHECK: return [[VAL_109]] : i32 ^bb1(%a : i32) : return %a : i32 ^bb2(%b : i32) : @@ -233,19 +347,43 @@ return %c : i32 ^bb4(%d : i32) : return %d : i32 + +// CHECK: ^bb5: +// CHECK: [[VAL_110:%.*]] = constant 0 : i32 +// CHECK: return [[VAL_110]] : i32 +// CHECK: } ^bb5 : %zero = constant 0 : i32 return %zero : i32 } -// CHECK-LABEL: @bar_select_case +// CHECK-LABEL: func @bar_select_case( +// CHECK-SAME: [[VAL_111:%.*]]: i32, [[VAL_112:%.*]]: i32) -> i32 { +// CHECK: [[VAL_113:%.*]] = constant 1 : i32 +// CHECK: [[VAL_114:%.*]] = constant 2 : i32 +// CHECK: [[VAL_115:%.*]] = constant 3 : i32 +// CHECK: [[VAL_116:%.*]] = constant 4 : i32 func @bar_select_case(%arg : i32, %arg2 : i32) -> i32 { %0 = constant 1 : i32 %1 = constant 2 : i32 %2 = constant 3 : i32 %3 = constant 4 : i32 - // CHECK: fir.select_case %{{.*}} : i32 [#fir.point, %{{.*}}, ^bb1(%{{.*}} : i32), #fir.lower, %{{.*}}, ^bb2(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32, i32), #fir.interval, %{{.*}}, %{{.*}}, ^bb3(%{{.*}}, %{{.*}} : i32, i32), #fir.upper, %{{.*}}, ^bb4(%{{.*}} : i32), unit, ^bb5] + +// CHECK: fir.select_case [[VAL_111]] : i32 [#fir.point, [[VAL_113]], ^bb1([[VAL_113]] : i32), #fir.lower, [[VAL_114]], ^bb2([[VAL_115]], [[VAL_111]], [[VAL_112]], [[VAL_114]] : i32, i32, i32, i32), #fir.interval, [[VAL_115]], [[VAL_116]], ^bb3([[VAL_115]], [[VAL_112]] : i32, i32), #fir.upper, [[VAL_111]], ^bb4([[VAL_114]] : i32), unit, ^bb5] fir.select_case %arg : i32 [#fir.point, %0, ^bb1(%0:i32), #fir.lower, %1, ^bb2(%2,%arg,%arg2,%1:i32,i32,i32,i32), #fir.interval, %2, %3, ^bb3(%2,%arg2:i32,i32), #fir.upper, %arg, ^bb4(%1:i32), unit, ^bb5] + +// CHECK: ^bb1([[VAL_117:%.*]]: i32): +// CHECK: return [[VAL_117]] : i32 +// CHECK: ^bb2([[VAL_118:%.*]]: i32, [[VAL_119:%.*]]: i32, [[VAL_120:%.*]]: i32, [[VAL_121:%.*]]: i32): +// CHECK: [[VAL_122:%.*]] = addi [[VAL_118]], [[VAL_119]] : i32 +// CHECK: [[VAL_123:%.*]] = muli [[VAL_122]], [[VAL_120]] : i32 +// CHECK: [[VAL_124:%.*]] = addi [[VAL_123]], [[VAL_121]] : i32 +// CHECK: return [[VAL_124]] : i32 +// CHECK: ^bb3([[VAL_125:%.*]]: i32, [[VAL_126:%.*]]: i32): +// CHECK: [[VAL_127:%.*]] = addi [[VAL_125]], [[VAL_126]] : i32 +// CHECK: return [[VAL_127]] : i32 +// CHECK: ^bb4([[VAL_128:%.*]]: i32): +// CHECK: return [[VAL_128]] : i32 ^bb1(%a : i32) : return %a : i32 ^bb2(%b : i32, %b2:i32, %b3:i32, %b4:i32) : @@ -258,146 +396,211 @@ return %7 : i32 ^bb4(%d : i32) : return %d : i32 + +// CHECK: ^bb5: +// CHECK: [[VAL_129:%.*]] = constant 0 : i32 +// CHECK: return [[VAL_129]] : i32 +// CHECK: } ^bb5 : %zero = constant 0 : i32 return %zero : i32 } -// CHECK-LABEL: @global_var +// CHECK-LABEL: fir.global @global_var : i32 { +// CHECK: [[VAL_130:%.*]] = constant 1 : i32 +// CHECK: fir.has_value [[VAL_130]] : i32 +// CHECK: } fir.global @global_var : i32 { %0 = constant 1 : i32 fir.has_value %0 : i32 } -// CHECK-LABEL: @global_constant +// CHECK-LABEL: fir.global @global_constant constant : i32 { +// CHECK: [[VAL_131:%.*]] = constant 934 : i32 +// CHECK: fir.has_value [[VAL_131]] : i32 +// CHECK: } fir.global @global_constant constant : i32 { %0 = constant 934 : i32 fir.has_value %0 : i32 } -// CHECK-LABEL: @global_derived +// CHECK-LABEL: fir.global @global_derived : !fir.type { +// CHECK: fir.global_len "f", 1 : i32 +// CHECK: [[VAL_132:%.*]] = fir.undefined !fir.type +// CHECK: fir.has_value [[VAL_132]] : !fir.type +// CHECK: } fir.global @global_derived : !fir.type { - // CHECK: fir.global_len "f", 1 : i32 fir.global_len f, 1 : i32 %0 = fir.undefined !fir.type fir.has_value %0 : !fir.type } -// CHECK-LABEL: @dispatch_tbl +// CHECK-LABEL: fir.dispatch_table @dispatch_tbl { +// CHECK: fir.dt_entry "method", @method_impl +// CHECK: } fir.dispatch_table @dispatch_tbl { - // CHECK: fir.dt_entry "method", @method_impl fir.dt_entry "method", @method_impl } -// CHECK-LABEL: @compare_real +// CHECK-LABEL: func @compare_real( +// CHECK-SAME: [[VAL_133:%.*]]: !fir.real<16>, [[VAL_134:%.*]]: !fir.real<16>) { func @compare_real(%a : !fir.real<16>, %b : !fir.real<16>) { - // CHECK: fir.cmpf "false", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "oeq", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "ogt", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "oge", %{{.*}}, %{{.*}} : !fir.real<16> + +// CHECK: [[VAL_135:%.*]] = fir.cmpf "false", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_136:%.*]] = fir.cmpf "oeq", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_137:%.*]] = fir.cmpf "ogt", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_138:%.*]] = fir.cmpf "oge", [[VAL_133]], [[VAL_134]] : !fir.real<16> %d0 = fir.cmpf "false", %a, %b : !fir.real<16> %d1 = fir.cmpf "oeq", %a, %b : !fir.real<16> %d2 = fir.cmpf "ogt", %a, %b : !fir.real<16> %d3 = fir.cmpf "oge", %a, %b : !fir.real<16> - // CHECK: fir.cmpf "olt", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "ole", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "one", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "ord", %{{.*}}, %{{.*}} : !fir.real<16> + +// CHECK: [[VAL_139:%.*]] = fir.cmpf "olt", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_140:%.*]] = fir.cmpf "ole", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_141:%.*]] = fir.cmpf "one", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_142:%.*]] = fir.cmpf "ord", [[VAL_133]], [[VAL_134]] : !fir.real<16> %a0 = fir.cmpf "olt", %a, %b : !fir.real<16> %a1 = fir.cmpf "ole", %a, %b : !fir.real<16> %a2 = fir.cmpf "one", %a, %b : !fir.real<16> %a3 = fir.cmpf "ord", %a, %b : !fir.real<16> - // CHECK: fir.cmpf "ueq", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "ugt", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "uge", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "ult", %{{.*}}, %{{.*}} : !fir.real<16> + +// CHECK: [[VAL_143:%.*]] = fir.cmpf "ueq", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_144:%.*]] = fir.cmpf "ugt", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_145:%.*]] = fir.cmpf "uge", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_146:%.*]] = fir.cmpf "ult", [[VAL_133]], [[VAL_134]] : !fir.real<16> %b0 = fir.cmpf "ueq", %a, %b : !fir.real<16> %b1 = fir.cmpf "ugt", %a, %b : !fir.real<16> %b2 = fir.cmpf "uge", %a, %b : !fir.real<16> %b3 = fir.cmpf "ult", %a, %b : !fir.real<16> - // CHECK: fir.cmpf "ule", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "une", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "uno", %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.cmpf "true", %{{.*}}, %{{.*}} : !fir.real<16> + +// CHECK: [[VAL_147:%.*]] = fir.cmpf "ule", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_148:%.*]] = fir.cmpf "une", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_149:%.*]] = fir.cmpf "uno", [[VAL_133]], [[VAL_134]] : !fir.real<16> +// CHECK: [[VAL_150:%.*]] = fir.cmpf "true", [[VAL_133]], [[VAL_134]] : !fir.real<16> %c0 = fir.cmpf "ule", %a, %b : !fir.real<16> %c1 = fir.cmpf "une", %a, %b : !fir.real<16> %c2 = fir.cmpf "uno", %a, %b : !fir.real<16> %c3 = fir.cmpf "true", %a, %b : !fir.real<16> + +// CHECK: return +// CHECK: } return } -// CHECK-LABEL: @compare_complex +// CHECK-LABEL: func @compare_complex( +// CHECK-SAME: [[VAL_151:%.*]]: !fir.complex<16>, [[VAL_152:%.*]]: !fir.complex<16>) { func @compare_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) { - // CHECK: fir.cmpc "false", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "oeq", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "ogt", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "oge", %{{.*}}, %{{.*}} : !fir.complex<16> + +// CHECK: [[VAL_153:%.*]] = fir.cmpc "false", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_154:%.*]] = fir.cmpc "oeq", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_155:%.*]] = fir.cmpc "ogt", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_156:%.*]] = fir.cmpc "oge", [[VAL_151]], [[VAL_152]] : !fir.complex<16> %d0 = fir.cmpc "false", %a, %b : !fir.complex<16> %d1 = fir.cmpc "oeq", %a, %b : !fir.complex<16> %d2 = fir.cmpc "ogt", %a, %b : !fir.complex<16> %d3 = fir.cmpc "oge", %a, %b : !fir.complex<16> - // CHECK: fir.cmpc "olt", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "ole", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "one", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "ord", %{{.*}}, %{{.*}} : !fir.complex<16> + +// CHECK: [[VAL_157:%.*]] = fir.cmpc "olt", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_158:%.*]] = fir.cmpc "ole", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_159:%.*]] = fir.cmpc "one", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_160:%.*]] = fir.cmpc "ord", [[VAL_151]], [[VAL_152]] : !fir.complex<16> %a0 = fir.cmpc "olt", %a, %b : !fir.complex<16> %a1 = fir.cmpc "ole", %a, %b : !fir.complex<16> %a2 = fir.cmpc "one", %a, %b : !fir.complex<16> %a3 = fir.cmpc "ord", %a, %b : !fir.complex<16> - // CHECK: fir.cmpc "ueq", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "ugt", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "uge", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "ult", %{{.*}}, %{{.*}} : !fir.complex<16> + +// CHECK: [[VAL_161:%.*]] = fir.cmpc "ueq", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_162:%.*]] = fir.cmpc "ugt", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_163:%.*]] = fir.cmpc "uge", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_164:%.*]] = fir.cmpc "ult", [[VAL_151]], [[VAL_152]] : !fir.complex<16> %b0 = fir.cmpc "ueq", %a, %b : !fir.complex<16> %b1 = fir.cmpc "ugt", %a, %b : !fir.complex<16> %b2 = fir.cmpc "uge", %a, %b : !fir.complex<16> %b3 = fir.cmpc "ult", %a, %b : !fir.complex<16> - // CHECK: fir.cmpc "ule", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "une", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "uno", %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.cmpc "true", %{{.*}}, %{{.*}} : !fir.complex<16> + +// CHECK: [[VAL_165:%.*]] = fir.cmpc "ule", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_166:%.*]] = fir.cmpc "une", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_167:%.*]] = fir.cmpc "uno", [[VAL_151]], [[VAL_152]] : !fir.complex<16> +// CHECK: [[VAL_168:%.*]] = fir.cmpc "true", [[VAL_151]], [[VAL_152]] : !fir.complex<16> %c0 = fir.cmpc "ule", %a, %b : !fir.complex<16> %c1 = fir.cmpc "une", %a, %b : !fir.complex<16> %c2 = fir.cmpc "uno", %a, %b : !fir.complex<16> %c3 = fir.cmpc "true", %a, %b : !fir.complex<16> +// CHECK: return +// CHECK: } return } -// CHECK-LABEL: @arith_real +// CHECK-LABEL: func @arith_real( +// CHECK-SAME: [[VAL_169:%.*]]: !fir.real<16>, [[VAL_170:%.*]]: !fir.real<16>) -> !fir.real<16> { func @arith_real(%a : !fir.real<16>, %b : !fir.real<16>) -> !fir.real<16> { + +// CHECK: [[VAL_171:%.*]] = constant 1.0 +// CHECK: [[VAL_172:%.*]] = fir.convert [[VAL_171]] : (f32) -> !fir.real<16> +// CHECK: [[VAL_173:%.*]] = fir.negf [[VAL_169]] : !fir.real<16> +// CHECK: [[VAL_174:%.*]] = fir.addf [[VAL_172]], [[VAL_173]] : !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> %c1 = constant 1.0 : f32 %0 = fir.convert %c1 : (f32) -> !fir.real<16> - // CHECK: %[[R1:.*]] = fir.negf %{{.*}} : !fir.real<16> - // CHECK: fir.addf %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: %[[R3:.*]] = fir.subf %{{.*}}, %{{.*}} : !fir.real<16> - // CHECK: fir.mulf %[[R1]], %[[R3]] : !fir.real<16> - // CHECK: fir.divf %{{.*}}, %{{.*}} : !fir.real<16> %1 = fir.negf %a : !fir.real<16> %2 = fir.addf %0, %1 : !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> +// CHECK: } return %5 : !fir.real<16> } -// CHECK-LABEL: @arith_complex +// CHECK-LABEL: func @arith_complex( +// CHECK-SAME: [[VAL_178:%.*]]: !fir.complex<16>, [[VAL_179:%.*]]: !fir.complex<16>) -> !fir.complex<16> { func @arith_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) -> !fir.complex<16> { - // CHECK: fir.negc %{{.*}} : !fir.complex<16> - // CHECK: fir.addc %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.subc %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.mulc %{{.*}}, %{{.*}} : !fir.complex<16> - // CHECK: fir.divc %{{.*}}, %{{.*}} : !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> %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 %5 : !fir.complex<16> } -// CHECK-LABEL: @character_literal +// CHECK-LABEL: func @character_literal() -> !fir.array<13x!fir.char<1>> { func @character_literal() -> !fir.array<13 x !fir.char<1>> { - // CHECK: fir.string_lit "Hello, World!"(13) : !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: } +} + +// CHECK-LABEL: func @earlyexit2(i32) -> i1 +func @earlyexit2(%a : i32) -> i1 + +// CHECK-LABEL: func @early_exit( +// CHECK-SAME: [[VAL_186:%.*]]: i1, [[VAL_187:%.*]]: i32) -> i1 { +func @early_exit(%ok : i1, %k : i32) -> i1 { +// CHECK: [[VAL_188:%.*]] = constant 1 : index +// CHECK: [[VAL_189:%.*]] = 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_195:%.*]] = call @earlyexit2([[VAL_194]]) : (i32) -> i1 +// CHECK: fir.result [[VAL_195]], [[VAL_194]] : i1, i32 +// CHECK: } + %newOk:2 = fir.iterate_while (%i = %c1 to %c100 step %c1) and (%ok_ = %ok) iter_args(%v = %k) -> (i32) { + %stop = call @earlyexit2(%v) : (i32) -> i1 + fir.result %stop, %v : i1, i32 + } +// CHECK: return [[VAL_190]] : i1 +// CHECK: } + return %newOk#0 : i1 }