MLIR 24.0.0git
LoopAnnotationImporter.cpp
Go to the documentation of this file.
1//===- LoopAnnotationImporter.cpp - Loop annotation import ----------------===//
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
10#include "llvm/IR/Constants.h"
11
12using namespace mlir;
13using namespace mlir::LLVM;
14using namespace mlir::LLVM::detail;
15
16namespace {
17/// Helper class that keeps the state of one metadata to attribute conversion.
18struct LoopMetadataConversion {
19 LoopMetadataConversion(const llvm::MDNode *node, Location loc,
20 LoopAnnotationImporter &loopAnnotationImporter)
21 : node(node), loc(loc), loopAnnotationImporter(loopAnnotationImporter),
22 ctx(loc->getContext()){};
23 /// Converts this structs loop metadata node into a LoopAnnotationAttr.
24 LoopAnnotationAttr convert();
25
26 /// Initializes the shared state for the conversion member functions.
27 LogicalResult initConversionState();
28
29 /// Helper function to get and erase a property.
30 const llvm::MDNode *lookupAndEraseProperty(StringRef name);
31
32 /// Helper functions to lookup and convert MDNodes into a specifc attribute
33 /// kind. These functions return null-attributes if there is no node with the
34 /// specified name, or failure, if the node is ill-formatted.
35 FailureOr<BoolAttr> lookupUnitNode(StringRef name);
36 FailureOr<BoolAttr> lookupBoolNode(StringRef name, bool negated = false);
37 FailureOr<BoolAttr> lookupIntNodeAsBoolAttr(StringRef name);
38 FailureOr<IntegerAttr> lookupIntNode(StringRef name);
39 FailureOr<llvm::MDNode *> lookupMDNode(StringRef name);
40 FailureOr<SmallVector<llvm::MDNode *>> lookupMDNodes(StringRef name);
41 FailureOr<LoopAnnotationAttr> lookupFollowupNode(StringRef name);
42 FailureOr<BoolAttr> lookupBooleanUnitNode(StringRef enableName,
43 StringRef disableName,
44 bool negated = false);
45
46 /// Conversion functions for sub-attributes.
47 FailureOr<LoopVectorizeAttr> convertVectorizeAttr();
48 FailureOr<LoopInterleaveAttr> convertInterleaveAttr();
49 FailureOr<LoopUnrollAttr> convertUnrollAttr();
50 FailureOr<LoopUnrollAndJamAttr> convertUnrollAndJamAttr();
51 FailureOr<LoopLICMAttr> convertLICMAttr();
52 FailureOr<LoopDistributeAttr> convertDistributeAttr();
53 FailureOr<LoopPipelineAttr> convertPipelineAttr();
54 FailureOr<LoopPeeledAttr> convertPeeledAttr();
55 FailureOr<LoopUnswitchAttr> convertUnswitchAttr();
56 FailureOr<SmallVector<AccessGroupAttr>> convertParallelAccesses();
57 FusedLoc convertStartLoc();
58 FailureOr<FusedLoc> convertEndLoc();
59
60 llvm::SmallVector<llvm::DILocation *, 2> locations;
61 llvm::StringMap<const llvm::MDNode *> propertyMap;
62 const llvm::MDNode *node;
63 Location loc;
64 LoopAnnotationImporter &loopAnnotationImporter;
65 MLIRContext *ctx;
66};
67} // namespace
68
69LogicalResult LoopMetadataConversion::initConversionState() {
70 // Check if it's a valid node.
71 if (node->getNumOperands() == 0 ||
72 dyn_cast<llvm::MDNode>(node->getOperand(0)) != node)
73 return emitWarning(loc) << "invalid loop node";
74
75 for (const llvm::MDOperand &operand : llvm::drop_begin(node->operands())) {
76 if (auto *diLoc = dyn_cast<llvm::DILocation>(operand)) {
77 locations.push_back(diLoc);
78 continue;
79 }
80
81 auto *property = dyn_cast<llvm::MDNode>(operand);
82 if (!property)
83 return emitWarning(loc) << "expected all loop properties to be either "
84 "debug locations or metadata nodes";
85
86 if (property->getNumOperands() == 0)
87 return emitWarning(loc) << "cannot import empty loop property";
88
89 auto *nameNode = dyn_cast<llvm::MDString>(property->getOperand(0));
90 if (!nameNode)
91 return emitWarning(loc) << "cannot import loop property without a name";
92 StringRef name = nameNode->getString();
93
94 bool succ = propertyMap.try_emplace(name, property).second;
95 if (!succ)
96 return emitWarning(loc)
97 << "cannot import loop properties with duplicated names " << name;
98 }
99
100 return success();
101}
102
103const llvm::MDNode *
104LoopMetadataConversion::lookupAndEraseProperty(StringRef name) {
105 auto it = propertyMap.find(name);
106 if (it == propertyMap.end())
107 return nullptr;
108 const llvm::MDNode *property = it->getValue();
109 propertyMap.erase(it);
110 return property;
111}
112
113FailureOr<BoolAttr> LoopMetadataConversion::lookupUnitNode(StringRef name) {
114 const llvm::MDNode *property = lookupAndEraseProperty(name);
115 if (!property)
116 return BoolAttr(nullptr);
117
118 if (property->getNumOperands() != 1)
119 return emitWarning(loc)
120 << "expected metadata node " << name << " to hold no value";
121
122 return BoolAttr::get(ctx, true);
123}
124
125FailureOr<BoolAttr> LoopMetadataConversion::lookupBooleanUnitNode(
126 StringRef enableName, StringRef disableName, bool negated) {
127 auto enable = lookupUnitNode(enableName);
128 auto disable = lookupUnitNode(disableName);
129 if (failed(enable) || failed(disable))
130 return failure();
131
132 if (*enable && *disable)
133 return emitWarning(loc)
134 << "expected metadata nodes " << enableName << " and " << disableName
135 << " to be mutually exclusive.";
136
137 if (*enable)
138 return BoolAttr::get(ctx, !negated);
139
140 if (*disable)
141 return BoolAttr::get(ctx, negated);
142 return BoolAttr(nullptr);
143}
144
145FailureOr<BoolAttr> LoopMetadataConversion::lookupBoolNode(StringRef name,
146 bool negated) {
147 const llvm::MDNode *property = lookupAndEraseProperty(name);
148 if (!property)
149 return BoolAttr(nullptr);
150
151 auto emitNodeWarning = [&]() {
152 return emitWarning(loc)
153 << "expected metadata node " << name << " to hold a boolean value";
154 };
155
156 if (property->getNumOperands() != 2)
157 return emitNodeWarning();
158 llvm::ConstantInt *val =
159 llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
160 if (!val || val->getBitWidth() != 1)
161 return emitNodeWarning();
162
163 return BoolAttr::get(ctx, val->getValue().getLimitedValue(1) ^ negated);
164}
165
166FailureOr<BoolAttr>
167LoopMetadataConversion::lookupIntNodeAsBoolAttr(StringRef name) {
168 const llvm::MDNode *property = lookupAndEraseProperty(name);
169 if (!property)
170 return BoolAttr(nullptr);
171
172 auto emitNodeWarning = [&]() {
173 return emitWarning(loc)
174 << "expected metadata node " << name << " to hold an integer value";
175 };
176
177 if (property->getNumOperands() != 2)
178 return emitNodeWarning();
179 llvm::ConstantInt *val =
180 llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
181 if (!val || val->getBitWidth() != 32)
182 return emitNodeWarning();
183
184 return BoolAttr::get(ctx, val->getValue().getLimitedValue(1));
185}
186
187FailureOr<IntegerAttr> LoopMetadataConversion::lookupIntNode(StringRef name) {
188 const llvm::MDNode *property = lookupAndEraseProperty(name);
189 if (!property)
190 return IntegerAttr(nullptr);
191
192 auto emitNodeWarning = [&]() {
193 return emitWarning(loc)
194 << "expected metadata node " << name << " to hold an i32 value";
195 };
196
197 if (property->getNumOperands() != 2)
198 return emitNodeWarning();
199
200 llvm::ConstantInt *val =
201 llvm::mdconst::dyn_extract<llvm::ConstantInt>(property->getOperand(1));
202 if (!val || val->getBitWidth() != 32)
203 return emitNodeWarning();
204
205 return IntegerAttr::get(IntegerType::get(ctx, 32),
206 val->getValue().getLimitedValue());
207}
208
209FailureOr<llvm::MDNode *> LoopMetadataConversion::lookupMDNode(StringRef name) {
210 const llvm::MDNode *property = lookupAndEraseProperty(name);
211 if (!property)
212 return nullptr;
213
214 auto emitNodeWarning = [&]() {
215 return emitWarning(loc)
216 << "expected metadata node " << name << " to hold an MDNode";
217 };
218
219 if (property->getNumOperands() != 2)
220 return emitNodeWarning();
221
222 auto *node = dyn_cast<llvm::MDNode>(property->getOperand(1));
223 if (!node)
224 return emitNodeWarning();
225
226 return node;
227}
228
229FailureOr<SmallVector<llvm::MDNode *>>
230LoopMetadataConversion::lookupMDNodes(StringRef name) {
231 const llvm::MDNode *property = lookupAndEraseProperty(name);
232 SmallVector<llvm::MDNode *> res;
233 if (!property)
234 return res;
235
236 auto emitNodeWarning = [&]() {
237 return emitWarning(loc) << "expected metadata node " << name
238 << " to hold one or multiple MDNodes";
239 };
240
241 if (property->getNumOperands() < 2)
242 return emitNodeWarning();
243
244 for (unsigned i = 1, e = property->getNumOperands(); i < e; ++i) {
245 auto *node = dyn_cast<llvm::MDNode>(property->getOperand(i));
246 if (!node)
247 return emitNodeWarning();
248 res.push_back(node);
249 }
250
251 return res;
252}
253
254FailureOr<LoopAnnotationAttr>
255LoopMetadataConversion::lookupFollowupNode(StringRef name) {
256 auto node = lookupMDNode(name);
257 if (failed(node))
258 return failure();
259 if (*node == nullptr)
260 return LoopAnnotationAttr(nullptr);
261
262 return loopAnnotationImporter.translateLoopAnnotation(*node, loc);
263}
264
265static bool isEmptyOrNull(const Attribute attr) { return !attr; }
266
267template <typename T>
268static bool isEmptyOrNull(const SmallVectorImpl<T> &vec) {
269 return vec.empty();
270}
271
272/// Helper function that only creates and attribute of type T if all argument
273/// conversion were successfull and at least one of them holds a non-null value.
274template <typename T, typename... P>
275static T createIfNonNull(MLIRContext *ctx, const P &...args) {
276 bool anyFailed = (failed(args) || ...);
277 if (anyFailed)
278 return {};
279
280 bool allEmpty = (isEmptyOrNull(*args) && ...);
281 if (allEmpty)
282 return {};
283
284 return T::get(ctx, *args...);
285}
286
287FailureOr<LoopVectorizeAttr> LoopMetadataConversion::convertVectorizeAttr() {
288 FailureOr<BoolAttr> enable = lookupBooleanUnitNode(
289 "llvm.loop.vectorize.enable", "llvm.loop.vectorize.disable",
290 /*negated=*/true);
291 FailureOr<BoolAttr> predicateEnable =
292 lookupBooleanUnitNode("llvm.loop.vectorize.predicate.enable",
293 "llvm.loop.vectorize.predicate.disable");
294 FailureOr<BoolAttr> scalableEnable =
295 lookupBooleanUnitNode("llvm.loop.vectorize.scalable.enable",
296 "llvm.loop.vectorize.scalable.disable");
297 FailureOr<IntegerAttr> width = lookupIntNode("llvm.loop.vectorize.width");
298 FailureOr<LoopAnnotationAttr> followupVec =
299 lookupFollowupNode("llvm.loop.vectorize.followup_vectorized");
300 FailureOr<LoopAnnotationAttr> followupEpi =
301 lookupFollowupNode("llvm.loop.vectorize.followup_epilogue");
302 FailureOr<LoopAnnotationAttr> followupAll =
303 lookupFollowupNode("llvm.loop.vectorize.followup_all");
304
305 return createIfNonNull<LoopVectorizeAttr>(ctx, enable, predicateEnable,
306 scalableEnable, width, followupVec,
307 followupEpi, followupAll);
308}
309
310FailureOr<LoopInterleaveAttr> LoopMetadataConversion::convertInterleaveAttr() {
311 FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.interleave.count");
312 return createIfNonNull<LoopInterleaveAttr>(ctx, count);
313}
314
315FailureOr<LoopUnrollAttr> LoopMetadataConversion::convertUnrollAttr() {
316 FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
317 "llvm.loop.unroll.enable", "llvm.loop.unroll.disable", /*negated=*/true);
318 FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.unroll.count");
319 FailureOr<BoolAttr> runtimeDisable =
320 lookupUnitNode("llvm.loop.unroll.runtime.disable");
321 FailureOr<BoolAttr> full = lookupUnitNode("llvm.loop.unroll.full");
322 FailureOr<LoopAnnotationAttr> followupUnrolled =
323 lookupFollowupNode("llvm.loop.unroll.followup_unrolled");
324 FailureOr<LoopAnnotationAttr> followupRemainder =
325 lookupFollowupNode("llvm.loop.unroll.followup_remainder");
326 FailureOr<LoopAnnotationAttr> followupAll =
327 lookupFollowupNode("llvm.loop.unroll.followup_all");
328
329 return createIfNonNull<LoopUnrollAttr>(ctx, disable, count, runtimeDisable,
330 full, followupUnrolled,
331 followupRemainder, followupAll);
332}
333
334FailureOr<LoopUnrollAndJamAttr>
335LoopMetadataConversion::convertUnrollAndJamAttr() {
336 FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
337 "llvm.loop.unroll_and_jam.enable", "llvm.loop.unroll_and_jam.disable",
338 /*negated=*/true);
339 FailureOr<IntegerAttr> count =
340 lookupIntNode("llvm.loop.unroll_and_jam.count");
341 FailureOr<LoopAnnotationAttr> followupOuter =
342 lookupFollowupNode("llvm.loop.unroll_and_jam.followup_outer");
343 FailureOr<LoopAnnotationAttr> followupInner =
344 lookupFollowupNode("llvm.loop.unroll_and_jam.followup_inner");
345 FailureOr<LoopAnnotationAttr> followupRemainderOuter =
346 lookupFollowupNode("llvm.loop.unroll_and_jam.followup_remainder_outer");
347 FailureOr<LoopAnnotationAttr> followupRemainderInner =
348 lookupFollowupNode("llvm.loop.unroll_and_jam.followup_remainder_inner");
349 FailureOr<LoopAnnotationAttr> followupAll =
350 lookupFollowupNode("llvm.loop.unroll_and_jam.followup_all");
352 ctx, disable, count, followupOuter, followupInner, followupRemainderOuter,
353 followupRemainderInner, followupAll);
354}
355
356FailureOr<LoopLICMAttr> LoopMetadataConversion::convertLICMAttr() {
357 FailureOr<BoolAttr> disable = lookupUnitNode("llvm.licm.disable");
358 FailureOr<BoolAttr> versioningDisable =
359 lookupUnitNode("llvm.loop.licm_versioning.disable");
360 return createIfNonNull<LoopLICMAttr>(ctx, disable, versioningDisable);
361}
362
363FailureOr<LoopDistributeAttr> LoopMetadataConversion::convertDistributeAttr() {
364 FailureOr<BoolAttr> disable = lookupBooleanUnitNode(
365 "llvm.loop.distribute.enable", "llvm.loop.distribute.disable",
366 /*negated=*/true);
367 FailureOr<LoopAnnotationAttr> followupCoincident =
368 lookupFollowupNode("llvm.loop.distribute.followup_coincident");
369 FailureOr<LoopAnnotationAttr> followupSequential =
370 lookupFollowupNode("llvm.loop.distribute.followup_sequential");
371 FailureOr<LoopAnnotationAttr> followupFallback =
372 lookupFollowupNode("llvm.loop.distribute.followup_fallback");
373 FailureOr<LoopAnnotationAttr> followupAll =
374 lookupFollowupNode("llvm.loop.distribute.followup_all");
375 return createIfNonNull<LoopDistributeAttr>(ctx, disable, followupCoincident,
376 followupSequential,
377 followupFallback, followupAll);
378}
379
380FailureOr<LoopPipelineAttr> LoopMetadataConversion::convertPipelineAttr() {
381 FailureOr<BoolAttr> disable = lookupBoolNode("llvm.loop.pipeline.disable");
382 FailureOr<IntegerAttr> initiationinterval =
383 lookupIntNode("llvm.loop.pipeline.initiationinterval");
384 return createIfNonNull<LoopPipelineAttr>(ctx, disable, initiationinterval);
385}
386
387FailureOr<LoopPeeledAttr> LoopMetadataConversion::convertPeeledAttr() {
388 FailureOr<IntegerAttr> count = lookupIntNode("llvm.loop.peeled.count");
389 return createIfNonNull<LoopPeeledAttr>(ctx, count);
390}
391
392FailureOr<LoopUnswitchAttr> LoopMetadataConversion::convertUnswitchAttr() {
393 FailureOr<BoolAttr> partialDisable =
394 lookupUnitNode("llvm.loop.unswitch.partial.disable");
395 return createIfNonNull<LoopUnswitchAttr>(ctx, partialDisable);
396}
397
398FailureOr<SmallVector<AccessGroupAttr>>
399LoopMetadataConversion::convertParallelAccesses() {
400 FailureOr<SmallVector<llvm::MDNode *>> nodes =
401 lookupMDNodes("llvm.loop.parallel_accesses");
402 if (failed(nodes))
403 return failure();
404 SmallVector<AccessGroupAttr> refs;
405 for (llvm::MDNode *node : *nodes) {
406 FailureOr<SmallVector<AccessGroupAttr>> accessGroups =
407 loopAnnotationImporter.lookupAccessGroupAttrs(node);
408 if (failed(accessGroups)) {
409 emitWarning(loc) << "could not lookup access group";
410 continue;
411 }
412 llvm::append_range(refs, *accessGroups);
413 }
414 return refs;
415}
416
417FusedLoc LoopMetadataConversion::convertStartLoc() {
418 if (locations.empty())
419 return {};
420 return dyn_cast<FusedLoc>(
421 loopAnnotationImporter.moduleImport.translateLoc(locations[0]));
422}
423
424FailureOr<FusedLoc> LoopMetadataConversion::convertEndLoc() {
425 if (locations.size() < 2)
426 return FusedLoc();
427 if (locations.size() > 2)
428 return emitError(loc)
429 << "expected loop metadata to have at most two DILocations";
430 return dyn_cast<FusedLoc>(
431 loopAnnotationImporter.moduleImport.translateLoc(locations[1]));
432}
433
434LoopAnnotationAttr LoopMetadataConversion::convert() {
435 if (failed(initConversionState()))
436 return {};
437
438 FailureOr<BoolAttr> disableNonForced =
439 lookupUnitNode("llvm.loop.disable_nonforced");
440 FailureOr<LoopVectorizeAttr> vecAttr = convertVectorizeAttr();
441 FailureOr<LoopInterleaveAttr> interleaveAttr = convertInterleaveAttr();
442 FailureOr<LoopUnrollAttr> unrollAttr = convertUnrollAttr();
443 FailureOr<LoopUnrollAndJamAttr> unrollAndJamAttr = convertUnrollAndJamAttr();
444 FailureOr<LoopLICMAttr> licmAttr = convertLICMAttr();
445 FailureOr<LoopDistributeAttr> distributeAttr = convertDistributeAttr();
446 FailureOr<LoopPipelineAttr> pipelineAttr = convertPipelineAttr();
447 FailureOr<LoopPeeledAttr> peeledAttr = convertPeeledAttr();
448 FailureOr<LoopUnswitchAttr> unswitchAttr = convertUnswitchAttr();
449 FailureOr<BoolAttr> mustProgress = lookupUnitNode("llvm.loop.mustprogress");
450 FailureOr<BoolAttr> isVectorized =
451 lookupIntNodeAsBoolAttr("llvm.loop.isvectorized");
452 FailureOr<SmallVector<AccessGroupAttr>> parallelAccesses =
453 convertParallelAccesses();
454
455 // Drop the metadata if there are parts that cannot be imported.
456 if (!propertyMap.empty()) {
457 for (auto name : propertyMap.keys())
458 emitWarning(loc) << "unknown loop annotation " << name;
459 return {};
460 }
461
462 FailureOr<FusedLoc> startLoc = convertStartLoc();
463 FailureOr<FusedLoc> endLoc = convertEndLoc();
464
466 ctx, disableNonForced, vecAttr, interleaveAttr, unrollAttr,
467 unrollAndJamAttr, licmAttr, distributeAttr, pipelineAttr, peeledAttr,
468 unswitchAttr, mustProgress, isVectorized, startLoc, endLoc,
469 parallelAccesses);
470}
471
472LoopAnnotationAttr
474 Location loc) {
475 if (!node)
476 return {};
477
478 // Note: This check is necessary to distinguish between failed translations
479 // and not yet attempted translations.
480 auto it = loopMetadataMapping.find(node);
481 if (it != loopMetadataMapping.end())
482 return it->getSecond();
483
484 LoopAnnotationAttr attr = LoopMetadataConversion(node, loc, *this).convert();
485
486 mapLoopMetadata(node, attr);
487 return attr;
488}
489
490LogicalResult
492 Location loc) {
494 if (!node->getNumOperands())
495 accessGroups.push_back(node);
496 for (const llvm::MDOperand &operand : node->operands()) {
497 auto *childNode = dyn_cast<llvm::MDNode>(operand);
498 if (!childNode)
499 return failure();
500 accessGroups.push_back(cast<llvm::MDNode>(operand.get()));
501 }
502
503 // Convert all entries of the access group list to access group operations.
504 for (const llvm::MDNode *accessGroup : accessGroups) {
505 if (accessGroupMapping.count(accessGroup))
506 continue;
507 // Verify the access group node is distinct and empty.
508 if (accessGroup->getNumOperands() != 0 || !accessGroup->isDistinct())
509 return emitWarning(loc)
510 << "expected an access group node to be empty and distinct";
511
512 // Add a mapping from the access group node to the newly created attribute.
513 accessGroupMapping[accessGroup] = builder.getAttr<AccessGroupAttr>();
514 }
515 return success();
516}
517
518FailureOr<SmallVector<AccessGroupAttr>>
519LoopAnnotationImporter::lookupAccessGroupAttrs(const llvm::MDNode *node) const {
520 // An access group node is either a single access group or an access group
521 // list.
522 SmallVector<AccessGroupAttr> accessGroups;
523 if (!node->getNumOperands())
524 accessGroups.push_back(accessGroupMapping.lookup(node));
525 for (const llvm::MDOperand &operand : node->operands()) {
526 auto *node = cast<llvm::MDNode>(operand.get());
527 accessGroups.push_back(accessGroupMapping.lookup(node));
528 }
529 // Exit if one of the access group node lookups failed.
530 if (llvm::is_contained(accessGroups, nullptr))
531 return failure();
532 return accessGroups;
533}
return success()
static T createIfNonNull(MLIRContext *ctx, const P &...args)
Helper function that only creates and attribute of type T if all argument conversion were successfull...
static bool isEmptyOrNull(const Attribute attr)
b getContext())
Attributes are known-constant values of operations.
Definition Attributes.h:25
static BoolAttr get(MLIRContext *context, bool value)
Location translateLoc(llvm::DILocation *loc)
Translates the debug location.
LoopAnnotationAttr translateLoopAnnotation(const llvm::MDNode *node, Location loc)
LogicalResult translateAccessGroup(const llvm::MDNode *node, Location loc)
Converts all LLVM access groups starting from node to MLIR access group attributes.
ModuleImport & moduleImport
The ModuleImport owning this instance.
FailureOr< SmallVector< AccessGroupAttr > > lookupAccessGroupAttrs(const llvm::MDNode *node) const
Returns the access group attribute that map to the access group nodes starting from the access group ...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.