brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.0 KiB · 8b14e71 Raw
387 lines · cpp
1//===- DialectSymbolParser.cpp - MLIR Dialect Symbol Parser  --------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the parser for the dialect symbols, such as extended10// attributes and types.11//12//===----------------------------------------------------------------------===//13 14#include "AsmParserImpl.h"15#include "Parser.h"16#include "mlir/AsmParser/AsmParserState.h"17#include "mlir/IR/AsmState.h"18#include "mlir/IR/Attributes.h"19#include "mlir/IR/BuiltinAttributeInterfaces.h"20#include "mlir/IR/BuiltinAttributes.h"21#include "mlir/IR/BuiltinTypes.h"22#include "mlir/IR/Dialect.h"23#include "mlir/IR/DialectImplementation.h"24#include "mlir/IR/MLIRContext.h"25#include "mlir/Support/LLVM.h"26#include "llvm/Support/MemoryBuffer.h"27#include "llvm/Support/SourceMgr.h"28#include <cassert>29#include <cstddef>30#include <utility>31 32using namespace mlir;33using namespace mlir::detail;34using llvm::MemoryBuffer;35using llvm::SourceMgr;36 37namespace {38/// This class provides the main implementation of the DialectAsmParser that39/// allows for dialects to parse attributes and types. This allows for dialect40/// hooking into the main MLIR parsing logic.41class CustomDialectAsmParser : public AsmParserImpl<DialectAsmParser> {42public:43  CustomDialectAsmParser(StringRef fullSpec, Parser &parser)44      : AsmParserImpl<DialectAsmParser>(parser.getToken().getLoc(), parser),45        fullSpec(fullSpec) {}46  ~CustomDialectAsmParser() override = default;47 48  /// Returns the full specification of the symbol being parsed. This allows49  /// for using a separate parser if necessary.50  StringRef getFullSymbolSpec() const override { return fullSpec; }51 52private:53  /// The full symbol specification.54  StringRef fullSpec;55};56} // namespace57 58///59///   pretty-dialect-sym-body ::= '<' pretty-dialect-sym-contents+ '>'60///   pretty-dialect-sym-contents ::= pretty-dialect-sym-body61///                                  | '(' pretty-dialect-sym-contents+ ')'62///                                  | '[' pretty-dialect-sym-contents+ ']'63///                                  | '{' pretty-dialect-sym-contents+ '}'64///                                  | '[^[<({>\])}\0]+'65///66ParseResult Parser::parseDialectSymbolBody(StringRef &body,67                                           bool &isCodeCompletion) {68  // Symbol bodies are a relatively unstructured format that contains a series69  // of properly nested punctuation, with anything else in the middle. Scan70  // ahead to find it and consume it if successful, otherwise emit an error.71  const char *curPtr = getTokenSpelling().data();72 73  // Scan over the nested punctuation, bailing out on error and consuming until74  // we find the end. We know that we're currently looking at the '<', so we can75  // go until we find the matching '>' character.76  assert(*curPtr == '<');77  SmallVector<char, 8> nestedPunctuation;78  const char *codeCompleteLoc = state.lex.getCodeCompleteLoc();79 80  // Functor used to emit an unbalanced punctuation error.81  auto emitPunctError = [&] {82    return emitError() << "unbalanced '" << nestedPunctuation.back()83                       << "' character in pretty dialect name";84  };85  // Functor used to check for unbalanced punctuation.86  auto checkNestedPunctuation = [&](char expectedToken) -> ParseResult {87    if (nestedPunctuation.back() != expectedToken)88      return emitPunctError();89    nestedPunctuation.pop_back();90    return success();91  };92  const char *curBufferEnd = state.lex.getBufferEnd();93  do {94    // Handle code completions, which may appear in the middle of the symbol95    // body.96    if (curPtr == codeCompleteLoc) {97      isCodeCompletion = true;98      nestedPunctuation.clear();99      break;100    }101 102    if (curBufferEnd == curPtr) {103      if (!nestedPunctuation.empty())104        return emitPunctError();105      return emitError("unexpected nul or EOF in pretty dialect name");106    }107 108    char c = *curPtr++;109    switch (c) {110    case '\0':111      // This also handles the EOF case.112      if (!nestedPunctuation.empty())113        return emitPunctError();114      return emitError("unexpected nul or EOF in pretty dialect name");115    case '<':116    case '[':117    case '(':118    case '{':119      nestedPunctuation.push_back(c);120      continue;121 122    case '-':123      // The sequence `->` is treated as special token.124      if (*curPtr == '>')125        ++curPtr;126      continue;127 128    case '>':129      if (failed(checkNestedPunctuation('<')))130        return failure();131      break;132    case ']':133      if (failed(checkNestedPunctuation('[')))134        return failure();135      break;136    case ')':137      if (failed(checkNestedPunctuation('(')))138        return failure();139      break;140    case '}':141      if (failed(checkNestedPunctuation('{')))142        return failure();143      break;144    case '"': {145      // Dispatch to the lexer to lex past strings.146      resetToken(curPtr - 1);147      curPtr = state.curToken.getEndLoc().getPointer();148 149      // Handle code completions, which may appear in the middle of the symbol150      // body.151      if (state.curToken.isCodeCompletion()) {152        isCodeCompletion = true;153        nestedPunctuation.clear();154        break;155      }156 157      // Otherwise, ensure this token was actually a string.158      if (state.curToken.isNot(Token::string))159        return failure();160      break;161    }162 163    default:164      continue;165    }166  } while (!nestedPunctuation.empty());167 168  // Ok, we succeeded, remember where we stopped, reset the lexer to know it is169  // consuming all this stuff, and return.170  resetToken(curPtr);171 172  unsigned length = curPtr - body.begin();173  body = StringRef(body.data(), length);174  return success();175}176 177/// Parse an extended dialect symbol.178template <typename Symbol, typename SymbolAliasMap, typename CreateFn>179static Symbol parseExtendedSymbol(Parser &p, AsmParserState *asmState,180                                  SymbolAliasMap &aliases,181                                  CreateFn &&createSymbol) {182  Token tok = p.getToken();183 184  // Handle code completion of the extended symbol.185  StringRef identifier = tok.getSpelling().drop_front();186  if (tok.isCodeCompletion() && identifier.empty())187    return p.codeCompleteDialectSymbol(aliases);188 189  // Parse the dialect namespace.190  SMRange range = p.getToken().getLocRange();191  SMLoc loc = p.getToken().getLoc();192  p.consumeToken();193 194  // Check to see if this is a pretty name.195  auto [dialectName, symbolData] = identifier.split('.');196  bool isPrettyName = !symbolData.empty() || identifier.back() == '.';197 198  // Check to see if the symbol has trailing data, i.e. has an immediately199  // following '<'.200  bool hasTrailingData =201      p.getToken().is(Token::less) &&202      identifier.bytes_end() == p.getTokenSpelling().bytes_begin();203 204  // If there is no '<' token following this, and if the typename contains no205  // dot, then we are parsing a symbol alias.206  if (!hasTrailingData && !isPrettyName) {207    // Check for an alias for this type.208    auto aliasIt = aliases.find(identifier);209    if (aliasIt == aliases.end())210      return (p.emitWrongTokenError("undefined symbol alias id '" + identifier +211                                    "'"),212              nullptr);213    if (asmState) {214      if constexpr (std::is_same_v<Symbol, Type>)215        asmState->addTypeAliasUses(identifier, range);216      else217        asmState->addAttrAliasUses(identifier, range);218    }219    return aliasIt->second;220  }221 222  // If this isn't an alias, we are parsing a dialect-specific symbol. If the223  // name contains a dot, then this is the "pretty" form. If not, it is the224  // verbose form that looks like <...>.225  if (!isPrettyName) {226    // Point the symbol data to the end of the dialect name to start.227    symbolData = StringRef(dialectName.end(), 0);228 229    // Parse the body of the symbol.230    bool isCodeCompletion = false;231    if (p.parseDialectSymbolBody(symbolData, isCodeCompletion))232      return nullptr;233    symbolData = symbolData.drop_front();234 235    // If the body contained a code completion it won't have the trailing `>`236    // token, so don't drop it.237    if (!isCodeCompletion)238      symbolData = symbolData.drop_back();239  } else {240    loc = SMLoc::getFromPointer(symbolData.data());241 242    // If the dialect's symbol is followed immediately by a <, then lex the body243    // of it into prettyName.244    if (hasTrailingData && p.parseDialectSymbolBody(symbolData))245      return nullptr;246  }247 248  return createSymbol(dialectName, symbolData, loc);249}250 251/// Parse an extended attribute.252///253///   extended-attribute ::= (dialect-attribute | attribute-alias)254///   dialect-attribute  ::= `#` dialect-namespace `<` attr-data `>`255///                          (`:` type)?256///                        | `#` alias-name pretty-dialect-sym-body? (`:` type)?257///   attribute-alias    ::= `#` alias-name258///259Attribute Parser::parseExtendedAttr(Type type) {260  MLIRContext *ctx = getContext();261  Attribute attr = parseExtendedSymbol<Attribute>(262      *this, state.asmState, state.symbols.attributeAliasDefinitions,263      [&](StringRef dialectName, StringRef symbolData, SMLoc loc) -> Attribute {264        // Parse an optional trailing colon type.265        Type attrType = type;266        if (consumeIf(Token::colon) && !(attrType = parseType()))267          return Attribute();268 269        // If we found a registered dialect, then ask it to parse the attribute.270        if (Dialect *dialect =271                builder.getContext()->getOrLoadDialect(dialectName)) {272          // Temporarily reset the lexer to let the dialect parse the attribute.273          const char *curLexerPos = getToken().getLoc().getPointer();274          resetToken(symbolData.data());275 276          // Parse the attribute.277          CustomDialectAsmParser customParser(symbolData, *this);278          Attribute attr = dialect->parseAttribute(customParser, attrType);279          resetToken(curLexerPos);280          return attr;281        }282 283        // Otherwise, form a new opaque attribute.284        return OpaqueAttr::getChecked(285            [&] { return emitError(loc); }, StringAttr::get(ctx, dialectName),286            symbolData, attrType ? attrType : NoneType::get(ctx));287      });288 289  // Ensure that the attribute has the same type as requested.290  auto typedAttr = dyn_cast_or_null<TypedAttr>(attr);291  if (type && typedAttr && typedAttr.getType() != type) {292    emitError("attribute type different than expected: expected ")293        << type << ", but got " << typedAttr.getType();294    return nullptr;295  }296  return attr;297}298 299/// Parse an extended type.300///301///   extended-type ::= (dialect-type | type-alias)302///   dialect-type  ::= `!` dialect-namespace `<` `"` type-data `"` `>`303///   dialect-type  ::= `!` alias-name pretty-dialect-attribute-body?304///   type-alias    ::= `!` alias-name305///306Type Parser::parseExtendedType() {307  MLIRContext *ctx = getContext();308  return parseExtendedSymbol<Type>(309      *this, state.asmState, state.symbols.typeAliasDefinitions,310      [&](StringRef dialectName, StringRef symbolData, SMLoc loc) -> Type {311        // If we found a registered dialect, then ask it to parse the type.312        if (auto *dialect = ctx->getOrLoadDialect(dialectName)) {313          // Temporarily reset the lexer to let the dialect parse the type.314          const char *curLexerPos = getToken().getLoc().getPointer();315          resetToken(symbolData.data());316 317          // Parse the type.318          CustomDialectAsmParser customParser(symbolData, *this);319          Type type = dialect->parseType(customParser);320          resetToken(curLexerPos);321          return type;322        }323 324        // Otherwise, form a new opaque type.325        return OpaqueType::getChecked([&] { return emitError(loc); },326                                      StringAttr::get(ctx, dialectName),327                                      symbolData);328      });329}330 331//===----------------------------------------------------------------------===//332// mlir::parseAttribute/parseType333//===----------------------------------------------------------------------===//334 335/// Parses a symbol, of type 'T', and returns it if parsing was successful. If336/// parsing failed, nullptr is returned.337template <typename T, typename ParserFn>338static T parseSymbol(StringRef inputStr, MLIRContext *context,339                     size_t *numReadOut, bool isKnownNullTerminated,340                     ParserFn &&parserFn) {341  // Set the buffer name to the string being parsed, so that it appears in error342  // diagnostics.343  auto memBuffer =344      isKnownNullTerminated345          ? MemoryBuffer::getMemBuffer(inputStr,346                                       /*BufferName=*/inputStr)347          : MemoryBuffer::getMemBufferCopy(inputStr, /*BufferName=*/inputStr);348  SourceMgr sourceMgr;349  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());350  SymbolState aliasState;351  ParserConfig config(context);352  ParserState state(sourceMgr, config, aliasState, /*asmState=*/nullptr,353                    /*codeCompleteContext=*/nullptr);354  Parser parser(state);355 356  Token startTok = parser.getToken();357  T symbol = parserFn(parser);358  if (!symbol)359    return T();360 361  // Provide the number of bytes that were read.362  Token endTok = parser.getToken();363  size_t numRead =364      endTok.getLoc().getPointer() - startTok.getLoc().getPointer();365  if (numReadOut) {366    *numReadOut = numRead;367  } else if (numRead != inputStr.size()) {368    parser.emitError(endTok.getLoc()) << "found trailing characters: '"369                                      << inputStr.drop_front(numRead) << "'";370    return T();371  }372  return symbol;373}374 375Attribute mlir::parseAttribute(StringRef attrStr, MLIRContext *context,376                               Type type, size_t *numRead,377                               bool isKnownNullTerminated) {378  return parseSymbol<Attribute>(379      attrStr, context, numRead, isKnownNullTerminated,380      [type](Parser &parser) { return parser.parseAttribute(type); });381}382Type mlir::parseType(StringRef typeStr, MLIRContext *context, size_t *numRead,383                     bool isKnownNullTerminated) {384  return parseSymbol<Type>(typeStr, context, numRead, isKnownNullTerminated,385                           [](Parser &parser) { return parser.parseType(); });386}387