[flang-commits] [flang] bc03423 - [flang] Upstream recent work on FIR to llvm-project.

Eric Schweitz via flang-commits flang-commits at lists.llvm.org
Mon Apr 27 17:49:16 PDT 2020


Author: Eric Schweitz
Date: 2020-04-27T17:48:57-07:00
New Revision: bc0342383dde329ab2bb07085900a533f0c053db

URL: https://github.com/llvm/llvm-project/commit/bc0342383dde329ab2bb07085900a533f0c053db
DIFF: https://github.com/llvm/llvm-project/commit/bc0342383dde329ab2bb07085900a533f0c053db.diff

LOG: [flang] Upstream recent work on FIR to llvm-project.

Summary:

Reviewers: DavidTruby, sscalpone, jeanPerier

Subscribers: mgorny, aartbik, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D78835

Added: 
    

Modified: 
    flang/include/flang/Optimizer/Dialect/FIRDialect.h
    flang/include/flang/Optimizer/Dialect/FIROps.h
    flang/include/flang/Optimizer/Dialect/FIROps.td
    flang/include/flang/Optimizer/Dialect/FIRType.h
    flang/lib/Optimizer/Dialect/FIRAttr.cpp
    flang/lib/Optimizer/Dialect/FIRDialect.cpp
    flang/lib/Optimizer/Dialect/FIROps.cpp
    flang/lib/Optimizer/Dialect/FIRType.cpp
    flang/test/Fir/fir-ops.fir

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Dialect/FIRDialect.h b/flang/include/flang/Optimizer/Dialect/FIRDialect.h
index 7a8fc18937fc..92fd23b5044f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRDialect.h
+++ b/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

diff  --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index f5763693f7bb..992fe48d539c 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/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;
 

diff  --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 15d1bbf89001..8ecb1711c7e0 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/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_AllocatableBaseOp<string mnemonic, list<OpTrait> traits = []> :
   );
 }
 
-class fir_AllocatableOp<string mnemonic, list<OpTrait> traits =[]> :
-    fir_AllocatableBaseOp<mnemonic, !listconcat([NoSideEffect], traits)>,
+class fir_AllocatableOp<string mnemonic, list<OpTrait> traits = []> :
+    fir_AllocatableBaseOp<mnemonic,
+	!listconcat(traits, [MemoryEffects<[MemAlloc]>])>,
     fir_TwoBuilders<fir_AllocateOpBuilder, fir_NamedAllocateOpBuilder>,
     Arguments<(ins TypeAttr:$in_type, Variadic<AnyIntegerType>:$args)> {
 
@@ -263,18 +265,27 @@ class fir_AllocatableOp<string mnemonic, list<OpTrait> traits =[]> :
     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<mlir::IntegerAttr>(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 @@ class fir_AllocatableOp<string mnemonic, list<OpTrait> traits =[]> :
   // Verify checks common to all allocation operations
   string allocVerify = [{
     llvm::SmallVector<llvm::StringRef, 8> 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_AllocaOp : fir_AllocatableOp<"alloca"> {
   }];
 }
 
-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_LoadOp : fir_OneResultOp<"load", []> {
   }];
 }
 
-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 @@ def fir_UndefOp : fir_OneResultOp<"undefined", [NoSideEffect]> {
   let assemblyFormat = "type($intype) attr-dict";
 
   let verifier = [{
-    if (auto ref = getType().dyn_cast<fir::ReferenceType>())
-      return emitOpError("undefined values of type !fir.ref not allowed");
+    // allow `undef : ref<T>` since it is a possible from transformations
     return mlir::success();
   }];
 }
@@ -508,7 +520,7 @@ def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> {
   }];
 }
 
-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 @@ def fir_FreeMemOp : fir_Op<"freemem", []> {
   let assemblyFormat = "$heapref attr-dict `:` type($heapref)";
 }
 
-//===----------------------------------------------------------------------===//// Terminator operations
+//===----------------------------------------------------------------------===//
+// Terminator operations
 //===----------------------------------------------------------------------===//
 
 class fir_SwitchTerminatorOp<string mnemonic, list<OpTrait> traits = []> :
@@ -547,29 +560,15 @@ class fir_SwitchTerminatorOp<string mnemonic, list<OpTrait> traits = []> :
 
   let successors = (successor VariadicSuccessor<AnySuccessor>:$targets);
 
-  let builders = [OpBuilder<
-    "Builder *, OperationState &result, Value selector,"
-    "ValueRange properOperands, ArrayRef<Block *> destinations,"
-    "ArrayRef<ValueRange> operands = {},"
-    "ArrayRef<NamedAttribute> 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<mlir::ArrayAttr>(getCasesAttr()).size();
+    }
 
     // The selector is the value being tested to determine the destination
     mlir::Value getSelector() { return selector(); }
@@ -596,12 +595,51 @@ class fir_SwitchTerminatorOp<string mnemonic, list<OpTrait> traits = []> :
       else
         p.printSuccessor(succ);
     }
+
+    unsigned targetOffsetSize();
   }];
 }
 
 class fir_IntegralSwitchTerminatorOp<string mnemonic,
     list<OpTrait> traits = []> : fir_SwitchTerminatorOp<mnemonic, traits> {
 
+  let skipDefaultBuilders = 1;
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState &result, Value selector,"
+    "ArrayRef<int64_t> compareOperands, ArrayRef<Block *> destinations,"
+    "ArrayRef<ValueRange> destOperands = {},"
+    "ArrayRef<NamedAttribute> attributes = {}",
+    [{
+      result.addOperands(selector);
+      llvm::SmallVector<mlir::Attribute, 8> 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<int32_t, 8> argOffs;
+      int32_t sumArgs = 0;
+      for (std::remove_const_t<decltype(count)> 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 @@ class fir_IntegralSwitchTerminatorOp<string mnemonic,
           getSelector().getType().isa<fir::IntType>()))
       return emitOpError("must be an integer");
     auto cases = getAttrOfType<mlir::ArrayAttr>(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<mlir::IntegerAttr>() || attr.isa<mlir::UnitAttr>()) {
-        // ok
-      } else {
+      if (!(attr.isa<mlir::IntegerAttr>() || attr.isa<mlir::UnitAttr>()))
         return emitOpError("invalid case alternative");
-      }
     }
     return mlir::success();
   }];
