1 //===- CodeGenHelpers.cpp - MLIR op definitions 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // OpDefinitionsGen uses the description of operations to generate C++ 10 // definitions for ops. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/TableGen/CodeGenHelpers.h" 15 #include "mlir/TableGen/Operator.h" 16 #include "llvm/ADT/SetVector.h" 17 #include "llvm/Support/FormatVariadic.h" 18 #include "llvm/Support/Path.h" 19 #include "llvm/TableGen/Record.h" 20 21 using namespace llvm; 22 using namespace mlir; 23 using namespace mlir::tblgen; 24 25 StaticVerifierFunctionEmitter::StaticVerifierFunctionEmitter( 26 const llvm::RecordKeeper &records) 27 : uniqueOutputLabel(getUniqueName(records)) {} 28 29 StaticVerifierFunctionEmitter & 30 StaticVerifierFunctionEmitter::setSelf(StringRef str) { 31 fctx.withSelf(str); 32 return *this; 33 } 34 35 StaticVerifierFunctionEmitter & 36 StaticVerifierFunctionEmitter::setBuilder(StringRef str) { 37 fctx.withBuilder(str); 38 return *this; 39 } 40 41 void StaticVerifierFunctionEmitter::emitConstraintMethodsInNamespace( 42 StringRef signatureFormat, StringRef errorHandlerFormat, 43 StringRef cppNamespace, ArrayRef<const void *> constraints, raw_ostream &os, 44 bool emitDecl) { 45 llvm::Optional<NamespaceEmitter> namespaceEmitter; 46 if (!emitDecl) 47 namespaceEmitter.emplace(os, cppNamespace); 48 49 emitConstraintMethods(signatureFormat, errorHandlerFormat, constraints, os, 50 emitDecl); 51 } 52 53 StringRef StaticVerifierFunctionEmitter::getConstraintFn( 54 const Constraint &constraint) const { 55 auto it = localTypeConstraints.find(constraint.getAsOpaquePointer()); 56 assert(it != localTypeConstraints.end() && "expected valid constraint fn"); 57 return it->second; 58 } 59 60 std::string StaticVerifierFunctionEmitter::getUniqueName( 61 const llvm::RecordKeeper &records) { 62 // Use the input file name when generating a unique name. 63 std::string inputFilename = records.getInputFilename(); 64 65 // Drop all but the base filename. 66 StringRef nameRef = llvm::sys::path::filename(inputFilename); 67 nameRef.consume_back(".td"); 68 69 // Sanitize any invalid characters. 70 std::string uniqueName; 71 for (char c : nameRef) { 72 if (llvm::isAlnum(c) || c == '_') 73 uniqueName.push_back(c); 74 else 75 uniqueName.append(llvm::utohexstr((unsigned char)c)); 76 } 77 return uniqueName; 78 } 79 80 void StaticVerifierFunctionEmitter::emitConstraintMethods( 81 StringRef signatureFormat, StringRef errorHandlerFormat, 82 ArrayRef<const void *> constraints, raw_ostream &rawOs, bool emitDecl) { 83 raw_indented_ostream os(rawOs); 84 85 // Record the mapping from predicate to constraint. If two constraints has the 86 // same predicate and constraint summary, they can share the same verification 87 // function. 88 llvm::DenseMap<Pred, const void *> predToConstraint; 89 for (auto it : llvm::enumerate(constraints)) { 90 std::string name; 91 Constraint constraint = Constraint::getFromOpaquePointer(it.value()); 92 Pred pred = constraint.getPredicate(); 93 auto iter = predToConstraint.find(pred); 94 if (iter != predToConstraint.end()) { 95 do { 96 Constraint built = Constraint::getFromOpaquePointer(iter->second); 97 // We may have the different constraints but have the same predicate, 98 // for example, ConstraintA and Variadic<ConstraintA>, note that 99 // Variadic<> doesn't introduce new predicate. In this case, we can 100 // share the same predicate function if they also have consistent 101 // summary, otherwise we may report the wrong message while verification 102 // fails. 103 if (constraint.getSummary() == built.getSummary()) { 104 name = getConstraintFn(built).str(); 105 break; 106 } 107 ++iter; 108 } while (iter != predToConstraint.end() && iter->first == pred); 109 } 110 111 if (!name.empty()) { 112 localTypeConstraints.try_emplace(it.value(), name); 113 continue; 114 } 115 116 // Generate an obscure and unique name for this type constraint. 117 name = (Twine("__mlir_ods_local_type_constraint_") + uniqueOutputLabel + 118 Twine(it.index())) 119 .str(); 120 predToConstraint.insert( 121 std::make_pair(constraint.getPredicate(), it.value())); 122 localTypeConstraints.try_emplace(it.value(), name); 123 124 // Only generate the methods if we are generating definitions. 125 if (emitDecl) 126 continue; 127 128 os << formatv(signatureFormat.data(), name) << " {\n"; 129 os.indent() << "if (!(" << tgfmt(constraint.getConditionTemplate(), &fctx) 130 << ")) {\n"; 131 os.indent() << "return " 132 << formatv(errorHandlerFormat.data(), 133 escapeString(constraint.getSummary())) 134 << ";\n"; 135 os.unindent() << "}\nreturn ::mlir::success();\n"; 136 os.unindent() << "}\n\n"; 137 } 138 } 139 140 std::string mlir::tblgen::escapeString(StringRef value) { 141 std::string ret; 142 llvm::raw_string_ostream os(ret); 143 os.write_escaped(value); 144 return os.str(); 145 } 146