llvm-project/mlir/lib/AsmParser/LocationParser.cpp
Jacques Pienaar 01e75646a5
[mlir] Add FileRange location type. (#80213)
This location type represents a contiguous range inside a file. It is
effectively a pair of FileLineCols. Add new type and make FileLineCol a
view for case where it matches existing previous one.

The location includes filename and optional start line & col, and end
line & col. Considered common cases are file:line, file:line:col,
file:line:start_col to file:line:end_col and general range within same
file. In memory its encoded as trailing objects. This keeps the memory
requirement the same as FileLineColLoc today (makes the rather common
File:Line cheaper) at the expense of extra work at decoding time. Kept the unsigned
type.

There was the option to always have file range be castable to
FileLineColLoc. This cast would just drop other fields. That may result
in some simpler staging. TBD.

This is a rather minimal change, it does not yet add bindings (C or
Python), lowering to LLVM debug locations etc. that supports end line:cols.

---------

Co-authored-by: River Riddle <riddleriver@gmail.com>
2024-11-23 05:12:04 -08:00

238 lines
7.4 KiB
C++

//===- LocationParser.cpp - MLIR Location Parser -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Parser.h"
#include "Token.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Location.h"
#include "mlir/Support/LLVM.h"
#include <optional>
using namespace mlir;
using namespace mlir::detail;
/// Specific location instances.
///
/// location-inst ::= filelinecol-location |
/// name-location |
/// callsite-location |
/// fused-location |
/// unknown-location
/// filelinecol-location ::= string-literal ':' integer-literal
/// ':' integer-literal
/// name-location ::= string-literal
/// callsite-location ::= 'callsite' '(' location-inst 'at' location-inst ')'
/// fused-location ::= fused ('<' attribute-value '>')?
/// '[' location-inst (location-inst ',')* ']'
/// unknown-location ::= 'unknown'
///
ParseResult Parser::parseCallSiteLocation(LocationAttr &loc) {
consumeToken(Token::bare_identifier);
// Parse the '('.
if (parseToken(Token::l_paren, "expected '(' in callsite location"))
return failure();
// Parse the callee location.
LocationAttr calleeLoc;
if (parseLocationInstance(calleeLoc))
return failure();
// Parse the 'at'.
if (getToken().isNot(Token::bare_identifier) ||
getToken().getSpelling() != "at")
return emitWrongTokenError("expected 'at' in callsite location");
consumeToken(Token::bare_identifier);
// Parse the caller location.
LocationAttr callerLoc;
if (parseLocationInstance(callerLoc))
return failure();
// Parse the ')'.
if (parseToken(Token::r_paren, "expected ')' in callsite location"))
return failure();
// Return the callsite location.
loc = CallSiteLoc::get(calleeLoc, callerLoc);
return success();
}
ParseResult Parser::parseFusedLocation(LocationAttr &loc) {
consumeToken(Token::bare_identifier);
// Try to parse the optional metadata.
Attribute metadata;
if (consumeIf(Token::less)) {
metadata = parseAttribute();
if (!metadata)
return failure();
// Parse the '>' token.
if (parseToken(Token::greater,
"expected '>' after fused location metadata"))
return failure();
}
SmallVector<Location, 4> locations;
auto parseElt = [&] {
LocationAttr newLoc;
if (parseLocationInstance(newLoc))
return failure();
locations.push_back(newLoc);
return success();
};
if (parseCommaSeparatedList(Delimiter::Square, parseElt,
" in fused location"))
return failure();
// Return the fused location.
loc = FusedLoc::get(locations, metadata, getContext());
return success();
}
ParseResult Parser::parseNameOrFileLineColRange(LocationAttr &loc) {
auto *ctx = getContext();
auto str = getToken().getStringValue();
consumeToken(Token::string);
std::optional<unsigned> startLine, startColumn, endLine, endColumn;
// If the next token is ':' this is a filelinecol location.
if (consumeIf(Token::colon)) {
// Parse the line number.
if (getToken().isNot(Token::integer))
return emitWrongTokenError(
"expected integer line number in FileLineColRange");
startLine = getToken().getUnsignedIntegerValue();
if (!startLine)
return emitWrongTokenError(
"expected integer line number in FileLineColRange");
consumeToken(Token::integer);
// Parse the ':'.
if (getToken().isNot(Token::colon)) {
loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine);
return success();
}
consumeToken(Token::colon);
// Parse the column number.
if (getToken().isNot(Token::integer)) {
return emitWrongTokenError(
"expected integer column number in FileLineColRange");
}
startColumn = getToken().getUnsignedIntegerValue();
if (!startColumn.has_value())
return emitError("expected integer column number in FileLineColRange");
consumeToken(Token::integer);
if (!isCurrentTokenAKeyword() || getTokenSpelling() != "to") {
loc = FileLineColLoc::get(ctx, str, *startLine, *startColumn);
return success();
}
consumeToken();
// Parse the line number.
if (getToken().is(Token::integer)) {
endLine = getToken().getUnsignedIntegerValue();
if (!endLine) {
return emitWrongTokenError(
"expected integer line number in FileLineColRange");
}
consumeToken(Token::integer);
}
// Parse the ':'.
if (getToken().isNot(Token::colon)) {
return emitWrongTokenError(
"expected either integer or `:` post `to` in FileLineColRange");
}
consumeToken(Token::colon);
// Parse the column number.
if (getToken().isNot(Token::integer)) {
return emitWrongTokenError(
"expected integer column number in FileLineColRange");
}
endColumn = getToken().getUnsignedIntegerValue();
if (!endColumn.has_value())
return emitError("expected integer column number in FileLineColRange");
consumeToken(Token::integer);
if (endLine.has_value()) {
loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine,
*startColumn, *endLine, *endColumn);
} else {
loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine,
*startColumn, *endColumn);
}
return success();
}
// Otherwise, this is a NameLoc.
// Check for a child location.
if (consumeIf(Token::l_paren)) {
// Parse the child location.
LocationAttr childLoc;
if (parseLocationInstance(childLoc))
return failure();
loc = NameLoc::get(StringAttr::get(ctx, str), childLoc);
// Parse the closing ')'.
if (parseToken(Token::r_paren,
"expected ')' after child location of NameLoc"))
return failure();
} else {
loc = NameLoc::get(StringAttr::get(ctx, str));
}
return success();
}
ParseResult Parser::parseLocationInstance(LocationAttr &loc) {
// Handle aliases.
if (getToken().is(Token::hash_identifier)) {
Attribute locAttr = parseExtendedAttr(Type());
if (!locAttr)
return failure();
if (!(loc = dyn_cast<LocationAttr>(locAttr)))
return emitError("expected location attribute, but got") << locAttr;
return success();
}
// Handle either name or filelinecol locations.
if (getToken().is(Token::string))
return parseNameOrFileLineColRange(loc);
// Bare tokens required for other cases.
if (!getToken().is(Token::bare_identifier))
return emitWrongTokenError("expected location instance");
// Check for the 'callsite' signifying a callsite location.
if (getToken().getSpelling() == "callsite")
return parseCallSiteLocation(loc);
// If the token is 'fused', then this is a fused location.
if (getToken().getSpelling() == "fused")
return parseFusedLocation(loc);
// Check for a 'unknown' for an unknown location.
if (getToken().getSpelling() == "unknown") {
consumeToken(Token::bare_identifier);
loc = UnknownLoc::get(getContext());
return success();
}
return emitWrongTokenError("expected location instance");
}