Clean up and improve the parser handling of basic block labels, now that we

have a designator.  This improves diagnostics and merges handling between CFG
and ML functions more.  This also eliminates hard coded parser knowledge of
terminator keywords, allowing dialects to define their own terminators.

PiperOrigin-RevId: 227239398
This commit is contained in:
Chris Lattner 2018-12-29 13:36:59 -08:00 committed by jpienaar
parent 37579ae8c4
commit aaa1d77e96
4 changed files with 76 additions and 44 deletions

View File

@ -1888,7 +1888,7 @@ public:
parseOptionalBlockArgList(SmallVectorImpl<BlockArgument *> &results,
Block *owner);
ParseResult parseBlock();
ParseResult parseBlock(Block *blockToUse);
ParseResult parseBlockBody(Block *block);
/// After the function is finished parsing, this function checks to see if
@ -1940,7 +1940,7 @@ public:
// Define the block with the specified name. Returns the Block* or
// nullptr in the case of redefinition.
Block *defineBlockNamed(StringRef name, SMLoc loc);
Block *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
// Operations
ParseResult parseOperation();
@ -1992,18 +1992,24 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
if (getToken().is(Token::r_brace))
return emitError("function must have a body");
// If we had named arguments, then just parse the first block into the
// block we have already created.
// If we had named arguments, then we don't allow a block name.
if (hadNamedArguments) {
if (parseBlockBody(&function->front()))
return ParseFailure;
if (getToken().is(Token::caret_identifier))
return emitError("invalid block name in function with named arguments");
}
// The first block is already created and should be filled in.
auto firstBlock = &function->front();
// Parse the remaining list of blocks.
while (!consumeIf(Token::r_brace))
if (parseBlock())
while (!consumeIf(Token::r_brace)) {
if (parseBlock(firstBlock))
return ParseFailure;
// Create the second and subsequent block.
firstBlock = nullptr;
}
// Verify that all referenced blocks were defined.
if (!forwardRef.empty()) {
SmallVector<std::pair<const char *, Block *>, 4> errors;
@ -2019,16 +2025,6 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
return ParseFailure;
}
// Now that the function body has been fully parsed we check the invariants
// of any branching terminators.
if (function->isCFG()) {
for (auto &block : *function) {
auto *term = block.getTerminator();
auto *abstractOp = term->getAbstractOperation();
if (term->getNumSuccessors() != 0 && abstractOp)
abstractOp->verifyInvariants(term);
}
}
return finalizeFunction(braceLoc);
}
@ -2039,13 +2035,22 @@ ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
/// block-id ::= caret-id
/// block-arg-list ::= `(` ssa-id-and-type-list? `)`
///
ParseResult FunctionParser::parseBlock() {
ParseResult FunctionParser::parseBlock(Block *blockToUse) {
Block *block = blockToUse;
// The first block for a function is already created.
if (block) {
// The name for a first block is optional.
if (getToken().isNot(Token::caret_identifier))
return parseBlockBody(block);
}
SMLoc nameLoc = getToken().getLoc();
auto name = getTokenSpelling();
if (parseToken(Token::caret_identifier, "expected block name"))
return ParseFailure;
auto *block = defineBlockNamed(name, nameLoc);
block = defineBlockNamed(name, nameLoc, block);
// Fail if redefinition.
if (!block)
@ -2072,8 +2077,7 @@ ParseResult FunctionParser::parseBlockBody(Block *block) {
builder.setInsertionPointToEnd(block);
// Parse the list of operations that make up the body of the block.
while (getToken().isNot(Token::kw_return, Token::kw_br, Token::kw_cond_br,
Token::r_brace)) {
while (getToken().isNot(Token::caret_identifier, Token::r_brace)) {
switch (getToken().getKind()) {
default:
if (parseOperation())
@ -2090,12 +2094,6 @@ ParseResult FunctionParser::parseBlockBody(Block *block) {
}
}
// TODO: Merge terminator parsing into the loop above?
if (getToken().isNot(Token::r_brace)) {
// Parse the terminator operation.
if (parseOperation())
return ParseFailure;
}
return ParseSuccess;
}
@ -2335,10 +2333,14 @@ Block *FunctionParser::getBlockNamed(StringRef name, SMLoc loc) {
/// Define the block with the specified name. Returns the Block* or nullptr in
/// the case of redefinition.
Block *FunctionParser::defineBlockNamed(StringRef name, SMLoc loc) {
Block *FunctionParser::defineBlockNamed(StringRef name, SMLoc loc,
Block *existing) {
auto &blockAndLoc = blocksByName[name];
if (!blockAndLoc.first) {
blockAndLoc.first = builder.createBlock();
// If the caller provided a block, use it. Otherwise create a new one.
if (!existing)
existing = builder.createBlock();
blockAndLoc.first = existing;
blockAndLoc.second = loc;
return blockAndLoc.first;
}
@ -2388,10 +2390,28 @@ ParseResult FunctionParser::parseOptionalBlockArgList(
if (getToken().is(Token::r_brace))
return ParseSuccess;
// If the block already has arguments, then we're handling the entry block.
// Parse and register the names for the arguments, but do not add them.
bool definingExistingArgs = owner->getNumArguments() != 0;
unsigned nextArgument = 0;
return parseCommaSeparatedList([&]() -> ParseResult {
auto type = parseSSADefOrUseAndType<Type>(
[&](SSAUseInfo useInfo, Type type) -> Type {
BlockArgument *arg = owner->addArgument(type);
BlockArgument *arg;
if (!definingExistingArgs) {
arg = owner->addArgument(type);
} else if (nextArgument >= owner->getNumArguments()) {
emitError("too many arguments specified in argument list");
return {};
} else {
arg = owner->getArgument(nextArgument++);
if (arg->getType() != type) {
emitError("argument and block argument type mismatch");
return {};
}
}
if (addDefinition(useInfo, arg))
return {};
return type;
@ -2842,7 +2862,7 @@ ParseResult FunctionParser::parseForInst() {
// MLIR contains for instruction with those nested instructions that have been
// successfully parsed.
if (parseToken(Token::l_brace, "expected '{' before instruction list") ||
parseBlockBody(forInst->getBody()) ||
parseBlock(forInst->getBody()) ||
parseToken(Token::r_brace, "expected '}' after instruction list"))
return ParseFailure;
@ -3093,7 +3113,7 @@ ParseResult FunctionParser::parseIfInst() {
// the if instruction with the portion of the body that has been
// successfully parsed.
if (parseToken(Token::l_brace, "expected '{' before instruction list") ||
parseBlockBody(thenClause) ||
parseBlock(thenClause) ||
parseToken(Token::r_brace, "expected '}' after instruction list"))
return ParseFailure;
@ -3116,7 +3136,7 @@ ParseResult FunctionParser::parseElseClause(Block *elseClause) {
}
if (parseToken(Token::l_brace, "expected '{' before instruction list") ||
parseBlockBody(elseClause) ||
parseBlock(elseClause) ||
parseToken(Token::r_brace, "expected '}' after instruction list"))
return ParseFailure;
return ParseSuccess;
@ -3316,11 +3336,11 @@ ParseResult ModuleParser::parseFunc(Function::Kind kind) {
// Create the parser.
auto parser = FunctionParser(getState(), function);
bool hadNamedArguments = !argNames.empty() || function->isML();
bool hadNamedArguments = !argNames.empty();
// CFG functions don't auto-create a block, so create one now.
// TODO(clattner): FIX THIS.
if (hadNamedArguments && kind == Function::Kind::CFGFunc) {
if (kind == Function::Kind::CFGFunc) {
auto *entry = new Block();
function->push_back(entry);
entry->addArguments(type.getInputs());

View File

@ -88,10 +88,8 @@ TOK_OPERATOR(star, "*")
// Keywords. These turn "foo" into Token::kw_foo enums.
TOK_KEYWORD(attributes)
TOK_KEYWORD(bf16)
TOK_KEYWORD(br)
TOK_KEYWORD(ceildiv)
TOK_KEYWORD(cfgfunc)
TOK_KEYWORD(cond_br)
TOK_KEYWORD(dense)
TOK_KEYWORD(else)
TOK_KEYWORD(splat)
@ -110,7 +108,6 @@ TOK_KEYWORD(min)
TOK_KEYWORD(mlfunc)
TOK_KEYWORD(mod)
TOK_KEYWORD(opaque)
TOK_KEYWORD(return)
TOK_KEYWORD(size)
TOK_KEYWORD(step)
TOK_KEYWORD(tensor)

View File

@ -102,11 +102,11 @@ cfgfunc @block_redef() {
// -----
cfgfunc @no_terminator() {
cfgfunc @no_terminator() { // expected-error {{block with no terminator}}
^bb40:
return
^bb41:
^bb42: // expected-error {{expected operation name in quotes}}
^bb42:
return
}
@ -339,7 +339,7 @@ cfgfunc @undef() {
mlfunc @missing_rbrace() {
return
mlfunc @d() {return} // expected-error {{expected block name}}
mlfunc @d() {return} // expected-error {{custom op 'mlfunc' is unknown}}
// -----
@ -370,7 +370,8 @@ cfgfunc @argError() {
// -----
cfgfunc @bbargMismatch(i32, f32) { // expected-error {{first block of function must have 2 arguments to match function signature}}
cfgfunc @bbargMismatch(i32, f32) {
// expected-error @+1 {{argument and block argument type mismatch}}
^bb42(%0: f32):
return
}
@ -745,7 +746,7 @@ cfgfunc @elementsattr_malformed_opaque1() -> () {
// -----
cfgfunc @redundant_signature(%a : i32) -> () {
^bb0(%b : i32): // expected-error {{expected operation name in quotes}}
^bb0(%b : i32): // expected-error {{invalid block name in function with named arguments}}
return
}

View File

@ -725,3 +725,17 @@ cfgfunc @sparsevectorattr() -> () {
"foof320"(){bar: sparse<vector<0 x f32>, [], []>} : () -> ()
return
}
// CHECK-LABEL: mlfunc @loops_with_blockids() {
mlfunc @loops_with_blockids() {
^block0:
for %i = 1 to 100 step 2 {
^block1:
for %j = 1 to 200 {
^block2:
}
}
return
}