@@ -750,6 +791,17 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> {
     ```
   }];
 
+  let skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<"Builder *builder, OperationState &result, Value selector,"
+    "ArrayRef<mlir::Attribute> compareAttrs, ArrayRef<ValueRange> cmpOperands,"
+    "ArrayRef<Block *> destinations, ArrayRef<ValueRange> destOperands = {},"
+    "ArrayRef<NamedAttribute> attributes = {}">,
+    OpBuilder<"Builder *builder, OperationState &result, Value selector,"
+    "ArrayRef<mlir::Attribute> compareAttrs, ArrayRef<Value> cmpOpList,"
+    "ArrayRef<Block *> destinations, ArrayRef<ValueRange> destOperands = {},"
+    "ArrayRef<NamedAttribute> attributes = {}">];
+
   let parser = "return parseSelectCase(parser, result);";
 
   let printer = [{
@@ -786,23 +838,30 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> {
           getSelector().getType().isa<fir::CharacterType>()))
       return emitOpError("must be an integer, character, or logical");
     auto cases = getAttrOfType<mlir::ArrayAttr>(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<fir::PointIntervalAttr>() ||
-          attr.isa<fir::LowerBoundAttr>() ||
-          attr.isa<fir::UpperBoundAttr>() ||
-          attr.isa<fir::ClosedIntervalAttr>() ||
-          attr.isa<mlir::UnitAttr>()) {
-        // ok
-      } else {
+      if (!(attr.isa<fir::PointIntervalAttr>() ||
+            attr.isa<fir::LowerBoundAttr>() ||
+            attr.isa<fir::UpperBoundAttr>() ||
+            attr.isa<fir::ClosedIntervalAttr>() ||
+            attr.isa<mlir::UnitAttr>()))
         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 @@ def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> {
     ```
   }];
 
+  let skipDefaultBuilders = 1;
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState &result, Value selector,"
+    "ArrayRef<mlir::Attribute> typeOperands,"
+    "ArrayRef<Block *> destinations, ArrayRef<ValueRange> destOperands = {},"
+    "ArrayRef<NamedAttribute> 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<int32_t, 8> argOffs;
+      int32_t sumArgs = 0;
+      for (std::remove_const_t<decltype(count)> 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 @@ def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> {
     if (!(getSelector().getType().isa<fir::BoxType>()))
       return emitOpError("must be a boxed type");
     auto cases = getAttrOfType<mlir::ArrayAttr>(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<fir::ExactTypeAttr>() || attr.isa<fir::SubclassAttr>() ||
-          attr.isa<mlir::UnitAttr>()) {
-        // ok
-      } else {
+      if (!(attr.isa<fir::ExactTypeAttr>() || attr.isa<fir::SubclassAttr>() ||
+            attr.isa<mlir::UnitAttr>()))
         return emitOpError("invalid type-case alternative");
-      }
     }
     return mlir::success();
   }];
