MLIR  14.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 
13 using namespace mlir;
14 
15 //===----------------------------------------------------------------------===//
16 // AsmParserState::Impl
17 //===----------------------------------------------------------------------===//
18 
20  /// A map from a SymbolRefAttr to a range of uses.
21  using SymbolUseMap =
23 
24  struct PartialOpDef {
25  explicit PartialOpDef(const OperationName &opName) {
26  if (opName.hasTrait<OpTrait::SymbolTable>())
27  symbolTable = std::make_unique<SymbolUseMap>();
28  }
29 
30  /// Return if this operation is a symbol table.
31  bool isSymbolTable() const { return symbolTable.get(); }
32 
33  /// If this operation is a symbol table, the following contains symbol uses
34  /// within this operation.
35  std::unique_ptr<SymbolUseMap> symbolTable;
36  };
37 
38  /// Resolve any symbol table uses in the IR.
39  void resolveSymbolUses();
40 
41  /// A mapping from operations in the input source file to their parser state.
44 
45  /// A mapping from blocks in the input source file to their parser state.
48 
49  /// A set of value definitions that are placeholders for forward references.
50  /// This map should be empty if the parser finishes successfully.
52 
53  /// The symbol table operations within the IR.
56 
57  /// A stack of partial operation definitions that have been started but not
58  /// yet finalized.
60 
61  /// A stack of symbol use scopes. This is used when collecting symbol table
62  /// uses during parsing.
64 
65  /// A symbol table containing all of the symbol table operations in the IR.
67 };
68 
70  SmallVector<Operation *> symbolOps;
71  for (auto &opAndUseMapIt : symbolTableOperations) {
72  for (auto &it : *opAndUseMapIt.second) {
73  symbolOps.clear();
74  if (failed(symbolTable.lookupSymbolIn(
75  opAndUseMapIt.first, it.first.cast<SymbolRefAttr>(), symbolOps)))
76  continue;
77 
78  for (ArrayRef<llvm::SMRange> useRange : it.second) {
79  for (const auto &symIt : llvm::zip(symbolOps, useRange)) {
80  auto opIt = operationToIdx.find(std::get<0>(symIt));
81  if (opIt != operationToIdx.end())
82  operations[opIt->second]->symbolUses.push_back(std::get<1>(symIt));
83  }
84  }
85  }
86  }
87 }
88 
89 //===----------------------------------------------------------------------===//
90 // AsmParserState
91 //===----------------------------------------------------------------------===//
92 
93 AsmParserState::AsmParserState() : impl(std::make_unique<Impl>()) {}
96  impl = std::move(other.impl);
97  return *this;
98 }
99 
100 //===----------------------------------------------------------------------===//
101 // Access State
102 
104  return llvm::make_pointee_range(llvm::makeArrayRef(impl->blocks));
105 }
106 
108  -> const BlockDefinition * {
109  auto it = impl->blocksToIdx.find(block);
110  return it == impl->blocksToIdx.end() ? nullptr : &*impl->blocks[it->second];
111 }
112 
114  return llvm::make_pointee_range(llvm::makeArrayRef(impl->operations));
115 }
116 
118  -> const OperationDefinition * {
119  auto it = impl->operationToIdx.find(op);
120  return it == impl->operationToIdx.end() ? nullptr
121  : &*impl->operations[it->second];
122 }
123 
124 llvm::SMRange AsmParserState::convertIdLocToRange(llvm::SMLoc loc) {
125  if (!loc.isValid())
126  return llvm::SMRange();
127 
128  // Return if the given character is a valid identifier character.
129  auto isIdentifierChar = [](char c) {
130  return isalnum(c) || c == '$' || c == '.' || c == '_' || c == '-';
131  };
132 
133  const char *curPtr = loc.getPointer();
134  while (*curPtr && isIdentifierChar(*(++curPtr)))
135  continue;
136  return llvm::SMRange(loc, llvm::SMLoc::getFromPointer(curPtr));
137 }
138 
139 //===----------------------------------------------------------------------===//
140 // Populate State
141 
143  startOperationDefinition(topLevelOp->getName());
144 
145  // If the top-level operation is a symbol table, push a new symbol scope.
146  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
147  if (partialOpDef.isSymbolTable())
148  impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
149 }
150 
152  assert(!impl->partialOperations.empty() &&
153  "expected valid partial operation definition");
154  Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
155 
156  // If this operation is a symbol table, resolve any symbol uses.
157  if (partialOpDef.isSymbolTable()) {
158  impl->symbolTableOperations.emplace_back(
159  topLevelOp, std::move(partialOpDef.symbolTable));
160  }
161  impl->resolveSymbolUses();
162 }
163 
165  impl->partialOperations.emplace_back(opName);
166 }
167 
169  Operation *op, llvm::SMRange nameLoc, llvm::SMLoc endLoc,
170  ArrayRef<std::pair<unsigned, llvm::SMLoc>> resultGroups) {
171  assert(!impl->partialOperations.empty() &&
172  "expected valid partial operation definition");
173  Impl::PartialOpDef partialOpDef = impl->partialOperations.pop_back_val();
174 
175  // Build the full operation definition.
176  std::unique_ptr<OperationDefinition> def =
177  std::make_unique<OperationDefinition>(op, nameLoc, endLoc);
178  for (auto &resultGroup : resultGroups)
179  def->resultGroups.emplace_back(resultGroup.first,
180  convertIdLocToRange(resultGroup.second));
181  impl->operationToIdx.try_emplace(op, impl->operations.size());
182  impl->operations.emplace_back(std::move(def));
183 
184  // If this operation is a symbol table, resolve any symbol uses.
185  if (partialOpDef.isSymbolTable()) {
186  impl->symbolTableOperations.emplace_back(
187  op, std::move(partialOpDef.symbolTable));
188  }
189 }
190 
192  assert(!impl->partialOperations.empty() &&
193  "expected valid partial operation definition");
194 
195  // If the parent operation of this region is a symbol table, we also push a
196  // new symbol scope.
197  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
198  if (partialOpDef.isSymbolTable())
199  impl->symbolUseScopes.push_back(partialOpDef.symbolTable.get());
200 }
201 
203  assert(!impl->partialOperations.empty() &&
204  "expected valid partial operation definition");
205 
206  // If the parent operation of this region is a symbol table, pop the symbol
207  // scope for this region.
208  Impl::PartialOpDef &partialOpDef = impl->partialOperations.back();
209  if (partialOpDef.isSymbolTable())
210  impl->symbolUseScopes.pop_back();
211 }
212 
213 void AsmParserState::addDefinition(Block *block, llvm::SMLoc location) {
214  auto it = impl->blocksToIdx.find(block);
215  if (it == impl->blocksToIdx.end()) {
216  impl->blocksToIdx.try_emplace(block, impl->blocks.size());
217  impl->blocks.emplace_back(std::make_unique<BlockDefinition>(
218  block, convertIdLocToRange(location)));
219  return;
220  }
221 
222  // If an entry already exists, this was a forward declaration that now has a
223  // proper definition.
224  impl->blocks[it->second]->definition.loc = convertIdLocToRange(location);
225 }
226 
228  llvm::SMLoc location) {
229  auto it = impl->blocksToIdx.find(blockArg.getOwner());
230  assert(it != impl->blocksToIdx.end() &&
231  "expected owner block to have an entry");
232  BlockDefinition &def = *impl->blocks[it->second];
233  unsigned argIdx = blockArg.getArgNumber();
234 
235  if (def.arguments.size() <= argIdx)
236  def.arguments.resize(argIdx + 1);
237  def.arguments[argIdx] = SMDefinition(convertIdLocToRange(location));
238 }
239 
241  // Handle the case where the value is an operation result.
242  if (OpResult result = value.dyn_cast<OpResult>()) {
243  // Check to see if a definition for the parent operation has been recorded.
244  // If one hasn't, we treat the provided value as a placeholder value that
245  // will be refined further later.
246  Operation *parentOp = result.getOwner();
247  auto existingIt = impl->operationToIdx.find(parentOp);
248  if (existingIt == impl->operationToIdx.end()) {
249  impl->placeholderValueUses[value].append(locations.begin(),
250  locations.end());
251  return;
252  }
253 
254  // If a definition does exist, locate the value's result group and add the
255  // use. The result groups are ordered by increasing start index, so we just
256  // need to find the last group that has a smaller/equal start index.
257  unsigned resultNo = result.getResultNumber();
258  OperationDefinition &def = *impl->operations[existingIt->second];
259  for (auto &resultGroup : llvm::reverse(def.resultGroups)) {
260  if (resultNo >= resultGroup.startIndex) {
261  for (llvm::SMLoc loc : locations)
262  resultGroup.definition.uses.push_back(convertIdLocToRange(loc));
263  return;
264  }
265  }
266  llvm_unreachable("expected valid result group for value use");
267  }
268 
269  // Otherwise, this is a block argument.
270  BlockArgument arg = value.cast<BlockArgument>();
271  auto existingIt = impl->blocksToIdx.find(arg.getOwner());
272  assert(existingIt != impl->blocksToIdx.end() &&
273  "expected valid block definition for block argument");
274  BlockDefinition &blockDef = *impl->blocks[existingIt->second];
275  SMDefinition &argDef = blockDef.arguments[arg.getArgNumber()];
276  for (llvm::SMLoc loc : locations)
277  argDef.uses.emplace_back(convertIdLocToRange(loc));
278 }
279 
281  auto it = impl->blocksToIdx.find(block);
282  if (it == impl->blocksToIdx.end()) {
283  it = impl->blocksToIdx.try_emplace(block, impl->blocks.size()).first;
284  impl->blocks.emplace_back(std::make_unique<BlockDefinition>(block));
285  }
286 
287  BlockDefinition &def = *impl->blocks[it->second];
288  for (llvm::SMLoc loc : locations)
289  def.definition.uses.push_back(convertIdLocToRange(loc));
290 }
291 
292 void AsmParserState::addUses(SymbolRefAttr refAttr,
293  ArrayRef<llvm::SMRange> locations) {
294  // Ignore this symbol if no scopes are active.
295  if (impl->symbolUseScopes.empty())
296  return;
297 
298  assert((refAttr.getNestedReferences().size() + 1) == locations.size() &&
299  "expected the same number of references as provided locations");
300  (*impl->symbolUseScopes.back())[refAttr].emplace_back(locations.begin(),
301  locations.end());
302 }
303 
304 void AsmParserState::refineDefinition(Value oldValue, Value newValue) {
305  auto it = impl->placeholderValueUses.find(oldValue);
306  assert(it != impl->placeholderValueUses.end() &&
307  "expected `oldValue` to be a placeholder");
308  addUses(newValue, it->second);
309  impl->placeholderValueUses.erase(oldValue);
310 }
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, llvm::SMRange nameLoc, llvm::SMLoc endLoc, ArrayRef< std::pair< unsigned, llvm::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.
void addUses(Value value, ArrayRef< llvm::SMLoc > locations)
Add a source uses of the given value.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
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:423
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.
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.
unsigned getArgNumber() const
Returns the number of this argument.
Definition: Value.h:310
static constexpr const bool value
void startRegionDefinition()
Start a definition for a region nested under the current operation.
static llvm::SMRange convertIdLocToRange(llvm::SMLoc loc)
Returns (heuristically) the range of an identifier given a SMLoc corresponding to the start of an ide...
Block * getOwner() const
Returns the block that owns this argument.
Definition: Value.h:307
bool hasTrait() const
Returns true if the operation was registered with a particular trait, e.g.
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:242
SmallVector< llvm::SMRange > uses
The source location of all uses of the definition.
SmallVector< std::pair< Operation *, std::unique_ptr< SymbolUseMap > > > symbolTableOperations
The symbol table operations within the IR.
U dyn_cast() const
Definition: Value.h:99
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)
This class represents an argument of a Block.
Definition: Value.h:298
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:84
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:107
void addDefinition(Block *block, llvm::SMLoc location)
Add a definition of the given entity.
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:57
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.
SmallVector< std::unique_ptr< OperationDefinition > > operations
A mapping from operations in the input source file to their parser state.
DenseMap< Value, SmallVector< llvm::SMLoc > > placeholderValueUses
A set of value definitions that are placeholders for forward references.