[OpAsmParser] Add a parseCommaSeparatedList helper and beef up Delimeter.

Lots of custom ops have hand-rolled comma-delimited parsing loops, as does
the MLIR parser itself.  Provides a standard interface for doing this that
is less error prone and less boilerplate.

While here, extend Delimiter to support <> and {} delimited sequences as
well (I have a use for <> in CIRCT specifically).

Differential Revision: https://reviews.llvm.org/D110122
This commit is contained in:
Chris Lattner 2021-09-20 18:27:40 -07:00
parent 073b254cff
commit 58abc8c34b
13 changed files with 284 additions and 248 deletions

View File

@ -473,6 +473,47 @@ public:
return success();
}
/// These are the supported delimiters around operand lists and region
/// argument lists, used by parseOperandList and parseRegionArgumentList.
enum class Delimiter {
/// Zero or more operands with no delimiters.
None,
/// Parens surrounding zero or more operands.
Paren,
/// Square brackets surrounding zero or more operands.
Square,
/// <> brackets surrounding zero or more operands.
LessGreater,
/// {} brackets surrounding zero or more operands.
Braces,
/// Parens supporting zero or more operands, or nothing.
OptionalParen,
/// Square brackets supporting zero or more ops, or nothing.
OptionalSquare,
/// <> brackets supporting zero or more ops, or nothing.
OptionalLessGreater,
/// {} brackets surrounding zero or more operands, or nothing.
OptionalBraces,
};
/// Parse a list of comma-separated items with an optional delimiter. If a
/// delimiter is provided, then an empty list is allowed. If not, then at
/// least one element will be parsed.
///
/// contextMessage is an optional message appended to "expected '('" sorts of
/// diagnostics when parsing the delimeters.
virtual ParseResult
parseCommaSeparatedList(Delimiter delimiter,
function_ref<ParseResult()> parseElementFn,
StringRef contextMessage = StringRef()) = 0;
/// Parse a comma separated list of elements that must have at least one entry
/// in it.
ParseResult
parseCommaSeparatedList(function_ref<ParseResult()> parseElementFn) {
return parseCommaSeparatedList(Delimiter::None, parseElementFn);
}
//===--------------------------------------------------------------------===//
// Attribute Parsing
//===--------------------------------------------------------------------===//
@ -610,21 +651,6 @@ public:
/// Parse a single operand if present.
virtual OptionalParseResult parseOptionalOperand(OperandType &result) = 0;
/// These are the supported delimiters around operand lists and region
/// argument lists, used by parseOperandList and parseRegionArgumentList.
enum class Delimiter {
/// Zero or more operands with no delimiters.
None,
/// Parens surrounding zero or more operands.
Paren,
/// Square brackets surrounding zero or more operands.
Square,
/// Parens supporting zero or more operands, or nothing.
OptionalParen,
/// Square brackets supporting zero or more ops, or nothing.
OptionalSquare,
};
/// Parse zero or more SSA comma-separated operand references with a specified
/// surrounding delimiter, and an optional required operand count.
virtual ParseResult

View File

