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