brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.6 KiB · 11a2db4 Raw
593 lines · cpp
1//===- LLVMIRConversionGen.cpp - MLIR LLVM IR builder generator -----------===//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// This file uses tablegen definitions of the LLVM IR Dialect operations to10// generate the code building the LLVM IR from it.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/TableGen/Argument.h"15#include "mlir/TableGen/Attribute.h"16#include "mlir/TableGen/EnumInfo.h"17#include "mlir/TableGen/GenInfo.h"18#include "mlir/TableGen/Operator.h"19 20#include "llvm/ADT/Sequence.h"21#include "llvm/ADT/StringExtras.h"22#include "llvm/ADT/Twine.h"23#include "llvm/Support/FormatVariadic.h"24#include "llvm/Support/raw_ostream.h"25#include "llvm/TableGen/Error.h"26#include "llvm/TableGen/Record.h"27#include "llvm/TableGen/TableGenBackend.h"28 29using namespace llvm;30using namespace mlir;31 32static LogicalResult emitError(const Record &record, const Twine &message) {33  PrintError(&record, message);34  return failure();35}36 37namespace {38// Helper structure to return a position of the substring in a string.39struct StringLoc {40  size_t pos;41  size_t length;42 43  // Take a substring identified by this location in the given string.44  StringRef in(StringRef str) const { return str.substr(pos, length); }45 46  // A location is invalid if its position is outside the string.47  explicit operator bool() { return pos != std::string::npos; }48};49} // namespace50 51// Find the next TableGen variable in the given pattern.  These variables start52// with a `$` character and can contain alphanumeric characters or underscores.53// Return the position of the variable in the pattern and its length, including54// the `$` character.  The escape syntax `$$` is also detected and returned.55static StringLoc findNextVariable(StringRef str) {56  size_t startPos = str.find('$');57  if (startPos == std::string::npos)58    return {startPos, 0};59 60  // If we see "$$", return immediately.61  if (startPos != str.size() - 1 && str[startPos + 1] == '$')62    return {startPos, 2};63 64  // Otherwise, the symbol spans until the first character that is not65  // alphanumeric or '_'.66  size_t endPos = str.find_if_not([](char c) { return isAlnum(c) || c == '_'; },67                                  startPos + 1);68  if (endPos == std::string::npos)69    endPos = str.size();70 71  return {startPos, endPos - startPos};72}73 74// Check if `name` is a variadic operand of `op`. Seach all operands since the75// MLIR and LLVM IR operand order may differ and only for the latter the76// variadic operand is guaranteed to be at the end of the operands list.77static bool isVariadicOperandName(const tblgen::Operator &op, StringRef name) {78  for (int i = 0, e = op.getNumOperands(); i < e; ++i)79    if (op.getOperand(i).name == name)80      return op.getOperand(i).isVariadic();81  return false;82}83 84// Check if `result` is a known name of a result of `op`.85static bool isResultName(const tblgen::Operator &op, StringRef name) {86  for (int i = 0, e = op.getNumResults(); i < e; ++i)87    if (op.getResultName(i) == name)88      return true;89  return false;90}91 92// Check if `name` is a known name of an attribute of `op`.93static bool isAttributeName(const tblgen::Operator &op, StringRef name) {94  return llvm::any_of(95      op.getAttributes(),96      [name](const tblgen::NamedAttribute &attr) { return attr.name == name; });97}98 99// Check if `name` is a known name of an operand of `op`.100static bool isOperandName(const tblgen::Operator &op, StringRef name) {101  for (int i = 0, e = op.getNumOperands(); i < e; ++i)102    if (op.getOperand(i).name == name)103      return true;104  return false;105}106 107// Return the `op` argument index of the argument with the given `name`.108static FailureOr<int> getArgumentIndex(const tblgen::Operator &op,109                                       StringRef name) {110  for (int i = 0, e = op.getNumArgs(); i != e; ++i)111    if (op.getArgName(i) == name)112      return i;113  return failure();114}115 116// Emit to `os` the operator-name driven check and the call to LLVM IRBuilder117// for one definition of an LLVM IR Dialect operation.118static LogicalResult emitOneBuilder(const Record &record, raw_ostream &os) {119  auto op = tblgen::Operator(record);120 121  if (!record.getValue("llvmBuilder"))122    return emitError(record, "expected 'llvmBuilder' field");123 124  // Return early if there is no builder specified.125  StringRef builderStrRef = record.getValueAsString("llvmBuilder");126  if (builderStrRef.empty())127    return success();128 129  // Progressively create the builder string by replacing $-variables with130  // value lookups.  Keep only the not-yet-traversed part of the builder pattern131  // to avoid re-traversing the string multiple times.132  std::string builder;133  llvm::raw_string_ostream bs(builder);134  while (StringLoc loc = findNextVariable(builderStrRef)) {135    auto name = loc.in(builderStrRef).drop_front();136    auto getterName = op.getGetterName(name);137    // First, insert the non-matched part as is.138    bs << builderStrRef.substr(0, loc.pos);139    // Then, rewrite the name based on its kind.140    bool isVariadicOperand = isVariadicOperandName(op, name);141    if (isOperandName(op, name)) {142      auto result =143          isVariadicOperand144              ? formatv("moduleTranslation.lookupValues(op.{0}())", getterName)145              : formatv("moduleTranslation.lookupValue(op.{0}())", getterName);146      bs << result;147    } else if (isAttributeName(op, name)) {148      bs << formatv("op.{0}()", getterName);149    } else if (isResultName(op, name)) {150      bs << formatv("moduleTranslation.mapValue(op.{0}())", getterName);151    } else if (name == "_resultType") {152      bs << "moduleTranslation.convertType(op.getResult().getType())";153    } else if (name == "_hasResult") {154      bs << "opInst.getNumResults() == 1";155    } else if (name == "_location") {156      bs << "opInst.getLoc()";157    } else if (name == "_numOperands") {158      bs << "opInst.getNumOperands()";159    } else if (name == "$") {160      bs << '$';161    } else {162      return emitError(163          record, "expected keyword, argument, or result, but got " + name);164    }165    // Finally, only keep the untraversed part of the string.166    builderStrRef = builderStrRef.substr(loc.pos + loc.length);167  }168 169  // Output the check and the rewritten builder string.170  os << "if (auto op = dyn_cast<" << op.getQualCppClassName()171     << ">(opInst)) {\n";172  os << bs.str() << builderStrRef << "\n";173  os << "  return success();\n";174  os << "}\n";175 176  return success();177}178 179// Emit all builders.  Returns false on success because of the generator180// registration requirements.181static bool emitBuilders(const RecordKeeper &records, raw_ostream &os) {182  for (const Record *def : records.getAllDerivedDefinitions("LLVM_OpBase")) {183    if (failed(emitOneBuilder(*def, os)))184      return true;185  }186  return false;187}188 189using ConditionFn = mlir::function_ref<llvm::Twine(const Record &record)>;190 191// Emit a conditional call to the MLIR builder of the LLVM dialect operation to192// build for the given LLVM IR instruction. A condition function `conditionFn`193// emits a check to verify the opcode or intrinsic identifier of the LLVM IR194// instruction matches the LLVM dialect operation to build.195static LogicalResult emitOneMLIRBuilder(const Record &record, raw_ostream &os,196                                        ConditionFn conditionFn) {197  auto op = tblgen::Operator(record);198 199  if (!record.getValue("mlirBuilder"))200    return emitError(record, "expected 'mlirBuilder' field");201 202  // Return early if there is no builder specified.203  StringRef builderStrRef = record.getValueAsString("mlirBuilder");204  if (builderStrRef.empty())205    return success();206 207  // Access the argument index array that maps argument indices to LLVM IR208  // operand indices. If the operation defines no custom mapping, set the array209  // to the identity permutation.210  std::vector<int64_t> llvmArgIndices =211      record.getValueAsListOfInts("llvmArgIndices");212  if (llvmArgIndices.empty())213    append_range(llvmArgIndices, seq<int64_t>(0, op.getNumArgs()));214  if (llvmArgIndices.size() != static_cast<size_t>(op.getNumArgs())) {215    return emitError(216        record,217        "expected 'llvmArgIndices' size to match the number of arguments");218  }219 220  // Progressively create the builder string by replacing $-variables. Keep only221  // the not-yet-traversed part of the builder pattern to avoid re-traversing222  // the string multiple times. Additionally, emit an argument string223  // immediately before the builder string. This argument string converts all224  // operands used by the builder to MLIR values and returns failure if one of225  // the conversions fails.226  std::string arguments, builder;227  llvm::raw_string_ostream as(arguments), bs(builder);228  while (StringLoc loc = findNextVariable(builderStrRef)) {229    auto name = loc.in(builderStrRef).drop_front();230    // First, insert the non-matched part as is.231    bs << builderStrRef.substr(0, loc.pos);232    // Then, rewrite the name based on its kind.233    FailureOr<int> argIndex = getArgumentIndex(op, name);234    if (succeeded(argIndex)) {235      // Access the LLVM IR operand that maps to the given argument index using236      // the provided argument indices mapping.237      int64_t idx = llvmArgIndices[*argIndex];238      if (idx < 0) {239        return emitError(240            record, "expected non-negative operand index for argument " + name);241      }242      if (isAttributeName(op, name)) {243        bs << formatv("llvmOperands[{0}]", idx);244      } else {245        if (isVariadicOperandName(op, name)) {246          as << formatv(247              "FailureOr<SmallVector<Value>> _llvmir_gen_operand_{0} = "248              "moduleImport.convertValues(llvmOperands.drop_front({1}));\n",249              name, idx);250        } else {251          as << formatv("FailureOr<Value> _llvmir_gen_operand_{0} = "252                        "moduleImport.convertValue(llvmOperands[{1}]);\n",253                        name, idx);254        }255        as << formatv("if (failed(_llvmir_gen_operand_{0}))\n"256                      "  return failure();\n",257                      name);258        bs << formatv("*_llvmir_gen_operand_{0}", name);259      }260    } else if (isResultName(op, name)) {261      if (op.getNumResults() != 1)262        return emitError(record, "expected op to have one result");263      bs << "moduleImport.mapValue(inst)";264    } else if (name == "_op") {265      bs << "moduleImport.mapNoResultOp(inst)";266    } else if (name == "_int_attr") {267      bs << "moduleImport.matchIntegerAttr";268    } else if (name == "_float_attr") {269      bs << "moduleImport.matchFloatAttr";270    } else if (name == "_var_attr") {271      bs << "moduleImport.matchLocalVariableAttr";272    } else if (name == "_label_attr") {273      bs << "moduleImport.matchLabelAttr";274    } else if (name == "_fpExceptionBehavior_attr") {275      bs << "moduleImport.matchFPExceptionBehaviorAttr";276    } else if (name == "_roundingMode_attr") {277      bs << "moduleImport.matchRoundingModeAttr";278    } else if (name == "_resultType") {279      bs << "moduleImport.convertType(inst->getType())";280    } else if (name == "_location") {281      bs << "moduleImport.translateLoc(inst->getDebugLoc())";282    } else if (name == "_builder") {283      bs << "odsBuilder";284    } else if (name == "_qualCppClassName") {285      bs << op.getQualCppClassName();286    } else if (name == "$") {287      bs << '$';288    } else {289      return emitError(290          record, "expected keyword, argument, or result, but got " + name);291    }292    // Finally, only keep the untraversed part of the string.293    builderStrRef = builderStrRef.substr(loc.pos + loc.length);294  }295 296  // Output the check, the argument conversion, and the builder string.297  os << "if (" << conditionFn(record) << ") {\n";298  os << as.str() << "\n";299  os << bs.str() << builderStrRef << "\n";300  os << "  return success();\n";301  os << "}\n";302 303  return success();304}305 306// Emit all intrinsic MLIR builders. Returns false on success because of the307// generator registration requirements.308static bool emitIntrMLIRBuilders(const RecordKeeper &records, raw_ostream &os) {309  // Emit condition to check if "llvmEnumName" matches the intrinsic id.310  auto emitIntrCond = [](const Record &record) {311    return "intrinsicID == llvm::Intrinsic::" +312           record.getValueAsString("llvmEnumName");313  };314  for (const Record *def :315       records.getAllDerivedDefinitions("LLVM_IntrOpBase")) {316    if (failed(emitOneMLIRBuilder(*def, os, emitIntrCond)))317      return true;318  }319  return false;320}321 322// Emit all op builders. Returns false on success because of the323// generator registration requirements.324static bool emitOpMLIRBuilders(const RecordKeeper &records, raw_ostream &os) {325  // Emit condition to check if "llvmInstName" matches the instruction opcode.326  auto emitOpcodeCond = [](const Record &record) {327    return "inst->getOpcode() == llvm::Instruction::" +328           record.getValueAsString("llvmInstName");329  };330  for (const Record *def : records.getAllDerivedDefinitions("LLVM_OpBase")) {331    if (failed(emitOneMLIRBuilder(*def, os, emitOpcodeCond)))332      return true;333  }334  return false;335}336 337namespace {338// Wrapper class around a Tablegen definition of an LLVM enum attribute case.339class LLVMEnumCase : public tblgen::EnumCase {340public:341  using tblgen::EnumCase::EnumCase;342 343  // Constructs a case from a non LLVM-specific enum attribute case.344  explicit LLVMEnumCase(const tblgen::EnumCase &other)345      : tblgen::EnumCase(&other.getDef()) {}346 347  // Returns the C++ enumerant for the LLVM API.348  StringRef getLLVMEnumerant() const {349    return def->getValueAsString("llvmEnumerant");350  }351};352 353// Wraper class around a Tablegen definition of an LLVM enum attribute.354class LLVMEnumInfo : public tblgen::EnumInfo {355public:356  using tblgen::EnumInfo::EnumInfo;357 358  // Returns the C++ enum name for the LLVM API.359  StringRef getLLVMClassName() const {360    return def->getValueAsString("llvmClassName");361  }362 363  // Returns all associated cases viewed as LLVM-specific enum cases.364  std::vector<LLVMEnumCase> getAllCases() const {365    std::vector<LLVMEnumCase> cases;366 367    for (auto &c : tblgen::EnumInfo::getAllCases())368      cases.emplace_back(c);369 370    return cases;371  }372 373  std::vector<LLVMEnumCase> getAllUnsupportedCases() const {374    const auto *inits = def->getValueAsListInit("unsupported");375 376    std::vector<LLVMEnumCase> cases;377    cases.reserve(inits->size());378 379    for (const llvm::Init *init : *inits)380      cases.emplace_back(cast<llvm::DefInit>(init));381 382    return cases;383  }384};385 386// Wraper class around a Tablegen definition of a C-style LLVM enum attribute.387class LLVMCEnumInfo : public tblgen::EnumInfo {388public:389  using tblgen::EnumInfo::EnumInfo;390 391  // Returns the C++ enum name for the LLVM API.392  StringRef getLLVMClassName() const {393    return def->getValueAsString("llvmClassName");394  }395 396  // Returns all associated cases viewed as LLVM-specific enum cases.397  std::vector<LLVMEnumCase> getAllCases() const {398    std::vector<LLVMEnumCase> cases;399 400    for (auto &c : tblgen::EnumInfo::getAllCases())401      cases.emplace_back(c);402 403    return cases;404  }405};406} // namespace407 408// Emits conversion function "LLVMClass convertEnumToLLVM(Enum)" and containing409// switch-based logic to convert from the MLIR LLVM dialect enum attribute case410// (Enum) to the corresponding LLVM API enumerant411static void emitOneEnumToConversion(const Record *record, raw_ostream &os) {412  LLVMEnumInfo enumInfo(record);413  StringRef llvmClass = enumInfo.getLLVMClassName();414  StringRef cppClassName = enumInfo.getEnumClassName();415  StringRef cppNamespace = enumInfo.getCppNamespace();416 417  // Emit the function converting the enum attribute to its LLVM counterpart.418  os << formatv(419      "[[maybe_unused]] static {0} convert{1}ToLLVM({2}::{1} value) {{\n",420      llvmClass, cppClassName, cppNamespace);421  os << "  switch (value) {\n";422 423  for (const auto &enumerant : enumInfo.getAllCases()) {424    StringRef llvmEnumerant = enumerant.getLLVMEnumerant();425    StringRef cppEnumerant = enumerant.getSymbol();426    os << formatv("  case {0}::{1}::{2}:\n", cppNamespace, cppClassName,427                  cppEnumerant);428    os << formatv("    return {0}::{1};\n", llvmClass, llvmEnumerant);429  }430 431  os << "  }\n";432  os << formatv("  llvm_unreachable(\"unknown {0} type\");\n",433                enumInfo.getEnumClassName());434  os << "}\n\n";435}436 437// Emits conversion function "LLVMClass convertEnumToLLVM(Enum)" and containing438// switch-based logic to convert from the MLIR LLVM dialect enum attribute case439// (Enum) to the corresponding LLVM API C-style enumerant440static void emitOneCEnumToConversion(const Record *record, raw_ostream &os) {441  LLVMCEnumInfo enumAttr(record);442  StringRef llvmClass = enumAttr.getLLVMClassName();443  StringRef cppClassName = enumAttr.getEnumClassName();444  StringRef cppNamespace = enumAttr.getCppNamespace();445 446  // Emit the function converting the enum attribute to its LLVM counterpart.447  os << formatv("[[maybe_unused]] static int64_t "448                "convert{0}ToLLVM({1}::{0} value) {{\n",449                cppClassName, cppNamespace);450  os << "  switch (value) {\n";451 452  for (const auto &enumerant : enumAttr.getAllCases()) {453    StringRef llvmEnumerant = enumerant.getLLVMEnumerant();454    StringRef cppEnumerant = enumerant.getSymbol();455    os << formatv("  case {0}::{1}::{2}:\n", cppNamespace, cppClassName,456                  cppEnumerant);457    os << formatv("    return static_cast<int64_t>({0}::{1});\n", llvmClass,458                  llvmEnumerant);459  }460 461  os << "  }\n";462  os << formatv("  llvm_unreachable(\"unknown {0} type\");\n",463                enumAttr.getEnumClassName());464  os << "}\n\n";465}466 467// Emits conversion function "Enum convertEnumFromLLVM(LLVMClass)" and468// containing switch-based logic to convert from the LLVM API enumerant to MLIR469// LLVM dialect enum attribute (Enum).470static void emitOneEnumFromConversion(const Record *record, raw_ostream &os) {471  LLVMEnumInfo enumInfo(record);472  StringRef llvmClass = enumInfo.getLLVMClassName();473  StringRef cppClassName = enumInfo.getEnumClassName();474  StringRef cppNamespace = enumInfo.getCppNamespace();475 476  // Emit the function converting the enum attribute from its LLVM counterpart.477  os << formatv("[[maybe_unused]] inline {0}::{1} convert{1}FromLLVM({2} "478                "value) {{\n",479                cppNamespace, cppClassName, llvmClass);480  os << "  switch (value) {\n";481 482  for (const auto &enumerant : enumInfo.getAllCases()) {483    StringRef llvmEnumerant = enumerant.getLLVMEnumerant();484    StringRef cppEnumerant = enumerant.getSymbol();485    os << formatv("  case {0}::{1}:\n", llvmClass, llvmEnumerant);486    os << formatv("    return {0}::{1}::{2};\n", cppNamespace, cppClassName,487                  cppEnumerant);488  }489  for (const auto &enumerant : enumInfo.getAllUnsupportedCases()) {490    StringRef llvmEnumerant = enumerant.getLLVMEnumerant();491    os << formatv("  case {0}::{1}:\n", llvmClass, llvmEnumerant);492    os << formatv("    llvm_unreachable(\"unsupported case {0}::{1}\");\n",493                  enumInfo.getLLVMClassName(), llvmEnumerant);494  }495 496  os << "  }\n";497  os << formatv("  llvm_unreachable(\"unknown {0} type\");",498                enumInfo.getLLVMClassName());499  os << "}\n\n";500}501 502// Emits conversion function "Enum convertEnumFromLLVM(LLVMEnum)" and503// containing switch-based logic to convert from the LLVM API C-style enumerant504// to MLIR LLVM dialect enum attribute (Enum).505static void emitOneCEnumFromConversion(const Record *record, raw_ostream &os) {506  LLVMCEnumInfo enumInfo(record);507  StringRef llvmClass = enumInfo.getLLVMClassName();508  StringRef cppClassName = enumInfo.getEnumClassName();509  StringRef cppNamespace = enumInfo.getCppNamespace();510 511  // Emit the function converting the enum attribute from its LLVM counterpart.512  os << formatv("[[maybe_unused]] inline {0}::{1} convert{1}FromLLVM(int64_t "513                "value) {{\n",514                cppNamespace, cppClassName);515  os << "  switch (value) {\n";516 517  for (const auto &enumerant : enumInfo.getAllCases()) {518    StringRef llvmEnumerant = enumerant.getLLVMEnumerant();519    StringRef cppEnumerant = enumerant.getSymbol();520    os << formatv("  case static_cast<int64_t>({0}::{1}):\n", llvmClass,521                  llvmEnumerant);522    os << formatv("    return {0}::{1}::{2};\n", cppNamespace, cppClassName,523                  cppEnumerant);524  }525 526  os << "  }\n";527  os << formatv("  llvm_unreachable(\"unknown {0} type\");",528                enumInfo.getLLVMClassName());529  os << "}\n\n";530}531 532// Emits conversion functions between MLIR enum attribute case and corresponding533// LLVM API enumerants for all registered LLVM dialect enum attributes.534template <bool ConvertTo>535static bool emitEnumConversionDefs(const RecordKeeper &records,536                                   raw_ostream &os) {537  for (const Record *def : records.getAllDerivedDefinitions("LLVM_EnumAttr"))538    if (ConvertTo)539      emitOneEnumToConversion(def, os);540    else541      emitOneEnumFromConversion(def, os);542 543  for (const Record *def : records.getAllDerivedDefinitions("LLVM_CEnumAttr"))544    if (ConvertTo)545      emitOneCEnumToConversion(def, os);546    else547      emitOneCEnumFromConversion(def, os);548 549  return false;550}551 552static void emitOneIntrinsic(const Record &record, raw_ostream &os) {553  auto op = tblgen::Operator(record);554  os << "llvm::Intrinsic::" << record.getValueAsString("llvmEnumName") << ",\n";555}556 557// Emit the list of LLVM IR intrinsics identifiers that are convertible to a558// matching MLIR LLVM dialect intrinsic operation.559static bool emitConvertibleIntrinsics(const RecordKeeper &records,560                                      raw_ostream &os) {561  for (const Record *def : records.getAllDerivedDefinitions("LLVM_IntrOpBase"))562    emitOneIntrinsic(*def, os);563 564  return false;565}566 567static mlir::GenRegistration568    genLLVMIRConversions("gen-llvmir-conversions",569                         "Generate LLVM IR conversions", emitBuilders);570 571static mlir::GenRegistration genOpFromLLVMIRConversions(572    "gen-op-from-llvmir-conversions",573    "Generate conversions of operations from LLVM IR", emitOpMLIRBuilders);574 575static mlir::GenRegistration genIntrFromLLVMIRConversions(576    "gen-intr-from-llvmir-conversions",577    "Generate conversions of intrinsics from LLVM IR", emitIntrMLIRBuilders);578 579static mlir::GenRegistration580    genEnumToLLVMConversion("gen-enum-to-llvmir-conversions",581                            "Generate conversions of EnumAttrs to LLVM IR",582                            emitEnumConversionDefs</*ConvertTo=*/true>);583 584static mlir::GenRegistration585    genEnumFromLLVMConversion("gen-enum-from-llvmir-conversions",586                              "Generate conversions of EnumAttrs from LLVM IR",587                              emitEnumConversionDefs</*ConvertTo=*/false>);588 589static mlir::GenRegistration genConvertibleLLVMIRIntrinsics(590    "gen-convertible-llvmir-intrinsics",591    "Generate list of convertible LLVM IR intrinsics",592    emitConvertibleIntrinsics);593