@ -158,7 +158,6 @@ static ParseResult parseExecuteOp(OpAsmParser &parser, OperationState &result) {
// Sizes of parsed variadic operands, will be updated below after parsing.
int32_t numDependencies = 0;
int32_t numOperands = 0;
auto tokenTy = TokenType::get(ctx);
@ -179,38 +178,27 @@ static ParseResult parseExecuteOp(OpAsmParser &parser, OperationState &result) {
SmallVector<Type, 4> valueTypes;
SmallVector<Type, 4> unwrappedTypes;
if (succeeded(parser.parseOptionalLParen())) {
auto argsLoc = parser.getCurrentLocation();
// Parse a single instance of `%value as %unwrapped : !async.value<!type>`.
auto parseAsyncValueArg = [&]() -> ParseResult {
if (parser.parseOperand(valueArgs.emplace_back()) ||
parser.parseKeyword("as") ||
parser.parseOperand(unwrappedArgs.emplace_back()) ||
parser.parseColonType(valueTypes.emplace_back()))
return failure();
// Parse a single instance of `%value as %unwrapped : !async.value<!type>`.
auto parseAsyncValueArg = [&]() -> ParseResult {
if (parser.parseOperand(valueArgs.emplace_back()) ||
parser.parseKeyword("as") ||
parser.parseOperand(unwrappedArgs.emplace_back()) ||
parser.parseColonType(valueTypes.emplace_back()))
return failure();
auto valueTy = valueTypes.back().dyn_cast<ValueType>();
unwrappedTypes.emplace_back(valueTy ? valueTy.getValueType() : Type());
auto valueTy = valueTypes.back().dyn_cast<ValueType>();
unwrappedTypes.emplace_back(valueTy ? valueTy.getValueType() : Type());
return success();
};
return success();
};
auto argsLoc = parser.getCurrentLocation();
if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::OptionalParen,
parseAsyncValueArg) ||
parser.resolveOperands(valueArgs, valueTypes, argsLoc, result.operands))
return failure();
// If the next token is `)` skip async value arguments parsing.
if (failed(parser.parseOptionalRParen())) {
do {
if (parseAsyncValueArg())
return failure();
} while (succeeded(parser.parseOptionalComma()));
if (parser.parseRParen() ||
parser.resolveOperands(valueArgs, valueTypes, argsLoc,
result.operands))
return failure();
}
numOperands = valueArgs.size();
}
int32_t numOperands = valueArgs.size();
// Add derived `operand_segment_sizes` attribute based on parsed operands.
auto operandSegmentSizes = DenseIntElementsAttr::get(

View File

@ -77,22 +77,16 @@ static ParseResult
parseOperandAndTypeList(OpAsmParser &parser,
SmallVectorImpl<OpAsmParser::OperandType> &operands,
SmallVectorImpl<Type> &types) {
if (parser.parseLParen())
return failure();
do {
OpAsmParser::OperandType operand;
Type type;
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
operands.push_back(operand);
types.push_back(type);
} while (succeeded(parser.parseOptionalComma()));
if (parser.parseRParen())
return failure();
return success();
return parser.parseCommaSeparatedList(
OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
OpAsmParser::OperandType operand;
Type type;
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
operands.push_back(operand);
types.push_back(type);
return success();
});
}
/// Parse an allocate clause with allocators and a list of operands with types.
@ -108,30 +102,24 @@ static ParseResult parseAllocateAndAllocator(
SmallVectorImpl<Type> &typesAllocate,
SmallVectorImpl<OpAsmParser::OperandType> &operandsAllocator,
SmallVectorImpl<Type> &typesAllocator) {
if (parser.parseLParen())
return failure();
do {
OpAsmParser::OperandType operand;
Type type;
return parser.parseCommaSeparatedList(
OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
OpAsmParser::OperandType operand;
Type type;
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
operandsAllocator.push_back(operand);
typesAllocator.push_back(type);
if (parser.parseArrow())
return failure();
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
operandsAllocator.push_back(operand);
typesAllocator.push_back(type);
if (parser.parseArrow())
return failure();
if (parser.parseOperand(operand) || parser.parseColonType(type))
return failure();
operandsAllocate.push_back(operand);
typesAllocate.push_back(type);
} while (succeeded(parser.parseOptionalComma()));
if (parser.parseRParen())
return failure();
return success();
operandsAllocate.push_back(operand);
typesAllocate.push_back(type);
return success();
});
}
static LogicalResult verifyParallelOp(ParallelOp op) {

View File

@ -1778,16 +1778,16 @@ static ParseResult parseEntryPointOp(OpAsmParser &parser,
if (!parser.parseOptionalComma()) {
// Parse the interface variables
do {
// The name of the interface variable attribute isnt important
auto attrName = "var_symbol";
FlatSymbolRefAttr var;
NamedAttrList attrs;
if (parser.parseAttribute(var, Type(), attrName, attrs)) {
return failure();
}
interfaceVars.push_back(var);
} while (!parser.parseOptionalComma());
if (parser.parseCommaSeparatedList([&]() -> ParseResult {
// The name of the interface variable attribute isnt important
FlatSymbolRefAttr var;
NamedAttrList attrs;
if (parser.parseAttribute(var, Type(), "var_symbol", attrs))
return failure();
interfaceVars.push_back(var);
return success();
}))
return failure();
}
state.addAttribute(kInterfaceAttrName,
parser.getBuilder().getArrayAttr(interfaceVars));

View File

@ -2197,13 +2197,12 @@ static ParseResult parseSwitchOpCases(
SmallVectorImpl<Block *> &caseDestinations,
SmallVectorImpl<SmallVector<OpAsmParser::OperandType>> &caseOperands,
SmallVectorImpl<SmallVector<Type>> &caseOperandTypes) {
if (failed(parser.parseKeyword("default")) || failed(parser.parseColon()) ||
failed(parser.parseSuccessor(defaultDestination)))
if (parser.parseKeyword("default") || parser.parseColon() ||
parser.parseSuccessor(defaultDestination))
return failure();
if (succeeded(parser.parseOptionalLParen())) {
if (failed(parser.parseRegionArgumentList(defaultOperands)) ||
failed(parser.parseColonTypeList(defaultOperandTypes)) ||
failed(parser.parseRParen()))
if (parser.parseRegionArgumentList(defaultOperands) ||
parser.parseColonTypeList(defaultOperandTypes) || parser.parseRParen())
return failure();
}

View File

@ -474,26 +474,22 @@ ParseResult AffineParser::parseIdentifierDefinition(AffineExpr idExpr) {
/// Parse the list of dimensional identifiers to an affine map.
ParseResult AffineParser::parseDimIdList(unsigned &numDims) {
if (parseToken(Token::l_paren,
"expected '(' at start of dimensional identifiers list")) {
return failure();
}
auto parseElt = [&]() -> ParseResult {
auto dimension = getAffineDimExpr(numDims++, getContext());
return parseIdentifierDefinition(dimension);
};
return parseCommaSeparatedListUntil(Token::r_paren, parseElt);
return parseCommaSeparatedList(Delimiter::Paren, parseElt,
" in dimensional identifier list");
}
/// Parse the list of symbolic identifiers to an affine map.
ParseResult AffineParser::parseSymbolIdList(unsigned &numSymbols) {
consumeToken(Token::l_square);
auto parseElt = [&]() -> ParseResult {
auto symbol = getAffineSymbolExpr(numSymbols++, getContext());
return parseIdentifierDefinition(symbol);
};
return parseCommaSeparatedListUntil(Token::r_square, parseElt);
return parseCommaSeparatedList(Delimiter::Square, parseElt,
" in symbol list");
}
/// Parse the list of symbolic identifiers to an affine map.
@ -544,21 +540,6 @@ ParseResult AffineParser::parseAffineMapOrIntegerSetInline(AffineMap &map,
ParseResult
AffineParser::parseAffineMapOfSSAIds(AffineMap &map,
OpAsmParser::Delimiter delimiter) {
Token::Kind rightToken;
switch (delimiter) {
case OpAsmParser::Delimiter::Square:
if (parseToken(Token::l_square, "expected '['"))
return failure();
rightToken = Token::r_square;
break;
case OpAsmParser::Delimiter::Paren:
if (parseToken(Token::l_paren, "expected '('"))
return failure();
rightToken = Token::r_paren;
break;
default:
return emitError("unexpected delimiter");
}
SmallVector<AffineExpr, 4> exprs;
auto parseElt = [&]() -> ParseResult {
@ -571,9 +552,9 @@ AffineParser::parseAffineMapOfSSAIds(AffineMap &map,
// 1-d affine expressions); the list can be empty. Grammar:
// multi-dim-affine-expr ::= `(` `)`
// | `(` affine-expr (`,` affine-expr)* `)`
if (parseCommaSeparatedListUntil(rightToken, parseElt,
/*allowEmptyList=*/true))
if (parseCommaSeparatedList(delimiter, parseElt, " in affine map"))
return failure();
// Parsed a valid affine map.
map = AffineMap::get(numDimOperands, dimsAndSymbols.size() - numDimOperands,
exprs, getContext());
@ -594,8 +575,6 @@ ParseResult AffineParser::parseAffineExprOfSSAIds(AffineExpr &expr) {
/// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)`
AffineMap AffineParser::parseAffineMapRange(unsigned numDims,
unsigned numSymbols) {
parseToken(Token::l_paren, "expected '(' at start of affine map range");
SmallVector<AffineExpr, 4> exprs;
auto parseElt = [&]() -> ParseResult {
auto elt = parseAffineExpr();
@ -608,7 +587,8 @@ AffineMap AffineParser::parseAffineMapRange(unsigned numDims,
// 1-d affine expressions). Grammar:
// multi-dim-affine-expr ::= `(` `)`
// | `(` affine-expr (`,` affine-expr)* `)`
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt, true))
if (parseCommaSeparatedList(Delimiter::Paren, parseElt,
" in affine map range"))
return AffineMap();
// Parsed a valid affine map.
@ -662,10 +642,6 @@ AffineExpr AffineParser::parseAffineConstraint(bool *isEq) {
///
IntegerSet AffineParser::parseIntegerSetConstraints(unsigned numDims,
unsigned numSymbols) {
if (parseToken(Token::l_paren,
"expected '(' at start of integer set constraint list"))
return IntegerSet();
SmallVector<AffineExpr, 4> constraints;
SmallVector<bool, 4> isEqs;
auto parseElt = [&]() -> ParseResult {
@ -680,7 +656,8 @@ IntegerSet AffineParser::parseIntegerSetConstraints(unsigned numDims,
};
// Parse a list of affine constraints (comma-separated).
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt, true))
if (parseCommaSeparatedList(Delimiter::Paren, parseElt,
" in integer set constraint list"))
return IntegerSet();
// If no constraints were parsed, then treat this as a degenerate 'true' case.

View File

@ -67,15 +67,13 @@ Attribute Parser::parseAttribute(Type type) {
// Parse an array attribute.
case Token::l_square: {
consumeToken(Token::l_square);
SmallVector<Attribute, 4> elements;
auto parseElt = [&]() -> ParseResult {
elements.push_back(parseAttribute());
return elements.back() ? success() : failure();
};
if (parseCommaSeparatedListUntil(Token::r_square, parseElt))
if (parseCommaSeparatedList(Delimiter::Square, parseElt))
return nullptr;
return builder.getArrayAttr(elements);
}
@ -262,9 +260,6 @@ OptionalParseResult Parser::parseOptionalAttribute(StringAttr &attribute,
/// attribute-entry ::= (bare-id | string-literal) `=` attribute-value
///
ParseResult Parser::parseAttributeDict(NamedAttrList &attributes) {
if (parseToken(Token::l_brace, "expected '{' in attribute dictionary"))
return failure();
llvm::SmallDenseSet<Identifier> seenKeys;
auto parseElt = [&]() -> ParseResult {
// The name of an attribute can either be a bare identifier, or a string.
@ -300,7 +295,8 @@ ParseResult Parser::parseAttributeDict(NamedAttrList &attributes) {
return success();
};
if (parseCommaSeparatedListUntil(Token::r_brace, parseElt))
if (parseCommaSeparatedList(Delimiter::Braces, parseElt,
" in attribute dictionary"))
return failure();
return success();
@ -769,8 +765,6 @@ ParseResult TensorLiteralParser::parseElement() {
/// parseList([[1, 2], 3]) -> Failure
/// parseList([[1, [2, 3]], [4, [5]]]) -> Failure
ParseResult TensorLiteralParser::parseList(SmallVectorImpl<int64_t> &dims) {
p.consumeToken(Token::l_square);
auto checkDims = [&](const SmallVectorImpl<int64_t> &prevDims,
const SmallVectorImpl<int64_t> &newDims) -> ParseResult {
if (prevDims == newDims)
@ -782,7 +776,7 @@ ParseResult TensorLiteralParser::parseList(SmallVectorImpl<int64_t> &dims) {
bool first = true;
SmallVector<int64_t, 4> newDims;
unsigned size = 0;
auto parseCommaSeparatedList = [&]() -> ParseResult {
auto parseOneElement = [&]() -> ParseResult {
SmallVector<int64_t, 4> thisDims;
if (p.getToken().getKind() == Token::l_square) {
if (parseList(thisDims))
@ -797,7 +791,7 @@ ParseResult TensorLiteralParser::parseList(SmallVectorImpl<int64_t> &dims) {
first = false;
return success();
};
if (p.parseCommaSeparatedListUntil(Token::r_square, parseCommaSeparatedList))
if (p.parseCommaSeparatedList(Parser::Delimiter::Square, parseOneElement))
return failure();
// Return the sublists' dimensions with 'size' prepended.

View File

@ -82,9 +82,8 @@ ParseResult Parser::parseFusedLocation(LocationAttr &loc) {
return success();
};
if (parseToken(Token::l_square, "expected '[' in fused location") ||
parseCommaSeparatedList(parseElt) ||
parseToken(Token::r_square, "expected ']' in fused location"))
if (parseCommaSeparatedList(Delimiter::Square, parseElt,
" in fused location"))
return failure();
// Return the fused location.

View File

@ -35,20 +35,90 @@ using llvm::SourceMgr;
// Parser
//===----------------------------------------------------------------------===//
/// Parse a comma separated list of elements that must have at least one entry
/// in it.
/// Parse a list of comma-separated items with an optional delimiter. If a
/// delimiter is provided, then an empty list is allowed. If not, then at
/// least one element will be parsed.
ParseResult
Parser::parseCommaSeparatedList(function_ref<ParseResult()> parseElement) {
Parser::parseCommaSeparatedList(Delimiter delimiter,
function_ref<ParseResult()> parseElementFn,
StringRef contextMessage) {
switch (delimiter) {
case Delimiter::None:
break;
case Delimiter::OptionalParen:
if (getToken().isNot(Token::l_paren))
return success();
LLVM_FALLTHROUGH;
case Delimiter::Paren:
if (parseToken(Token::l_paren, "expected '('" + contextMessage))
return failure();
// Check for empty list.
if (consumeIf(Token::r_paren))
return success();
break;
case Delimiter::OptionalLessGreater:
// Check for absent list.
if (getToken().isNot(Token::less))
return success();
LLVM_FALLTHROUGH;
case Delimiter::LessGreater:
if (parseToken(Token::less, "expected '<'" + contextMessage))
return success();
// Check for empty list.
if (consumeIf(Token::greater))
return success();
break;
case Delimiter::OptionalSquare:
if (getToken().isNot(Token::l_square))
return success();
LLVM_FALLTHROUGH;
case Delimiter::Square:
if (parseToken(Token::l_square, "expected '['" + contextMessage))
return failure();
// Check for empty list.
if (consumeIf(Token::r_square))
return success();
break;
case Delimiter::OptionalBraces:
if (getToken().isNot(Token::l_brace))
return success();
LLVM_FALLTHROUGH;
case Delimiter::Braces:
if (parseToken(Token::l_brace, "expected '{'" + contextMessage))
return failure();
// Check for empty list.
if (consumeIf(Token::r_brace))
return success();
break;
}
// Non-empty case starts with an element.
if (parseElement())
if (parseElementFn())
return failure();
// Otherwise we have a list of comma separated elements.
while (consumeIf(Token::comma)) {
if (parseElement())
if (parseElementFn())
return failure();
}
return success();
switch (delimiter) {
case Delimiter::None:
return success();
case Delimiter::OptionalParen:
case Delimiter::Paren:
return parseToken(Token::r_paren, "expected ')'" + contextMessage);
case Delimiter::OptionalLessGreater:
case Delimiter::LessGreater:
return parseToken(Token::greater, "expected '>'" + contextMessage);
case Delimiter::OptionalSquare:
case Delimiter::Square:
return parseToken(Token::r_square, "expected ']'" + contextMessage);
case Delimiter::OptionalBraces:
case Delimiter::Braces:
return parseToken(Token::r_brace, "expected '}'" + contextMessage);
}
llvm_unreachable("Unknown delimiter");
}
/// Parse a comma-separated list of elements, terminated with an arbitrary
@ -1282,6 +1352,15 @@ public:
return parser.parseOptionalInteger(result);
}
/// Parse a list of comma-separated items with an optional delimiter. If a
/// delimiter is provided, then an empty list is allowed. If not, then at
/// least one element will be parsed.
ParseResult parseCommaSeparatedList(Delimiter delimiter,
function_ref<ParseResult()> parseElt,
StringRef contextMessage) override {
return parser.parseCommaSeparatedList(delimiter, parseElt, contextMessage);
}
//===--------------------------------------------------------------------===//
// Attribute Parsing
//===--------------------------------------------------------------------===//
@ -1467,67 +1546,37 @@ public:
Delimiter delimiter = Delimiter::None) {
auto startLoc = parser.getToken().getLoc();
// Handle delimiters.
switch (delimiter) {
case Delimiter::None:
// Don't check for the absence of a delimiter if the number of operands
// is unknown (and hence the operand list could be empty).
if (requiredOperandCount == -1)
break;
// Token already matches an identifier and so can't be a delimiter.
if (parser.getToken().is(Token::percent_identifier))
break;
// Test against known delimiters.
if (parser.getToken().is(Token::l_paren) ||
parser.getToken().is(Token::l_square))
return emitError(startLoc, "unexpected delimiter");
return emitError(startLoc, "invalid operand");
case Delimiter::OptionalParen:
if (parser.getToken().isNot(Token::l_paren))
return success();
LLVM_FALLTHROUGH;
case Delimiter::Paren:
if (parser.parseToken(Token::l_paren, "expected '(' in operand list"))
return failure();
break;
case Delimiter::OptionalSquare:
if (parser.getToken().isNot(Token::l_square))
return success();
LLVM_FALLTHROUGH;
case Delimiter::Square:
if (parser.parseToken(Token::l_square, "expected '[' in operand list"))
return failure();
break;
// The no-delimiter case has some special handling for better diagnostics.
if (delimiter == Delimiter::None) {
// parseCommaSeparatedList doesn't handle the missing case for "none",
// so we handle it custom here.
if (parser.getToken().isNot(Token::percent_identifier)) {
// If we didn't require any operands or required exactly zero (weird)
// then this is success.
if (requiredOperandCount == -1 || requiredOperandCount == 0)
return success();
// Otherwise, try to produce a nice error message.
if (parser.getToken().is(Token::l_paren) ||
parser.getToken().is(Token::l_square))
return emitError(startLoc, "unexpected delimiter");
return emitError(startLoc, "invalid operand");
}
}
// Check for zero operands.
if (parser.getToken().is(Token::percent_identifier)) {
do {
OperandType operandOrArg;
if (isOperandList ? parseOperand(operandOrArg)
: parseRegionArgument(operandOrArg))
return failure();
result.push_back(operandOrArg);
} while (parser.consumeIf(Token::comma));
}
// Handle delimiters. If we reach here, the optional delimiters were
// present, so we need to parse their closing one.
switch (delimiter) {
case Delimiter::None:
break;
case Delimiter::OptionalParen:
case Delimiter::Paren:
if (parser.parseToken(Token::r_paren, "expected ')' in operand list"))
auto parseOneOperand = [&]() -> ParseResult {
OperandType operandOrArg;
if (isOperandList ? parseOperand(operandOrArg)
: parseRegionArgument(operandOrArg))
return failure();
break;
case Delimiter::OptionalSquare:
case Delimiter::Square:
if (parser.parseToken(Token::r_square, "expected ']' in operand list"))
return failure();
break;
}
result.push_back(operandOrArg);
return success();
};
if (parseCommaSeparatedList(delimiter, parseOneOperand, " in operand list"))
return failure();
// Check that we got the expected # of elements.
if (requiredOperandCount != -1 &&
result.size() != static_cast<size_t>(requiredOperandCount))
return emitError(startLoc, "expected ")

View File

@ -24,6 +24,8 @@ namespace detail {
/// include state.
class Parser {
public:
using Delimiter = OpAsmParser::Delimiter;
Builder builder;
Parser(ParserState &state) : builder(state.context), state(state) {}
@ -39,9 +41,20 @@ public:
function_ref<ParseResult()> parseElement,
bool allowEmptyList = true);
/// Parse a list of comma-separated items with an optional delimiter. If a
/// delimiter is provided, then an empty list is allowed. If not, then at
/// least one element will be parsed.
ParseResult
parseCommaSeparatedList(Delimiter delimiter,
function_ref<ParseResult()> parseElementFn,
StringRef contextMessage = StringRef());
/// Parse a comma separated list of elements that must have at least one entry
/// in it.
ParseResult parseCommaSeparatedList(function_ref<ParseResult()> parseElement);
ParseResult
parseCommaSeparatedList(function_ref<ParseResult()> parseElementFn) {
return parseCommaSeparatedList(Delimiter::None, parseElementFn);
}
ParseResult parsePrettyDialectSymbolName(StringRef &prettyName);
@ -276,7 +289,7 @@ public:
ParseResult
parseAffineMapOfSSAIds(AffineMap &map,
function_ref<ParseResult(bool)> parseElement,
OpAsmParser::Delimiter delimiter);
Delimiter delimiter);
/// Parse an AffineExpr where dim and symbol identifiers are SSA ids.
ParseResult

View File

@ -165,15 +165,12 @@ ParseResult Parser::parseStridedLayout(int64_t &offset,
return emitError("expected comma after offset value");
// Parse stride list.
if (!consumeIf(Token::kw_strides))
return emitError("expected `strides` keyword after offset specification");
if (!consumeIf(Token::colon))
return emitError("expected colon after `strides` keyword");
if (failed(parseStrideList(strides)))
return emitError("invalid braces-enclosed stride list");
if (llvm::any_of(strides, [](int64_t st) { return st == 0; }))
return emitError("invalid memref stride");
if (parseToken(Token::kw_strides,
"expected `strides` keyword after offset specification") ||
parseToken(Token::colon, "expected colon after `strides` keyword") ||
parseStrideList(strides))
return failure();
return success();
}
@ -560,31 +557,30 @@ ParseResult Parser::parseXInDimensionList() {
// Parse a comma-separated list of dimensions, possibly empty:
// stride-list ::= `[` (dimension (`,` dimension)*)? `]`
ParseResult Parser::parseStrideList(SmallVectorImpl<int64_t> &dimensions) {
if (!consumeIf(Token::l_square))
return failure();
// Empty list early exit.
if (consumeIf(Token::r_square))
return success();
while (true) {
if (consumeIf(Token::question)) {
dimensions.push_back(MemRefType::getDynamicStrideOrOffset());
} else {
// This must be an integer value.
int64_t val;
if (getToken().getSpelling().getAsInteger(10, val))
return emitError("invalid integer value: ") << getToken().getSpelling();
// Make sure it is not the one value for `?`.
if (ShapedType::isDynamic(val))
return emitError("invalid integer value: ")
<< getToken().getSpelling()
<< ", use `?` to specify a dynamic dimension";
dimensions.push_back(val);
consumeToken(Token::integer);
}
if (!consumeIf(Token::comma))
break;
}
if (!consumeIf(Token::r_square))
return failure();
return success();
return parseCommaSeparatedList(
Delimiter::Square,
[&]() -> ParseResult {
if (consumeIf(Token::question)) {
dimensions.push_back(MemRefType::getDynamicStrideOrOffset());
} else {
// This must be an integer value.
int64_t val;
if (getToken().getSpelling().getAsInteger(10, val))
return emitError("invalid integer value: ")
<< getToken().getSpelling();
// Make sure it is not the one value for `?`.
if (ShapedType::isDynamic(val))
return emitError("invalid integer value: ")
<< getToken().getSpelling()
<< ", use `?` to specify a dynamic dimension";
if (val == 0)
return emitError("invalid memref stride");
dimensions.push_back(val);
consumeToken(Token::integer);
}
return success();
},
" in stride list");
}

View File

@ -29,7 +29,9 @@
#hello_world = affine_map<(i, j) [s0] -> (((s0 + (i + j) + 5), j)> // expected-error {{expected ')'}}
// -----
#hello_world = affine_map<(i, j) [s0] -> i + s0, j)> // expected-error {{expected '(' at start of affine map range}}
// expected-error @+1 {{expected '(' in affine map range}}
#hello_world = affine_map<(i, j) [s0] -> i + s0, j)>
// -----
#hello_world = affine_map<(i, j) [s0] -> (x)> // expected-error {{use of undeclared identifier}}
@ -47,6 +49,8 @@
#hello_world = affine_map<(i, j) [s0, s1] -> (+i, j)> // expected-error {{missing left operand of binary op}}
// -----
#hello_world = affine_map<(i, j) [s0, s1] -> (i, *j)> // expected-error {{missing left operand of binary op}}
// -----
@ -91,7 +95,8 @@
#hello_world = affine_map<(i, j) [s0, s1] -> (i, i mod (2+i))> // expected-error {{non-affine expression: right operand of mod has to be either a constant or symbolic}}
// -----
#hello_world = affine_map<(i, j) [s0, s1] -> (-1*i j, j)> // expected-error {{expected ',' or ')'}}
// expected-error @+1 {{expected ')' in affine map range}}
#hello_world = affine_map<(i, j) [s0, s1] -> (-1*i j, j)>
// -----
#hello_world = affine_map<(i, j) -> (i, 3*d0 + )> // expected-error {{use of undeclared identifier}}

View File

@ -92,7 +92,8 @@ func @memref_stride_missing_colon_2(memref<42x42xi8, offset: 0, strides [?, ?]>)
// -----
func @memref_stride_invalid_strides(memref<42x42xi8, offset: 0, strides: ()>) // expected-error {{invalid braces-enclosed stride list}}
// expected-error @+1 {{expected '['}}
func @memref_stride_invalid_strides(memref<42x42xi8, offset: 0, strides: ()>)
// -----
@ -633,7 +634,8 @@ func @invalid_bound_map(%N : i32) {
// -----
#set0 = affine_set<(i)[N, M] : )i >= 0)> // expected-error {{expected '(' at start of integer set constraint list}}
// expected-error @+1 {{expected '(' in integer set constraint list}}
#set0 = affine_set<(i)[N, M] : )i >= 0)>
// -----
#set0 = affine_set<(i)[N] : (i >= 0, N - i >= 0)>