llvm/mlir/include/mlir/Dialect/OpenMP/OpenMPOpBase.td

//===- OpenMPOpBase.td - OpenMP dialect shared definitions -*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains shared definitions for the OpenMP dialect.
//
//===----------------------------------------------------------------------===//

#ifndef OPENMP_OP_BASE
#define OPENMP_OP_BASE

include "mlir/Dialect/OpenMP/OpenMPAttrDefs.td"
include "mlir/Dialect/OpenMP/OpenMPDialect.td"
include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td"
include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.td"
include "mlir/IR/OpBase.td"

//===----------------------------------------------------------------------===//
// OpenMP dialect type constraints.
//===----------------------------------------------------------------------===//

class OpenMP_Type<string name, string typeMnemonic> :
      TypeDef<OpenMP_Dialect, name> {
  let mnemonic = typeMnemonic;
}

// Type which can be constraint accepting standard integers and indices.
def IntLikeType : AnyTypeOf<[AnyInteger, Index]>;

def OpenMP_PointerLikeType : TypeAlias<OpenMP_PointerLikeTypeInterface,
	"OpenMP-compatible variable type">;

def OpenMP_MapBoundsType : OpenMP_Type<"MapBounds", "map_bounds_ty"> {
  let summary = "Type for representing omp map clause bounds information";
}

//===----------------------------------------------------------------------===//
// Base classes for OpenMP dialect operations.
//===----------------------------------------------------------------------===//

// Base class for representing OpenMP clauses.
//
// Clauses are meant to be used in a mixin-style pattern to help define OpenMP
// operations in a scalable way, since often the same clause can be applied to
// multiple different operations.
//
// To keep the representation of clauses consistent across different operations,
// each clause must define a set of arguments (values and attributes) which will
// become input arguments of each OpenMP operation that accepts that clause.
//
// It is also recommended that an assembly format and description are defined
// for each clause wherever posible, to make sure they are always printed,
// parsed and described in the same way.
//
// Optionally, operation traits and extra class declarations might be attached
// to clauses, which will be forwarded to all operations that include them.
//
// An `OpenMP_Op` can inhibit the inheritance of `traits`, `arguments`,
// `description` and `extraClassDeclaration` fields from any given
// `OpenMP_Clause` by setting to 1 the corresponding "skip" template argument
// bit. Additionally, the `skipAssemblyFormat` bit inhibits the inheritance of
// fields used to populate `OpenMP_Op`'s `assemblyFormat` (i.e.
// `reqAssemblyFormat` and `optAssemblyFormat`).
class OpenMP_Clause<bit skipTraits, bit skipArguments, bit skipAssemblyFormat,
                    bit skipDescription, bit skipExtraClassDeclaration> {
  bit ignoreTraits = skipTraits;
  list<Trait> traits = [];

  bit ignoreArgs = skipArguments;
  dag arguments;

  bit ignoreAsmFormat = skipAssemblyFormat;
  string reqAssemblyFormat = "";
  string optAssemblyFormat = "";

  bit ignoreDesc = skipDescription;
  string description = "";

  bit ignoreExtraDecl = skipExtraClassDeclaration;
  string extraClassDeclaration = "";
}

