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