Add AsmParser::parseDecimalInteger. (#96255)

An attribute parser needs to parse lists of possibly negative integers
separated by x in a way which is foiled by parseInteger handling hex
formats and parseIntegerInDimensionList does not allow negatives.

---------

Co-authored-by: Jacques Pienaar <jpienaar@google.com>
This commit is contained in:
quartersdg
2024-07-23 20:12:40 -07:00
committed by GitHub
parent 785d376d12
commit 690dc4eff1
7 changed files with 149 additions and 6 deletions

View File

@@ -714,16 +714,27 @@ public:
return *parseResult;
}
/// Parse a decimal integer value from the stream.
template <typename IntT>
ParseResult parseDecimalInteger(IntT &result) {
auto loc = getCurrentLocation();
OptionalParseResult parseResult = parseOptionalDecimalInteger(result);
if (!parseResult.has_value())
return emitError(loc, "expected decimal integer value");
return *parseResult;
}
/// Parse an optional integer value from the stream.
virtual OptionalParseResult parseOptionalInteger(APInt &result) = 0;
virtual OptionalParseResult parseOptionalDecimalInteger(APInt &result) = 0;
template <typename IntT>
OptionalParseResult parseOptionalInteger(IntT &result) {
private:
template <typename IntT, typename ParseFn>
OptionalParseResult parseOptionalIntegerAndCheck(IntT &result,
ParseFn &&parseFn) {
auto loc = getCurrentLocation();
// Parse the unsigned variant.
APInt uintResult;
OptionalParseResult parseResult = parseOptionalInteger(uintResult);
OptionalParseResult parseResult = parseFn(uintResult);
if (!parseResult.has_value() || failed(*parseResult))
return parseResult;
@@ -737,6 +748,20 @@ public:
return success();
}
public:
template <typename IntT>
OptionalParseResult parseOptionalInteger(IntT &result) {
return parseOptionalIntegerAndCheck(
result, [&](APInt &result) { return parseOptionalInteger(result); });
}
template <typename IntT>
OptionalParseResult parseOptionalDecimalInteger(IntT &result) {
return parseOptionalIntegerAndCheck(result, [&](APInt &result) {
return parseOptionalDecimalInteger(result);
});
}
/// These are the supported delimiters around operand lists and region
/// argument lists, used by parseOperandList.
enum class Delimiter {

View File

@@ -322,6 +322,11 @@ public:
return parser.parseOptionalInteger(result);
}
/// Parse an optional integer value from the stream.
OptionalParseResult parseOptionalDecimalInteger(APInt &result) override {
return parser.parseOptionalDecimalInteger(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.

View File

@@ -41,6 +41,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Alignment.h"
@@ -307,6 +308,45 @@ OptionalParseResult Parser::parseOptionalInteger(APInt &result) {
return success();
}
/// Parse an optional integer value only in decimal format from the stream.
OptionalParseResult Parser::parseOptionalDecimalInteger(APInt &result) {
Token curToken = getToken();
if (curToken.isNot(Token::integer, Token::minus)) {
return std::nullopt;
}
bool negative = consumeIf(Token::minus);
Token curTok = getToken();
if (parseToken(Token::integer, "expected integer value")) {
return failure();
}
StringRef spelling = curTok.getSpelling();
// If the integer is in hexadecimal return only the 0. The lexer has already
// moved past the entire hexidecimal encoded integer so we reset the lex
// pointer to just past the 0 we actualy want to consume.
if (spelling[0] == '0' && spelling.size() > 1 &&
llvm::toLower(spelling[1]) == 'x') {
result = 0;
state.lex.resetPointer(spelling.data() + 1);
consumeToken();
return success();
}
if (spelling.getAsInteger(10, result))
return emitError(curTok.getLoc(), "integer value too large");
// Make sure we have a zero at the top so we return the right signedness.
if (result.isNegative())
result = result.zext(result.getBitWidth() + 1);
// Process the negative sign if present.
if (negative)
result.negate();
return success();
}
/// Parse a floating point value from an integer literal token.
ParseResult Parser::parseFloatFromIntegerLiteral(
std::optional<APFloat> &result, const Token &tok, bool isNegative,

View File

@@ -144,6 +144,9 @@ public:
/// Parse an optional integer value from the stream.
OptionalParseResult parseOptionalInteger(APInt &result);
/// Parse an optional integer value only in decimal format from the stream.
OptionalParseResult parseOptionalDecimalInteger(APInt &result);
/// Parse a floating point value from an integer literal token.
ParseResult parseFloatFromIntegerLiteral(std::optional<APFloat> &result,
const Token &tok, bool isNegative,

View File

@@ -81,6 +81,15 @@ def AttrWithTrait : Test_Attr<"AttrWithTrait", [TestAttrTrait]> {
let mnemonic = "attr_with_trait";
}
// An attribute of a list of decimal formatted integers in similar format to shapes.
def TestDecimalShapeAttr : Test_Attr<"TestDecimalShape"> {
let mnemonic = "decimal_shape";
let parameters = (ins ArrayRefParameter<"int64_t">:$shape);
let hasCustomAssemblyFormat = 1;
}
// Test support for ElementsAttrInterface.
def TestI64ElementsAttr : Test_Attr<"TestI64Elements", [ElementsAttrInterface]> {
let mnemonic = "i64_elements";

View File

@@ -13,9 +13,12 @@
#include "TestAttributes.h"
#include "TestDialect.h"
#include "TestTypes.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/DialectImplementation.h"
#include "mlir/IR/ExtensibleDialect.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Types.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/Hashing.h"
@@ -63,6 +66,39 @@ void CompoundAAttr::print(AsmPrinter &printer) const {
// CompoundAAttr
//===----------------------------------------------------------------------===//
Attribute TestDecimalShapeAttr::parse(AsmParser &parser, Type type) {
if (parser.parseLess()){
return Attribute();
}
SmallVector<int64_t> shape;
if (parser.parseOptionalGreater()) {
auto parseDecimal = [&]() {
shape.emplace_back();
auto parseResult = parser.parseOptionalDecimalInteger(shape.back());
if (!parseResult.has_value() || failed(*parseResult)) {
parser.emitError(parser.getCurrentLocation()) << "expected an integer";
return failure();
}
return success();
};
if (failed(parseDecimal())) {
return Attribute();
}
while (failed(parser.parseOptionalGreater())) {
if (failed(parser.parseXInDimensionList()) || failed(parseDecimal())) {
return Attribute();
}
}
}
return get(parser.getContext(), shape);
}
void TestDecimalShapeAttr::print(AsmPrinter &printer) const {
printer << "<";
llvm::interleave(getShape(), printer, "x");
printer << ">";
}
Attribute TestI64ElementsAttr::parse(AsmParser &parser, Type type) {
SmallVector<uint64_t> elements;
if (parser.parseLess() || parser.parseLSquare())

View File

@@ -1,4 +1,4 @@
// RUN: mlir-opt %s | mlir-opt -verify-diagnostics | FileCheck %s
// RUN: mlir-opt %s -split-input-file -verify-diagnostics | FileCheck %s
// CHECK-LABEL: func private @compoundA()
// CHECK-SAME: #test.cmpnd_a<1, !test.smpla, [5, 6]>
@@ -19,3 +19,28 @@ func.func private @qualifiedAttr() attributes {foo = #test.cmpnd_nested_outer_qu
func.func private @overriddenAttr() attributes {
foo = #test.override_builder<5>
}
// CHECK-LABEL: @decimalIntegerShapeEmpty
// CHECK-SAME: foo = #test.decimal_shape<>
func.func private @decimalIntegerShapeEmpty() attributes {
foo = #test.decimal_shape<>
}
// CHECK-LABEL: @decimalIntegerShape
// CHECK-SAME: foo = #test.decimal_shape<5>
func.func private @decimalIntegerShape() attributes {
foo = #test.decimal_shape<5>
}
// CHECK-LABEL: @decimalIntegerShapeMultiple
// CHECK-SAME: foo = #test.decimal_shape<0x3x7>
func.func private @decimalIntegerShapeMultiple() attributes {
foo = #test.decimal_shape<0x3x7>
}
// -----
func.func private @hexdecimalInteger() attributes {
// expected-error @below {{expected an integer}}
sdg = #test.decimal_shape<1x0xb>
}