Skip to content

Commit

Permalink
dialects: (builtin) verify that the elements of dense satisfy type (#…
Browse files Browse the repository at this point in the history
…3544)

Instead of scary doc string, warn the user at the point of
DenseArrayBase parsing/creation.
  • Loading branch information
superlopuh authored Nov 29, 2024
1 parent eaa9371 commit 9e66e8e
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 36 deletions.
12 changes: 12 additions & 0 deletions tests/dialects/test_builtin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from collections.abc import Sequence

import pytest
Expand Down Expand Up @@ -30,6 +31,7 @@
VectorRankConstraint,
VectorType,
f32,
i8,
i32,
i64,
)
Expand Down Expand Up @@ -296,6 +298,16 @@ def test_dense_as_tuple():
assert ints.as_tuple() == (1, 1, 2, 3, 5, 8)


def test_create_dense_int():
with pytest.raises(
ValueError,
match=re.escape(
"Integer value 99999999 is out of range for type i8 which supports values in the range [-128, 256)"
),
):
DenseArrayBase.create_dense_int(i8, (99999999, 255, 256))


def test_strides():
assert ShapedType.strides_for_shape(()) == ()
assert ShapedType.strides_for_shape((), factor=2) == ()
Expand Down
23 changes: 23 additions & 0 deletions tests/filecheck/dialects/builtin/dense_array_invalid.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: xdsl-opt %s --parsing-diagnostics --split-input-file | filecheck %s --strict-whitespace --match-full-lines

// CHECK:"builtin.module" () {"test" = array<i32: "", 3>} ({
// CHECK-NEXT: ^^
// CHECK-NEXT: Expected integer literal
"builtin.module" () {"test" = array<i32: "", 3>} ({
})

// -----

// CHECK:"builtin.module" () {"test" = array<()->(): 2, 5, 2>} ({
// CHECK-NEXT: ^^^^^^
// CHECK-NEXT: dense array element type must be an integer or floating point type
"builtin.module" () {"test" = array<()->(): 2, 5, 2>} ({
})

// -----

// CHECK:"builtin.module" () {"test" = array<i8: 99999999, 255, 256>} ({
// CHECK-NEXT: ^^^^^^^^
// CHECK-NEXT: Integer value 99999999 is out of range for type i8 which supports values in the range [-128, 256)
"builtin.module" () {"test" = array<i8: 99999999, 255, 256>} ({
})

This file was deleted.

This file was deleted.

32 changes: 16 additions & 16 deletions xdsl/dialects/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,15 @@ def __init__(
def value_range(self) -> tuple[int, int]:
return self.signedness.data.value_range(self.width.data)

def verify_value(self, value: int):
min_value, max_value = self.value_range()

if not (min_value <= value < max_value):
raise VerifyException(
f"Integer value {value} is out of range for type {self} which supports "
f"values in the range [{min_value}, {max_value})"
)

@property
def bitwidth(self) -> int:
return self.width.data
Expand Down Expand Up @@ -493,14 +502,7 @@ def verify(self) -> None:
if isinstance(int_type := self.type, IndexType):
return

min_value, max_value = int_type.value_range()

if not (min_value <= self.value.data < max_value):
raise VerifyException(
f"Integer value {self.value.data} is out of range for "
f"type {self.type} which supports values in the "
f"range [{min_value}, {max_value})"
)
int_type.verify_value(self.value.data)

@staticmethod
def parse_with_type(
Expand Down Expand Up @@ -978,6 +980,12 @@ def create_dense_int(
else:
attr_list = cast(Sequence[IntAttr], data)

try:
for attr in attr_list:
data_type.verify_value(attr.data)
except VerifyException as e:
raise ValueError(str(e))

return DenseArrayBase([data_type, ArrayAttr(attr_list)])

@staticmethod
Expand Down Expand Up @@ -1023,14 +1031,6 @@ def from_list(
raise TypeError(f"Unsupported element type {data_type}")

def as_tuple(self) -> tuple[int, ...] | tuple[float, ...]:
"""
Get the "raw" data out as a tuple. This will not
apply the datatype restrictions that the array element
type would suggest!
e.g. given a dense<i8: 99999999, 255, 256>, as_tuple()
would return 1234567, 255, 256 and not 135, 255, 0 (mod 256)
"""
return tuple(x.data for x in self.data.data)


Expand Down
54 changes: 46 additions & 8 deletions xdsl/parser/attribute_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
convert_u32_to_f32,
convert_u64_to_f64,
)
from xdsl.utils.exceptions import ParseError
from xdsl.utils.exceptions import ParseError, VerifyException
from xdsl.utils.isattr import isattr
from xdsl.utils.lexer import Position, Span, StringLiteral, Token

Expand Down Expand Up @@ -893,14 +893,43 @@ def _parse_builtin_dense_resource_attr(self, _name: Span) -> DenseResourceAttr:
type = self.parse_type()
return DenseResourceAttr.from_params(resource_handle, type)

def _parse_typed_integer(
self,
type: IntegerType,
allow_boolean: bool = True,
allow_negative: bool = True,
context_msg: str = "",
) -> int:
"""
Parse an (possible negative) integer. The integer can
either be decimal or hexadecimal.
Optionally allow parsing of 'true' or 'false' into 1 and 0.
"""

pos = self.pos
res = self.parse_integer(
allow_boolean=allow_boolean,
allow_negative=allow_negative,
context_msg=context_msg,
)

try:
type.verify_value(res)
except VerifyException as e:
self.raise_error(str(e), pos, self.pos)

return res

def _parse_builtin_densearray_attr(self, name: Span) -> DenseArrayBase | None:
self.parse_characters("<", " in dense array")
pos = self.pos
element_type = self.parse_attribute()

if not isinstance(element_type, IntegerType | AnyFloat):
raise ParseError(
name,
"dense array element type must be an " "integer or floating point type",
self.raise_error(
"dense array element type must be an integer or floating point type",
pos,
self.pos,
)

# Empty array
Expand All @@ -909,13 +938,22 @@ def _parse_builtin_densearray_attr(self, name: Span) -> DenseArrayBase | None:

self.parse_characters(":", " in dense array")

values = self.parse_comma_separated_list(
self.Delimiter.NONE, lambda: self.parse_number(allow_boolean=True)
)
if isinstance(element_type, IntegerType):
values = self.parse_comma_separated_list(
self.Delimiter.NONE,
lambda: self._parse_typed_integer(element_type, allow_boolean=True),
)
res = DenseArrayBase.create_dense_int(element_type, values)
else:
values = self.parse_comma_separated_list(
self.Delimiter.NONE,
lambda: self.parse_float(),
)
res = DenseArrayBase.create_dense_float(element_type, values)

self.parse_characters(">", " in dense array")

return DenseArrayBase.from_list(element_type, values)
return res

def _parse_builtin_affine_map(self, _name: Span) -> AffineMapAttr:
self.parse_characters("<", " in affine_map attribute")
Expand Down

0 comments on commit 9e66e8e

Please sign in to comment.