//===-- OpenMPOpsInterfaces.td - OpenMP op interfaces ------*- 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 is the OpenMP Dialect interfaces definition file. // //===----------------------------------------------------------------------===// #ifndef OPENMP_OPS_INTERFACES #define OPENMP_OPS_INTERFACES include "mlir/IR/OpBase.td" // Internal class to hold definitions of BlockArgOpenMPOpInterface methods, // based on the name of the clause and what clause comes earlier in the list. // // The clause order will define the expected relative order between block // arguments corresponding to each of these clauses. class BlockArgOpenMPClause { // Default-implemented method, overriden by the corresponding clause. It // returns the range of operands passed to the operation associated to the // clause. // // For the override to work, the clause tablegen definition must contain a // `Variadic<...> $clause_name_vars` argument. // // Usage example: // // ```c++ // OperandRange reductionVars = op.getReductionVars(); // ``` InterfaceMethod varsMethod = InterfaceMethod< "Get operation operands associated to `" # clauseNameSnake # "`.", "::mlir::OperandRange", "get" # clauseNameCamel # "Vars", (ins), [{}], [{ return {0, 0}; }] >; // It returns the number of entry block arguments introduced by the given // clause. // // By default, it will be the number of operands corresponding to that clause, // but it can be overriden by operations where this might not be the case // (e.g. `map` clause in `omp.target_update`). // // Usage example: // // ```c++ // unsigned numInReductionArgs = op.numInReductionBlockArgs(); // ``` InterfaceMethod numArgsMethod = InterfaceMethod< "Get number of block arguments defined by `" # clauseNameSnake # "`.", "unsigned", "num" # clauseNameCamel # "BlockArgs", (ins), [{}], "return $_op." # varsMethod.name # "().size();" >; // Unified access method for the start index of clause-associated entry block // arguments. // // Usage example: // // ```c++ // auto iface = cast(op); // unsigned firstMapIndex = iface.getMapBlockArgsStart(); // ``` InterfaceMethod startMethod = InterfaceMethod< "Get start index of block arguments defined by `" # clauseNameSnake # "`.", "unsigned", "get" # clauseNameCamel # "BlockArgsStart", (ins), !if(!initialized(previousClause), [{ auto iface = ::llvm::cast(*$_op); }] # "return iface." # previousClause.startMethod.name # "() + iface." # previousClause.numArgsMethod.name # "();", "return 0;" ) >; // Unified access method for clause-associated entry block arguments. // // Usage example: // // ```c++ // auto iface = cast(op); // ArrayRef reductionArgs = iface.getReductionBlockArgs(); // ``` InterfaceMethod blockArgsMethod = InterfaceMethod< "Get block arguments defined by `" # clauseNameSnake # "`.", "::llvm::MutableArrayRef<::mlir::BlockArgument>", "get" # clauseNameCamel # "BlockArgs", (ins), [{ auto iface = ::llvm::cast(*$_op); return $_op->getRegion(0).getArguments().slice( }] # "iface." # startMethod.name # "(), iface." # numArgsMethod.name # "());" >; } def BlockArgHasDeviceAddrClause : BlockArgOpenMPClause< "has_device_addr", "HasDeviceAddr", ?>; def BlockArgHostEvalClause : BlockArgOpenMPClause< "host_eval", "HostEval", BlockArgHasDeviceAddrClause>; def BlockArgInReductionClause : BlockArgOpenMPClause< "in_reduction", "InReduction", BlockArgHostEvalClause>; def BlockArgMapClause : BlockArgOpenMPClause< "map", "Map", BlockArgInReductionClause>; def BlockArgPrivateClause : BlockArgOpenMPClause< "private", "Private", BlockArgMapClause>; def BlockArgReductionClause : BlockArgOpenMPClause< "reduction", "Reduction", BlockArgPrivateClause>; def BlockArgTaskReductionClause : BlockArgOpenMPClause< "task_reduction", "TaskReduction", BlockArgReductionClause>; def BlockArgUseDeviceAddrClause : BlockArgOpenMPClause< "use_device_addr", "UseDeviceAddr", BlockArgTaskReductionClause>; def BlockArgUseDevicePtrClause : BlockArgOpenMPClause< "use_device_ptr", "UseDevicePtr", BlockArgUseDeviceAddrClause>; def BlockArgOpenMPOpInterface : OpInterface<"BlockArgOpenMPOpInterface"> { let description = [{ OpenMP operations that define entry block arguments as part of the representation of its clauses. }]; let cppNamespace = "::mlir::omp"; defvar clauses = [ BlockArgHasDeviceAddrClause, BlockArgHostEvalClause, BlockArgInReductionClause, BlockArgMapClause, BlockArgPrivateClause, BlockArgReductionClause, BlockArgTaskReductionClause, BlockArgUseDeviceAddrClause, BlockArgUseDevicePtrClause ]; let methods = !listconcat( !foreach(clause, clauses, clause.varsMethod), !foreach(clause, clauses, clause.numArgsMethod), !foreach(clause, clauses, clause.startMethod), !foreach(clause, clauses, clause.blockArgsMethod), [ InterfaceMethod< "Get the total number of clause-defined entry block arguments", "unsigned", "numClauseBlockArgs", (ins), "return " # !interleave( !foreach(clause, clauses, "$_op." # clause.numArgsMethod.name # "()"), " + ") # ";" >, InterfaceMethod< "Populate a vector of pairs representing the matching between operands " "and entry block arguments.", "void", "getBlockArgsPairs", (ins "::llvm::SmallVectorImpl> &" : $pairs), [{ auto iface = ::llvm::cast(*$_op); pairs.reserve(pairs.size() + iface.numClauseBlockArgs()); }] # !interleave(!foreach(clause, clauses, [{ }] # "if (iface." # clause.numArgsMethod.name # "() > 0) {" # [{ }] # " for (auto [var, arg] : ::llvm::zip_equal(" # "iface." # clause.varsMethod.name # "()," # "iface." # clause.blockArgsMethod.name # "()))" # [{ pairs.emplace_back(var, arg); } }]), "\n") > ] ); let verify = [{ auto iface = ::llvm::cast($_op); unsigned expectedArgs = iface.numClauseBlockArgs(); if ($_op->getRegion(0).getNumArguments() < expectedArgs) return $_op->emitOpError() << "expected at least " << expectedArgs << " entry block argument(s)"; return ::mlir::success(); }]; } def OutlineableOpenMPOpInterface : OpInterface<"OutlineableOpenMPOpInterface"> { let description = [{ OpenMP operations whose region will be outlined will implement this interface. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod<"Get alloca block", "::mlir::Block*", "getAllocaBlock", (ins), [{ // For taskloop: put the allocas inside of the wrapped loop. Loop wrappers // are expected to contain only the wrapped loop (or another loop wrapper) if (LoopWrapperInterface loopWrapper = mlir::dyn_cast($_op.getOperation())) return &loopWrapper.getWrappedLoop()->getRegion(0).front(); return &$_op.getRegion().front(); }]>, ]; } def MapClauseOwningOpInterface : OpInterface<"MapClauseOwningOpInterface"> { let description = [{ OpenMP operations which own a list of omp::MapInfoOp's implement this interface to allow generic access to deal with map operands to more easily manipulate this class of operations. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod<"Get map operands", "::mlir::OperandRange", "getMapVars", (ins), [{ return $_op.getMapVars(); }]>, InterfaceMethod<"Get mutable map operands", "::mlir::MutableOperandRange", "getMapVarsMutable", (ins), [{ return $_op.getMapVarsMutable(); }]>, InterfaceMethod<"Get operand index for a map clause", "int64_t", "getOperandIndexForMap", (ins "::mlir::Value":$map), [{ return std::distance($_op.getMapVars().begin(), llvm::find($_op.getMapVars(), map)); }]>, ]; } def LoopWrapperInterface : OpInterface<"LoopWrapperInterface"> { let description = [{ OpenMP operations that wrap a single loop nest. They must only contain a single region with a single block in which there's a single operation and a terminator. That nested operation must be another loop wrapper or an `omp.loop_nest`. Operation-specific verifiers should make the following checks in their verifier, additionally to what the interface itself checks: - If `getNestedWrapper() != nullptr`, is the type of the nested wrapper allowed in that context? This check might require looking at the parent as well. - If the operation is a `ComposableOpInterface`, check that it is consistent with the potential existence of a `LoopWrapperInterface` parent and whether `getNestedWrapper() != nullptr`. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod< /*description=*/[{ If there is another loop wrapper immediately nested inside, return that operation. Assumes this operation is a valid loop wrapper. }], /*retTy=*/"::mlir::omp::LoopWrapperInterface", /*methodName=*/"getNestedWrapper", (ins), [{}], [{ Operation *nested = &*$_op->getRegion(0).op_begin(); return ::llvm::dyn_cast(nested); }] >, InterfaceMethod< /*description=*/[{ Return the loop nest nested directly or indirectly inside of this loop wrapper. Assumes this operation is a valid loop wrapper. }], /*retTy=*/"::mlir::Operation *", /*methodName=*/"getWrappedLoop", (ins), [{}], [{ if (LoopWrapperInterface nested = $_op.getNestedWrapper()) return nested.getWrappedLoop(); return &*$_op->getRegion(0).op_begin(); }] > ]; let extraClassDeclaration = [{ /// Interface verifier implementation. llvm::LogicalResult verifyImpl(); }]; let verify = [{ return ::llvm::cast<::mlir::omp::LoopWrapperInterface>($_op).verifyImpl(); }]; let verifyWithRegions = 1; } def ComposableOpInterface : OpInterface<"ComposableOpInterface"> { let description = [{ OpenMP operations that can represent a single leaf of a composite OpenMP construct. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod< /*description=*/[{ Check whether the operation is representing a leaf of a composite OpenMP construct. }], /*retTy=*/"bool", /*methodName=*/"isComposite", (ins ), [{}], [{ return $_op->hasAttr("omp.composite"); }] >, InterfaceMethod< /*description=*/[{ Mark the operation as part of an OpenMP composite construct. }], /*retTy=*/"void", /*methodName=*/"setComposite", (ins "bool":$val), [{}], [{ if (val) $_op->setDiscardableAttr("omp.composite", mlir::UnitAttr::get($_op->getContext())); else $_op->removeDiscardableAttr("omp.composite"); }] > ]; } def DeclareTargetInterface : OpInterface<"DeclareTargetInterface"> { let description = [{ OpenMP operations that support declare target have this interface. For example, FuncOp's and llvm.GlobalOp/fir.GlobalOp's. This interface allows simple manipulation and introspection of the declare target attribute that can be applied to these operations. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod< /*description=*/[{ Set the declare target attribute on the current operation with the specified attribute arguments. }], /*retTy=*/"void", /*methodName=*/"setDeclareTarget", (ins "mlir::omp::DeclareTargetDeviceType":$deviceType, "mlir::omp::DeclareTargetCaptureClause":$captureClause, "bool":$automap), [{}], [{ $_op->setAttr("omp.declare_target", mlir::omp::DeclareTargetAttr::get( $_op->getContext(), mlir::omp::DeclareTargetDeviceTypeAttr::get( $_op->getContext(), deviceType), mlir::omp::DeclareTargetCaptureClauseAttr::get( $_op->getContext(), captureClause), mlir::BoolAttr::get($_op->getContext(), automap))); }]>, InterfaceMethod< /*description=*/[{ Checks if the declare target attribute has been applied and exists on the current operation. Returns true if it exists on it, otherwise returns false. }], /*retTy=*/"bool", /*methodName=*/"isDeclareTarget", (ins), [{}], [{ return $_op->hasAttr("omp.declare_target"); }]>, InterfaceMethod< /*description=*/[{ Returns the DeclareTargetDeviceType segment of the DeclareTarget attribute if it exists on the current operation. Otherwise it returns null. }], /*retTy=*/"mlir::omp::DeclareTargetDeviceType", /*methodName=*/"getDeclareTargetDeviceType", (ins), [{}], [{ if (mlir::Attribute dTar = $_op->getAttr("omp.declare_target")) if (auto dAttr = llvm::dyn_cast_or_null(dTar)) return dAttr.getDeviceType().getValue(); return {}; }]>, InterfaceMethod< /*description=*/[{ Returns the DeclareTargetCaptureClause segment of the DeclareTarget attribute if it exists on the current operation. Otherwise it returns null. }], /*retTy=*/"mlir::omp::DeclareTargetCaptureClause", /*methodName=*/"getDeclareTargetCaptureClause", (ins), [{}], [{ if (mlir::Attribute dTar = $_op->getAttr("omp.declare_target")) if (auto dAttr = llvm::dyn_cast_or_null(dTar)) return dAttr.getCaptureClause().getValue(); return {}; }]>, InterfaceMethod< /*description=*/[{ Return true if the DeclareTarget attribute has the AUTOMAP modifier. }], /*retTy=*/"bool", /*methodName=*/"getDeclareTargetAutomap", (ins), [{}], [{ if (mlir::Attribute dTar = $_op->getAttr("omp.declare_target")) if (auto dAttr = llvm::dyn_cast_or_null(dTar)) if (auto autoVal = dAttr.getAutomap()) return autoVal.getValue(); return false; }]> ]; } def OffloadModuleInterface : OpInterface<"OffloadModuleInterface"> { let description = [{ Operations that represent a module for offloading (host or device) should have this interface. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod< /*description=*/[{ Set the attribute on the current module with the specified boolean argument. }], /*retTy=*/"void", /*methodName=*/"setIsTargetDevice", (ins "bool":$isTargetDevice), [{}], [{ $_op->setAttr( mlir::StringAttr::get($_op->getContext(), llvm::Twine{"omp.is_target_device"}), mlir::BoolAttr::get($_op->getContext(), isTargetDevice)); }]>, InterfaceMethod< /*description=*/[{ Get the attribute on the current module if it exists and return its value, if it doesn't exist it returns false by default. }], /*retTy=*/"bool", /*methodName=*/"getIsTargetDevice", (ins), [{}], [{ if (Attribute isTargetDevice = $_op->getAttr("omp.is_target_device")) if (::llvm::isa(isTargetDevice)) return ::llvm::dyn_cast(isTargetDevice).getValue(); return false; }]>, InterfaceMethod< /*description=*/[{ Set the attribute on the current module with the specified boolean argument. }], /*retTy=*/"void", /*methodName=*/"setIsGPU", (ins "bool":$isGPU), [{}], [{ $_op->setAttr( mlir::StringAttr::get($_op->getContext(), "omp.is_gpu"), mlir::BoolAttr::get($_op->getContext(), isGPU)); }]>, InterfaceMethod< /*description=*/[{ Get the attribute on the current module if it exists and return its value, if it doesn't exist it returns false by default. }], /*retTy=*/"bool", /*methodName=*/"getIsGPU", (ins), [{}], [{ if (Attribute isTargetCGAttr = $_op->getAttr("omp.is_gpu")) if (auto isTargetCGVal = ::llvm::dyn_cast(isTargetCGAttr)) return isTargetCGVal.getValue(); return false; }]>, InterfaceMethod< /*description=*/[{ Get the FlagsAttr attribute on the current module if it exists and return the attribute, if it doesn't exit it returns a nullptr }], /*retTy=*/"mlir::omp::FlagsAttr", /*methodName=*/"getFlags", (ins), [{}], [{ if (Attribute flags = $_op->getAttr("omp.flags")) return ::llvm::dyn_cast_or_null(flags); return nullptr; }]>, InterfaceMethod< /*description=*/[{ Apply an omp.FlagsAttr to a module with the specified values for the flags }], /*retTy=*/"void", /*methodName=*/"setFlags", (ins "uint32_t":$debugKind, "bool":$assumeTeamsOversubscription, "bool":$assumeThreadsOversubscription, "bool":$assumeNoThreadState, "bool":$assumeNoNestedParallelism, "uint32_t":$openmpDeviceVersion, "bool":$noGPULib), [{}], [{ $_op->setAttr(("omp." + mlir::omp::FlagsAttr::getMnemonic()).str(), mlir::omp::FlagsAttr::get($_op->getContext(), debugKind, assumeTeamsOversubscription, assumeThreadsOversubscription, assumeNoThreadState, assumeNoNestedParallelism, noGPULib, openmpDeviceVersion)); }]>, InterfaceMethod< /*description=*/[{ Set a StringAttr on the current module containing the host IR file path. This file path is used in two-phase compilation during the device phase to generate device side LLVM IR when lowering MLIR. }], /*retTy=*/"void", /*methodName=*/"setHostIRFilePath", (ins "std::string":$hostIRFilePath), [{}], [{ $_op->setAttr( mlir::StringAttr::get($_op->getContext(), llvm::Twine{"omp.host_ir_filepath"}), mlir::StringAttr::get($_op->getContext(), hostIRFilePath)); }]>, InterfaceMethod< /*description=*/[{ Find the host-ir file path StringAttr from the current module if it exists and return its contained value, if it doesn't exist it returns an empty string. This file path is used in two-phase compilation during the device phase to generate device side LLVM IR when lowering MLIR. }], /*retTy=*/"llvm::StringRef", /*methodName=*/"getHostIRFilePath", (ins), [{}], [{ if (Attribute filepath = $_op->getAttr("omp.host_ir_filepath")) if (::llvm::isa(filepath)) return ::llvm::dyn_cast(filepath).getValue(); return {}; }]>, InterfaceMethod< /*description=*/[{ Get the omp.requires attribute on the operator if it's present and return its value. If it doesn't exist, return `ClauseRequires::none` by default. }], /*retTy=*/"::mlir::omp::ClauseRequires", /*methodName=*/"getRequires", (ins), [{}], [{ if (Attribute requiresAttr = $_op->getAttr("omp.requires")) if (auto requiresVal = ::llvm::dyn_cast(requiresAttr)) return requiresVal.getValue(); return mlir::omp::ClauseRequires::none; }]>, InterfaceMethod< /*description=*/[{ Set the omp.requires attribute on the operator to the specified clauses. }], /*retTy=*/"void", /*methodName=*/"setRequires", (ins "::mlir::omp::ClauseRequires":$clauses), [{}], [{ $_op->setAttr(mlir::StringAttr::get($_op->getContext(), "omp.requires"), mlir::omp::ClauseRequiresAttr::get($_op->getContext(), clauses)); }]>, InterfaceMethod< /*description=*/[{ Get the omp.target_triples attribute on the operator if it's present and return its value. If it doesn't exist, return an empty array by default. }], /*retTy=*/"::llvm::ArrayRef<::mlir::Attribute>", /*methodName=*/"getTargetTriples", (ins), [{}], [{ if (Attribute triplesAttr = $_op->getAttr("omp.target_triples")) if (auto triples = ::llvm::dyn_cast<::mlir::ArrayAttr>(triplesAttr)) return triples.getValue(); return {}; }]>, InterfaceMethod< /*description=*/[{ Set the omp.target_triples attribute on the operation. }], /*retTy=*/"void", /*methodName=*/"setTargetTriples", (ins "::llvm::ArrayRef<::std::string>":$targetTriples), [{}], [{ auto names = ::llvm::to_vector(::llvm::map_range( targetTriples, [&](::std::string str) -> ::mlir::Attribute { return mlir::StringAttr::get($_op->getContext(), str); })); $_op->setAttr( ::mlir::StringAttr::get($_op->getContext(), "omp.target_triples"), ::mlir::ArrayAttr::get($_op->getContext(), names)); }]> ]; } def LoopTransformationInterface : OpInterface<"LoopTransformationInterface"> { let description = [{ Methods that are common for OpenMP loop transformation operations. }]; let cppNamespace = "::mlir::omp"; let methods = [ InterfaceMethod< /*description=*/[{ Get the indices for the arguments that represent CanonicalLoopInfo applyees, i.e. loops that are transformed/consumed by this operation. }], /*returnType=*/ "std::pair", /*methodName=*/ "getApplyeesODSOperandIndexAndLength", /*args=*/(ins) >, InterfaceMethod< /*description=*/[{ Get the indices for the arguments that represent CanonicalLoopInfo generatees, i.e. loops that are emitted by this operation. }], /*returnType=*/ "std::pair", /*methodName=*/ "getGenerateesODSOperandIndexAndLength", /*args=*/(ins) >, InterfaceMethod< /*description=*/[{ Return the number of applyees of this loop transformation. }], /*returnType=*/ "unsigned", /*methodName=*/ "getNumApplyees", /*args=*/ (ins), /*methodBody=*/ "", /*defaultImpl=*/[{ return $_op.getApplyeesODSOperandIndexAndLength().second; }] >, InterfaceMethod< /*description=*/[{ Return the number of generatees of this loop transformation. }], /*returnType=*/ "unsigned", /*methodName=*/ "getNumGeneratees", /*args=*/ (ins), /*methodBody=*/ "", /*defaultImpl=*/[{ return $_op.getGenerateesODSOperandIndexAndLength().second; }] >, InterfaceMethod< /*description=*/[{ Return whether the provided operand is an applyee of this operation. }], /*returnType=*/ "unsigned", /*methodName=*/ "isApplyee", /*args=*/ (ins "unsigned":$opnum), /*methodBody=*/ "", /*defaultImpl=*/[{ auto applyeeArgs = $_op.getApplyeesODSOperandIndexAndLength(); return (applyeeArgs.first <= opnum && opnum < applyeeArgs.first + applyeeArgs.second) ; }] >, InterfaceMethod< /*description=*/[{ Return whether the provided operand is a generatee of this operation. }], /*returnType=*/ "unsigned", /*methodName=*/ "isGeneratee", /*args=*/ (ins "unsigned":$opnum), /*methodBody=*/ "", /*defaultImpl=*/[{ auto generateeArgs = $_op.getGenerateesODSOperandIndexAndLength(); return (generateeArgs.first <= opnum && opnum < generateeArgs.first + generateeArgs.second) ; }] >, ]; } #endif // OPENMP_OPS_INTERFACES