278 lines · cpp
1//===- LLVMIntrinsicGen.cpp - TableGen utility for converting intrinsics --===//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 is a TableGen generator that converts TableGen definitions for LLVM10// intrinsics to TableGen definitions for MLIR operations.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/TableGen/GenInfo.h"15 16#include "llvm/ADT/SmallBitVector.h"17#include "llvm/ADT/StringSwitch.h"18#include "llvm/CodeGenTypes/MachineValueType.h"19#include "llvm/Support/CommandLine.h"20#include "llvm/Support/PrettyStackTrace.h"21#include "llvm/Support/Regex.h"22#include "llvm/Support/Signals.h"23#include "llvm/TableGen/Error.h"24#include "llvm/TableGen/Main.h"25#include "llvm/TableGen/Record.h"26#include "llvm/TableGen/TableGenBackend.h"27 28using llvm::Record;29using llvm::RecordKeeper;30using llvm::Regex;31using namespace mlir;32 33static llvm::cl::OptionCategory intrinsicGenCat("Intrinsics Generator Options");34 35static llvm::cl::opt<std::string>36 nameFilter("llvmir-intrinsics-filter",37 llvm::cl::desc("Only keep the intrinsics with the specified "38 "substring in their record name"),39 llvm::cl::cat(intrinsicGenCat));40 41static llvm::cl::opt<std::string>42 opBaseClass("dialect-opclass-base",43 llvm::cl::desc("The base class for the ops in the dialect we "44 "are planning to emit"),45 llvm::cl::init("LLVM_IntrOp"), llvm::cl::cat(intrinsicGenCat));46 47static llvm::cl::opt<std::string> accessGroupRegexp(48 "llvmir-intrinsics-access-group-regexp",49 llvm::cl::desc("Mark intrinsics that match the specified "50 "regexp as taking an access group metadata"),51 llvm::cl::cat(intrinsicGenCat));52 53static llvm::cl::opt<std::string> aliasAnalysisRegexp(54 "llvmir-intrinsics-alias-analysis-regexp",55 llvm::cl::desc("Mark intrinsics that match the specified "56 "regexp as taking alias.scopes, noalias, and tbaa metadata"),57 llvm::cl::cat(intrinsicGenCat));58 59// Used to represent the indices of overloadable operands/results.60using IndicesTy = llvm::SmallBitVector;61 62/// Return a CodeGen value type entry from a type record.63static llvm::MVT::SimpleValueType getValueType(const Record *rec) {64 return StringSwitch<llvm::MVT::SimpleValueType>(65 rec->getValueAsDef("VT")->getValueAsString("LLVMName"))66#define GET_VT_ATTR(Ty, Sz, Any, Int, FP, Vec, Sc, Tup, NF, NElem, EltTy) \67 .Case(#Ty, llvm::MVT::Ty)68#include "llvm/CodeGen/GenVT.inc"69#undef GET_VT_ATTR70 .Case("INVALID_SIMPLE_VALUE_TYPE", llvm::MVT::INVALID_SIMPLE_VALUE_TYPE);71}72 73/// Return the indices of the definitions in a list of definitions that74/// represent overloadable types75static IndicesTy getOverloadableTypeIdxs(const Record &record,76 const char *listName) {77 auto results = record.getValueAsListOfDefs(listName);78 IndicesTy overloadedOps(results.size());79 for (const auto &r : llvm::enumerate(results)) {80 llvm::MVT::SimpleValueType vt = getValueType(r.value());81 switch (vt) {82 case llvm::MVT::iAny:83 case llvm::MVT::fAny:84 case llvm::MVT::Any:85 case llvm::MVT::pAny:86 case llvm::MVT::vAny:87 overloadedOps.set(r.index());88 break;89 default:90 continue;91 }92 }93 return overloadedOps;94}95 96namespace {97/// A wrapper for LLVM's Tablegen class `Intrinsic` that provides accessors to98/// the fields of the record.99class LLVMIntrinsic {100public:101 LLVMIntrinsic(const Record &record) : record(record) {}102 103 /// Get the name of the operation to be used in MLIR. Uses the appropriate104 /// field if not empty, constructs a name by replacing underscores with dots105 /// in the record name otherwise.106 std::string getOperationName() const {107 StringRef name = record.getValueAsString(fieldName);108 if (!name.empty())109 return name.str();110 111 name = record.getName();112 assert(name.starts_with("int_") &&113 "LLVM intrinsic names are expected to start with 'int_'");114 name = name.drop_front(4);115 SmallVector<StringRef, 8> chunks;116 StringRef targetPrefix = record.getValueAsString("TargetPrefix");117 name.split(chunks, '_');118 auto *chunksBegin = chunks.begin();119 // Remove the target prefix from target specific intrinsics.120 if (!targetPrefix.empty()) {121 assert(targetPrefix == *chunksBegin &&122 "Intrinsic has TargetPrefix, but "123 "record name doesn't begin with it");124 assert(chunks.size() >= 2 &&125 "Intrinsic has TargetPrefix, but "126 "chunks has only one element meaning the intrinsic name is empty");127 ++chunksBegin;128 }129 return llvm::join(chunksBegin, chunks.end(), ".");130 }131 132 /// Get the name of the record without the "intrinsic" prefix.133 StringRef getProperRecordName() const {134 StringRef name = record.getName();135 assert(name.starts_with("int_") &&136 "LLVM intrinsic names are expected to start with 'int_'");137 return name.drop_front(4);138 }139 140 /// Get the number of operands.141 unsigned getNumOperands() const {142 auto operands = record.getValueAsListOfDefs(fieldOperands);143 assert(llvm::all_of(144 operands,145 [](const Record *r) { return r->isSubClassOf("LLVMType"); }) &&146 "expected operands to be of LLVM type");147 return operands.size();148 }149 150 /// Get the number of results. Note that LLVM does not support multi-value151 /// operations so, in fact, multiple results will be returned as a value of152 /// structure type.153 unsigned getNumResults() const {154 auto results = record.getValueAsListOfDefs(fieldResults);155 for (const Record *r : results) {156 (void)r;157 assert(r->isSubClassOf("LLVMType") &&158 "expected operands to be of LLVM type");159 }160 return results.size();161 }162 163 /// Return true if the intrinsic may have side effects, i.e. does not have the164 /// `IntrNoMem` property.165 bool hasSideEffects() const {166 return llvm::none_of(167 record.getValueAsListOfDefs(fieldTraits),168 [](const Record *r) { return r->getName() == "IntrNoMem"; });169 }170 171 /// Return true if the intrinsic is commutative, i.e. has the respective172 /// property.173 bool isCommutative() const {174 return llvm::any_of(175 record.getValueAsListOfDefs(fieldTraits),176 [](const Record *r) { return r->getName() == "Commutative"; });177 }178 179 IndicesTy getOverloadableOperandsIdxs() const {180 return getOverloadableTypeIdxs(record, fieldOperands);181 }182 183 IndicesTy getOverloadableResultsIdxs() const {184 return getOverloadableTypeIdxs(record, fieldResults);185 }186 187private:188 /// Names of the fields in the Intrinsic LLVM Tablegen class.189 const char *fieldName = "LLVMName";190 const char *fieldOperands = "ParamTypes";191 const char *fieldResults = "RetTypes";192 const char *fieldTraits = "IntrProperties";193 194 const Record &record;195};196} // namespace197 198/// Prints the elements in "range" separated by commas and surrounded by "[]".199template <typename Range>200void printBracketedRange(const Range &range, llvm::raw_ostream &os) {201 os << '[';202 llvm::interleaveComma(range, os);203 os << ']';204}205 206/// Emits ODS (TableGen-based) code for `record` representing an LLVM intrinsic.207/// Returns true on error, false on success.208static bool emitIntrinsic(const Record &record, llvm::raw_ostream &os) {209 LLVMIntrinsic intr(record);210 211 Regex accessGroupMatcher(accessGroupRegexp);212 bool requiresAccessGroup =213 !accessGroupRegexp.empty() && accessGroupMatcher.match(record.getName());214 215 Regex aliasAnalysisMatcher(aliasAnalysisRegexp);216 bool requiresAliasAnalysis = !aliasAnalysisRegexp.empty() &&217 aliasAnalysisMatcher.match(record.getName());218 219 // Prepare strings for traits, if any.220 SmallVector<StringRef, 2> traits;221 if (intr.isCommutative())222 traits.push_back("Commutative");223 if (!intr.hasSideEffects())224 traits.push_back("NoMemoryEffect");225 226 // Prepare strings for operands.227 SmallVector<StringRef, 8> operands(intr.getNumOperands(), "LLVM_Type");228 if (requiresAccessGroup)229 operands.push_back(230 "OptionalAttr<LLVM_AccessGroupArrayAttr>:$access_groups");231 if (requiresAliasAnalysis) {232 operands.push_back("OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes");233 operands.push_back(234 "OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes");235 operands.push_back("OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa");236 }237 238 // Emit the definition.239 os << "def LLVM_" << intr.getProperRecordName() << " : " << opBaseClass240 << "<\"" << intr.getOperationName() << "\", ";241 printBracketedRange(intr.getOverloadableResultsIdxs().set_bits(), os);242 os << ", ";243 printBracketedRange(intr.getOverloadableOperandsIdxs().set_bits(), os);244 os << ", ";245 printBracketedRange(traits, os);246 os << ", " << intr.getNumResults() << ", "247 << (requiresAccessGroup ? "1" : "0") << ", "248 << (requiresAliasAnalysis ? "1" : "0") << ">, Arguments<(ins"249 << (operands.empty() ? "" : " ");250 llvm::interleaveComma(operands, os);251 os << ")>;\n\n";252 253 return false;254}255 256/// Traverses the list of TableGen definitions derived from the "Intrinsic"257/// class and generates MLIR ODS definitions for those intrinsics that have258/// the name matching the filter.259static bool emitIntrinsics(const RecordKeeper &records, llvm::raw_ostream &os) {260 llvm::emitSourceFileHeader("Operations for LLVM intrinsics", os, records);261 os << "include \"mlir/Dialect/LLVMIR/LLVMOpBase.td\"\n";262 os << "include \"mlir/Interfaces/SideEffectInterfaces.td\"\n\n";263 264 auto defs = records.getAllDerivedDefinitions("Intrinsic");265 for (const Record *r : defs) {266 if (!nameFilter.empty() && !r->getName().contains(nameFilter))267 continue;268 if (emitIntrinsic(*r, os))269 return true;270 }271 272 return false;273}274 275static mlir::GenRegistration genLLVMIRIntrinsics("gen-llvmir-intrinsics",276 "Generate LLVM IR intrinsics",277 emitIntrinsics);278