Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signedness semantics for integer types #201

Merged
merged 5 commits into from Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 21 additions & 10 deletions tests/filecheck/mlir-conversion/builtin_attrs.mlir
Expand Up @@ -16,6 +16,19 @@

// CHECK: (i32, i64, i1)

"func.func"() ({
^bb0(%arg0: si32, %arg1: si64, %arg2: si1):
"func.return"() : () -> ()
}) {function_type = (si32, si64, si1) -> (), sym_name = "signed_int_type"} : () -> ()

// CHECK: (si32, si64, si1)

"func.func"() ({
^bb0(%arg0: ui32, %arg1: ui64, %arg2: si1):
"func.return"() : () -> ()
}) {function_type = (ui32, ui64, ui1) -> (), sym_name = "unsigned_int_type"} : () -> ()

// CHECK: (ui32, ui64, ui1)

"func.func"() ({
^bb0(%arg0: f16, %arg1: f32, %arg2: f64):
Expand All @@ -31,19 +44,19 @@

"func.func"() ({}) {function_type = () -> (), value = true, sym_name = "true_attr"} : () -> ()

// CHECK: value = true
// CHECK: "value" = true
webmiche marked this conversation as resolved.
Show resolved Hide resolved

"func.func"() ({}) {function_type = () -> (), value = 1 : i1, sym_name = "true_explicit_attr"} : () -> ()

// CHECK: value = true
// CHECK: "value" = true

"func.func"() ({}) {function_type = () -> (), value = false, sym_name = "false_attr"} : () -> ()

// CHECK: value = false
// CHECK: "value" = false

"func.func"() ({}) {function_type = () -> (), value = 0 : i1, sym_name = "false_explicit_attr"} : () -> ()

// CHECK: value = false
// CHECK: "value" = false


"func.func"() ({}) {function_type = () -> (), value = 42 : i32, sym_name = "int_attr"} : () -> ()
Expand Down Expand Up @@ -80,19 +93,17 @@
// CHECK: (tensor<4xf32>, tensor<f32>, tensor<1x12xi32>, tensor<*xf64>)

"func.func"() ({}) {function_type = () -> (),
value1 = dense<0> : tensor<1xi32>,
value1 = dense<[0]> : tensor<1xi32>,
webmiche marked this conversation as resolved.
Show resolved Hide resolved
value2 = dense<[0.0, 1.0]> : tensor<2xf64>,
sym_name = "dense_attr"} : () -> ()

// CHECK: dense<[0]> : tensor<1xi32>
// CHECK: dense<[0.0, 1.0]> : tensor<2xf64>
// CHECK: "value1" = dense<[0]> : tensor<1xi32>, "value2" = dense<[0.0, 1.0]> : tensor<2xf64>

"func.func"() ({}) {function_type = () -> (),
value1 = opaque<"test", "contents">
value1 = opaque<"test", "contents">,
value2 = opaque<"test", "contents"> : tensor<2xf64>,
sym_name = "dense_attr"} : () -> ()

// CHECK: opaque<"test", "contents">
// CHECK: opaque<"test", "contents"> : tensor<2xf64>
// CHECK: "value1" = opaque<"test", "contents">, "value2" = opaque<"test", "contents"> : tensor<2xf64>

}) : () -> ()
19 changes: 10 additions & 9 deletions tests/filecheck/parser-printer/builtin_types.xdsl
Expand Up @@ -2,14 +2,15 @@


builtin.module() {
func.func() ["sym_name" = "tuple_to_tuple", "function_type" = !fun<[!tuple<[!i32]>], [!tuple<[!i32]>]>, "sym_visibility" = "private"] {
^0(%0 : !tuple<[!i32]>):
func.return(%0 : !tuple<[!i32]>)
}
}
func.func() ["sym_name" = "tuple_to_tuple", "value" = !fun<[!tuple<[!i32]>], [!tuple<[!i32]>]>, "function_type" = !fun<[], []>] {}
// CHECK: "value" = !fun<[!tuple<[!i32]>], [!tuple<[!i32]>]>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite get this change. AFAIU, you are using a function operation here to show that !fun<[!tuple<[!i32]>], [!tuple<[!i32]>]> can be parsed/printed. Why use a function operation? Aren't there easier operations to do that with? You can give sym_name and value to any operation to keep the name of the test around as well, right?

