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