From: Eric Schweitz Date: Fri, 16 Aug 2019 23:14:22 +0000 (-0700) Subject: [flang] Add mlir-tablegen for FIR ops X-Git-Tag: llvmorg-12-init~9537^2~746 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9325f9974ab9612bd44c53ee62894a0072dd7997;p=platform%2Fupstream%2Fllvm.git [flang] Add mlir-tablegen for FIR ops Original-commit: flang-compiler/f18@aae4b525dab5192ab7bc2d9004014025cfd9c64f Reviewed-on: https://github.com/flang-compiler/f18/pull/668 Tree-same-pre-rewrite: false --- diff --git a/flang/include/fir/FIROps.td b/flang/include/fir/FIROps.td new file mode 100644 index 0000000..b19045d --- /dev/null +++ b/flang/include/fir/FIROps.td @@ -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, + "FIR dialect type">; + +def fir_CharacterType : Type()">, + "FIR character type">; +def fir_ComplexType : Type()">, + "FIR complex type">; +def fir_IntegerType : Type()">, + "FIR integer type">; +def fir_LogicalType : Type()">, + "FIR logical type">; +def fir_RealType : Type()">, + "FIR real type">; + +def AnyIntegerLike : TypeConstraint, "any integer">; +def AnyLogicalLike : TypeConstraint, "any logical">; +def AnyRealLike : TypeConstraint, "any real">; + +def fir_RecordType : Type()">, + "FIR derived type">; +def fir_SequenceType : Type()">, + "array type">; + +def AnyCompositeType : TypeConstraint, "any composite">; + +def fir_ReferenceType : Type()">, + "reference type">; +def fir_HeapType : Type()">, + "allocatable type">; +def fir_PointerType : Type()">, + "pointer type">; + +def AnyReferenceLike : TypeConstraint, "any reference">; + +def fir_BoxType : Type()">, "box type">; +def fir_BoxCharType : Type()">, + "box character type">; +def fir_BoxProcType : Type()">, + "box procedure type">; + +def AnyBoxLike : TypeConstraint, "any box">; + +def AnyRefOrBox : TypeConstraint, + "any reference or box">; + +def fir_DimsType : Type()">, "dim type">; +def fir_TypeDescType : Type()">, + "type desc type">; +def fir_FieldType : Type()">, "field type">; + +def AnyCoordinateLike : TypeConstraint, + "any coordinate index">; +def AnyCoordinateType : Type; + +class fir_Op traits> + : Op; + +def fir_AllocateOpBuilder : OpBuilder< + "Builder *, OperationState *result, Type resultType," + "Value *size = {}, ArrayRef 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 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 operands, ArrayRef attributes = {}", + [{ + if (resultType) + result->addTypes(resultType); + result->addOperands(operands); + for (auto namedAttr : attributes) { + result->addAttribute(namedAttr.first, namedAttr.second); + } + }]>; + +class fir_OneResultOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let builders = [fir_OneResultOpBuilder]; +} + +class fir_TwoBuilders { + list builders = [b1, b2]; +} + +class fir_AllocatableBaseOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let arguments = (ins + OptionalAttr:$name, + OptionalAttr:$target + ); +} + +class fir_AllocatableOp traits =[]> : + fir_AllocatableBaseOp, + fir_TwoBuilders { +} + +// 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` 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(); + 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`. + 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 traits = []> : + fir_Op, + Arguments<(ins Variadic:$args)>, Results<(outs)> { + let builders = [OpBuilder< + "Builder *, OperationState *result, " + "Value *selector," + "ArrayRef properOperands, " + "ArrayRef destinations, " + "ArrayRef> operands = {}, " + "ArrayRef 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>; + + // 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 type objects + +def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, + Arguments<(ins AnyType:$val, Variadic:$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 + %box = fir.embox %ref : !fir.box + %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 + %dims = fir.gendims(1, 100, 1) : !fir.dims<1> + %box = fir.embox %ref, %dims : !fir.box + %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 + %box = fir.embox %ptr : !fir.box + %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:$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:$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)> { + 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:$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); + 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 lbOperands, AffineMap lbMap, " + "ArrayRef 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); + 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)>, + Arguments<(ins StrAttr:$proc, Variadic:$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)>, + Arguments<(ins StrAttr:$method, Variadic:$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`. + }]; +} + +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