[flang] Add mlir-tablegen for FIR ops
authorEric Schweitz <eschweitz@nvidia.com>
Fri, 16 Aug 2019 23:14:22 +0000 (16:14 -0700)
committerEric Schweitz <eschweitz@nvidia.com>
Fri, 16 Aug 2019 23:14:22 +0000 (16:14 -0700)
Original-commit: flang-compiler/f18@aae4b525dab5192ab7bc2d9004014025cfd9c64f
Reviewed-on: https://github.com/flang-compiler/f18/pull/668
Tree-same-pre-rewrite: false

flang/include/fir/FIROps.td [new file with mode: 0644]

diff --git a/flang/include/fir/FIROps.td b/flang/include/fir/FIROps.td
new file mode 100644 (file)
index 0000000..b19045d
--- /dev/null
@@ -0,0 +1,780 @@
+// Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// Definition of the FIR dialect operations
+//
+
+#ifdef FIR_DIALECT_FIR_OPS
+#else
+#define FIR_DIALECT_FIR_OPS
+
+#ifdef OP_BASE
+#else
+include "mlir/IR/OpBase.td"
+#endif
+
+def fir_Dialect : Dialect {
+  let name = "fir";
+}
+
+def fir_Type : Type<CPred<"fir::isa_fir_or_std_type($_self)">,
+    "FIR dialect type">;
+
+def fir_CharacterType : Type<CPred<"$_self.isa<fir::CharacterType>()">,
+    "FIR character type">;
+def fir_ComplexType : Type<CPred<"$_self.isa<fir::ComplexType>()">,
+    "FIR complex type">;
+def fir_IntegerType : Type<CPred<"$_self.isa<fir::IntegerType>()">,
+    "FIR integer type">;
+def fir_LogicalType : Type<CPred<"$_self.isa<fir::LogicalType>()">,
+    "FIR logical type">;
+def fir_RealType : Type<CPred<"$_self.isa<fir::RealType>()">,
+    "FIR real type">;
+
+def AnyIntegerLike : TypeConstraint<Or<[IntegerLike.predicate,
+    fir_IntegerType.predicate]>, "any integer">;
+def AnyLogicalLike : TypeConstraint<Or<[BoolLike.predicate,
+    fir_LogicalType.predicate]>, "any logical">;
+def AnyRealLike : TypeConstraint<Or<[FloatLike.predicate,
+    fir_RealType.predicate]>, "any real">;
+
+def fir_RecordType : Type<CPred<"$_self.isa<fir::RecordType>()">,
+    "FIR derived type">;
+def fir_SequenceType : Type<CPred<"$_self.isa<fir::SequenceType>()">,
+    "array type">;
+
+def AnyCompositeType : TypeConstraint<Or<[fir_RecordType.predicate,
+    fir_SequenceType.predicate]>, "any composite">;
+
+def fir_ReferenceType : Type<CPred<"$_self.isa<fir::ReferenceType>()">,
+    "reference type">;
+def fir_HeapType : Type<CPred<"$_self.isa<fir::HeapType>()">,
+    "allocatable type">;
+def fir_PointerType : Type<CPred<"$_self.isa<fir::PointerType>()">,
+    "pointer type">;
+
+def AnyReferenceLike : TypeConstraint<Or<[fir_ReferenceType.predicate,
+    fir_HeapType.predicate, fir_PointerType.predicate]>, "any reference">;
+
+def fir_BoxType : Type<CPred<"$_self.isa<fir::BoxType>()">, "box type">;
+def fir_BoxCharType : Type<CPred<"$_self.isa<fir::BoxCharType>()">,
+    "box character type">;
+def fir_BoxProcType : Type<CPred<"$_self.isa<fir::BoxProcType>()">,
+    "box procedure type">;
+
+def AnyBoxLike : TypeConstraint<Or<[fir_BoxType.predicate,
+    fir_BoxCharType.predicate, fir_BoxProcType.predicate]>, "any box">;
+
+def AnyRefOrBox : TypeConstraint<Or<[fir_ReferenceType.predicate,
+    fir_HeapType.predicate, fir_PointerType.predicate, fir_BoxType.predicate]>,
+    "any reference or box">;
+
+def fir_DimsType : Type<CPred<"$_self.isa<fir::DimsType>()">, "dim type">;
+def fir_TypeDescType : Type<CPred<"$_self.isa<fir::TypeDescType>()">,
+    "type desc type">;
+def fir_FieldType : Type<CPred<"$_self.isa<fir::FieldType>()">, "field type">;
+
+def AnyCoordinateLike : TypeConstraint<Or<[IntegerLike.predicate,
+    fir_IntegerType.predicate, fir_FieldType.predicate]>,
+    "any coordinate index">;
+def AnyCoordinateType : Type<AnyCoordinateLike.predicate, "coordinate type">;
+
+class fir_Op<string mnemonic, list<OpTrait> traits>
+  : Op<fir_Dialect, mnemonic, traits>;
+
+def fir_AllocateOpBuilder : OpBuilder<
+  "Builder *, OperationState *result, Type resultType,"
+  "Value *size = {}, ArrayRef<NamedAttribute> attributes = {}",
+  [{
+    assert(resultType);
+    result->addTypes(resultType);
+    if (size)
+      result->addOperands(size);
+    for (auto namedAttr : attributes)
+      result->addAttribute(namedAttr.first, namedAttr.second);
+  }]>;
+
+def fir_NamedAllocateOpBuilder : OpBuilder<
+  "Builder *builder, OperationState *result, Type resultType,"
+  "StringRef name, Value *size = {}, ArrayRef<NamedAttribute> attributes = {}",
+  [{
+    assert(resultType);
+    result->addTypes(resultType);
+    if (size)
+      result->addOperands(size);
+    result->addAttribute("name", builder->getStringAttr(name));
+    for (auto namedAttr : attributes)
+      result->addAttribute(namedAttr.first, namedAttr.second);
+  }]>;
+
+def fir_OneResultOpBuilder : OpBuilder<
+  "Builder *, OperationState *result, Type resultType, "
+  "ArrayRef<Value *> operands, ArrayRef<NamedAttribute> attributes = {}",
+  [{
+    if (resultType)
+      result->addTypes(resultType);
+    result->addOperands(operands);
+    for (auto namedAttr : attributes) {
+      result->addAttribute(namedAttr.first, namedAttr.second);
+    }
+  }]>;
+
+class fir_OneResultOp<string mnemonic, list<OpTrait> traits = []> :
+    fir_Op<mnemonic, traits>, Results<(outs fir_Type:$res)> {
+  let builders = [fir_OneResultOpBuilder];
+}
+
+class fir_TwoBuilders<OpBuilder b1, OpBuilder b2> {
+  list<OpBuilder> builders = [b1, b2];
+}
+
+class fir_AllocatableBaseOp<string mnemonic, list<OpTrait> traits = []> :
+    fir_Op<mnemonic, traits>, Results<(outs fir_Type:$res)> {
+  let arguments = (ins
+    OptionalAttr<StrAttr>:$name,
+    OptionalAttr<BoolAttr>:$target
+  );
+}
+
+class fir_AllocatableOp<string mnemonic, list<OpTrait> traits =[]> :
+    fir_AllocatableBaseOp<mnemonic, !listconcat([NoSideEffect], traits)>,
+    fir_TwoBuilders<fir_AllocateOpBuilder, fir_NamedAllocateOpBuilder> {
+}
+
+// Memory SSA operations
+
+def fir_AllocaExpr : fir_AllocatableOp<"alloca"> {
+  let summary = "allocate storage for a temporary on the stack given a type";
+
+  let description = [{
+    This primitive operation is used to allocate an object on the stack.  A
+    reference to the object of type `!fir.ref<T>` is returned.  The returned
+    object has an undefined state.  The allocation can be given an optional
+    name.  The allocation may have a dynamic repetition count for allocating
+    a sequence of locations for the specified type.
+  }];
+
+  let extraClassDeclaration = [{
+    mlir::Type getAllocatedType();
+  }];
+}
+
+def fir_LoadExpr : fir_OneResultOp<"load", [NoSideEffect]>,
+    Arguments<(ins fir_ReferenceType:$ref)> {
+  let summary = "load a value from a memory reference";
+
+  let description = [{
+    Load a value from a memory reference into a virtual register.  Produces
+    an immutable ssa-value of the referent type.
+  }];
+
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState *result, Value *refVal",
+    [{
+      fir::ReferenceType refTy =
+        refVal->getType().cast<fir::ReferenceType>();
+      result->addOperands(refVal);
+      result->addTypes(refTy.getEleTy());
+    }]
+  >];
+}
+
+def fir_StoreExpr : fir_Op<"store", []>,
+    Arguments<(ins AnyType:$val, fir_ReferenceType:$ref)> {
+  let summary = "store an SSA-value to a memory location";
+
+  let description = [{
+    Store an ssa-value (virtual register) to a memory reference.  The stored
+    value must be of the same type as the referent type of the memory
+    reference.
+  }];
+}
+
+def fir_UndefOp : fir_OneResultOp<"undefined", [NoSideEffect]> {
+  let summary = "explicit undefined value of some type";
+
+  let description = [{
+    Constructs an ssa-value of the specified type with an undefined value.
+    This operation is typically created internally by the mem2reg conversion
+    pass.
+  }];
+}
+
+def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> {
+  let summary = "allocate storage on the heap for an object of a given type";
+
+  let description = [{
+    Creates a heap memory reference suitable for storing a value of the
+    given type, T.  The heap refernce returned has type `!fir.heap<T>`.
+    The memory object is in an undefined state.  `allocmem` operations must
+    be paired with `freemem` operations to avoid memory leaks.
+  }];
+}
+
+def fir_FreeMemOp : fir_Op<"freemem", []>, Arguments<(ins fir_HeapType)> {
+  let summary = "free a heap object";
+
+  let description = [{
+    Deallocates a heap memory reference that was allocated by an `allocmem`.
+    The memory object that is deallocated is placed in an undefined state
+    after `fir.freemem`.  Optimizations may treat the loading of an object
+    in the undefined state as undefined behavior.
+  }];
+}
+
+// Terminator operations
+
+class fir_SwitchTerminatorOp<string mnemonic, list<OpTrait> traits = []> :
+    fir_Op<mnemonic, !listconcat(traits, [Terminator])>,
+    Arguments<(ins Variadic<AnyType>:$args)>, Results<(outs)> {
+  let builders = [OpBuilder<
+    "Builder *, OperationState *result, "
+    "Value *selector,"
+    "ArrayRef<Value *> properOperands, "
+    "ArrayRef<Block *> destinations, "
+    "ArrayRef<ArrayRef<Value *>> operands = {}, "
+    "ArrayRef<NamedAttribute> attributes = {}",
+    [{
+      result->addOperands(selector);
+      result->addOperands(properOperands);
+      for (auto kvp : llvm::zip(destinations, operands)) {
+        result->addSuccessor(std::get<0>(kvp), std::get<1>(kvp));
+      }
+      for (auto namedAttr : attributes) {
+        result->addAttribute(namedAttr.first, namedAttr.second);
+      }
+    }]
+  >];
+
+  let extraClassDeclaration = [{
+    using Conditions = mlir::Value *;
+    using BranchTuple = std::tuple<Conditions, mlir::Block *,
+      llvm::ArrayRef<mlir::Value *>>;
+
+    // The number of destination conditions that may be tested
+    unsigned getNumConditions() {
+      return getNumDest();
+    }
+
+    // The selector is the value being tested to determine the destination
+    mlir::Value *getSelector() {
+      return getOperand(0);
+    }
+
+    // The value of the `dest`-th destination's condition to test against
+    Conditions getCondition(unsigned dest) {
+      assert(dest < getNumConditions());
+      return getOperand(getDestOperandIndex(dest) - 1);
+    }
+
+    // The number of blocks that may be branched to
+    unsigned getNumDest() {
+      return getOperation()->getNumSuccessors();
+    }
+
+    // The `dest`-th destination block
+    mlir::Block *getDest(unsigned dest) {
+      return getOperation()->getSuccessor(dest);
+    }
+
+    // The number of operands to the `dest`-th destination block
+    unsigned getNumDestOperands(unsigned dest) {
+      return getOperation()->getNumSuccessorOperands(dest);
+    }
+
+    // The `i`-th operand to the `dest`-th destination block
+    mlir::Value *getDestOperand(unsigned dest, unsigned i) {
+      assert(dest < getNumDest());
+      assert(i < getNumDestOperands(dest));
+      return getOperand(getDestOperandIndex(dest) + i);
+    }
+
+    operand_iterator dest_operand_begin(unsigned dest) {
+      return operand_begin() + getDestOperandIndex(dest);
+    }
+
+    operand_iterator dest_operand_end(unsigned dest) {
+      return dest_operand_begin(dest) + getNumDestOperands(dest);
+    }
+
+    operand_range getDestOperands(unsigned dest) {
+      return {dest_operand_begin(dest), dest_operand_end(dest)};
+    }
+
+  private:
+    // Get the argument index of the `dest`-th destination
+    unsigned getDestOperandIndex(unsigned dest) {
+      if (dest == 0) {
+        return 2;
+      }
+      return getNumDestOperands(dest) + getDestOperandIndex(dest - 1);
+    }
+  }];
+}
+
+def fir_SelectOp : fir_SwitchTerminatorOp<"select"> {
+  let summary = "a multiway branch";
+
+  let description = [{
+    A multiway branch terminator with similar semantics to C's `switch`
+    statement.  A selector value is matched against a list of constants
+    of the same type for a match.  When a match is found, control is
+    transferred to the corresponding basic block.  A `select` must have
+    at least one basic block with a corresponding `undefined` match, and
+    that block will be selected when all other conditions fail to match.
+  }];
+}
+
+def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> {
+  let summary = "Fortran's SELECT CASE statement";
+
+  let description = [{
+    Similar to `select`, `select_case` provides a way to express Fortran's
+    SELECT CASE construct.  In this case, the selector value is matched
+    against variables (not just constants) and ranges.  The structure is
+    the same as `select`, but `select_case` allows for the expression of
+    more complex match conditions.
+  }];
+}
+
+def fir_SelectRankOp : fir_SwitchTerminatorOp<"select_rank"> {
+  let summary = "Fortran's SELECT RANK statement";
+
+  let description = [{
+    Similar to `select`, `select_rank` provides a way to express Fortran's
+    SELECT RANK construct.  In this case, the rank of the selector value
+    is matched against constants of integer type.  The structure is the
+    same as `select`, but `select_rank` determines the rank of the selector
+    variable at runtime to determine the best match.
+  }];
+}
+
+def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> {
+  let summary = "Fortran's SELECT TYPE statement";
+
+  let description = [{
+    Similar to `select`, `select_type` provides a way to express Fortran's
+    SELECT TYPE construct.  In this case, the type of the selector value
+    is matched against a list of type descriptors.  The structure is the
+    same as `select`, but `select_type` determines the type of the selector
+    variable at runtime to determine the best match.
+  }];
+}
+
+def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> {
+  let summary = "the unreachable instruction";
+
+  let description = [{
+    Terminates a basic block with the assertion that the end of the block
+    will never be reached at runtime.  This instruction can be used
+    immediately after a call to the Fortran runtime to terminate the
+    program, for example.
+  }];
+}
+
+def fir_FirEndOp : fir_Op<"end", [Terminator]> {
+  let summary = "the end instruction";
+
+  let description = [{
+    The end terminator is a special terminator used inside various FIR
+    operations that have regions.  End is thus the custom (and only) terminator
+    for these operations.  It is implicit and need not appear in the textual
+    representation.
+  }];
+}
+
+// Operations on !fir.box<T> type objects
+
+def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>,
+    Arguments<(ins AnyType:$val, Variadic<AnyInteger>:$params,
+                   fir_DimsType:$dims)> {
+  let summary = "boxes a given reference and (optional) dimension information";
+
+  let description = [{
+    Create a boxed reference value. In Fortran, the implementation can require
+    extra information about an entity, such as its type, rank, etc.  This
+    auxilliary information is packaged and abstracted as a value with box type.
+  }];
+}
+
+def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>,
+    Results<(outs fir_BoxCharType)>,
+    Arguments<(ins fir_CharacterType:$character, AnyInteger:$len)> {
+  let summary = "boxes a given CHARACTER reference and its LEN parameter";
+
+  let description = [{
+    Create a boxed CHARACTER value. The CHARACTER type has the LEN type
+    parameter, the value of which may only be known at runtime.  Therefore,
+    a variable of type CHARACTER has both its data reference as well as a
+    LEN type parameter.
+  }];
+}
+
+def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]>,
+    Results<(outs fir_BoxProcType)>,
+    Arguments<(ins AnyType:$proc, AnyType:$host)> {
+  let summary = "boxes a given procedure and optional host context";
+
+  let description = [{
+    Creates an abstract encapsulation of a PROCEDURE POINTER
+    along with an optional pointer to a host instance context. An internal
+    procedure may require a host instance for execution.
+  }];
+}
+
+def fir_UnboxOp : fir_Op<"unbox", [NoSideEffect]>,
+    Results<(outs fir_ReferenceType, AnyInteger, AnyInteger, fir_TypeDescType,
+             AnyInteger, fir_DimsType)>,
+    Arguments<(ins fir_BoxType:$box)> {
+  let summary = "unbox the boxed value into a tuple value";
+
+  let description = [{
+    Unboxes a value of `box` type into a tuple of information abstracted in
+    that boxed value.
+  }];
+}
+
+def fir_UnboxCharOp : fir_Op<"unboxchar", [NoSideEffect]>,
+    Results<(outs fir_ReferenceType, AnyInteger)>,
+    Arguments<(ins fir_BoxCharType:$character)> {
+  let summary = "unbox a boxchar value into a pair value";
+
+  let description = [{
+    Unboxes a value of `boxchar` type into a pair consisting of a memory
+    reference to the CHARACTER data and the LEN type parameter.
+  }];
+}
+
+def fir_UnboxProcOp : fir_Op<"unboxproc", [NoSideEffect]>,
+    Results<(outs fir_ReferenceType, fir_ReferenceType)>,
+    Arguments<(ins fir_BoxProcType:$proc)> {
+  let summary = "unbox a boxproc value into a pair value";
+
+  let description = [{
+    Unboxes a value of `boxproc` type into a pair consisting of a procedure
+    pointer and a pointer to a host context.
+  }];
+}
+
+def fir_BoxAddrOp : fir_OneResultOp<"box_addr", [NoSideEffect]>,
+    Arguments<(ins fir_BoxType:$val)> {
+  let summary = "return a memory reference to the boxed value";
+
+  let description = [{
+    This operator is overloaded to work with values of type `box`,
+    `boxchar`, and `boxproc`.  The result for each of these
+    cases, respectively, is the address of the data, the address of the
+    CHARACTER data, and the address of the procedure.
+  }];
+}
+
+def fir_BoxCharLenOp : fir_Op<"boxchar_len", [NoSideEffect]>,
+    Results<(outs AnyInteger)>, Arguments<(ins fir_BoxCharType:$val)> {
+  let summary = "return the LEN type parameter from a boxchar value";
+
+  let description = [{
+    Extracts the LEN type parameter from a `boxchar` value.
+  }];
+}
+
+def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]>,
+    Results<(outs AnyInteger, AnyInteger, AnyInteger)>,
+    Arguments<(ins fir_BoxType:$val, AnyInteger:$dim)> {
+  let summary = "return the dynamic dimension information for the boxed value";
+
+  let description = [{
+    Returns the triple of lower bound, extent, and stride for `dim` dimension
+    of `val`, which must have a `box` type.  The dimensions are enumerated from
+    left to right from 0 to rank-1. This operation has undefined behavior if
+    `dim` is out of bounds.
+  }];
+}
+
+def fir_BoxEleSizeOp : fir_OneResultOp<"box_elesize", [NoSideEffect]>,
+    Arguments<(ins fir_BoxType:$val)> {
+  let summary = "return the size of an element of the boxed value";
+
+  let description = [{
+    Returns the size of an element in an entity of `box` type.  This size may
+    not be known until runtime.
+  }];
+}
+
+def fir_BoxIsAllocOp : fir_Op<"box_isalloc", [NoSideEffect]>,
+    Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> {
+  let summary = "is the boxed value an ALLOCATABLE?";
+
+  let description = [{
+    Determine if the boxed value was from an ALLOCATABLE entity.
+
+      %ref = ... : !fir.heap<i64>
+      %box = fir.embox %ref : !fir.box<i64>
+      %isheap = fir.box_isalloc %box : i1
+  }];
+}
+
+def fir_BoxIsArrayOp : fir_Op<"box_isarray", [NoSideEffect]>,
+    Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> {
+  let summary = "is the boxed value an array?";
+
+  let description = [{
+    Determine if the boxed value has a positive (> 0) rank.
+
+      %ref = ... : !fir.ref<i64>
+      %dims = fir.gendims(1, 100, 1) : !fir.dims<1>
+      %box = fir.embox %ref, %dims : !fir.box<i64>
+      %isarr = fir.box_isarray %box : i1
+  }];
+}
+
+def fir_BoxIsPtrOp : fir_Op<"box_isptr", [NoSideEffect]>,
+    Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> {
+  let summary = "is the boxed value a POINTER?";
+
+  let description = [{
+    Determine if the boxed value was from a POINTER entity.
+
+      %ptr = ... : !fir.ptr<i64>
+      %box = fir.embox %ptr : !fir.box<i64>
+      %isptr = fir.box_isptr %box : i1
+  }];
+}
+
+def fir_BoxProcHostOp : fir_Op<"boxproc_host", [NoSideEffect]>,
+    Results<(outs fir_ReferenceType)>, Arguments<(ins fir_BoxProcType:$val)> {
+  let summary = "returns the host instance pointer (or null)";
+
+  let description = [{
+    Extract the host context pointer from a `boxproc` value.
+  }];
+}
+
+def fir_BoxRankOp : fir_OneResultOp<"box_rank", [NoSideEffect]>,
+    Arguments<(ins fir_BoxType:$val)> {
+  let summary = "return the number of dimensions for the boxed value";
+
+  let description = [{
+    Return the rank of a value of `box` type.  If the value is scalar, the
+    rank is 0.
+  }];
+}
+
+def fir_BoxTypeDescOp : fir_OneResultOp<"box_tdesc", [NoSideEffect]>,
+    Arguments<(ins fir_BoxType:$val)> {
+  let summary = "return the type descriptor for the boxed value";
+
+  let description = [{
+    Return the opaque type descriptor of a value of `box` type.
+  }];
+}
+
+// Record and array type operations
+
+def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]>,
+    Results<(outs fir_ReferenceType)>, Arguments<(ins AnyRefOrBox:$ref,
+    Variadic<AnyCoordinateType>:$coor)> {
+  let summary = "Finds the coordinate (location) of a value in memory";
+
+  let description = [{
+    Determine a memory reference given a memory reference of composite type
+    and a list of index values.
+  }];
+}
+
+def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]>,
+    Arguments<(ins AnyCompositeType:$adt, Variadic<AnyCoordinateType>:$coor)> {
+  let summary = "Extract a value from an aggregate SSA-value";
+
+  let description = [{
+    Extract a subobject value given a value of composite type and a list of
+    index values.
+  }];
+}
+
+def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> {
+  let summary = "create a field index value from a field identifier";
+
+  let description = [{
+    Generate a field (offset) value from an identifier.  Field values may be
+    lowered into exact offsets when the layout of a Fortran derived type is
+    known at compile-time. The type of a field value is `!fir.field` and
+    these values can be used with the `fir.coordinate_of`, `fir.extract_value`,
+    or `fir.insert_value` instructions to compute (abstract) addresses of 
+    subobjects.
+  }];
+
+  let arguments = (ins StrAttr:$field);
+
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState *result, StringRef fieldName",
+    [{
+      result->addAttribute("name", builder->getStringAttr(fieldName));
+    }]
+  >];
+}
+
+def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, 
+    Arguments<(ins Variadic<AnyInteger>)> {
+  let summary = "generate a value of type `!fir.dims`";
+
+  let description = [{
+    The arguments are an ordered list of integral type values that is a
+    multiple of 3 in length.  Each such triple is defined as: the lower
+    index, the extent, and the stride for that dimension. The dimension
+    information is given in the same row-to-column order as Fortran. This
+    abstract dimension value must describe a reified object, so all dimension
+    information must be specified.  The extent must be non-negative and the
+    stride must not be zero.
+  }];
+}
+
+def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>,
+    Arguments<(ins AnyCompositeType:$adt, AnyType:$val,
+    Variadic<AnyCoordinateType>:$coor)> {
+  let summary = "insert a new sub-value into a copy of an existing aggregate";
+
+  let description = [{
+    Insert a value into a composite value.
+  }];
+}
+
+def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> {
+  let summary =
+    "create a field index value from a LEN type parameter identifier";
+
+  let description = [{
+    Generate a field (offset) value from an LEN parameter identifier.  Field
+    values may be lowered into exact offsets when the layout of a Fortran
+    derived type is known at compile-time. The type of a field value is
+    `!fir.field` and these values can be used with the `fir.coordinate_of`,
+    `fir.extract_value`, or `fir.insert_value` instructions to compute
+    (abstract) addresses of subobjects.
+  }];
+
+  let arguments = (ins StrAttr:$field);
+
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState *result, StringRef fieldName",
+    [{
+      result->addAttribute("name", builder->getStringAttr(fieldName));
+    }]
+  >];
+}
+
+// Fortran loops
+
+def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
+
+def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> {
+  let summary = "generalized loop operation";
+  let description = [{
+    A generalized Fortran loop construct.
+  }];
+  let arguments = (ins Variadic<AnyType>);
+  let regions = (region SizedRegion<1>:$region);
+  let skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<"Builder *builder, OperationState *result, "
+              "int64_t lowerBound, int64_t upperBound, int64_t step = 1">,
+    OpBuilder<"Builder *builder, OperationState *result, "
+              "ArrayRef<Value *> lbOperands, AffineMap lbMap, "
+              "ArrayRef<Value *> ubOperands, AffineMap ubMap, "
+              "int64_t step = 1">
+  ];
+  //let hasCanonicalizer = 1;
+}
+
+def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> {
+  let summary = "generalized conditional operation";
+  let description = [{
+    This is a generalized conditional construct.
+  }];
+  let arguments = (ins Variadic<AnyType>);
+  let regions = (region SizedRegion<1>:$whereRegion, AnyRegion:$otherRegion);
+  let skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<"Builder *builder, OperationState *result, "
+              "Value *cond, bool withOtherRegion">
+  ];
+}
+
+// Procedure call operations
+
+def fir_CallOp : fir_Op<"call", []>,
+    Results<(outs Variadic<AnyType>)>,
+    Arguments<(ins StrAttr:$proc, Variadic<AnyType>:$args)> {
+  let summary = "call a procedure directly";
+
+  let description = [{
+    Provides a custom parser and pretty printer to allow for a slightly more
+    readable syntax in the FIR dialect, e.g. `fir.call @sub(%12)`.
+  }];
+
+  let parser = [{ return fir::parseCallOp(parser, result); }];
+  let printer = [{ fir::printCallOp(p, *this); }];
+}
+
+def fir_DispatchOp : fir_Op<"dispatch", []>,
+    Results<(outs Variadic<AnyType>)>,
+    Arguments<(ins StrAttr:$method, Variadic<AnyType>:$args)> {
+  let summary = "call a type-bound procedure";
+
+  let description = [{
+    Dynamic dispatch to the specified method.
+  }];
+}
+
+// Other misc. operations
+
+def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]>,
+    Arguments<(ins AnyType:$value)> {
+  let summary = "encapsulates all Fortran scalar type conversions";
+
+  let description = [{
+    Generalized type conversion. Not all pairs of types have conversions.
+  }];
+}
+
+def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> {
+  let summary = "generate a type descriptor for a given type";
+
+  let description = [{
+    Generates a constant object that is an abstract type descriptor of the
+    specified type.  The meta-type of a type descriptor for the type `T`
+    is `!fir.tdesc<T>`.
+  }];
+}
+
+def fir_NoReassocOp : fir_OneResultOp<"no_reassoc",
+    [SameOperandsAndResultType]>, Arguments<(ins fir_Type:$val)> {
+  let summary = "synthetic op to prevent reassociation";
+
+  let description = [{
+    The operation is to make sure that the Fortran optimizer does not
+    reassociate operations when they are syntactically surrounded by
+    parenthesis.
+  }];
+}
+
+def fir_DTEntryOp : fir_OneResultOp<"dt_entry", [NoSideEffect]>,
+    Arguments<(ins StrAttr:$name, FunctionType:$callee)> {
+  let summary = "map entry in a dispatch table";
+
+  let description = [{
+    An entry in a dispatch table.
+  }];
+}
+
+#endif