// Base class for representing OpenMP operations.
//
// This is a subclass of the builtin `Op` for the OpenMP dialect. By default,
// some of its fields are initialized according to the list of OpenMP clauses
// passed as template argument:
//   - `traits`: It is a union of the traits list passed as template argument
//     and those inherited from the `traits` field of all clauses.
//   - `arguments`: They are a concatenation of clause-inherited arguments. They
//     are saved to a `clausesArgs` field to allow overriding the arguments
//     field in the definition of the operation and still being able to include
//     those inherited from clauses.
//   - `assemblyFormat`: It is a concatenation of the `reqAssemblyFormat`
//     followed by an `oilist()` containing the `optAssemblyFormat` of all
//     clauses. The format string is completed with $region (if `singleRegion =
//     true`) followed by `attr-dict`. This field remains uninitialized if no
//     non-empty `{req,opt}AssemblyFormat` strings are inherited from clauses.
//     The `clausesAssemblyFormat` field holds all the format string except for
//     "$region attr-dict", and the `clauses{Req,Opt}AssemblyFormat` fields
//     hold the required and optional parts of the format string separately, so
//     that an operation overriding `assemblyFormat` can still benefit from the
//     auto-generated format for its clauses.
//   - `description`: This is still required to be defined by the operation.
//     However, a `clausesDescription` field is provided containing a
//     concatenation of descriptions of all clauses, to be appended to the
//     operation's `description` field.
//   - `extraClassDeclaration`: It contains a concatenation of the
//     `extraClassDeclaration` of all clauses. This string is also stored in
//     `clausesExtraClassDeclaration`, so that an operation overriding this
//     field can append the clause-inherited ones as well.
//
// The `regions` field will contain a single `AnyRegion:$region` element if the
// `singleRegion` bit template argument is set to 1. Otherwise, it will be
// empty.
class OpenMP_Op<string mnemonic, list<Trait> traits = [],
                list<OpenMP_Clause> clauses = [], bit singleRegion = false> :
    Op<OpenMP_Dialect, mnemonic,
    // The resulting operation's traits list will be the concatenation of
    // explicit operation traits and all traits attached to the clauses of the
    // operation. Repetitions are skipped.
    !listconcat(traits,
      !listremove(
        !foldl([]<Trait>,
               !foreach(clause,
                        !filter(fClause, clauses, !not(fClause.ignoreTraits)),
                        clause.traits),
               acc, traitList, !listconcat(acc, !listremove(traitList, acc))),
        traits
      )
    )> {
  list<OpenMP_Clause> clauseList = clauses;

  // Aggregate `arguments` fields of all clauses into a single dag, to be used
  // by operations to populate their `arguments` field.
  defvar argsFilteredClauses =
    !filter(clause, clauses, !not(clause.ignoreArgs));

  dag clausesArgs =
    !foldl((ins), !foreach(clause, argsFilteredClauses, clause.arguments),
           acc, argList, !con(acc, argList));

  // Create assembly format string by concatenating format strings separately
  // for required and optional clauses. Then, required clauses format strings
  // are joined with spaces in between. Optional clauses format strings are
  // wrapped into an unsorted list of optional values and separated by "|"
  // characters.

  // Required clauses.
  defvar asmFormatFilteredReqClauses =
    !filter(clause, clauses, !not(!or(clause.ignoreAsmFormat,
                                     !empty(clause.reqAssemblyFormat))));

  defvar asmFormatReqClauseStrings =
    !foreach(clause, asmFormatFilteredReqClauses, clause.reqAssemblyFormat);

  string clausesReqAssemblyFormat = !interleave(asmFormatReqClauseStrings, " ");

  // Optional clauses.
  defvar asmFormatFilteredOptClauses =
    !filter(clause, clauses, !not(!or(clause.ignoreAsmFormat,
                                     !empty(clause.optAssemblyFormat))));

  defvar asmFormatOptClauseStrings =
    !foreach(clause, asmFormatFilteredOptClauses, clause.optAssemblyFormat);

  string clausesOptAssemblyFormat = !interleave(asmFormatOptClauseStrings, "|");

  string clausesAssemblyFormat =
    !if(!empty(asmFormatReqClauseStrings), "", clausesReqAssemblyFormat # " ") #
    !if(!empty(asmFormatOptClauseStrings), "",
        "oilist(" # clausesOptAssemblyFormat # ")");

  // Put together descriptions of all clauses into a single string.
  defvar descFilteredClauses =
    !filter(clause, clauses, !not(clause.ignoreDesc));

  string clausesDescription =
    !interleave(!foreach(clause, descFilteredClauses, clause.description), "");
  
  // Aggregate `extraClassDeclaration` of all clauses that define it.
  defvar extraDeclFilteredClauses =
    !filter(clause, clauses, !not(clause.ignoreExtraDecl));

  string clausesExtraClassDeclaration =
    !interleave(!foreach(clause, extraDeclFilteredClauses,
                         clause.extraClassDeclaration), "\n");

  // The default arguments, assembly format and extra class declarations for
  // OpenMP operations are those defined by their args and clauses.
  let arguments = clausesArgs;
  let assemblyFormat =
    !if(!empty(clausesAssemblyFormat), ?,
        clausesAssemblyFormat # !if(singleRegion, " $region", "") #
        " attr-dict");
  let extraClassDeclaration = clausesExtraClassDeclaration;

  // By default, the op will have zero regions. Setting `singleRegion = true`
  // will result in a single region named `$region`.
  let regions = !if(singleRegion, (region AnyRegion:$region), (region));
}

#endif  // OPENMP_OP_BASE