I feel like I am confused by these tests each time I see them. Would it make sense to have an empty test.testing operation just to make these tests easier to understand?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll add a test.testing in a following PR, that should make things better!


func.func() ["sym_name" = "integer_type", "value" = !i32, "function_type" = !fun<[], []>] {}
// CHECK: "value" = !i32

func.func() ["sym_name" = "signed_integer_type", "value" = !si32, "function_type" = !fun<[], []>] {}
// CHECK: "value" = !si32

// CHECK: func.func() ["sym_name" = "tuple_to_tuple", "function_type" = !fun<[!tuple<[!i32]>], [!tuple<[!i32]>]>, "sym_visibility" = "private"] {
// CHECK-NEXT: ^0(%0 : !tuple<[!i32]>):
// CHECK-NEXT: func.return(%0 : !tuple<[!i32]>)
// CHECK-NEXT: }
func.func() ["sym_name" = "unsigned_integer_type", "value" = !ui32, "function_type" = !fun<[], []>] {}
// CHECK: "value" = !ui32
}
62 changes: 55 additions & 7 deletions xdsl/dialects/builtin.py
@@ -1,19 +1,21 @@
from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import TypeAlias, List, cast, Type, Sequence, Optional

from xdsl.ir import (MLContext, TYPE_CHECKING, Data, MLIRType,
ParametrizedAttribute, Operation, SSAValue)
from xdsl.irdl import (AttributeDef, VarOperandDef, VarRegionDef, VarResultDef,
irdl_attr_definition, attr_constr_coercion,
irdl_to_attr_constraint, irdl_op_definition, builder,
ParameterDef, SingleBlockRegionDef, TypeVar, Generic,
GenericData, AttrConstraint, Any, Attribute, Region,
VerifyException, AnyAttr)
irdl_data_definition, irdl_to_attr_constraint,
irdl_op_definition, builder, ParameterDef,
SingleBlockRegionDef, TypeVar, Generic, GenericData,
AttrConstraint, Any, Attribute, Region, VerifyException,
AnyAttr)

if TYPE_CHECKING:
from xdsl.parser import Parser
from xdsl.parser import Parser, ParserError
from xdsl.printer import Printer


Expand Down Expand Up @@ -121,15 +123,61 @@ def from_int(data: int) -> IntAttr:
return IntAttr(data)


class Signedness(Enum):
"Signedness semantics for integer"

SIGNLESS = 0
"No signedness semantics"

SIGNED = 1
UNSIGNED = 2


@irdl_data_definition
class SignednessAttr(Data[Signedness]):
name = "signedness"

@staticmethod
def parse_parameter(parser: Parser) -> Signedness:
if parser.parse_optional_string("signless") is not None:
return Signedness.SIGNLESS
elif parser.parse_optional_string("signed") is not None:
return Signedness.SIGNED
elif parser.parse_optional_string("unsigned") is not None:
return Signedness.UNSIGNED
raise ParserError(parser.get_pos(), "Expected signedness")

@staticmethod
def print_parameter(data: Signedness, printer: Printer) -> None:
if data == Signedness.SIGNLESS:
printer.print_string("signless")
elif data == Signedness.SIGNED:
printer.print_string("signed")
elif data == Signedness.UNSIGNED:
printer.print_string("unsigned")
else:
raise ValueError(f"Invalid signedness {data}")

@staticmethod
@builder
def from_enum(signedness: Signedness) -> SignednessAttr:
return SignednessAttr(signedness)


@irdl_attr_definition
class IntegerType(ParametrizedAttribute):
name = "integer_type"
width: ParameterDef[IntAttr]
signedness: ParameterDef[SignednessAttr]

@staticmethod
@builder
def from_width(width: int) -> IntegerType:
return IntegerType([IntAttr.from_int(width)])
def from_width(
width: int,
signedness: Signedness = Signedness.SIGNLESS) -> IntegerType:
return IntegerType(
[IntAttr.from_int(width),
SignednessAttr.from_enum(signedness)])


