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