@@ -1016,9 +1111,6 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]> {
 }
 
 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 @@ def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> {
     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_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> {
 }
 
 def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]> {
-
   let summary = "boxes a given procedure and optional host context";
 
   let description = [{
@@ -1232,6 +1327,8 @@ def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]> {
   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 @@ def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]> {
   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 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> {
 
     ```mlir
       %i = ... : index
-      %h = ... : !fir.heap<!fir.array<?:f32>>
-      %p = fir.coordinate_of %h, %i : (!fir.heap<!fir.array<?:f32>>, index) -> !fir.ref<f32>
+      %h = ... : !fir.heap<!fir.array<100 x f32>>
+      %p = fir.coordinate_of %h, %i : (!fir.heap<!fir.array<100 x f32>>, index) -> !fir.ref<f32>
     ```
 
     In the example, `%p` will be a pointer to the `%i`-th f32 value in the
@@ -1457,11 +1556,27 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> {
 
   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<fir::SequenceType>()) {
+        if (arrTy.hasUnknownShape())
+          return emitOpError("cannot find coordinate in unknown shape");
+        if (arrTy.getConstantRows() < arrTy.getDimension() - 1)
+          return emitOpError("cannot find coordinate with unknown extents");
+      }
+    }
     // Recovering a LEN type parameter only makes sense from a boxed value
     for (auto co : coor())
       if (dyn_cast_or_null<LenParamIndexOp>(co.getDefiningOp())) {
@@ -1470,8 +1585,28 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> {
         if (!ref().getType().dyn_cast<BoxType>())
           return emitOpError("len_param_index must be used on box type");
       }
+    if (auto attr = getAttr(CoordinateOp::baseType())) {
+      if (!attr.isa<mlir::TypeAttr>())
+        return emitOpError("improperly constructed");
+    } else {
+      return emitOpError("must have base type");
+    }
     return mlir::success();
   }];
+
+  let skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "Type type, Value ref, ValueRange coor,"
+	      "ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "Type type, ValueRange operands,"
+	      "ArrayRef<NamedAttribute> 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 @@ def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]> {
     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,52 @@ def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
 // Fortran loops
+//===----------------------------------------------------------------------===//
 
-def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
+def fir_ResultOp : fir_Op<"result", [NoSideEffect, ReturnLike, Terminator]> {
+  let summary = "special terminator for use in fir region operations";
+
+  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<AnyType>:$results);
+  let builders = [
+    OpBuilder<"Builder *builder, OperationState &result", "/* do nothing */">
+  ];
+
+  let assemblyFormat = "($results^ `:` type($results))? attr-dict";
+
+  let verifier = [{ return ::verify(*this); }];
+}
 
-def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> {
+def FirRegionTerminator : SingleBlockImplicitTerminator<"ResultOp">;
+
+class region_Op<string mnemonic, list<OpTrait> traits = []> :
+    fir_Op<mnemonic,
+    !listconcat(traits, [FirRegionTerminator, RecursiveSideEffects])> {
+  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<LoopLikeOpInterface>]> {
   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<f32>, index) -> !fir.ref<f32>
@@ -1741,143 +1908,100 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> {
   let arguments = (ins
     Index:$lowerBound,
     Index:$upperBound,
-    Variadic<Index>:$optStep,
-    OptionalAttr<I64Attr>:$constantStep,
+    Index:$step,
+    Variadic<AnyType>:$initArgs,
     OptionalAttr<UnitAttr>:$unordered
   );
-
-  let results = (outs Variadic<Index>:$lastVal);
-
+  let results = (outs
+    Variadic<AnyType>:$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<NamedAttribute> attributes = {}">
+              "mlir::Value step, bool unordered = false,"
+              "ValueRange iterArgs = llvm::None,"
+              "ArrayRef<NamedAttribute> 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<mlir::ConstantIndexOp>(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<UnitAttr>(); }
+    static constexpr llvm::StringRef unorderedAttrName() { return "unordered"; }
 
-    /// Does loop set (and return) the final value of the control variable?
-    bool hasLastValue() { return lastVal().size(); }
-
-    /// 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 setLowerBound(Value bound) { getOperation()->setOperand(0, bound); }
+    void setUpperBound(Value bound) { getOperation()->setOperand(1, bound); }
+    void setStep(Value step) { getOperation()->setOperand(2, step); }
 
-    void setUpperBound(mlir::Value bound) {
-      getOperation()->setOperand(1, bound);
+    /// 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();
     }
 
-    void setStep(mlir::Value step) {
-      getOperation()->setOperand(2, step);
+    /// Get the body of the loop
+    mlir::Block *getBody() { return &region().front(); }
+
+    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<!T>
-      fir.where %56 {
+      fir.if %56 {
         fir.store %76 to %78 : !fir.ref<!T>
-      } otherwise {
+      } else {
         fir.store %77 to %78 : !fir.ref<!T>
       }
     ```
   }];
 
   let arguments = (ins I1:$condition);
+  let results = (outs Variadic<AnyType>:$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 &region : 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 +2016,81 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> {
   }];
 }
 
+def fir_IterWhileOp : region_Op<"iterate_while",
+    [DeclareOpInterfaceMethods<LoopLikeOpInterface>]> {
+  let summary = "DO loop with early exit condition";
+  let description = [{
+    This construct is useful for lowering implied-DO loops. It is very similar
+    to `fir::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<AnyType>:$initArgs
+  );
+  let results = (outs
+    I1:$iterateResult,
+    Variadic<AnyType>:$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<NamedAttribute> attributes = {}">
+  ];
+  
+  let extraClassDeclaration = [{
+    mlir::Block *getBody() { return &region().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 +2120,8 @@ def fir_CallOp : fir_Op<"call", []> {
   }];
 }
 
-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 +2222,8 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
     else if (auto v = val.dyn_cast<mlir::ArrayAttr>())
       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 +2231,11 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
         parser.parseRParen() ||
         parser.parseColonType(type))
       return mlir::failure();
+    if (!(type.isa<fir::CharacterType>() || type.isa<mlir::IntegerType>()))
+      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 +2284,7 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]> {
 
 class fir_ArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
     fir_Op<mnemonic,
-       !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>,
+           !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>,
     Results<(outs AnyType)> {
   let parser = [{
     return impl::parseOneResultSameOperandTypeOp(parser, result);
@@ -2095,7 +2295,7 @@ class fir_ArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
 
 class fir_UnaryArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
       fir_Op<mnemonic,
-         !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>,
+             !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>,
       Results<(outs AnyType)> {
   let parser = [{
     return impl::parseOneResultSameOperandTypeOp(parser, result);
@@ -2140,9 +2340,15 @@ class RealArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
       fir_ArithmeticOp<mnemonic, traits>,
       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 +2389,7 @@ def fir_CmpfOp : fir_Op<"cmpf",
   }];
 }
 
-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 +2398,8 @@ def fir_ConstcOp : fir_Op<"constc", [NoSideEffect]>,
     the standard dialect.
   }];
 
+  let results = (outs fir_ComplexType);
+  
   let parser = [{
     fir::RealAttr realp;
     fir::RealAttr imagp;
@@ -2290,6 +2497,7 @@ def fir_CmpcOp : fir_Op<"cmpc",
 
 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 +2516,7 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> {
 
 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 +2535,32 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]> {
   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<fir::BoxType>() && outType.isa<fir::BoxType>()) ||
+        (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<And<[CPred<"$_self.isa<TypeAttr>()">,
@@ -2396,7 +2631,7 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> {
 }
 
 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 +2675,86 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
         %z = constant 0 : index
         %o = constant 1 : index
         %4 = fir.insert_value %3, %1, %z : (tuple<i32, f32>, i32, index) -> tuple<i32, f32>
-        %5 = fir.insert_value %4, %1, %o : (tuple<i32, f32>, f32, index) -> tuple<i32, f32>
-        return %5
+        %5 = fir.insert_value %4, %2, %o : (tuple<i32, f32>, f32, index) -> tuple<i32, f32>
+        fir.has_value %5 : tuple<i32, f32>
       }
     ```
   }];
 
   let arguments = (ins
     StrAttr:$sym_name,
-    OptionalAttr<AnyAttr>:$initval,
-    UnitAttr:$constant,
-    TypeAttr:$type
+    SymbolRefAttr:$symref,
+    TypeAttr:$type,
+    OptionalAttr<AnyAttr>:$initVal,
+    OptionalAttr<UnitAttr>:$constant,
+    OptionalAttr<StrAttr>:$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<StringAttr>(
-      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<NamedAttribute> attrs = {}",
-    [{
-      result.addAttribute(typeAttrName(), mlir::TypeAttr::get(type));
-      result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
-                          builder->getStringAttr(name));
-      result.addAttributes(attrs);
-    }]>
+              "StringRef name, Type type, ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "StringRef name, bool isConstant, Type type,"
+	      "ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "StringRef name, Type type, StringAttr linkage = {},"
+	      "ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "StringRef name, bool isConstant, Type type,"
+	      "StringAttr linkage = {},"
+	      "ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "StringRef name, Type type, Attribute initVal,"
+	      "StringAttr linkage = {},"
+	      "ArrayRef<NamedAttribute> attrs = {}">,
+    OpBuilder<"mlir::Builder *builder, OperationState &result,"
+              "StringRef name, bool isConstant, Type type,"
+	      "Attribute initVal, StringAttr linkage = {},"
+	      "ArrayRef<NamedAttribute> 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<TypeAttr>(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 +2764,19 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
 
     /// 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<fir::FirEndOp>(getBlock().front());
+    }
+
+    mlir::FlatSymbolRefAttr getSymbol() {
+      return mlir::FlatSymbolRefAttr::get(getAttrOfType<mlir::StringAttr>(
+          mlir::SymbolTable::getSymbolAttrName()).getValue(), getContext());
+    }
   }];
 }
 
@@ -2594,8 +2827,10 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
   }];
 }
 
-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 = [{

diff  --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h
index 73174371718a..3749c87b9e94 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRType.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRType.h
@@ -24,6 +24,8 @@ class hash_code;
 namespace mlir {
 class DialectAsmParser;
 class DialectAsmPrinter;
+class ComplexType;
+class FloatType;
 } // namespace mlir
 
 namespace fir {
@@ -89,6 +91,19 @@ bool isa_fir_or_std_type(mlir::Type t);
 /// 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 @@ class CplxType : public mlir::Type::TypeBase<CplxType, mlir::Type,
 public:
   using Base::Base;
   static CplxType get(mlir::MLIRContext *ctxt, KindTy kind);
+
+  /// Get the corresponding fir.real<k> type.
+  mlir::Type getElementType() const;
+
   KindTy getFKind() const;
 };
 
@@ -324,6 +343,21 @@ class SequenceType : public mlir::Type::TypeBase<SequenceType, mlir::Type,
   /// 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<C...xCxT>`), unknown shape
+  /// (`array<*xT>`), or rows with shape and ending with column(s) of
+  /// unknown extent (`array<C...xCx?...x?xT>`).
+  bool hasConstantInterior() const;
+
   /// The value `-1` represents an unknown extent for a dimension
   static constexpr Extent getUnknownExtent() { return -1; }
 
@@ -394,6 +428,20 @@ mlir::Type parseFirType(FIROpsDialect *, mlir::DialectAsmParser &parser);
 
 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<fir::RealType>() || t.isa<mlir::FloatType>();
+}
+
+/// Is `t` a FIR or MLIR Complex type?
+inline bool isa_complex(mlir::Type t) {
+  return t.isa<fir::CplxType>() || t.isa<mlir::ComplexType>();
+}
+
 } // namespace fir
 
 #endif // OPTIMIZER_DIALECT_FIRTYPE_H

diff  --git a/flang/lib/Optimizer/Dialect/FIRAttr.cpp b/flang/lib/Optimizer/Dialect/FIRAttr.cpp
index 76cccbfb06d3..09780d306fb8 100644
--- a/flang/lib/Optimizer/Dialect/FIRAttr.cpp
+++ b/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;
 

diff  --git a/flang/lib/Optimizer/Dialect/FIRDialect.cpp b/flang/lib/Optimizer/Dialect/FIRDialect.cpp
index 9a56c6bc403e..6364c24137f2 100644
--- a/flang/lib/Optimizer/Dialect/FIRDialect.cpp
+++ b/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;
 

diff  --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index f62e1c4ced18..d8ef6f6f8515 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/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<llvm::StringRef> &visited) {
+                         llvm::SmallVectorImpl<llvm::StringRef> &visited,
+                         unsigned dynamicExtents = 0) {
   if (auto st = inType.dyn_cast<fir::SequenceType>()) {
     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<fir::RecordType>()) {
     // don't recurse if we're already visiting this one
     if (llvm::is_contained(visited, rt.getName()))
@@ -57,6 +64,15 @@ static bool verifyRecordLenParams(mlir::Type inType, unsigned numLenParams) {
   return false;
 }
 
+//===----------------------------------------------------------------------===//
+// AddfOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult fir::AddfOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  return mlir::constFoldBinaryOp<FloatAttr>(
+      opnds, [](APFloat a, APFloat b) { return a + b; });
+}
+
 //===----------------------------------------------------------------------===//
 // AllocaOp
 //===----------------------------------------------------------------------===//
@@ -100,6 +116,33 @@ mlir::Type fir::AllocMemOp::wrapResultType(mlir::Type intype) {
   return HeapType::get(intype);
 }
 
+//===----------------------------------------------------------------------===//
+// BoxAddrOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult fir::BoxAddrOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  if (auto v = val().getDefiningOp()) {
+    if (auto box = dyn_cast<fir::EmboxOp>(v))
+      return box.memref();
+    if (auto box = dyn_cast<fir::EmboxCharOp>(v))
+      return box.memref();
+  }
+  return {};
+}
+
+//===----------------------------------------------------------------------===//
+// BoxCharLenOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult
+fir::BoxCharLenOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  if (auto v = val().getDefiningOp()) {
+    if (auto box = dyn_cast<fir::EmboxCharOp>(v))
+      return box.len();
+  }
+  return {};
+}
+
 //===----------------------------------------------------------------------===//
 // BoxDimsOp
 //===----------------------------------------------------------------------===//
@@ -267,6 +310,103 @@ mlir::ParseResult fir::parseCmpcOp(mlir::OpAsmParser &parser,
   return parseCmpOp<fir::CmpcOp>(parser, result);
 }
 
+//===----------------------------------------------------------------------===//
+// ConvertOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult fir::ConvertOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  if (value().getType() == getType())
+    return value();
+  if (matchPattern(value(), m_Op<fir::ConvertOp>())) {
+    auto inner = cast<fir::ConvertOp>(value().getDefiningOp());
+    // (convert (convert 'a : logical -> i1) : i1 -> logical) ==> forward 'a
+    if (auto toTy = getType().dyn_cast<fir::LogicalType>())
+      if (auto fromTy = inner.value().getType().dyn_cast<fir::LogicalType>())
+        if (inner.getType().isa<mlir::IntegerType>() && (toTy == fromTy))
+          return inner.value();
+    // (convert (convert 'a : i1 -> logical) : logical -> i1) ==> forward 'a
+    if (auto toTy = getType().dyn_cast<mlir::IntegerType>())
+      if (auto fromTy = inner.value().getType().dyn_cast<mlir::IntegerType>())
+        if (inner.getType().isa<fir::LogicalType>() && (toTy == fromTy) &&
+            (fromTy.getWidth() == 1))
+          return inner.value();
+  }
+  return {};
+}
+
+bool fir::ConvertOp::isIntegerCompatible(mlir::Type ty) {
+  return ty.isa<mlir::IntegerType>() || ty.isa<mlir::IndexType>() ||
+         ty.isa<fir::IntType>() || ty.isa<fir::LogicalType>() ||
+         ty.isa<fir::CharacterType>();
+}
+
+bool fir::ConvertOp::isFloatCompatible(mlir::Type ty) {
+  return ty.isa<mlir::FloatType>() || ty.isa<fir::RealType>();
+}
+
+bool fir::ConvertOp::isPointerCompatible(mlir::Type ty) {
+  return ty.isa<fir::ReferenceType>() || ty.isa<fir::PointerType>() ||
+         ty.isa<fir::HeapType>() || ty.isa<mlir::MemRefType>() ||
+         ty.isa<fir::TypeDescType>();
+}
+
+//===----------------------------------------------------------------------===//
+// CoordinateOp
+//===----------------------------------------------------------------------===//
+
+static mlir::ParseResult parseCoordinateOp(mlir::OpAsmParser &parser,
+                                           mlir::OperationState &result) {
+  llvm::ArrayRef<mlir::Type> allOperandTypes;
+  llvm::ArrayRef<mlir::Type> allResultTypes;
+  llvm::SMLoc allOperandLoc = parser.getCurrentLocation();
+  llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> allOperands;
+  if (parser.parseOperandList(allOperands))
+    return failure();
+  if (parser.parseOptionalAttrDict(result.attributes))
+    return failure();
+  if (parser.parseColon())
+    return failure();
+
+  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<mlir::TypeAttr>().getValue();
+}
+
+void fir::CoordinateOp::build(Builder *, OperationState &result,
+                              mlir::Type resType, ValueRange operands,
+                              ArrayRef<NamedAttribute> attrs) {
+  assert(operands.size() >= 1u && "mismatched number of parameters");
+  result.addOperands(operands);
+  result.addAttribute(fir::CoordinateOp::baseType(),
+                      mlir::TypeAttr::get(operands[0].getType()));
+  result.attributes.append(attrs.begin(), attrs.end());
+  result.addTypes({resType});
+}
+
+void fir::CoordinateOp::build(Builder *builder, OperationState &result,
+                              mlir::Type resType, mlir::Value ref,
+                              ValueRange coor, ArrayRef<NamedAttribute> attrs) {
+  llvm::SmallVector<mlir::Value, 16> operands{ref};
+  operands.append(coor.begin(), coor.end());
+  build(builder, result, resType, operands, attrs);
+}
+
 //===----------------------------------------------------------------------===//
 // DispatchOp
 //===----------------------------------------------------------------------===//
@@ -341,10 +481,289 @@ void fir::GenTypeDescOp::build(Builder *, OperationState &result,
 // 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<NamedAttribute> 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<NamedAttribute> 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<NamedAttribute> 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<NamedAttribute> 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<NamedAttribute> attrs) {
+  build(builder, result, name, isConstant, type, StringAttr{}, attrs);
+}
+
+void fir::GlobalOp::build(mlir::Builder *builder, OperationState &result,
+                          StringRef name, Type type,
+                          ArrayRef<NamedAttribute> 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<const char *, 3> 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<mlir::NamedAttribute> 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<mlir::OpAsmParser::OperandType, 4> regionArgs;
+  // Induction variable.
+  regionArgs.push_back(inductionVariable);
+  regionArgs.push_back(iterateVar);
+  result.addTypes(i1Type);
+
+  if (mlir::succeeded(parser.parseOptionalKeyword("iter_args"))) {
+    llvm::SmallVector<mlir::OpAsmParser::OperandType, 4> operands;
+    llvm::SmallVector<mlir::Type, 4> regionTypes;
+    // Parse assignment list and results type list.
+    if (parser.parseAssignmentList(regionArgs, operands) ||
+        parser.parseArrowTypeList(regionTypes))
+      return mlir::failure();
+    // 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<mlir::Type, 4> 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<ConstantIndexOp>(op.step().getDefiningOp()))
+    if (cst.getValue() <= 0)
+      return op.emitOpError("constant step operand must be positive");
+
+  // Check that the body defines as single block argument for the induction
+  // variable.
+  auto *body = op.getBody();
+  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<mlir::Operation *> ops) {
+  for (auto op : ops)
+    op->moveBefore(*this);
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // LoadOp
 //===----------------------------------------------------------------------===//
@@ -367,65 +786,84 @@ mlir::ParseResult fir::LoadOp::getElementOf(mlir::Type &ele, mlir::Type ref) {
 // LoopOp
 //===----------------------------------------------------------------------===//
 
-void fir::LoopOp::build(mlir::Builder *builder, OperationState &result,
-                        mlir::Value lb, mlir::Value ub, ValueRange step,
-                        ArrayRef<NamedAttribute> 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<mlir::NamedAttribute> 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<mlir::OpAsmParser::OperandType, 4> regionArgs, operands;
+  llvm::SmallVector<mlir::Type, 4> 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,115 @@ fir::LoopOp fir::getForInductionVarOwner(mlir::Value val) {
   return dyn_cast_or_null<fir::LoopOp>(containingInst);
 }
 
+// Lifted from loop.loop
+static mlir::LogicalResult verify(fir::LoopOp op) {
+  if (auto cst = dyn_cast_or_null<ConstantIndexOp>(op.step().getDefiningOp()))
+    if (cst.getValue() <= 0)
+      return op.emitOpError("constant step operand must be positive");
+
+  // Check that the body defines as single block argument for the induction
+  // variable.
+  auto *body = op.getBody();
+  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<mlir::Operation *> ops) {
+  for (auto op : ops)
+    op->moveBefore(*this);
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// MulfOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult fir::MulfOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  return mlir::constFoldBinaryOp<FloatAttr>(
+      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<fir::WhereOp>(parentOp) || isa<fir::LoopOp>(parentOp) ||
+      isa<fir::IterWhileOp>(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();
+}
+
 //===----------------------------------------------------------------------===//
 // SelectOp
 //===----------------------------------------------------------------------===//
@@ -460,6 +1007,10 @@ static A getSubOperands(unsigned pos, A allArgs,
   return {std::next(allArgs.begin(), start), std::next(allArgs.begin(), end)};
 }
 
+static unsigned denseElementsSize(mlir::DenseIntElementsAttr attr) {
+  return attr.getNumElements();
+}
+
 llvm::Optional<mlir::OperandRange> fir::SelectOp::getCompareOperands(unsigned) {
   return {};
 }
@@ -486,6 +1037,11 @@ fir::SelectOp::getSuccessorOperands(llvm::ArrayRef<mlir::Value> operands,
 
 bool fir::SelectOp::canEraseSuccessorOperand() { return true; }
 
+unsigned fir::SelectOp::targetOffsetSize() {
+  return denseElementsSize(
+      getAttrOfType<mlir::DenseIntElementsAttr>(getTargetOffsetAttr()));
+}
+
 //===----------------------------------------------------------------------===//
 // SelectCaseOp
 //===----------------------------------------------------------------------===//
@@ -596,6 +1152,95 @@ static mlir::ParseResult parseSelectCase(mlir::OpAsmParser &parser,
   return mlir::success();
 }
 
+unsigned fir::SelectCaseOp::compareOffsetSize() {
+  return denseElementsSize(
+      getAttrOfType<mlir::DenseIntElementsAttr>(getCompareOffsetAttr()));
+}
+
+unsigned fir::SelectCaseOp::targetOffsetSize() {
+  return denseElementsSize(
+      getAttrOfType<mlir::DenseIntElementsAttr>(getTargetOffsetAttr()));
+}
+
+void fir::SelectCaseOp::build(mlir::Builder *builder,
+                              mlir::OperationState &result,
+                              mlir::Value selector,
+                              llvm::ArrayRef<mlir::Attribute> compareAttrs,
+                              llvm::ArrayRef<mlir::ValueRange> cmpOperands,
+                              llvm::ArrayRef<mlir::Block *> destinations,
+                              llvm::ArrayRef<mlir::ValueRange> destOperands,
+                              llvm::ArrayRef<mlir::NamedAttribute> attributes) {
+  result.addOperands(selector);
+  result.addAttribute(getCasesAttr(), builder->getArrayAttr(compareAttrs));
+  llvm::SmallVector<int32_t, 8> operOffs;
+  int32_t operSize = 0;
+  for (auto attr : compareAttrs) {
+    if (attr.isa<fir::ClosedIntervalAttr>()) {
+      operOffs.push_back(2);
+      operSize += 2;
+    } else if (attr.isa<mlir::UnitAttr>()) {
+      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<int32_t, 8> argOffs;
+  int32_t sumArgs = 0;
+  for (std::remove_const_t<decltype(count)> 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<mlir::Attribute> compareAttrs,
+                              llvm::ArrayRef<mlir::Value> cmpOpList,
+                              llvm::ArrayRef<mlir::Block *> destinations,
+                              llvm::ArrayRef<mlir::ValueRange> destOperands,
+                              llvm::ArrayRef<mlir::NamedAttribute> attributes) {
+  llvm::SmallVector<mlir::ValueRange, 16> cmpOpers;
+  auto iter = cmpOpList.begin();
+  for (auto &attr : compareAttrs) {
+    if (attr.isa<fir::ClosedIntervalAttr>()) {
+      cmpOpers.push_back(mlir::ValueRange({iter, iter + 2}));
+      iter += 2;
+    } else if (attr.isa<UnitAttr>()) {
+      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 +1272,11 @@ fir::SelectRankOp::getSuccessorOperands(llvm::ArrayRef<mlir::Value> operands,
 
 bool fir::SelectRankOp::canEraseSuccessorOperand() { return true; }
 
+unsigned fir::SelectRankOp::targetOffsetSize() {
+  return denseElementsSize(
+      getAttrOfType<mlir::DenseIntElementsAttr>(getTargetOffsetAttr()));
+}
+
 //===----------------------------------------------------------------------===//
 // SelectTypeOp
 //===----------------------------------------------------------------------===//
@@ -703,6 +1353,11 @@ static ParseResult parseSelectType(OpAsmParser &parser,
   return mlir::success();
 }
 
+unsigned fir::SelectTypeOp::targetOffsetSize() {
+  return denseElementsSize(
+      getAttrOfType<mlir::DenseIntElementsAttr>(getTargetOffsetAttr()));
+}
+
 //===----------------------------------------------------------------------===//
 // StoreOp
 //===----------------------------------------------------------------------===//
@@ -726,6 +1381,15 @@ bool fir::StringLitOp::isWideValue() {
   return eleTy.cast<fir::CharacterType>().getFKind() != 1;
 }
 
+//===----------------------------------------------------------------------===//
+// SubfOp
+//===----------------------------------------------------------------------===//
+
+mlir::OpFoldResult fir::SubfOp::fold(llvm::ArrayRef<mlir::Attribute> opnds) {
+  return mlir::constFoldBinaryOp<FloatAttr>(
+      opnds, [](APFloat a, APFloat b) { return a - b; });
+}
+
 //===----------------------------------------------------------------------===//
 // WhereOp
 //===----------------------------------------------------------------------===//
@@ -758,7 +1422,7 @@ static mlir::ParseResult parseWhereOp(OpAsmParser &parser,
 
   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 +1436,43 @@ static mlir::ParseResult parseWhereOp(OpAsmParser &parser,
   return mlir::success();
 }
 
+static LogicalResult verify(fir::WhereOp op) {
+  // Verify that the entry of each child region does not have arguments.
+  for (auto &region : 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 +1541,7 @@ mlir::FuncOp fir::createFuncOp(mlir::Location loc, mlir::ModuleOp module,
   if (auto f = module.lookupSymbol<mlir::FuncOp>(name))
     return f;
   mlir::OpBuilder modBuilder(module.getBodyRegion());
+  modBuilder.setInsertionPoint(module.getBody()->getTerminator());
   return modBuilder.create<mlir::FuncOp>(loc, name, type, attrs);
 }
 

diff  --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 1fedd00e5da3..97d169de7e77 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/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 @@ SequenceType parseSequence(mlir::DialectAsmParser &parser, mlir::Location) {
   return SequenceType::get(shape, eleTy, map);
 }
 
-bool verifyIntegerType(mlir::Type ty) {
+static bool verifyIntegerType(mlir::Type ty) {
   return ty.isa<mlir::IntegerType>() || ty.isa<IntType>();
 }
 
@@ -842,6 +839,14 @@ bool isa_ref_type(mlir::Type t) {
   return t.isa<ReferenceType>() || t.isa<PointerType>() || t.isa<HeapType>();
 }
 
+bool isa_box_type(mlir::Type t) {
+  return t.isa<BoxType>() || t.isa<BoxCharType>() || t.isa<BoxProcType>();
+}
+
+bool isa_passbyref_type(mlir::Type t) {
+  return t.isa<ReferenceType>() || isa_box_type(t);
+}
+
 bool isa_aggregate(mlir::Type t) {
   return t.isa<SequenceType>() || t.isa<RecordType>();
 }
@@ -905,6 +910,10 @@ CplxType fir::CplxType::get(mlir::MLIRContext *ctxt, KindTy kind) {
   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 @@ SequenceType::Shape fir::SequenceType::getShape() const {
   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 @@ llvm::SmallPtrSet<detail::RecordTypeStorage const *, 4> recordTypeVisited;
 
 } // namespace
 
+void fir::verifyIntegralType(mlir::Type type) {
+  if (verifyIntegerType(type) || type.isa<mlir::IndexType>())
+    return;
+  llvm_unreachable("expected integral type");
+}
+
 void fir::printFirType(FIROpsDialect *, mlir::Type ty,
                        mlir::DialectAsmPrinter &p) {
   auto &os = p.getStream();

diff  --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index bdadf5cd6f58..5821b7f29132 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/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<!fir.type<derived3{f:f32}>>
 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<!fir.type<derived3{f:f32}>>
 func @box3() -> !fir.box<!fir.type<derived3{f:f32}>>
 
 // 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<!fir.type<x>>)
+// CHECK: func @user_i64(i64)
+// CHECK: func @user_tdesc(!fir.tdesc<!fir.type<x>>)
 func @print_index3(index, index, index)
 func @user_i64(i64)
 func @user_tdesc(!fir.tdesc<!fir.type<x>>)
 
 // expect the void return to be omitted
 // CHECK-LABEL: func @store_tuple(tuple<!fir.type<qq1{f1:i32}>>)
+// CHECK: func @get_method_box() -> !fir.box<!fir.type<derived3{f:f32}>>
+// CHECK: func @method_impl(!fir.box<!fir.type<derived3{f:f32}>>)
 func @store_tuple(tuple<!fir.type<qq1{f1:i32}>>) -> ()
-
-// CHECK-LABEL: func @get_method_box() -> !fir.box<!fir.type<derived3{f:f32}>>
-// CHECK-LABEL: func @method_impl(!fir.box<!fir.type<derived3{f:f32}>>)
 func @get_method_box() -> !fir.box<!fir.type<derived3{f:f32}>>
 func @method_impl(!fir.box<!fir.type<derived3{f:f32}>>)
 
 // 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<!fir.array<10xi32>>
+// CHECK: [[VAL_2:%.*]] = fir.alloca i32
+// CHECK: [[VAL_3:%.*]] = constant 22 : i32
   %0 = fir.alloca !fir.array<10xi32>
-  // CHECK: fir.load %[[A0]] : !fir.ref<!fir.array<10xi32>>
   %1 = fir.load %0 : !fir.ref<!fir.array<10xi32>>
   %2 = fir.alloca i32
   %3 = constant 22 : i32
-  // CHECK: fir.store %{{.*}} to %{{.*}} : !fir.ref<i32>
+
+// CHECK: fir.store [[VAL_3]] to [[VAL_2]] : !fir.ref<i32>
+// CHECK: [[VAL_4:%.*]] = fir.undefined i32
+// CHECK: [[VAL_5:%.*]] = fir.allocmem !fir.array<100xf32>
+// CHECK: [[VAL_6:%.*]] = fir.embox [[VAL_5]] : (!fir.heap<!fir.array<100xf32>>) -> !fir.box<!fir.array<100xf32>>
   fir.store %3 to %2 : !fir.ref<i32>
-  // 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.array<100xf32>>) -> !fir.box<!fir.array<100xf32>>
   %6 = fir.embox %5 : (!fir.heap<!fir.array<100xf32>>) -> !fir.box<!fir.array<100xf32>>
-  // CHECK: fir.box_addr %{{.*}} : (!fir.box<!fir.array<100xf32>>) -> !fir.ref<!fir.array<100xf32>>
+
+// CHECK: [[VAL_7:%.*]] = fir.box_addr [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> !fir.ref<!fir.array<100xf32>>
+// CHECK: [[VAL_8:%.*]] = constant 0 : index
+// CHECK: [[VAL_9:%.*]]:3 = fir.box_dims [[VAL_6]], [[VAL_8]] : (!fir.box<!fir.array<100xf32>>, 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.array<100xf32>>) -> !fir.ref<!fir.array<100xf32>>
   %c0 = constant 0 : index
-  // CHECK: %[[A8:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}} : (!fir.box<!fir.array<100xf32>>, index) -> (index, index, index)
   %d1:3 = fir.box_dims %6, %c0 : (!fir.box<!fir.array<100xf32>>, 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<!fir.array<100xf32>>) -> i64
+
+// CHECK: [[VAL_11:%.*]] = fir.box_elesize [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> i64
+// CHECK: [[VAL_12:%.*]] = fir.box_isalloc [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> i1
+// CHECK: [[VAL_13:%.*]] = fir.box_isarray [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> i1
+// CHECK: [[VAL_14:%.*]] = fir.box_isptr [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> i1
+// CHECK: [[VAL_15:%.*]] = fir.box_rank [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> i64
   %9 = fir.box_elesize %6 : (!fir.box<!fir.array<100xf32>>) -> i64
-  // CHECK: fir.box_isalloc %[[A6]] : (!fir.box<!fir.array<100xf32>>) -> i1
   %10 = fir.box_isalloc %6 : (!fir.box<!fir.array<100xf32>>) -> i1
-  // CHECK: fir.box_isarray %[[A6]] : (!fir.box<!fir.array<100xf32>>) -> i1
   %11 = fir.box_isarray %6 : (!fir.box<!fir.array<100xf32>>) -> i1
-  // CHECK: fir.box_isptr %[[A6]] : (!fir.box<!fir.array<100xf32>>) -> i1
   %12 = fir.box_isptr %6 : (!fir.box<!fir.array<100xf32>>) -> i1
-  // CHECK: fir.box_rank %[[A6]] : (!fir.box<!fir.array<100xf32>>) -> i64
   %13 = fir.box_rank %6 : (!fir.box<!fir.array<100xf32>>) -> i64
-  // CHECK: fir.box_tdesc %[[A6]] : (!fir.box<!fir.array<100xf32>>) -> !fir.tdesc<!fir.array<100xf32>>
+
+// CHECK: [[VAL_16:%.*]] = fir.box_tdesc [[VAL_6]] : (!fir.box<!fir.array<100xf32>>) -> !fir.tdesc<!fir.array<100xf32>>
+// 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<i32>
   %14 = fir.box_tdesc %6 : (!fir.box<!fir.array<100xf32>>) -> !fir.tdesc<!fir.array<100xf32>>
   %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<i32>
   %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref<i32>
+
+// CHECK: [[VAL_21:%.*]] = constant 10 : i32
+// CHECK: [[VAL_22:%.*]] = fir.coordinate_of [[VAL_5]], [[VAL_21]] : (!fir.heap<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
+// CHECK: [[VAL_23:%.*]] = fir.field_index f, !fir.type<derived{f:f32}>
+// CHECK: [[VAL_24:%.*]] = fir.undefined !fir.type<derived{f:f32}>
+// CHECK: [[VAL_25:%.*]] = fir.extract_value [[VAL_24]], [[VAL_23]] : (!fir.type<derived{f:f32}>, !fir.field) -> f32
   %19 = constant 10 : i32
-  // CHECK: fir.coordinate_of %{{.*}}, %{{.*}} : (!fir.heap<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
   %20 = fir.coordinate_of %5, %19 : (!fir.heap<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
-  // CHECK: fir.field_index f, !fir.type<derived{f:f32}>
   %21 = fir.field_index f, !fir.type<derived{f:f32}>
-  // CHECK: fir.undefined !fir.type<derived{f:f32}>
   %22 = fir.undefined !fir.type<derived{f:f32}>
-  // CHECK: fir.extract_value %{{.*}}, %{{.*}} : (!fir.type<derived{f:f32}>, !fir.field) -> f32
   %23 = fir.extract_value %22, %21 : (!fir.type<derived{f:f32}>, !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<derived{f:f32}>, f32, !fir.field) -> !fir.type<derived{f:f32}>
+// CHECK: [[VAL_30:%.*]] = fir.len_param_index f, !fir.type<derived3{f:f32}>
   %c1 = constant 1 : i32
-  // 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<derived{f:f32}>, f32, !fir.field) -> !fir.type<derived{f:f32}>
   %25 = fir.insert_value %22, %cf1, %21 : (!fir.type<derived{f:f32}>, f32, !fir.field) -> !fir.type<derived{f:f32}>
-  // CHECK: fir.len_param_index f, !fir.type<derived3{f:f32}>
   %26 = fir.len_param_index f, !fir.type<derived3{f:f32}>
+
+// CHECK: [[VAL_31:%.*]] = fir.call @box3() : () -> !fir.box<!fir.type<derived3{f:f32}>>
+// CHECK: [[VAL_32:%.*]] = fir.dispatch "method"([[VAL_31]]) : (!fir.box<!fir.type<derived3{f:f32}>>) -> i32
+// CHECK: [[VAL_33:%.*]] = fir.convert [[VAL_32]] : (i32) -> i64
+// CHECK: [[VAL_34:%.*]] = fir.gentypedesc !fir.type<x>
+// CHECK: fir.call @user_tdesc([[VAL_34]]) : (!fir.tdesc<!fir.type<x>>) -> ()
+// CHECK: [[VAL_35:%.*]] = fir.no_reassoc [[VAL_33]] : i64
   %27 = fir.call @box3() : () -> !fir.box<!fir.type<derived3{f:f32}>>
-  // CHECK: fir.dispatch "method"(%{{.*}}) : (!fir.box<!fir.type<derived3{f:f32}>>) -> i32
   %28 = fir.dispatch "method"(%27) : (!fir.box<!fir.type<derived3{f:f32}>>) -> i32
-  // CHECK: fir.convert %{{.*}} : (i32) -> i64
   %29 = fir.convert %28 : (i32) -> i64
-  // CHECK: fir.gentypedesc !fir.type<x>
   %30 = fir.gentypedesc !fir.type<x>
   fir.call @user_tdesc(%30) : (!fir.tdesc<!fir.type<x>>) -> ()
-  // 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<!fir.array<100xf32>>
+// 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.array<100xf32>>
   fir.freemem %5 : !fir.heap<!fir.array<100xf32>>
   %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<qq2{f1:i32,f2:f64}>
+// CHECK: [[VAL_40:%.*]] = fir.alloca !fir.char<1>
+// CHECK: [[VAL_41:%.*]] = fir.alloca tuple<i32, f64>
+// CHECK: [[VAL_42:%.*]] = fir.embox [[VAL_38]] : (!fir.ref<i32>) -> !fir.box<i32>
+// CHECK: [[VAL_43:%.*]]:6 = fir.unbox [[VAL_42]] : (!fir.box<i32>) -> (!fir.ref<i32>, i32, i32, !fir.tdesc<i32>, 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<!fir.char<1>>, i32) -> !fir.boxchar<1>
+// CHECK: [[VAL_47:%.*]]:2 = fir.unboxchar [[VAL_46]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i32)
+// CHECK: [[VAL_48:%.*]] = fir.undefined !fir.type<qq2{f1:i32,f2:f64}>
+// 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<qq2{f1:i32,f2:f64}>, i32, i32) -> !fir.type<qq2{f1:i32,f2:f64}>
+// 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<qq2{f1:i32,f2:f64}>, f64, i32) -> !fir.type<qq2{f1:i32,f2:f64}>
+// CHECK: fir.store [[VAL_54]] to [[VAL_39]] : !fir.ref<!fir.type<qq2{f1:i32,f2:f64}>>
+// CHECK: [[VAL_55:%.*]] = fir.emboxproc @method_impl, [[VAL_41]] : ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<i32, f64>>) -> !fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>
+// CHECK: [[VAL_56:%.*]], [[VAL_57:%.*]] = fir.unboxproc [[VAL_55]] : (!fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>) -> ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<!fir.type<qq2{f1:i32,f2:f64}>>>)
+// 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<tuple<!fir.type<qq1{f1:i32}>>>)
+// CHECK: [[VAL_61:%.*]] = fir.load [[VAL_60]] : !fir.ref<tuple<!fir.type<qq1{f1:i32}>>>
+// CHECK: fir.call @store_tuple([[VAL_61]]) : (tuple<!fir.type<qq1{f1:i32}>>) -> ()
+// CHECK: return
+// CHECK: }
   %0 = fir.alloca i32
   %d6 = fir.alloca !fir.type<qq2{f1:i32,f2:f64}>
   %d3 = fir.alloca !fir.char<1>
   %e6 = fir.alloca tuple<i32,f64>
   %1 = fir.embox %0 : (!fir.ref<i32>) -> !fir.box<i32>
-  // CHECK: fir.unbox %{{.*}} : (!fir.box<i32>) -> (!fir.ref<i32>, i32, i32, !fir.tdesc<i32>, i32, !fir.dims<0>)
   %2:6 = fir.unbox %1 : (!fir.box<i32>) -> (!fir.ref<i32>,i32,i32,!fir.tdesc<i32>,i32,!fir.dims<0>)
   %c8 = constant 8 : i32
   %3 = fir.undefined !fir.char<1>
-  // CHECK: fir.emboxchar %{{.*}}, %{{.*}} : (!fir.ref<!fir.char<1>>, i32) -> !fir.boxchar<1>
-  // CHECK: fir.unboxchar %{{.*}} : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i32)
   %4 = fir.emboxchar %d3, %c8 : (!fir.ref<!fir.char<1>>, i32) -> !fir.boxchar<1>
   %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i32)
   %6 = fir.undefined !fir.type<qq2{f1:i32,f2:f64}>
@@ -139,8 +184,6 @@ func @boxing_match() {
   fir.store %a3 to %d6 : !fir.ref<!fir.type<qq2{f1:i32,f2:f64}>>
   %7 = fir.emboxproc @method_impl, %e6 : ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<i32,f64>>) -> !fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>
   %8:2 = fir.unboxproc %7 : (!fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>) -> ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<!fir.type<qq2{f1:i32,f2:f64}>>>)
-  // CHECK: fir.emboxproc @method_impl, %{{.*}} : ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<i32, f64>>) -> !fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>
-  // CHECK: fir.unboxproc %{{.*}} : (!fir.boxproc<(!fir.box<!fir.type<derived3{f:f32}>>) -> ()>) -> ((!fir.box<!fir.type<derived3{f:f32}>>) -> (), !fir.ref<tuple<!fir.type<qq2{f1:i32,f2:f64}>>>)
   %9 = fir.call @box2() : () -> !fir.boxproc<(i32, i32) -> i64>
   %10:2 = fir.unboxproc %9 : (!fir.boxproc<(i32, i32) -> i64>) -> ((i32, i32) -> i64, !fir.ref<tuple<!fir.type<qq1{f1:i32}>>>)
   %11 = fir.load %10#1 : !fir.ref<tuple<!fir.type<qq1{f1:i32}>>>
@@ -148,32 +191,61 @@ func @boxing_match() {
   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 @@ func @bar_select(%arg : i32, %arg2 : i32) -> i32 {
   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 @@ func @bar_select_rank(%arg : i32, %arg2 : i32) -> i32 {
   %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<!fir.type<derived3{f:f32}>>
+// CHECK: fir.dispatch "method"([[VAL_100]]) : (!fir.box<!fir.type<derived3{f:f32}>>) -> ()
 ^bb5 :
   %zero = constant 0 : i32
   %7 = fir.call @get_method_box() : () -> !fir.box<!fir.type<derived3{f:f32}>>
   fir.dispatch method(%7) : (!fir.box<!fir.type<derived3{f:f32}>>) -> ()
+
+// 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<!fir.type<name(param1:i32){fld:!fir.char<1>}>>) -> i32 {
 func @bar_select_type(%arg : !fir.box<!fir.type<name(param1:i32){fld:!fir.char<1>}>>) -> 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.type<name(param1:i32){fld:!fir.char<1>}>> [#fir.instance<!fir.int<4>>, ^bb1(%{{.*}} : i32), #fir.instance<!fir.int<8>>, ^bb2(%{{.*}} : i32), #fir.subsumed<!fir.int<2>>, ^bb3(%{{.*}} : i32), #fir.instance<!fir.int<1>>, ^bb4(%{{.*}} : i32), unit, ^bb5]
+
+// CHECK: fir.select_type [[VAL_101]] : !fir.box<!fir.type<name(param1:i32){fld:!fir.char<1>}>> [#fir.instance<!fir.int<4>>, ^bb1([[VAL_102]] : i32), #fir.instance<!fir.int<8>>, ^bb2([[VAL_104]] : i32), #fir.subsumed<!fir.int<2>>, ^bb3([[VAL_104]] : i32), #fir.instance<!fir.int<1>>, ^bb4([[VAL_103]] : i32), unit, ^bb5]
   fir.select_type %arg : !fir.box<!fir.type<name(param1:i32){fld:!fir.char<1>}>> [ #fir.instance<!fir.int<4>>,^bb1(%0:i32), #fir.instance<!fir.int<8>>,^bb2(%2:i32), #fir.subsumed<!fir.int<2>>,^bb3(%2:i32), #fir.instance<!fir.int<1>>,^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 @@ func @bar_select_type(%arg : !fir.box<!fir.type<name(param1:i32){fld:!fir.char<1
   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 @@ func @bar_select_case(%arg : i32, %arg2 : i32) -> i32 {
   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<minez(f:i32)> {
+// CHECK: fir.global_len "f", 1 : i32
+// CHECK: [[VAL_132:%.*]] = fir.undefined !fir.type<minez(f:i32)>
+// CHECK: fir.has_value [[VAL_132]] : !fir.type<minez(f:i32)>
+// CHECK: }
 fir.global @global_derived : !fir.type<minez(f:i32)> {
-  // CHECK: fir.global_len "f", 1 : i32
   fir.global_len f, 1 : i32
   %0 = fir.undefined !fir.type<minez>
   fir.has_value %0 : !fir.type<minez>
 }
 
-// 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
 }


        


More information about the flang-commits mailing list