i64 = IntegerType.from_width(64)
Expand Down
38 changes: 22 additions & 16 deletions xdsl/parser.py
Expand Up @@ -4,7 +4,7 @@
from xdsl.dialects.builtin import (
AnyFloat, AnyTensorType, AnyUnrankedTensorType, AnyVectorType,
DenseIntOrFPElementsAttr, Float16Type, Float32Type, Float64Type, FloatAttr,
FunctionType, IndexType, IntegerType, OpaqueAttr, StringAttr,
FunctionType, IndexType, IntegerType, OpaqueAttr, Signedness, StringAttr,
FlatSymbolRefAttr, IntegerAttr, ArrayAttr, TensorType, UnitAttr,
UnrankedTensorType, UnregisteredOp, VectorType)
from xdsl.irdl import Data
Expand Down Expand Up @@ -123,6 +123,10 @@ def __post_init__(self):
else:
self._pos = Position(self.str)

def get_pos(self) -> Position | None:
webmiche marked this conversation as resolved.
Show resolved Hide resolved
"""Return the current position."""
return self._pos

def get_char(self,
n: int = 1,
skip_white_space: bool = True) -> str | None:
Expand Down Expand Up @@ -680,9 +684,8 @@ def parse_optional_xdsl_builtin_attribute(self,

def parse_integer_type():
self.parse_char("!", skip_white_space=skip_white_space)
self.parse_char("i", skip_white_space=False)
val = self.parse_int_literal(skip_white_space=False)
return IntegerType.from_width(val)
return self.parse_mlir_integer_type(
skip_white_space=skip_white_space)

if int_type := self.try_parse(parse_integer_type):
return int_type
Expand Down Expand Up @@ -852,18 +855,21 @@ def parse_mlir_index_type(self,
raise ParserError(self._pos, "index type expected")

def parse_mlir_integer_type(self,
skip_white_space: bool = True
) -> IntegerType | None:
if (self.parse_optional_string("i", skip_white_space=skip_white_space)
or self.parse_optional_string(
"si", skip_white_space=skip_white_space)
or self.parse_optional_string(
"ui", skip_white_space=skip_white_space)):
width = self.parse_optional_int_literal()
if width is not None:
return IntegerType.from_width(width)
raise ParserError(self._pos, "integer type width expected")
raise ParserError(self._pos, "integer type expected")
skip_white_space: bool = True) -> IntegerType:
# Parse the optional signedness semantics
if self.parse_optional_string("si", skip_white_space=skip_white_space):
signedness = Signedness.SIGNED
elif self.parse_optional_string("ui",
skip_white_space=skip_white_space):
signedness = Signedness.UNSIGNED
elif self.parse_optional_string("i",
skip_white_space=skip_white_space):
signedness = Signedness.SIGNLESS
else:
raise ParserError(self._pos, "integer type expected")

val = self.parse_int_literal(skip_white_space=False)
return IntegerType.from_width(val, signedness)

def parse_optional_mlir_integer_type(self,
skip_white_space: bool = True
Expand Down
17 changes: 10 additions & 7 deletions xdsl/printer.py
Expand Up @@ -12,7 +12,7 @@
from xdsl.dialects.builtin import (
AnyIntegerAttr, AnyFloatAttr, AnyUnrankedTensorType, AnyVectorType,
DenseIntOrFPElementsAttr, Float16Type, Float32Type, Float64Type, FloatAttr,
IndexType, IntegerType, NoneAttr, OpaqueAttr, StringAttr,
IndexType, IntegerType, NoneAttr, OpaqueAttr, Signedness, StringAttr,
FlatSymbolRefAttr, IntegerAttr, ArrayAttr, ParametrizedAttribute, IntAttr,
TensorType, UnitAttr, FunctionType, UnrankedTensorType, UnregisteredOp,
VectorType)
Expand Down Expand Up @@ -290,12 +290,15 @@ def print_attribute(self, attribute: Attribute) -> None:
return

if isinstance(attribute, IntegerType):
width = attribute.parameters[0]
assert isinstance(width, IntAttr)
if self.target == self.Target.MLIR:
self.print(f'i{width.data}')
else:
self.print(f'!i{width.data}')
if self.target == self.Target.XDSL:
self.print("!")
if attribute.signedness.data == Signedness.SIGNLESS:
self.print("i")
elif attribute.signedness.data == Signedness.SIGNED:
self.print("si")
elif attribute.signedness.data == Signedness.UNSIGNED:
self.print("ui")
self.print(attribute.width.data)
return

if self.target == self.Target.MLIR:
Expand Down