MLIR  16.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/Operation.h"
11 #include "mlir/IR/SymbolTable.h"
12 #include "llvm/ADT/StringExtras.h"
13 
14 using namespace mlir;
15 
16 //===----------------------------------------------------------------------===//
17 // AsmParserState::Impl
18 //===----------------------------------------------------------------------===//
19 
21  /// A map from a SymbolRefAttr to a range of uses.
22  using SymbolUseMap =
24 
25  struct PartialOpDef {
26  explicit PartialOpDef(const OperationName &opName) {
27  if (opName.hasTrait<OpTrait::SymbolTable>())
28  symbolTable = std::make_unique<SymbolUseMap>();
29  }
30 
31  /// Return if this operation is a symbol table.
32  bool isSymbolTable() const { return symbolTable.get(); }
33 
34  /// If this operation is a symbol table, the following contains symbol uses
35  /// within this operation.
36  std::unique_ptr<SymbolUseMap> symbolTable;
37  };
38 
39  /// Resolve any symbol table uses in the IR.
40  void resolveSymbolUses();
41 
42  /// A mapping from operations in the input source file to their parser state.
45 
46  /// A mapping from blocks in the input source file to their parser state.
49 
50  /// A set of value definitions that are placeholders for forward references.
51  /// This map should be empty if the parser finishes successfully.
53 
54  /// The symbol table operations within the IR.
57 
58  /// A stack of partial operation definitions that have been started but not
59  /// yet finalized.
61 
62  /// A stack of symbol use scopes. This is used when collecting symbol table
63  /// uses during parsing.
65 
66  /// A symbol table containing all of the symbol table operations in the IR.
68 };
69 
71  SmallVector<Operation *> symbolOps;
72  for (auto &opAndUseMapIt : symbolTableOperations) {
73  for (auto &it : *opAndUseMapIt.second) {
74  symbolOps.clear();
75  if (failed(symbolTable.lookupSymbolIn(
76  opAndUseMapIt.first, it.first.cast<SymbolRefAttr>(), symbolOps)))
77  continue;
78 
79  for (ArrayRef<SMRange> useRange : it.second) {
80  for (const auto &symIt : llvm::zip(symbolOps, useRange)) {
81  auto opIt = operationToIdx.find(std::get<0>(symIt));
82  if (opIt != operationToIdx.end())
83  operations[opIt->second]->symbolUses.push_back(std::get<1>(symIt));
84  }
85  }
86  }
87  }
88 }
89 
90 //===----------------------------------------------------------------------===//
91 // AsmParserState
92 //===----------------------------------------------------------------------===//
93 
94 AsmParserState::AsmParserState() : impl(std::make_unique<Impl>()) {}
97  impl = std::move(other.impl);
98  return *this;
99 }
100 
101 //===----------------------------------------------------------------------===//
102 // Access State
103 
105  return llvm::make_pointee_range(llvm::makeArrayRef(impl->blocks));
106 }
107 
109  -> const BlockDefinition * {
110  auto it = impl->blocksToIdx.find(block);
111  return it == impl->blocksToIdx.end() ? nullptr : &*impl->blocks[it->second];
112 }
113 
115  return llvm::make_pointee_range(llvm::makeArrayRef(impl->operations));
116 }
117 
119  -> const OperationDefinition * {
120  auto it = impl->operationToIdx.find(op);
121  return it == impl->operationToIdx.end() ? nullptr
122  : &*impl->operations[it->second];
123 }
124 
125 /// Lex a string token whose contents start at the given `curPtr`. Returns the
126 /// position at the end of the string, after a terminal or invalid character
127 /// (e.g. `"` or `\0`).
128 static const char *lexLocStringTok(const char *curPtr) {
129  while (char c = *curPtr++) {
130  // Check for various terminal characters.
131  if (StringRef("\"\n\v\f").contains(c))
132  return curPtr;
133 
134  // Check for escape sequences.
135  if (c == '\\') {
136  // Check a few known escapes and \xx hex digits.
137  if (*curPtr == '"' || *curPtr == '\\' || *curPtr == 'n' || *curPtr == 't')
138  ++curPtr;
139  else if (llvm::isHexDigit(*curPtr) && llvm::isHexDigit(curPtr[1]))
140  curPtr += 2;
141  else
142  return curPtr;
143  }
144  }
145 
146  // If we hit this point, we've reached the end of the buffer. Update the end
147  // pointer to not point past the buffer.
148  return curPtr - 1;
149 }
150 
152  if (!loc.isValid())
153  return SMRange();
154  const char *curPtr = loc.getPointer();
155 
156  // Check if this is a string token.
157  if (*curPtr == '"') {
158  curPtr = lexLocStringTok(curPtr + 1);
159 
160  // Otherwise, default to handling an identifier.
161  } else {
162  // Return if the given character is a valid identifier character.
163  auto isIdentifierChar = [](char c) {
164  return isalnum(c) || c == '$' || c == '.' || c == '_' || c == '-';
165  };
166 
167  while (*curPtr && isIdentifierChar(*(++curPtr)))
168  continue;
169  }
170 
171  return SMRange(loc, SMLoc::getFromPointer(curPtr));
172 }
173 
174 //===----------------------------------------------------------------------===//
175 // Populate State
176 
178  startOperationDefinition(topLevelOp->getName());
179 
180  // If the top-level operation is a symbol table, push a new symbol scope.
181  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
182  if (partialOpDef.isSymbolTable())
183  impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
184 }
185 
187  assert(!impl->partialOperations.empty() &&
188  "expected valid partial operation definition");
189  Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
190 
191  // If this operation is a symbol table, resolve any symbol uses.
192  if (partialOpDef.isSymbolTable()) {
193  impl->symbolTableOperations.emplace_back(
194  topLevelOp, std::move(partialOpDef.symbolTable));
195  }
196  impl->resolveSymbolUses();
197 }
198 
200  impl->partialOperations.emplace_back(opName);
201 }
202 
204  Operation *op, SMRange nameLoc, SMLoc endLoc,
205  ArrayRef<std::pair<unsigned, SMLoc>> resultGroups) {
206  assert(!impl->partialOperations.empty() &&
207  "expected valid partial operation definition");
208  Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
209 
210  // Build the full operation definition.
211  std::unique_ptr<OperationDefinition> def =
212  std::make_unique<OperationDefinition>(op, nameLoc, endLoc);
213  for (auto &resultGroup : resultGroups)
214  def->resultGroups.emplace_back(resultGroup.first,
215  convertIdLocToRange(resultGroup.second));
216  impl->operationToIdx.try_emplace(op, impl->operations.size());
217  impl->operations.emplace_back(std::move(def));
218 
219  // If this operation is a symbol table, resolve any symbol uses.
220  if (partialOpDef.isSymbolTable()) {
221  impl->symbolTableOperations.emplace_back(
222  op, std::move(partialOpDef.symbolTable));
223  }
224 }
225 
227  assert(!impl->partialOperations.empty() &&
228  "expected valid partial operation definition");
229 
230  // If the parent operation of this region is a symbol table, we also push a
231  // new symbol scope.
232  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
233  if (partialOpDef.isSymbolTable())
234  impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
235 }
236 
238  assert(!impl->partialOperations.empty() &&
239  "expected valid partial operation definition");
240 
241  // If the parent operation of this region is a symbol table, pop the symbol
242  // scope for this region.
243  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
244  if (partialOpDef.isSymbolTable())
245  impl->symbolUseScopes.pop_back();
246 }
247 
248 void AsmParserState::addDefinition(Block *block, SMLoc location) {
249  auto it = impl->blocksToIdx.find(block);
250  if (it == impl->blocksToIdx.end()) {
251  impl->blocksToIdx.try_emplace(block, impl->blocks.size());
252  impl->blocks.emplace_back(std::make_unique<BlockDefinition>(
253  block, convertIdLocToRange(location)));
254  return;
255  }
256 
257  // If an entry already exists, this was a forward declaration that now has a
258  // proper definition.
259  impl->blocks[it->second]->definition.loc = convertIdLocToRange(location);
260 }
261 
262 void AsmParserState::addDefinition(BlockArgument blockArg, SMLoc location) {
263  auto it = impl->blocksToIdx.find(blockArg.getOwner());
264  assert(it != impl->blocksToIdx.end() &&
265  "expected owner block to have an entry");
266  BlockDefinition &def = *impl->blocks[it->second];
267  unsigned argIdx = blockArg.getArgNumber();
268 
269  if (def.arguments.size() <= argIdx)
270  def.arguments.resize(argIdx + 1);
271  def.arguments[argIdx] = SMDefinition(convertIdLocToRange(location));
272 }
273 
275  // Handle the case where the value is an operation result.
276  if (OpResult result = value.dyn_cast<OpResult>()) {
277  // Check to see if a definition for the parent operation has been recorded.
278  // If one hasn't, we treat the provided value as a placeholder value that
279  // will be refined further later.
280  Operation *parentOp = result.getOwner();
281  auto existingIt = impl->operationToIdx.find(parentOp);
282  if (existingIt == impl->operationToIdx.end()) {
283  impl->placeholderValueUses[value].append(locations.begin(),
284  locations.end());
285  return;
286  }
287 
288  // If a definition does exist, locate the value's result group and add the
289  // use. The result groups are ordered by increasing start index, so we just
290  // need to find the last group that has a smaller/equal start index.
291  unsigned resultNo = result.getResultNumber();
292  OperationDefinition &def = *impl->operations[existingIt->second];
293  for (auto &resultGroup : llvm::reverse(def.resultGroups)) {
294  if (resultNo >= resultGroup.startIndex) {
295  for (SMLoc loc : locations)
296  resultGroup.definition.uses.push_back(convertIdLocToRange(loc));
297  return;
298  }
299  }
300  llvm_unreachable("expected valid result group for value use");
301  }
302 
303  // Otherwise, this is a block argument.
304  BlockArgument arg = value.cast<BlockArgument>();
305  auto existingIt = impl->blocksToIdx.find(arg.getOwner());
306  assert(existingIt != impl->blocksToIdx.end() &&
307  "expected valid block definition for block argument");
308  BlockDefinition &blockDef = *impl->blocks[existingIt->second];
309  SMDefinition &argDef = blockDef.arguments[arg.getArgNumber()];
310  for (SMLoc loc : locations)
311  argDef.uses.emplace_back(convertIdLocToRange(loc));
312 }
313 
315  auto it = impl->blocksToIdx.find(block);
316  if (it == impl->blocksToIdx.end()) {
317  it = impl->blocksToIdx.try_emplace(block, impl->blocks.size()).first;
318  impl->blocks.emplace_back(std::make_unique<BlockDefinition>(block));
319  }
320 
321  BlockDefinition &def = *impl->blocks[it->second];
322  for (SMLoc loc : locations)
323  def.definition.uses.push_back(convertIdLocToRange(loc));
324 }
325 
326 void AsmParserState::addUses(SymbolRefAttr refAttr,
327  ArrayRef<SMRange> locations) {
328  // Ignore this symbol if no scopes are active.
329  if (impl->symbolUseScopes.empty())
330  return;
331 
332  assert((refAttr.getNestedReferences().size() + 1) == locations.size() &&
333  "expected the same number of references as provided locations");
334  (*impl->symbolUseScopes.back())[refAttr].emplace_back(locations.begin(),
335  locations.end());
336 }
337 
338 void AsmParserState::refineDefinition(Value oldValue, Value newValue) {
339  auto it = impl->placeholderValueUses.find(oldValue);
340  assert(it != impl->placeholderValueUses.end() &&
341  "expected `oldValue` to be a placeholder");
342  addUses(newValue, it->second);
343  impl->placeholderValueUses.erase(oldValue);
344 }
iterator_range< BlockDefIterator > getBlockDefs() const
Return a range of the BlockDefinitions held by the current parser state.
Include the generated interface declarations.
DenseMap< Block *, unsigned > blocksToIdx
void finalizeOperationDefinition(Operation *op, SMRange nameLoc, SMLoc endLoc, ArrayRef< std::pair< unsigned, SMLoc >> resultGroups=llvm::None)
Finalize the most recently started operation definition.
SmallVector< ResultGroupDefinition > resultGroups
Source definitions for any result groups of this operation.
This class represents a definition within the source manager, containing it&#39;s defining location and l...
This class represents state from a parsed MLIR textual format string.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
static SMRange convertIdLocToRange(SMLoc loc)
Returns (heuristically) the range of an identifier given a SMLoc corresponding to the start of an ide...
std::unique_ptr< SymbolUseMap > symbolTable
If this operation is a symbol table, the following contains symbol uses within this operation...
This is a value defined by a result of an operation.
Definition: Value.h:425
Block represents an ordered list of Operations.
Definition: Block.h:29
const OperationDefinition * getOpDef(Operation *op) const
Return the definition for the given operation, or nullptr if the given operation does not have a defi...
void refineDefinition(Value oldValue, Value newValue)
Add source uses for all the references nested under refAttr.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
void finalize(Operation *topLevelOp)
Finalize any in-progress parser state under the given top-level operation.
void resolveSymbolUses()
Resolve any symbol table uses in the IR.
SmallVector< SMRange > uses
The source location of all uses of the definition.
SymbolTableCollection symbolTable
A symbol table containing all of the symbol table operations in the IR.
llvm::pointee_iterator< ArrayRef< std::unique_ptr< BlockDefinition > >::iterator > BlockDefIterator
void startOperationDefinition(const OperationName &opName)
Start a definition for an operation with the given name.
void addUses(Value value, ArrayRef< SMLoc > locations)
Add a source uses of the given value.
unsigned getArgNumber() const
Returns the number of this argument.
Definition: Value.h:312
static constexpr const bool value
void startRegionDefinition()
Start a definition for a region nested under the current operation.
Block * getOwner() const
Returns the block that owns this argument.
Definition: Value.h:309
bool hasTrait() const
Returns true if the operation was registered with a particular trait, e.g.
static const char * lexLocStringTok(const char *curPtr)
Lex a string token whose contents start at the given curPtr.
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:242
void addDefinition(Block *block, SMLoc location)
Add a definition of the given entity.
SmallVector< std::pair< Operation *, std::unique_ptr< SymbolUseMap > > > symbolTableOperations
The symbol table operations within the IR.
U dyn_cast() const
Definition: Value.h:100
llvm::pointee_iterator< ArrayRef< std::unique_ptr< OperationDefinition > >::iterator > OperationDefIterator
SmallVector< PartialOpDef > partialOperations
A stack of partial operation definitions that have been started but not yet finalized.
A trait used to provide symbol table functionalities to a region operation.
Definition: SymbolTable.h:338
SmallVector< SymbolUseMap * > symbolUseScopes
A stack of symbol use scopes.
PartialOpDef(const OperationName &opName)
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
Definition: MLIRServer.cpp:93
This class represents an argument of a Block.
Definition: Value.h:300
This class represents the information for an operation definition within an input file...
This class represents the information for a block definition within the input file.
AsmParserState & operator=(AsmParserState &&other)
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:85
SMDefinition definition
The source location for the block, i.e.
bool isSymbolTable() const
Return if this operation is a symbol table.
void finalizeRegionDefinition()
Finalize the most recently started region definition.
iterator_range< OperationDefIterator > getOpDefs() const
Return a range of the OperationDefinitions held by the current parser state.
U cast() const
Definition: Value.h:108
void initialize(Operation *topLevelOp)
Initialize the state in preparation for populating more parser state under the given top-level operat...
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:50
DenseMap< Operation *, unsigned > operationToIdx
const BlockDefinition * getBlockDef(Block *block) const
Return the definition for the given block, or nullptr if the given block does not have a definition...
SmallVector< std::unique_ptr< BlockDefinition > > blocks
A mapping from blocks in the input source file to their parser state.
DenseMap< Value, SmallVector< SMLoc > > placeholderValueUses
A set of value definitions that are placeholders for forward references.
SmallVector< std::unique_ptr< OperationDefinition > > operations
A mapping from operations in the input source file to their parser state.