// -----
+// CHECK-LABEL: @succeededOilistTrivial
+func @succeededOilistTrivial() {
+ // CHECK: test.oilist_with_keywords_only keyword
+ test.oilist_with_keywords_only keyword
+ // CHECK: test.oilist_with_keywords_only otherKeyword
+ test.oilist_with_keywords_only otherKeyword
+ // CHECK: test.oilist_with_keywords_only keyword otherKeyword
+ test.oilist_with_keywords_only keyword otherKeyword
+ // CHECK: test.oilist_with_keywords_only keyword otherKeyword
+ test.oilist_with_keywords_only otherKeyword keyword
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @succeededOilistSimple
+func @succeededOilistSimple(%arg0 : i32, %arg1 : i32, %arg2 : i32) {
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32
+ test.oilist_with_simple_args keyword %arg0 : i32
+ // CHECK: test.oilist_with_simple_args otherKeyword %{{.*}} : i32
+ test.oilist_with_simple_args otherKeyword %arg0 : i32
+ // CHECK: test.oilist_with_simple_args thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args thirdKeyword %arg0 : i32
+
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32 otherKeyword %{{.*}} : i32
+ test.oilist_with_simple_args keyword %arg0 : i32 otherKeyword %arg1 : i32
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32 thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args keyword %arg0 : i32 thirdKeyword %arg1 : i32
+ // CHECK: test.oilist_with_simple_args otherKeyword %{{.*}} : i32 thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args thirdKeyword %arg0 : i32 otherKeyword %arg1 : i32
+
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32 otherKeyword %{{.*}} : i32 thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args keyword %arg0 : i32 otherKeyword %arg1 : i32 thirdKeyword %arg2 : i32
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32 otherKeyword %{{.*}} : i32 thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args otherKeyword %arg0 : i32 keyword %arg1 : i32 thirdKeyword %arg2 : i32
+ // CHECK: test.oilist_with_simple_args keyword %{{.*}} : i32 otherKeyword %{{.*}} : i32 thirdKeyword %{{.*}} : i32
+ test.oilist_with_simple_args otherKeyword %arg0 : i32 thirdKeyword %arg1 : i32 keyword %arg2 : i32
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @succeededOilistVariadic
+// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32)
+func @succeededOilistVariadic(%arg0: i32, %arg1: i32, %arg2: i32) {
+ // CHECK: test.oilist_variadic_with_parens keyword(%[[ARG0]], %[[ARG1]] : i32, i32)
+ test.oilist_variadic_with_parens keyword (%arg0, %arg1 : i32, i32)
+ // CHECK: test.oilist_variadic_with_parens keyword(%[[ARG0]], %[[ARG1]] : i32, i32) otherKeyword(%[[ARG2]], %[[ARG1]] : i32, i32)
+ test.oilist_variadic_with_parens otherKeyword (%arg2, %arg1 : i32, i32) keyword (%arg0, %arg1 : i32, i32)
+ // CHECK: test.oilist_variadic_with_parens keyword(%[[ARG0]], %[[ARG1]] : i32, i32) otherKeyword(%[[ARG0]], %[[ARG1]] : i32, i32) thirdKeyword(%[[ARG2]], %[[ARG0]], %[[ARG1]] : i32, i32, i32)
+ test.oilist_variadic_with_parens thirdKeyword (%arg2, %arg0, %arg1 : i32, i32, i32) keyword (%arg0, %arg1 : i32, i32) otherKeyword (%arg0, %arg1 : i32, i32)
+ return
+}
+
+// -----
+// CHECK-LABEL: succeededOilistCustom
+// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32)
+func @succeededOilistCustom(%arg0: i32, %arg1: i32, %arg2: i32) {
+ // CHECK: test.oilist_custom private(%[[ARG0]], %[[ARG1]] : i32, i32)
+ test.oilist_custom private (%arg0, %arg1 : i32, i32)
+ // CHECK: test.oilist_custom private(%[[ARG0]], %[[ARG1]] : i32, i32) nowait
+ test.oilist_custom private (%arg0, %arg1 : i32, i32) nowait
+ // CHECK: test.oilist_custom private(%arg0, %arg1 : i32, i32) nowait reduction (%arg1)
+ test.oilist_custom nowait reduction (%arg1) private (%arg0, %arg1 : i32, i32)
+ return
+}
+
+// -----
+
func @failedHasDominanceScopeOutsideDominanceFreeScope() -> () {
"test.ssacfg_region"() ({
test.graph_region {
}]>;
//===----------------------------------------------------------------------===//
+// OIList Element
+//===----------------------------------------------------------------------===//
+
+// CHECK: error: format ambiguity because bar is used in two adjacent oilist elements.
+def OIListAdjacentOIList : TestFormat_Op<[{
+ oilist ( `foo` | `bar` ) oilist ( `bar` | `buzz` ) attr-dict
+}]>;
+// CHECK: error: expected literal, but got ')'
+def OIListErrorExpectedLiteral : TestFormat_Op<[{
+ oilist( `keyword` | ) attr-dict
+}]>;
+// CHECK: error: expected literal, but got ')'
+def OIListErrorExpectedEmpty : TestFormat_Op<[{
+ oilist() attr-dict
+}]>;
+// CHECK: error: expected literal, but got '$arg0'
+def OIListErrorNoLiteral : TestFormat_Op<[{
+ oilist( $arg0 `:` type($arg0) | $arg1 `:` type($arg1) ) attr-dict
+}], [AttrSizedOperandSegments]>, Arguments<(ins Optional<AnyType>:$arg0, Optional<AnyType>:$arg1)>;
+// CHECK: error: format ambiguity because foo is used both in oilist element and the adjacent literal.
+def OIListLiteralAmbiguity : TestFormat_Op<[{
+ oilist( `foo` | `bar` ) `foo` attr-dict
+}]>;
+// CHECK: error: expected '(' before oilist argument list
+def OIListStartingToken : TestFormat_Op<[{
+ oilist `wrong` attr-dict
+}]>;
+
+// CHECK-NOT: error
+def OIListTrivial : TestFormat_Op<[{
+ oilist(`keyword` `(` `)` | `otherkeyword` `(` `)`) attr-dict
+}]>;
+def OIListSimple : TestFormat_Op<[{
+ oilist( `keyword` $arg0 `:` type($arg0)
+ | `otherkeyword` $arg1 `:` type($arg1)
+ | `thirdkeyword` $arg2 `:` type($arg2) )
+ attr-dict
+}], [AttrSizedOperandSegments]>, Arguments<(ins Optional<AnyType>:$arg0, Optional<AnyType>:$arg1, Optional<AnyType>:$arg2)>;
+def OIListVariadic : TestFormat_Op<[{
+ oilist( `keyword` `(` $args0 `:` type($args0) `)`
+ | `otherkeyword` `(` $args1 `:` type($args1) `)`
+ | `thirdkeyword` `(` $args2 `:` type($args2) `)`)
+ attr-dict
+}], [AttrSizedOperandSegments]>, Arguments<(ins Variadic<AnyType>:$args0, Variadic<AnyType>:$args1, Variadic<AnyType>:$args2)>;
+def OIListCustom : TestFormat_Op<[{
+ oilist( `private` `(` $arg0 `:` type($arg0) `)`
+ | `nowait`
+ | `reduction` custom<ReductionClause>($arg1, type($arg1))) attr-dict
+}], [AttrSizedOperandSegments]>, Arguments<(ins Optional<AnyType>:$arg0, Optional<AnyType>:$arg1)>;
+
+//===----------------------------------------------------------------------===//
// Optional Groups
//===----------------------------------------------------------------------===//
bool shouldBeQualifiedFlag = false;
};
+
+/// This class represents a group of order-independent optional clauses. Each
+/// clause starts with a literal element and has a coressponding parsing
+/// element. A parsing element is a continous sequence of format elements.
+/// Each clause can appear 0 or 1 time.
+class OIListElement : public DirectiveElementBase<DirectiveElement::OIList> {
+public:
+ OIListElement(std::vector<FormatElement *> &&literalElements,
+ std::vector<std::vector<FormatElement *>> &&parsingElements)
+ : literalElements(std::move(literalElements)),
+ parsingElements(std::move(parsingElements)) {}
+
+ /// Returns a range to iterate over the LiteralElements.
+ auto getLiteralElements() const {
+ // The use of std::function is unfortunate but necessary here. Lambda
+ // functions cannot be copied but std::function can be copied. This copy
+ // constructor is used in llvm::zip.
+ std::function<LiteralElement *(FormatElement * el)>
+ literalElementCastConverter =
+ [](FormatElement *el) { return cast<LiteralElement>(el); };
+ return llvm::map_range(literalElements, literalElementCastConverter);
+ }
+
+ /// Returns a range to iterate over the parsing elements corresponding to the
+ /// clauses.
+ ArrayRef<std::vector<FormatElement *>> getParsingElements() const {
+ return parsingElements;
+ }
+
+ /// Returns a range to iterate over tuples of parsing and literal elements.
+ auto getClauses() const {
+ return llvm::zip(getLiteralElements(), getParsingElements());
+ }
+
+private:
+ /// A vector of `LiteralElement` objects. Each element stores the keyword
+ /// for one case of oilist element. For example, an oilist element along with
+ /// the `literalElements` vector:
+ /// ```
+ /// oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]
+ /// literalElements = { `keyword`, `otherKeyword` }
+ /// ```
+ std::vector<FormatElement *> literalElements;
+
+ /// A vector of valid declarative assembly format vectors. Each object in
+ /// parsing elements is a vector of elements in assembly format syntax.
+ /// For example, an oilist element along with the parsingElements vector:
+ /// ```
+ /// oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]
+ /// parsingElements = {
+ /// { `=`, `(`, $arg0, `)` },
+ /// { `<`, $arg1, `>` }
+ /// }
+ /// ```
+ std::vector<std::vector<FormatElement *>> parsingElements;
+};
} // namespace
//===----------------------------------------------------------------------===//
return ::mlir::failure();
)";
+/// The code snippet used to generate a parser for OIList
+///
+/// {0}: literal keyword corresponding to a case for oilist
+const char *oilistParserCode = R"(
+ if ({0}Clause) {
+ return parser.emitError(parser.getNameLoc())
+ << "`{0}` clause can appear at most once in the expansion of the "
+ "oilist directive";
+ }
+ {0}Clause = true;
+ result.addAttribute("{0}", UnitAttr::get(parser.getContext()));
+)";
+
namespace {
/// The type of length for a given parse argument.
enum class ArgumentLengthKind {
for (FormatElement *childElement : optional->getElseElements())
genElementParserStorage(childElement, op, body);
+ } else if (auto *oilist = dyn_cast<OIListElement>(element)) {
+ for (ArrayRef<FormatElement *> pelement : oilist->getParsingElements())
+ for (FormatElement *element : pelement)
+ genElementParserStorage(element, op, body);
+
} else if (auto *custom = dyn_cast<CustomDirective>(element)) {
for (FormatElement *paramElement : custom->getArguments())
genElementParserStorage(paramElement, op, body);
}
body << "\n";
+ /// OIList Directive
+ } else if (OIListElement *oilist = dyn_cast<OIListElement>(element)) {
+ for (LiteralElement *le : oilist->getLiteralElements())
+ body << " bool " << le->getSpelling() << "Clause = false;\n";
+
+ // Generate the parsing loop
+ body << " while(true) {\n";
+ for (auto clause : oilist->getClauses()) {
+ LiteralElement *lelement = std::get<0>(clause);
+ ArrayRef<FormatElement *> pelement = std::get<1>(clause);
+ body << "if (succeeded(parser.parseOptional";
+ genLiteralParser(lelement->getSpelling(), body);
+ body << ")) {\n";
+ StringRef attrName = lelement->getSpelling();
+ body << formatv(oilistParserCode, attrName);
+ inferredAttributes.insert(attrName);
+ for (FormatElement *el : pelement)
+ genElementParser(el, body, attrTypeCtx);
+ body << " } else ";
+ }
+ body << " {\n";
+ body << " break;\n";
+ body << " }\n";
+ body << "}\n";
+
/// Literals.
} else if (LiteralElement *literal = dyn_cast<LiteralElement>(element)) {
body << " if (parser.parse";
return;
}
+ // Emit the OIList
+ if (auto *oilist = dyn_cast<OIListElement>(element)) {
+ genLiteralPrinter(" ", body, shouldEmitSpace, lastWasPunctuation);
+ for (auto clause : oilist->getClauses()) {
+ LiteralElement *lelement = std::get<0>(clause);
+ ArrayRef<FormatElement *> pelement = std::get<1>(clause);
+
+ body << " if ((*this)->hasAttrOfType<UnitAttr>(\""
+ << lelement->getSpelling() << "\")) {\n";
+ genLiteralPrinter(lelement->getSpelling(), body, shouldEmitSpace,
+ lastWasPunctuation);
+ for (FormatElement *element : pelement) {
+ genElementPrinter(element, body, op, shouldEmitSpace,
+ lastWasPunctuation);
+ }
+ body << " }\n";
+ }
+ return;
+ }
+
// Emit the attribute dictionary.
if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
genAttrDictPrinter(*this, op, body, attrDict->isWithKeyword());
/// Verify the state of operation successors within the format.
LogicalResult verifySuccessors(SMLoc loc);
+ LogicalResult verifyOIListElements(SMLoc loc,
+ ArrayRef<FormatElement *> elements);
+
/// Given the values of an `AllTypesMatch` trait, check for inferable type
/// resolution.
void handleAllTypesMatchConstraint(
bool withKeyword);
FailureOr<FormatElement *> parseFunctionalTypeDirective(SMLoc loc,
Context context);
+ FailureOr<FormatElement *> parseOIListDirective(SMLoc loc, Context context);
+ LogicalResult verifyOIListParsingElement(FormatElement *element, SMLoc loc);
FailureOr<FormatElement *> parseOperandsDirective(SMLoc loc, Context context);
FailureOr<FormatElement *> parseQualifiedDirective(SMLoc loc,
Context context);
if (failed(verifyAttributes(loc, elements)) ||
failed(verifyResults(loc, variableTyResolver)) ||
failed(verifyOperands(loc, variableTyResolver)) ||
- failed(verifyRegions(loc)) || failed(verifySuccessors(loc)))
+ failed(verifyRegions(loc)) || failed(verifySuccessors(loc)) ||
+ failed(verifyOIListElements(loc, elements)))
return failure();
// Collect the set of used attributes in the format.
return success();
}
+LogicalResult
+OpFormatParser::verifyOIListElements(SMLoc loc,
+ ArrayRef<FormatElement *> elements) {
+ // Check that all of the successors are within the format.
+ SmallVector<StringRef> prohibitedLiterals;
+ for (FormatElement *it : elements) {
+ if (auto *oilist = dyn_cast<OIListElement>(it)) {
+ if (!prohibitedLiterals.empty()) {
+ // We just saw an oilist element in last iteration. Literals should not
+ // match.
+ for (LiteralElement *literal : oilist->getLiteralElements()) {
+ if (find(prohibitedLiterals, literal->getSpelling()) !=
+ prohibitedLiterals.end()) {
+ return emitError(
+ loc, "format ambiguity because " + literal->getSpelling() +
+ " is used in two adjacent oilist elements.");
+ }
+ }
+ }
+ for (LiteralElement *literal : oilist->getLiteralElements())
+ prohibitedLiterals.push_back(literal->getSpelling());
+ } else if (auto *literal = dyn_cast<LiteralElement>(it)) {
+ if (find(prohibitedLiterals, literal->getSpelling()) !=
+ prohibitedLiterals.end()) {
+ return emitError(
+ loc,
+ "format ambiguity because " + literal->getSpelling() +
+ " is used both in oilist element and the adjacent literal.");
+ }
+ prohibitedLiterals.clear();
+ } else {
+ prohibitedLiterals.clear();
+ }
+ }
+ return success();
+}
+
void OpFormatParser::handleAllTypesMatchConstraint(
ArrayRef<StringRef> values,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
return parseReferenceDirective(loc, ctx);
case FormatToken::kw_type:
return parseTypeDirective(loc, ctx);
+ case FormatToken::kw_oilist:
+ return parseOIListDirective(loc, ctx);
default:
return emitError(loc, "unsupported directive kind");
return create<SuccessorsDirective>();
}
+FailureOr<FormatElement *>
+OpFormatParser::parseOIListDirective(SMLoc loc, Context context) {
+ if (failed(parseToken(FormatToken::l_paren,
+ "expected '(' before oilist argument list")))
+ return failure();
+ std::vector<FormatElement *> literalElements;
+ std::vector<std::vector<FormatElement *>> parsingElements;
+ do {
+ FailureOr<FormatElement *> lelement = parseLiteral(context);
+ if (failed(lelement))
+ return failure();
+ literalElements.push_back(*lelement);
+ parsingElements.push_back(std::vector<FormatElement *>());
+ std::vector<FormatElement *> &currParsingElements = parsingElements.back();
+ while (peekToken().getKind() != FormatToken::pipe &&
+ peekToken().getKind() != FormatToken::r_paren) {
+ FailureOr<FormatElement *> pelement = parseElement(context);
+ if (failed(pelement) ||
+ failed(verifyOIListParsingElement(*pelement, loc)))
+ return failure();
+ currParsingElements.push_back(*pelement);
+ }
+ if (peekToken().getKind() == FormatToken::pipe) {
+ consumeToken();
+ continue;
+ }
+ if (peekToken().getKind() == FormatToken::r_paren) {
+ consumeToken();
+ break;
+ }
+ } while (true);
+
+ return create<OIListElement>(std::move(literalElements),
+ std::move(parsingElements));
+}
+
+LogicalResult OpFormatParser::verifyOIListParsingElement(FormatElement *element,
+ SMLoc loc) {
+ return TypeSwitch<FormatElement *, LogicalResult>(element)
+ // Only optional attributes can be within an oilist parsing group.
+ .Case([&](AttributeVariable *attrEle) {
+ if (!attrEle->getVar()->attr.isOptional())
+ return emitError(loc, "only optional attributes can be used to "
+ "in an oilist parsing group");
+ return success();
+ })
+ // Only optional-like(i.e. variadic) operands can be within an oilist
+ // parsing group.
+ .Case([&](OperandVariable *ele) {
+ if (!ele->getVar()->isVariableLength())
+ return emitError(loc, "only variable length operands can be "
+ "used within an oilist parsing group");
+ return success();
+ })
+ // Only optional-like(i.e. variadic) results can be within an oilist
+ // parsing group.
+ .Case([&](ResultVariable *ele) {
+ if (!ele->getVar()->isVariableLength())
+ return emitError(loc, "only variable length results can be "
+ "used within an oilist parsing group");
+ return success();
+ })
+ .Case([&](RegionVariable *) {
+ // TODO: When ODS has proper support for marking "optional" regions, add
+ // a check here.
+ return success();
+ })
+ .Case([&](TypeDirective *ele) {
+ return verifyOIListParsingElement(ele->getArg(), loc);
+ })
+ .Case([&](FunctionalTypeDirective *ele) {
+ if (failed(verifyOIListParsingElement(ele->getInputs(), loc)))
+ return failure();
+ return verifyOIListParsingElement(ele->getResults(), loc);
+ })
+ // Literals, whitespace, and custom directives may be used.
+ .Case<LiteralElement, WhitespaceElement, CustomDirective,
+ FunctionalTypeDirective, OptionalElement>(
+ [&](FormatElement *) { return success(); })
+ .Default([&](FormatElement *) {
+ return emitError(loc, "only literals, types, and variables can be "
+ "used within an oilist group");
+ });
+}
+
FailureOr<FormatElement *> OpFormatParser::parseTypeDirective(SMLoc loc,
Context context) {
if (context == TypeDirectiveContext)