MLIR 22.0.0git
AsmParserState.cpp
Go to the documentation of this file.
1//===- AsmParserState.cpp -------------------------------------------------===//
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 "mlir/IR/Attributes.h"
11#include "mlir/IR/Operation.h"
12#include "mlir/IR/SymbolTable.h"
13#include "mlir/IR/Types.h"
14#include "mlir/IR/Value.h"
15#include "mlir/Support/LLVM.h"
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringMap.h"
20#include "llvm/ADT/iterator.h"
21#include "llvm/Support/ErrorHandling.h"
22#include <cassert>
23#include <cctype>
24#include <memory>
25#include <utility>
26
27using namespace mlir;
28
29//===----------------------------------------------------------------------===//
30// AsmParserState::Impl
31//===----------------------------------------------------------------------===//
32
34 /// A map from a SymbolRefAttr to a range of uses.
37
38 struct PartialOpDef {
39 explicit PartialOpDef(const OperationName &opName) {
40 if (opName.hasTrait<OpTrait::SymbolTable>())
41 symbolTable = std::make_unique<SymbolUseMap>();
42 }
43
44 /// Return if this operation is a symbol table.
45 bool isSymbolTable() const { return symbolTable.get(); }
46
47 /// If this operation is a symbol table, the following contains symbol uses
48 /// within this operation.
49 std::unique_ptr<SymbolUseMap> symbolTable;
50 };
51
52 /// Resolve any symbol table uses in the IR.
53 void resolveSymbolUses();
54
55 /// A mapping from operations in the input source file to their parser state.
58
59 /// A mapping from blocks in the input source file to their parser state.
62
63 /// A mapping from aliases in the input source file to their parser state.
66 llvm::StringMap<unsigned> attrAliasToIdx;
67 llvm::StringMap<unsigned> typeAliasToIdx;
68
69 /// A set of value definitions that are placeholders for forward references.
70 /// This map should be empty if the parser finishes successfully.
72
73 /// The symbol table operations within the IR.
76
77 /// A stack of partial operation definitions that have been started but not
78 /// yet finalized.
80
81 /// A stack of symbol use scopes. This is used when collecting symbol table
82 /// uses during parsing.
84
85 /// A symbol table containing all of the symbol table operations in the IR.
87};
88
91 for (auto &opAndUseMapIt : symbolTableOperations) {
92 for (auto &it : *opAndUseMapIt.second) {
93 symbolOps.clear();
94 if (failed(symbolTable.lookupSymbolIn(
95 opAndUseMapIt.first, cast<SymbolRefAttr>(it.first), symbolOps)))
96 continue;
97
98 for (ArrayRef<SMRange> useRange : it.second) {
99 for (const auto &symIt : llvm::zip(symbolOps, useRange)) {
100 auto opIt = operationToIdx.find(std::get<0>(symIt));
101 if (opIt != operationToIdx.end())
102 operations[opIt->second]->symbolUses.push_back(std::get<1>(symIt));
103 }
104 }
105 }
106 }
107}
108
109//===----------------------------------------------------------------------===//
110// AsmParserState
111//===----------------------------------------------------------------------===//
112
113AsmParserState::AsmParserState() : impl(std::make_unique<Impl>()) {}
116 impl = std::move(other.impl);
117 return *this;
118}
119
120//===----------------------------------------------------------------------===//
121// Access State
122//===----------------------------------------------------------------------===//
123
125 return llvm::make_pointee_range(llvm::ArrayRef(impl->blocks));
126}
127
129 -> const BlockDefinition * {
130 auto it = impl->blocksToIdx.find(block);
131 return it == impl->blocksToIdx.end() ? nullptr : &*impl->blocks[it->second];
132}
133
135 return llvm::make_pointee_range(llvm::ArrayRef(impl->operations));
136}
137
139 -> const OperationDefinition * {
140 auto it = impl->operationToIdx.find(op);
141 return it == impl->operationToIdx.end() ? nullptr
142 : &*impl->operations[it->second];
143}
144
147 return llvm::make_pointee_range(ArrayRef(impl->attrAliases));
148}
149
150auto AsmParserState::getAttributeAliasDef(StringRef name) const
151 -> const AttributeAliasDefinition * {
152 auto it = impl->attrAliasToIdx.find(name);
153 return it == impl->attrAliasToIdx.end() ? nullptr
154 : &*impl->attrAliases[it->second];
155}
156
159 return llvm::make_pointee_range(ArrayRef(impl->typeAliases));
160}
161
162auto AsmParserState::getTypeAliasDef(StringRef name) const
163 -> const TypeAliasDefinition * {
164 auto it = impl->typeAliasToIdx.find(name);
165 return it == impl->typeAliasToIdx.end() ? nullptr
166 : &*impl->typeAliases[it->second];
167}
168
169/// Lex a string token whose contents start at the given `curPtr`. Returns the
170/// position at the end of the string, after a terminal or invalid character
171/// (e.g. `"` or `\0`).
172static const char *lexLocStringTok(const char *curPtr) {
173 while (char c = *curPtr++) {
174 // Check for various terminal characters.
175 if (StringRef("\"\n\v\f").contains(c))
176 return curPtr;
177
178 // Check for escape sequences.
179 if (c == '\\') {
180 // Check a few known escapes and \xx hex digits.
181 if (*curPtr == '"' || *curPtr == '\\' || *curPtr == 'n' || *curPtr == 't')
182 ++curPtr;
183 else if (llvm::isHexDigit(*curPtr) && llvm::isHexDigit(curPtr[1]))
184 curPtr += 2;
185 else
186 return curPtr;
187 }
188 }
189
190 // If we hit this point, we've reached the end of the buffer. Update the end
191 // pointer to not point past the buffer.
192 return curPtr - 1;
193}
194
196 if (!loc.isValid())
197 return SMRange();
198 const char *curPtr = loc.getPointer();
199
200 // Check if this is a string token.
201 if (*curPtr == '"') {
202 curPtr = lexLocStringTok(curPtr + 1);
203
204 // Otherwise, default to handling an identifier.
205 } else {
206 // Return if the given character is a valid identifier character.
207 auto isIdentifierChar = [](char c) {
208 return isalnum(c) || c == '$' || c == '.' || c == '_' || c == '-';
209 };
210
211 while (*curPtr && isIdentifierChar(*(++curPtr)))
212 continue;
213 }
214
215 return SMRange(loc, SMLoc::getFromPointer(curPtr));
216}
217
218//===----------------------------------------------------------------------===//
219// Populate State
220//===----------------------------------------------------------------------===//
221
223 startOperationDefinition(topLevelOp->getName());
224
225 // If the top-level operation is a symbol table, push a new symbol scope.
226 Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
227 if (partialOpDef.isSymbolTable())
228 impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
229}
230
232 assert(!impl->partialOperations.empty() &&
233 "expected valid partial operation definition");
234 Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
235
236 // If this operation is a symbol table, resolve any symbol uses.
237 if (partialOpDef.isSymbolTable()) {
238 impl->symbolTableOperations.emplace_back(
239 topLevelOp, std::move(partialOpDef.symbolTable));
240 }
241 impl->resolveSymbolUses();
242}
243
245 impl->partialOperations.emplace_back(opName);
246}
247
249 Operation *op, SMRange nameLoc, SMLoc endLoc,
250 ArrayRef<std::pair<unsigned, SMLoc>> resultGroups) {
251 assert(!impl->partialOperations.empty() &&
252 "expected valid partial operation definition");
253 Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
254
255 // Build the full operation definition.
256 std::unique_ptr<OperationDefinition> def =
257 std::make_unique<OperationDefinition>(op, nameLoc, endLoc);
258 for (auto &resultGroup : resultGroups)
259 def->resultGroups.emplace_back(resultGroup.first,
260 convertIdLocToRange(resultGroup.second));
261 impl->operationToIdx.try_emplace(op, impl->operations.size());
262 impl->operations.emplace_back(std::move(def));
263
264 // If this operation is a symbol table, resolve any symbol uses.
265 if (partialOpDef.isSymbolTable()) {
266 impl->symbolTableOperations.emplace_back(
267 op, std::move(partialOpDef.symbolTable));
268 }
269}
270
272 assert(!impl->partialOperations.empty() &&
273 "expected valid partial operation definition");
274
275 // If the parent operation of this region is a symbol table, we also push a
276 // new symbol scope.
277 Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
278 if (partialOpDef.isSymbolTable())
279 impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
280}
281
283 assert(!impl->partialOperations.empty() &&
284 "expected valid partial operation definition");
285
286 // If the parent operation of this region is a symbol table, pop the symbol
287 // scope for this region.
288 Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
289 if (partialOpDef.isSymbolTable())
290 impl->symbolUseScopes.pop_back();
291}
292
293void AsmParserState::addDefinition(Block *block, SMLoc location) {
294 auto [it, inserted] =
295 impl->blocksToIdx.try_emplace(block, impl->blocks.size());
296 if (inserted) {
297 impl->blocks.emplace_back(std::make_unique<BlockDefinition>(
298 block, convertIdLocToRange(location)));
299 return;
300 }
301
302 // If an entry already exists, this was a forward declaration that now has a
303 // proper definition.
304 impl->blocks[it->second]->definition.loc = convertIdLocToRange(location);
305}
306
307void AsmParserState::addDefinition(BlockArgument blockArg, SMLoc location) {
308 auto it = impl->blocksToIdx.find(blockArg.getOwner());
309 assert(it != impl->blocksToIdx.end() &&
310 "expected owner block to have an entry");
311 BlockDefinition &def = *impl->blocks[it->second];
312 unsigned argIdx = blockArg.getArgNumber();
313
314 if (def.arguments.size() <= argIdx)
315 def.arguments.resize(argIdx + 1);
316 def.arguments[argIdx] = SMDefinition(convertIdLocToRange(location));
317}
318
319void AsmParserState::addAttrAliasDefinition(StringRef name, SMRange location,
320 Attribute value) {
321 auto [it, inserted] =
322 impl->attrAliasToIdx.try_emplace(name, impl->attrAliases.size());
323 // Location aliases may be referenced before they are defined.
324 if (inserted) {
325 impl->attrAliases.push_back(
326 std::make_unique<AttributeAliasDefinition>(name, location, value));
327 } else {
328 AttributeAliasDefinition &attr = *impl->attrAliases[it->second];
329 attr.definition.loc = location;
330 attr.value = value;
331 }
332}
333
334void AsmParserState::addTypeAliasDefinition(StringRef name, SMRange location,
335 Type value) {
336 [[maybe_unused]] auto [it, inserted] =
337 impl->typeAliasToIdx.try_emplace(name, impl->typeAliases.size());
338 assert(inserted && "unexpected attribute alias redefinition");
339 impl->typeAliases.push_back(
340 std::make_unique<TypeAliasDefinition>(name, location, value));
341}
342
344 // Handle the case where the value is an operation result.
345 if (OpResult result = dyn_cast<OpResult>(value)) {
346 // Check to see if a definition for the parent operation has been recorded.
347 // If one hasn't, we treat the provided value as a placeholder value that
348 // will be refined further later.
349 Operation *parentOp = result.getOwner();
350 auto existingIt = impl->operationToIdx.find(parentOp);
351 if (existingIt == impl->operationToIdx.end()) {
352 impl->placeholderValueUses[value].append(locations.begin(),
353 locations.end());
354 return;
355 }
356
357 // If a definition does exist, locate the value's result group and add the
358 // use. The result groups are ordered by increasing start index, so we just
359 // need to find the last group that has a smaller/equal start index.
360 unsigned resultNo = result.getResultNumber();
361 OperationDefinition &def = *impl->operations[existingIt->second];
362 for (auto &resultGroup : llvm::reverse(def.resultGroups)) {
363 if (resultNo >= resultGroup.startIndex) {
364 for (SMLoc loc : locations)
365 resultGroup.definition.uses.push_back(convertIdLocToRange(loc));
366 return;
367 }
368 }
369 llvm_unreachable("expected valid result group for value use");
370 }
371
372 // Otherwise, this is a block argument.
373 BlockArgument arg = cast<BlockArgument>(value);
374 auto existingIt = impl->blocksToIdx.find(arg.getOwner());
375 assert(existingIt != impl->blocksToIdx.end() &&
376 "expected valid block definition for block argument");
377 BlockDefinition &blockDef = *impl->blocks[existingIt->second];
378 SMDefinition &argDef = blockDef.arguments[arg.getArgNumber()];
379 for (SMLoc loc : locations)
380 argDef.uses.emplace_back(convertIdLocToRange(loc));
381}
382
384 auto [it, inserted] =
385 impl->blocksToIdx.try_emplace(block, impl->blocks.size());
386 if (inserted)
387 impl->blocks.emplace_back(std::make_unique<BlockDefinition>(block));
388
389 BlockDefinition &def = *impl->blocks[it->second];
390 for (SMLoc loc : locations)
391 def.definition.uses.push_back(convertIdLocToRange(loc));
392}
393
394void AsmParserState::addUses(SymbolRefAttr refAttr,
395 ArrayRef<SMRange> locations) {
396 // Ignore this symbol if no scopes are active.
397 if (impl->symbolUseScopes.empty())
398 return;
399
400 assert((refAttr.getNestedReferences().size() + 1) == locations.size() &&
401 "expected the same number of references as provided locations");
402 (*impl->symbolUseScopes.back())[refAttr].emplace_back(locations.begin(),
403 locations.end());
404}
405
406void AsmParserState::addAttrAliasUses(StringRef name, SMRange location) {
407 auto it = impl->attrAliasToIdx.find(name);
408 // Location aliases may be referenced before they are defined.
409 if (it == impl->attrAliasToIdx.end()) {
410 it = impl->attrAliasToIdx.try_emplace(name, impl->attrAliases.size()).first;
411 impl->attrAliases.push_back(
412 std::make_unique<AttributeAliasDefinition>(name));
413 }
414 AttributeAliasDefinition &def = *impl->attrAliases[it->second];
415 def.definition.uses.push_back(location);
416}
417
418void AsmParserState::addTypeAliasUses(StringRef name, SMRange location) {
419 auto it = impl->typeAliasToIdx.find(name);
420 // Location aliases may be referenced before they are defined.
421 assert(it != impl->typeAliasToIdx.end() &&
422 "expected valid type alias definition");
423 TypeAliasDefinition &def = *impl->typeAliases[it->second];
424 def.definition.uses.push_back(location);
425}
426
428 auto it = impl->placeholderValueUses.find(oldValue);
429 assert(it != impl->placeholderValueUses.end() &&
430 "expected `oldValue` to be a placeholder");
431 addUses(newValue, it->second);
432 impl->placeholderValueUses.erase(oldValue);
433}
static const char * lexLocStringTok(const char *curPtr)
Lex a string token whose contents start at the given curPtr.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
llvm::pointee_iterator< ArrayRef< std::unique_ptr< AttributeAliasDefinition > >::iterator > AttributeDefIterator
void startRegionDefinition()
Start a definition for a region nested under the current operation.
iterator_range< AttributeDefIterator > getAttributeAliasDefs() const
Return a range of the AttributeAliasDefinitions held by the current parser state.
void startOperationDefinition(const OperationName &opName)
Start a definition for an operation with the given name.
void addTypeAliasUses(StringRef name, SMRange locations)
iterator_range< BlockDefIterator > getBlockDefs() const
Return a range of the BlockDefinitions held by the current parser state.
llvm::pointee_iterator< ArrayRef< std::unique_ptr< TypeAliasDefinition > >::iterator > TypeDefIterator
const OperationDefinition * getOpDef(Operation *op) const
Return the definition for the given operation, or nullptr if the given operation does not have a defi...
llvm::pointee_iterator< ArrayRef< std::unique_ptr< OperationDefinition > >::iterator > OperationDefIterator
void initialize(Operation *topLevelOp)
Initialize the state in preparation for populating more parser state under the given top-level operat...
void finalizeOperationDefinition(Operation *op, SMRange nameLoc, SMLoc endLoc, ArrayRef< std::pair< unsigned, SMLoc > > resultGroups={})
Finalize the most recently started operation definition.
void addAttrAliasUses(StringRef name, SMRange locations)
const AttributeAliasDefinition * getAttributeAliasDef(StringRef name) const
Return the definition for the given attribute alias, or nullptr if the given alias does not have a de...
llvm::pointee_iterator< ArrayRef< std::unique_ptr< BlockDefinition > >::iterator > BlockDefIterator
const BlockDefinition * getBlockDef(Block *block) const
Return the definition for the given block, or nullptr if the given block does not have a definition.
const TypeAliasDefinition * getTypeAliasDef(StringRef name) const
Return the definition for the given type alias, or nullptr if the given alias does not have a definit...
void addAttrAliasDefinition(StringRef name, SMRange location, Attribute value)
void finalize(Operation *topLevelOp)
Finalize any in-progress parser state under the given top-level operation.
static SMRange convertIdLocToRange(SMLoc loc)
Returns (heuristically) the range of an identifier given a SMLoc corresponding to the start of an ide...
void addUses(Value value, ArrayRef< SMLoc > locations)
Add a source uses of the given value.
void refineDefinition(Value oldValue, Value newValue)
Refine the oldValue to the newValue.
iterator_range< OperationDefIterator > getOpDefs() const
Return a range of the OperationDefinitions held by the current parser state.
void finalizeRegionDefinition()
Finalize the most recently started region definition.
iterator_range< TypeDefIterator > getTypeAliasDefs() const
Return a range of the TypeAliasDefinitions held by the current parser state.
void addTypeAliasDefinition(StringRef name, SMRange location, Type value)
AsmParserState & operator=(AsmParserState &&other)
void addDefinition(Block *block, SMLoc location)
Add a definition of the given entity.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:309
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:321
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:318
Block represents an ordered list of Operations.
Definition Block.h:33
This is a value defined by a result of an operation.
Definition Value.h:457
A trait used to provide symbol table functionalities to a region operation.
bool hasTrait() const
Returns true if the operation was registered with a particular trait, e.g.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:119
This class represents a collection of SymbolTables.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
bool isSymbolTable() const
Return if this operation is a symbol table.
std::unique_ptr< SymbolUseMap > symbolTable
If this operation is a symbol table, the following contains symbol uses within this operation.
PartialOpDef(const OperationName &opName)
SymbolTableCollection symbolTable
A symbol table containing all of the symbol table operations in the IR.
DenseMap< Value, SmallVector< SMLoc > > placeholderValueUses
A set of value definitions that are placeholders for forward references.
DenseMap< Attribute, SmallVector< SmallVector< SMRange >, 0 > > SymbolUseMap
A map from a SymbolRefAttr to a range of uses.
void resolveSymbolUses()
Resolve any symbol table uses in the IR.
llvm::StringMap< unsigned > typeAliasToIdx
SmallVector< std::unique_ptr< OperationDefinition > > operations
A mapping from operations in the input source file to their parser state.
SmallVector< std::unique_ptr< AttributeAliasDefinition > > attrAliases
A mapping from aliases in the input source file to their parser state.
DenseMap< Operation *, unsigned > operationToIdx
SmallVector< std::unique_ptr< TypeAliasDefinition > > typeAliases
SmallVector< PartialOpDef > partialOperations
A stack of partial operation definitions that have been started but not yet finalized.
SmallVector< std::unique_ptr< BlockDefinition > > blocks
A mapping from blocks in the input source file to their parser state.
llvm::StringMap< unsigned > attrAliasToIdx
DenseMap< Block *, unsigned > blocksToIdx
SmallVector< std::pair< Operation *, std::unique_ptr< SymbolUseMap > > > symbolTableOperations
The symbol table operations within the IR.
SmallVector< SymbolUseMap * > symbolUseScopes
A stack of symbol use scopes.
This class represents the information for an attribute alias definition within the input file.
SMDefinition definition
The source location for the alias.
Attribute value
The value of the alias.
This class represents the information for a block definition within the input file.
SmallVector< SMDefinition > arguments
Source definitions for any arguments of this block.
SMDefinition definition
The source location for the block, i.e.
This class represents the information for an operation definition within an input file.
SmallVector< ResultGroupDefinition > resultGroups
Source definitions for any result groups of this operation.
This class represents a definition within the source manager, containing it's defining location and l...
SmallVector< SMRange > uses
The source location of all uses of the definition.
SMRange loc
The source location of the definition.
This class represents the information for type definition within the input file.
SMDefinition definition
The source location for the alias.