xref: /llvm-project/mlir/include/mlir/Target/LLVMIR/LLVMImportInterface.h (revision 6d2bbba187cd8fdc3e6e46cb753d4d9c6c276103)
1 //===- LLVMImportInterface.h - Import from LLVM interface -------*- C++ -*-===//
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 header file defines dialect interfaces for the LLVM IR import.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_TARGET_LLVMIR_LLVMIMPORTINTERFACE_H
14 #define MLIR_TARGET_LLVMIR_LLVMIMPORTINTERFACE_H
15 
16 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
17 #include "mlir/IR/Builders.h"
18 #include "mlir/IR/BuiltinAttributes.h"
19 #include "mlir/IR/Diagnostics.h"
20 #include "mlir/IR/DialectInterface.h"
21 #include "mlir/IR/Location.h"
22 #include "llvm/IR/Instruction.h"
23 #include "llvm/IR/Instructions.h"
24 #include "llvm/Support/FormatVariadic.h"
25 
26 namespace llvm {
27 class IRBuilderBase;
28 } // namespace llvm
29 
30 namespace mlir {
31 namespace LLVM {
32 class ModuleImport;
33 } // namespace LLVM
34 
35 /// Base class for dialect interfaces used to import LLVM IR. Dialects that can
36 /// be imported should provide an implementation of this interface for the
37 /// supported intrinsics. The interface may be implemented in a separate library
38 /// to avoid the "main" dialect library depending on LLVM IR. The interface can
39 /// be attached using the delayed registration mechanism available in
40 /// DialectRegistry.
41 class LLVMImportDialectInterface
42     : public DialectInterface::Base<LLVMImportDialectInterface> {
43 public:
44   LLVMImportDialectInterface(Dialect *dialect) : Base(dialect) {}
45 
46   /// Hook for derived dialect interfaces to implement the import of
47   /// intrinsics into MLIR.
48   virtual LogicalResult
49   convertIntrinsic(OpBuilder &builder, llvm::CallInst *inst,
50                    LLVM::ModuleImport &moduleImport) const {
51     return failure();
52   }
53 
54   /// Hook for derived dialect interfaces to implement the import of
55   /// instructions into MLIR.
56   virtual LogicalResult
57   convertInstruction(OpBuilder &builder, llvm::Instruction *inst,
58                      ArrayRef<llvm::Value *> llvmOperands,
59                      LLVM::ModuleImport &moduleImport) const {
60     return failure();
61   }
62 
63   /// Hook for derived dialect interfaces to implement the import of metadata
64   /// into MLIR. Attaches the converted metadata kind and node to the provided
65   /// operation.
66   virtual LogicalResult
67   setMetadataAttrs(OpBuilder &builder, unsigned kind, llvm::MDNode *node,
68                    Operation *op, LLVM::ModuleImport &moduleImport) const {
69     return failure();
70   }
71 
72   /// Hook for derived dialect interfaces to publish the supported intrinsics.
73   /// As every LLVM IR intrinsic has a unique integer identifier, the function
74   /// returns the list of supported intrinsic identifiers.
75   virtual ArrayRef<unsigned> getSupportedIntrinsics() const { return {}; }
76 
77   /// Hook for derived dialect interfaces to publish the supported instructions.
78   /// As every LLVM IR instruction has a unique integer identifier, the function
79   /// returns the list of supported instruction identifiers. These identifiers
80   /// will then be used to match LLVM instructions to the appropriate import
81   /// interface and `convertInstruction` method. It is an error to have multiple
82   /// interfaces overriding the same instruction.
83   virtual ArrayRef<unsigned> getSupportedInstructions() const { return {}; }
84 
85   /// Hook for derived dialect interfaces to publish the supported metadata
86   /// kinds. As every metadata kind has a unique integer identifier, the
87   /// function returns the list of supported metadata identifiers. `ctx` can be
88   /// used to obtain IDs of metadata kinds that do not have a fixed static one.
89   virtual ArrayRef<unsigned>
90   getSupportedMetadata(llvm::LLVMContext &ctx) const {
91     return {};
92   }
93 };
94 
95 /// Interface collection for the import of LLVM IR that dispatches to a concrete
96 /// dialect interface implementation. Queries the dialect interfaces to obtain a
97 /// list of the supported LLVM IR constructs and then builds a mapping for the
98 /// efficient dispatch.
99 class LLVMImportInterface
100     : public DialectInterfaceCollection<LLVMImportDialectInterface> {
101 public:
102   using Base::Base;
103 
104   /// Queries all registered dialect interfaces for the supported LLVM IR
105   /// intrinsic and metadata kinds and builds the dispatch tables for the
106   /// conversion. Returns failure if multiple dialect interfaces translate the
107   /// same LLVM IR intrinsic.
108   LogicalResult initializeImport(llvm::LLVMContext &llvmContext) {
109     for (const LLVMImportDialectInterface &iface : *this) {
110       // Verify the supported intrinsics have not been mapped before.
111       const auto *intrinsicIt =
112           llvm::find_if(iface.getSupportedIntrinsics(), [&](unsigned id) {
113             return intrinsicToDialect.count(id);
114           });
115       if (intrinsicIt != iface.getSupportedIntrinsics().end()) {
116         return emitError(
117             UnknownLoc::get(iface.getContext()),
118             llvm::formatv(
119                 "expected unique conversion for intrinsic ({0}), but "
120                 "got conflicting {1} and {2} conversions",
121                 *intrinsicIt, iface.getDialect()->getNamespace(),
122                 intrinsicToDialect.lookup(*intrinsicIt)->getNamespace()));
123       }
124       const auto *instructionIt =
125           llvm::find_if(iface.getSupportedInstructions(), [&](unsigned id) {
126             return instructionToDialect.count(id);
127           });
128       if (instructionIt != iface.getSupportedInstructions().end()) {
129         return emitError(
130             UnknownLoc::get(iface.getContext()),
131             llvm::formatv(
132                 "expected unique conversion for instruction ({0}), but "
133                 "got conflicting {1} and {2} conversions",
134                 *intrinsicIt, iface.getDialect()->getNamespace(),
135                 instructionToDialect.lookup(*intrinsicIt)
136                     ->getDialect()
137                     ->getNamespace()));
138       }
139       // Add a mapping for all supported intrinsic identifiers.
140       for (unsigned id : iface.getSupportedIntrinsics())
141         intrinsicToDialect[id] = iface.getDialect();
142       // Add a mapping for all supported instruction identifiers.
143       for (unsigned id : iface.getSupportedInstructions())
144         instructionToDialect[id] = &iface;
145       // Add a mapping for all supported metadata kinds.
146       for (unsigned kind : iface.getSupportedMetadata(llvmContext))
147         metadataToDialect[kind].push_back(iface.getDialect());
148     }
149 
150     return success();
151   }
152 
153   /// Converts the LLVM intrinsic to an MLIR operation if a conversion exists.
154   /// Returns failure otherwise.
155   LogicalResult convertIntrinsic(OpBuilder &builder, llvm::CallInst *inst,
156                                  LLVM::ModuleImport &moduleImport) const {
157     // Lookup the dialect interface for the given intrinsic.
158     Dialect *dialect = intrinsicToDialect.lookup(inst->getIntrinsicID());
159     if (!dialect)
160       return failure();
161 
162     // Dispatch the conversion to the dialect interface.
163     const LLVMImportDialectInterface *iface = getInterfaceFor(dialect);
164     assert(iface && "expected to find a dialect interface");
165     return iface->convertIntrinsic(builder, inst, moduleImport);
166   }
167 
168   /// Returns true if the given LLVM IR intrinsic is convertible to an MLIR
169   /// operation.
170   bool isConvertibleIntrinsic(llvm::Intrinsic::ID id) {
171     return intrinsicToDialect.count(id);
172   }
173 
174   /// Converts the LLVM instruction to an MLIR operation if a conversion exists.
175   /// Returns failure otherwise.
176   LogicalResult convertInstruction(OpBuilder &builder, llvm::Instruction *inst,
177                                    ArrayRef<llvm::Value *> llvmOperands,
178                                    LLVM::ModuleImport &moduleImport) const {
179     // Lookup the dialect interface for the given instruction.
180     const LLVMImportDialectInterface *iface =
181         instructionToDialect.lookup(inst->getOpcode());
182     if (!iface)
183       return failure();
184 
185     return iface->convertInstruction(builder, inst, llvmOperands, moduleImport);
186   }
187 
188   /// Returns true if the given LLVM IR instruction is convertible to an MLIR
189   /// operation.
190   bool isConvertibleInstruction(unsigned id) {
191     return instructionToDialect.count(id);
192   }
193 
194   /// Attaches the given LLVM metadata to the imported operation if a conversion
195   /// to one or more MLIR dialect attributes exists and succeeds. Returns
196   /// success if at least one of the conversions is successful and failure if
197   /// all of them fail.
198   LogicalResult setMetadataAttrs(OpBuilder &builder, unsigned kind,
199                                  llvm::MDNode *node, Operation *op,
200                                  LLVM::ModuleImport &moduleImport) const {
201     // Lookup the dialect interfaces for the given metadata.
202     auto it = metadataToDialect.find(kind);
203     if (it == metadataToDialect.end())
204       return failure();
205 
206     // Dispatch the conversion to the dialect interfaces.
207     bool isSuccess = false;
208     for (Dialect *dialect : it->getSecond()) {
209       const LLVMImportDialectInterface *iface = getInterfaceFor(dialect);
210       assert(iface && "expected to find a dialect interface");
211       if (succeeded(
212               iface->setMetadataAttrs(builder, kind, node, op, moduleImport)))
213         isSuccess = true;
214     }
215 
216     // Returns failure if all conversions fail.
217     return success(isSuccess);
218   }
219 
220   /// Returns true if the given LLVM IR metadata is convertible to an MLIR
221   /// attribute.
222   bool isConvertibleMetadata(unsigned kind) {
223     return metadataToDialect.count(kind);
224   }
225 
226 private:
227   DenseMap<unsigned, Dialect *> intrinsicToDialect;
228   DenseMap<unsigned, const LLVMImportDialectInterface *> instructionToDialect;
229   DenseMap<unsigned, SmallVector<Dialect *, 1>> metadataToDialect;
230 };
231 
232 } // namespace mlir
233 
234 #endif // MLIR_TARGET_LLVMIR_LLVMIMPORTINTERFACE_H
235