MLIR 24.0.0git
MLGOAddReflectionMap.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
12#include "mlir/IR/Attributes.h"
13#include "mlir/IR/Builders.h"
17#include "llvm/ADT/StringRef.h"
18#include "llvm/Support/Casting.h"
19#include "llvm/Support/FormatVariadic.h"
20
21using namespace mlir;
22using namespace emitc;
23
24namespace mlir {
25namespace emitc {
26#define GEN_PASS_DEF_MLGOADDREFLECTIONMAPPASS
27#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
28
29namespace {
30constexpr const char *mapLibraryHeader = "map";
31constexpr const char *stringLibraryHeader = "string";
32
33struct PatternMatchListener : public RewriterBase::Listener {
34 bool patternApplied = false;
35
36 void notifyOperationInserted(Operation *op,
37 OpBuilder::InsertPoint previous) override {
38 patternApplied = true;
39 }
40};
41
42IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
43 StringAttr includeAttr = builder.getStringAttr(headerName);
44 return IncludeOp::create(builder, module.getLoc(), includeAttr,
45 /*is_standard_include=*/builder.getUnitAttr());
46}
47
48class MLGOAddReflectionMapPass
49 : public impl::MLGOAddReflectionMapPassBase<MLGOAddReflectionMapPass> {
50 using MLGOAddReflectionMapPassBase::MLGOAddReflectionMapPassBase;
51 void runOnOperation() override {
52 mlir::ModuleOp moduleOp = getOperation();
53
54 RewritePatternSet patterns(&getContext());
55 populateMLGOAddReflectionMapPatterns(patterns, includedFieldAttrs);
56
57 PatternMatchListener listener;
58 walkAndApplyPatterns(moduleOp, std::move(patterns), &listener);
59
60 // If nothing was matched, no reflection maps were added, removing the need
61 // to add include headers for map and string
62 if (!listener.patternApplied)
63 return;
64
65 // Check if the map and/or string headers are already present
66 bool hasMapHdr = false;
67 bool hasStringHdr = false;
68 for (auto &op : *moduleOp.getBody()) {
69 IncludeOp includeOp = llvm::dyn_cast<IncludeOp>(op);
70 if (!includeOp)
71 continue;
72
73 if (includeOp.getIsStandardInclude()) {
74 auto include = includeOp.getInclude();
75
76 hasMapHdr |= include == mapLibraryHeader;
77 hasStringHdr |= include == stringLibraryHeader;
78 }
79
80 if (hasMapHdr && hasStringHdr)
81 return;
82 }
83
84 mlir::OpBuilder builder(moduleOp.getBody(), moduleOp.getBody()->begin());
85 if (!hasMapHdr)
86 addHeader(builder, moduleOp, mapLibraryHeader);
87
88 if (!hasStringHdr)
89 addHeader(builder, moduleOp, stringLibraryHeader);
90 }
91};
92
93} // namespace
94} // namespace emitc
95} // namespace mlir
96
97/// Rewrites a `emitc::ClassOp` to generate a reflection map of member fields
98/// and a lookup method.
99///
100/// Fields to be mapped are identified via `includedFieldAttrs` attributes
101/// (e.g., `emitc.field_ref`). Fields that do not have a matching attribute
102/// are omitted from the reflection map.
103///
104/// Before:
105/// ```mlir
106/// emitc.class @foo {
107/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref =
108/// ["another_feature"]}
109/// emitc.func @bar() { return }
110/// }
111/// ```
112///
113/// After:
114/// ```mlir
115/// emitc.class @foo {
116/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref =
117/// ["another_feature"]} emitc.field @reflectionMap : !emitc.opaque<"const
118/// std::map<std::string, char*>"> =
119/// #emitc.opaque<"{ { \"another_feature\",
120/// reinterpret_cast<char*>(&fieldName0) } }">
121/// emitc.func @getBufferForName(%name: !emitc.opaque<"std::string">) ->
122/// !emitc.ptr<!emitc.opaque<"char">> {
123/// %map = get_field @reflectionMap : !emitc.opaque<"const
124/// std::map<std::string, char*>"> %ptr = member_call_opaque %map
125/// "at"(%name) : ... return %ptr : !emitc.ptr<!emitc.opaque<"char">>
126/// }
127/// emitc.func @bar() { return }
128/// }
129/// ```
131public:
133 llvm::ArrayRef<std::string> includedFieldAttrs)
134 : OpRewritePattern<ClassOp>(context),
135 includedFieldAttrs(includedFieldAttrs) {}
136
137 LogicalResult matchAndRewrite(ClassOp classOp,
138 PatternRewriter &rewriter) const override {
139 MLIRContext *context = rewriter.getContext();
140
141 emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
142 context, "const std::map<std::string, char*>");
143
144 // Collect the names of all FieldOps that have one of the attributes in
145 // includedFieldAttrs to use the first element of the array attribute
146 // as the reflection map key
147 std::vector<std::pair<StringRef, StringRef>> fieldNames;
148 classOp.walk([&](FieldOp fieldOp) {
149 for (const auto &attr : includedFieldAttrs) {
150 auto arrayAttr =
151 dyn_cast_if_present<ArrayAttr>(fieldOp->getDiscardableAttr(attr));
152
153 if (!arrayAttr)
154 continue;
155
156 if (!arrayAttr.empty() && isa<StringAttr>(arrayAttr[0])) {
157 fieldNames.emplace_back(cast<StringAttr>(arrayAttr[0]).getValue(),
158 fieldOp.getName());
159 return;
160 }
161 }
162 });
163
164 if (fieldNames.empty())
165 return failure();
166
167 // Create reflection map contents
168 std::string reflectionMapContents;
169 reflectionMapContents += "{ ";
170 bool first = true;
171 for (const auto &[name, value] : fieldNames) {
172 if (!first)
173 reflectionMapContents += ", ";
174
175 first = false;
176 reflectionMapContents += llvm::formatv(
177 "{ \"{0}\", reinterpret_cast<char*>(&{1}) }", name, value);
178 }
179 reflectionMapContents += " }";
180
181 // Set insertion point before the first function or after all fields
182 // if there are no functions within the class
183 auto funcs = classOp.getBlock().getOps<FuncOp>();
184 auto it = funcs.begin();
185 if (it != funcs.end())
186 rewriter.setInsertionPoint(*it);
187 else
188 rewriter.setInsertionPointToEnd(&classOp.getBlock());
189
190 // To generate the following C++ code
191 // const std::map<std::string, char*> reflectionMap = {
192 // { "another_feature", reinterpret_cast<char*>(&fieldName0) },
193 // { "some_feature", reinterpret_cast<char*>(&fieldName1) },
194 // ...
195 // };
196 // This can be used to retrieve a pointer to the field's contents given the
197 // attribute string identifying the field
198 FieldOp reflectionMapField = FieldOp::create(
199 rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
200 /*sym_visibility=*/nullptr, TypeAttr::get(mapType),
201 emitc::OpaqueAttr::get(context, reflectionMapContents));
202
203 // Create getBufferForName method
204 emitc::OpaqueType nameType =
205 emitc::OpaqueType::get(rewriter.getContext(), "std::string");
206 emitc::OpaqueType charType =
207 emitc::OpaqueType::get(rewriter.getContext(), "char");
208 emitc::PointerType valType =
209 emitc::PointerType::get(rewriter.getContext(), charType);
210 FuncOp getBufferForNameFunc = FuncOp::create(
211 rewriter, reflectionMapField->getLoc(), "getBufferForName",
212 FunctionType::get(rewriter.getContext(), {nameType}, {valType}));
213
214 Block *body =
215 rewriter.createBlock(&getBufferForNameFunc.getBody(), {}, {nameType},
216 {reflectionMapField->getLoc()});
217 rewriter.setInsertionPointToStart(body);
218 GetFieldOp mapField = GetFieldOp::create(
219 rewriter, reflectionMapField->getLoc(), mapType, "reflectionMap");
220 Value nameArg = body->getArgument(0);
221 MemberCallOpaqueOp lookupCall = MemberCallOpaqueOp::create(
222 rewriter, reflectionMapField->getLoc(), valType, mapField.getResult(),
223 "at", ArrayAttr{}, ArrayAttr{}, ValueRange{nameArg});
224 ReturnOp::create(rewriter, reflectionMapField->getLoc(),
225 lookupCall.getResult(0));
226
227 return success();
228 }
229
230private:
231 /// The names of the attributes on FieldOps that contain the field name
232 /// metadata for the reflection map. The pass matches the first attribute
233 /// present in the order they are specified in this list.
234 llvm::SmallVector<std::string> includedFieldAttrs;
235};
236
238 RewritePatternSet &patterns,
239 llvm::ArrayRef<std::string> includedFieldAttrs) {
240 patterns.add<MLGOAddReflectionMapClass>(patterns.getContext(),
241 includedFieldAttrs);
242}
return success()
b getContext())
Rewrites a emitc::ClassOp to generate a reflection map of member fields and a lookup method.
LogicalResult matchAndRewrite(ClassOp classOp, PatternRewriter &rewriter) const override
MLGOAddReflectionMapClass(MLIRContext *context, llvm::ArrayRef< std::string > includedFieldAttrs)
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:153
UnitAttr getUnitAttr()
Definition Builders.cpp:106
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
MLIRContext * getContext() const
Definition Builders.h:56
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class represents a saved insertion point.
Definition Builders.h:330
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
void populateMLGOAddReflectionMapPatterns(RewritePatternSet &patterns, ArrayRef< std::string > includedFieldAttrs)
Include the generated interface declarations.
void walkAndApplyPatterns(Operation *op, const FrozenRewritePatternSet &patterns, RewriterBase::Listener *listener=nullptr)
A fast walk-based pattern rewrite driver.
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})