brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.4 KiB · aebc248 Raw
208 lines · cpp
1//===- OpenACCUtils.cpp ---------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Dialect/OpenACC/OpenACCUtils.h"10 11#include "mlir/Dialect/OpenACC/OpenACC.h"12#include "mlir/IR/SymbolTable.h"13#include "mlir/Interfaces/FunctionInterfaces.h"14#include "mlir/Interfaces/ViewLikeInterface.h"15#include "llvm/ADT/TypeSwitch.h"16#include "llvm/IR/Intrinsics.h"17#include "llvm/Support/Casting.h"18 19mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region &region) {20  mlir::Operation *parentOp = region.getParentOp();21  while (parentOp) {22    if (mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS>(parentOp))23      return parentOp;24    parentOp = parentOp->getParentOp();25  }26  return nullptr;27}28 29template <typename OpTy>30static bool isOnlyUsedByOpClauses(mlir::Value val, mlir::Region &region) {31  auto checkIfUsedOnlyByOpInside = [&](mlir::Operation *user) {32    // For any users which are not in the current acc region, we can ignore.33    // Return true so that it can be used in a `all_of` check.34    if (!region.isAncestor(user->getParentRegion()))35      return true;36    return mlir::isa<OpTy>(user);37  };38 39  return llvm::all_of(val.getUsers(), checkIfUsedOnlyByOpInside);40}41 42bool mlir::acc::isOnlyUsedByPrivateClauses(mlir::Value val,43                                           mlir::Region &region) {44  return isOnlyUsedByOpClauses<mlir::acc::PrivateOp>(val, region);45}46 47bool mlir::acc::isOnlyUsedByReductionClauses(mlir::Value val,48                                             mlir::Region &region) {49  return isOnlyUsedByOpClauses<mlir::acc::ReductionOp>(val, region);50}51 52std::optional<mlir::acc::ClauseDefaultValue>53mlir::acc::getDefaultAttr(Operation *op) {54  std::optional<mlir::acc::ClauseDefaultValue> defaultAttr;55  Operation *currOp = op;56 57  // Iterate outwards until a default clause is found (since OpenACC58  // specification notes that a visible default clause is the nearest default59  // clause appearing on the compute construct or a lexically containing data60  // construct.61  while (!defaultAttr.has_value() && currOp) {62    defaultAttr =63        llvm::TypeSwitch<mlir::Operation *,64                         std::optional<mlir::acc::ClauseDefaultValue>>(currOp)65            .Case<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::DataOp>(66                [&](auto op) { return op.getDefaultAttr(); })67            .Default([&](Operation *) { return std::nullopt; });68    currOp = currOp->getParentOp();69  }70 71  return defaultAttr;72}73 74mlir::acc::VariableTypeCategory mlir::acc::getTypeCategory(mlir::Value var) {75  mlir::acc::VariableTypeCategory typeCategory =76      mlir::acc::VariableTypeCategory::uncategorized;77  if (auto mappableTy = dyn_cast<mlir::acc::MappableType>(var.getType()))78    typeCategory = mappableTy.getTypeCategory(var);79  else if (auto pointerLikeTy =80               dyn_cast<mlir::acc::PointerLikeType>(var.getType()))81    typeCategory = pointerLikeTy.getPointeeTypeCategory(82        cast<TypedValue<mlir::acc::PointerLikeType>>(var),83        pointerLikeTy.getElementType());84  return typeCategory;85}86 87std::string mlir::acc::getVariableName(mlir::Value v) {88  Value current = v;89 90  // Walk through view operations until a name is found or can't go further91  while (Operation *definingOp = current.getDefiningOp()) {92    // Check for `acc.var_name` attribute93    if (auto varNameAttr =94            definingOp->getAttrOfType<VarNameAttr>(getVarNameAttrName()))95      return varNameAttr.getName().str();96 97    // If it is a data entry operation, get name via getVarName98    if (isa<ACC_DATA_ENTRY_OPS>(definingOp))99      if (auto name = acc::getVarName(definingOp))100        return name->str();101 102    // If it's a view operation, continue to the source103    if (auto viewOp = dyn_cast<ViewLikeOpInterface>(definingOp)) {104      current = viewOp.getViewSource();105      continue;106    }107 108    break;109  }110 111  return "";112}113 114std::string mlir::acc::getRecipeName(mlir::acc::RecipeKind kind,115                                     mlir::Type type) {116  assert(kind == mlir::acc::RecipeKind::private_recipe ||117         kind == mlir::acc::RecipeKind::firstprivate_recipe ||118         kind == mlir::acc::RecipeKind::reduction_recipe);119  if (!llvm::isa<mlir::acc::PointerLikeType, mlir::acc::MappableType>(type))120    return "";121 122  std::string recipeName;123  llvm::raw_string_ostream ss(recipeName);124  ss << (kind == mlir::acc::RecipeKind::private_recipe ? "privatization_"125         : kind == mlir::acc::RecipeKind::firstprivate_recipe126             ? "firstprivatization_"127             : "reduction_");128 129  // Print the type using its dialect-defined textual format.130  type.print(ss);131  ss.flush();132 133  // Replace invalid characters (anything that's not a letter, number, or134  // period) since this needs to be a valid MLIR identifier.135  for (char &c : recipeName) {136    if (!std::isalnum(static_cast<unsigned char>(c)) && c != '.' && c != '_') {137      if (c == '?')138        c = 'U';139      else if (c == '*')140        c = 'Z';141      else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' ||142               c == '}' || c == '<' || c == '>')143        c = '_';144      else145        c = 'X';146    }147  }148 149  return recipeName;150}151 152mlir::Value mlir::acc::getBaseEntity(mlir::Value val) {153  if (auto partialEntityAccessOp =154          dyn_cast<PartialEntityAccessOpInterface>(val.getDefiningOp())) {155    if (!partialEntityAccessOp.isCompleteView())156      return partialEntityAccessOp.getBaseEntity();157  }158 159  return val;160}161 162bool mlir::acc::isValidSymbolUse(mlir::Operation *user,163                                 mlir::SymbolRefAttr symbol,164                                 mlir::Operation **definingOpPtr) {165  mlir::Operation *definingOp =166      mlir::SymbolTable::lookupNearestSymbolFrom(user, symbol);167 168  // If there are no defining ops, we have no way to ensure validity because169  // we cannot check for any attributes.170  if (!definingOp)171    return false;172 173  if (definingOpPtr)174    *definingOpPtr = definingOp;175 176  // Check if the defining op is a recipe (private, reduction, firstprivate).177  // Recipes are valid as they get materialized before being offloaded to178  // device. They are only instructions for how to materialize.179  if (mlir::isa<mlir::acc::PrivateRecipeOp, mlir::acc::ReductionRecipeOp,180                mlir::acc::FirstprivateRecipeOp>(definingOp))181    return true;182 183  // Check if the defining op is a function184  if (auto func =185          mlir::dyn_cast_if_present<mlir::FunctionOpInterface>(definingOp)) {186    // If this symbol is actually an acc routine - then it is expected for it187    // to be offloaded - therefore it is valid.188    if (func->hasAttr(mlir::acc::getRoutineInfoAttrName()))189      return true;190 191    // If this symbol is a call to an LLVM intrinsic, then it is likely valid.192    // Check the following:193    // 1. The function is private194    // 2. The function has no body195    // 3. Name starts with "llvm."196    // 4. The function's name is a valid LLVM intrinsic name197    if (func.getVisibility() == mlir::SymbolTable::Visibility::Private &&198        func.getFunctionBody().empty() && func.getName().starts_with("llvm.") &&199        llvm::Intrinsic::lookupIntrinsicID(func.getName()) !=200            llvm::Intrinsic::not_intrinsic)201      return true;202  }203 204  // A declare attribute is needed for symbol references.205  bool hasDeclare = definingOp->hasAttr(mlir::acc::getDeclareAttrName());206  return hasDeclare;207}208