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 = dyn_cast_if_present<ArrayAttr>(fieldOp->getAttr(attr));
151
152 if (!arrayAttr)
153 continue;
154
155 if (!arrayAttr.empty() && isa<StringAttr>(arrayAttr[0])) {
156 fieldNames.emplace_back(cast<StringAttr>(arrayAttr[0]).getValue(),
157 fieldOp.getName());
158 return;
159 }
160 }
161 });
162
163 if (fieldNames.empty())
164 return failure();
165
166 // Create reflection map contents
167 std::string reflectionMapContents;
168 reflectionMapContents += "{ ";
169 bool first = true;
170 for (const auto &[name, value] : fieldNames) {
171 if (!first)
172 reflectionMapContents += ", ";
173
174 first = false;
175 reflectionMapContents += llvm::formatv(
176 "{ \"{0}\", reinterpret_cast<char*>(&{1}) }", name, value);
177 }
178 reflectionMapContents += " }";
179
180 // Set insertion point before the first function or after all fields
181 // if there are no functions within the class
182 auto funcs = classOp.getBlock().getOps<FuncOp>();
183 auto it = funcs.begin();
184 if (it != funcs.end())
185 rewriter.setInsertionPoint(*it);
186 else
187 rewriter.setInsertionPointToEnd(&classOp.getBlock());
188
189 // To generate the following C++ code
190 // const std::map<std::string, char*> reflectionMap = {
191 // { "another_feature", reinterpret_cast<char*>(&fieldName0) },
192 // { "some_feature", reinterpret_cast<char*>(&fieldName1) },
193 // ...
194 // };
195 // This can be used to retrieve a pointer to the field's contents given the
196 // attribute string identifying the field
197 FieldOp reflectionMapField = FieldOp::create(
198 rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
199 TypeAttr::get(mapType),
200 emitc::OpaqueAttr::get(context, reflectionMapContents));
201
202 // Create getBufferForName method
203 emitc::OpaqueType nameType =
204 emitc::OpaqueType::get(rewriter.getContext(), "std::string");
205 emitc::OpaqueType charType =
206 emitc::OpaqueType::get(rewriter.getContext(), "char");
207 emitc::PointerType valType =
208 emitc::PointerType::get(rewriter.getContext(), charType);
209 FuncOp getBufferForNameFunc = FuncOp::create(
210 rewriter, reflectionMapField->getLoc(), "getBufferForName",
211 FunctionType::get(rewriter.getContext(), {nameType}, {valType}));
212
213 Block *body =
214 rewriter.createBlock(&getBufferForNameFunc.getBody(), {}, {nameType},
215 {reflectionMapField->getLoc()});
216 rewriter.setInsertionPointToStart(body);
217 GetFieldOp mapField = GetFieldOp::create(
218 rewriter, reflectionMapField->getLoc(), mapType, "reflectionMap");
219 Value nameArg = body->getArgument(0);
220 MemberCallOpaqueOp lookupCall = MemberCallOpaqueOp::create(
221 rewriter, reflectionMapField->getLoc(), valType, mapField.getResult(),
222 "at", ArrayAttr{}, ArrayAttr{}, ValueRange{nameArg});
223 ReturnOp::create(rewriter, reflectionMapField->getLoc(),
224 lookupCall.getResult(0));
225
226 return success();
227 }
228
229private:
230 /// The names of the attributes on FieldOps that contain the field name
231 /// metadata for the reflection map. The pass matches the first attribute
232 /// present in the order they are specified in this list.
233 llvm::SmallVector<std::string> includedFieldAttrs;
234};
235
237 RewritePatternSet &patterns,
238 llvm::ArrayRef<std::string> includedFieldAttrs) {
239 patterns.add<MLGOAddReflectionMapClass>(patterns.getContext(),
240 includedFieldAttrs);
241}
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:102
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:267
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:329
This class helps build Operations.
Definition Builders.h:209
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:435
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:433
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:400
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:438
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={})