diff --git a/include/npcomp/Dialect/Basicpy/BasicpyDialect.h b/include/npcomp/Dialect/Basicpy/BasicpyDialect.h index 16a78a6ac..7b73e32e3 100644 --- a/include/npcomp/Dialect/Basicpy/BasicpyDialect.h +++ b/include/npcomp/Dialect/Basicpy/BasicpyDialect.h @@ -21,11 +21,12 @@ namespace Basicpy { namespace BasicpyTypes { enum Kind { // Dialect types. - UnknownType = TypeRanges::Basicpy, - NoneType, - BoolType, + BoolType = TypeRanges::Basicpy, EllipsisType, + NoneType, SlotObjectType, + StrType, + UnknownType, // Dialect attributes. SingletonAttr, @@ -37,28 +38,6 @@ namespace detail { struct SlotObjectTypeStorage; } // namespace detail -/// An unknown type that could be any supported python type. -class UnknownType : public Type::TypeBase { -public: - using Base::Base; - static bool kindof(unsigned kind) { - return kind == BasicpyTypes::UnknownType; - } - static UnknownType get(MLIRContext *context) { - return Base::get(context, BasicpyTypes::UnknownType); - } -}; - -/// The type of the Python `None` value. -class NoneType : public Type::TypeBase { -public: - using Base::Base; - static bool kindof(unsigned kind) { return kind == BasicpyTypes::NoneType; } - static NoneType get(MLIRContext *context) { - return Base::get(context, BasicpyTypes::NoneType); - } -}; - /// Python 'bool' type (can contain values True or False, corresponding to /// i1 constants of 0 or 1). class BoolType : public Type::TypeBase { @@ -82,6 +61,16 @@ public: } }; +/// The type of the Python `None` value. +class NoneType : public Type::TypeBase { +public: + using Base::Base; + static bool kindof(unsigned kind) { return kind == BasicpyTypes::NoneType; } + static NoneType get(MLIRContext *context) { + return Base::get(context, BasicpyTypes::NoneType); + } +}; + class SlotObjectType : public Type::TypeBase { public: @@ -101,6 +90,28 @@ public: } }; +/// The type of the Python `str` values. +class StrType : public Type::TypeBase { +public: + using Base::Base; + static bool kindof(unsigned kind) { return kind == BasicpyTypes::StrType; } + static StrType get(MLIRContext *context) { + return Base::get(context, BasicpyTypes::StrType); + } +}; + +/// An unknown type that could be any supported python type. +class UnknownType : public Type::TypeBase { +public: + using Base::Base; + static bool kindof(unsigned kind) { + return kind == BasicpyTypes::UnknownType; + } + static UnknownType get(MLIRContext *context) { + return Base::get(context, BasicpyTypes::UnknownType); + } +}; + #include "npcomp/Dialect/Basicpy/BasicpyOpsDialect.h.inc" } // namespace Basicpy diff --git a/include/npcomp/Dialect/Basicpy/BasicpyDialect.td b/include/npcomp/Dialect/Basicpy/BasicpyDialect.td index be0255834..7a44af7c8 100644 --- a/include/npcomp/Dialect/Basicpy/BasicpyDialect.td +++ b/include/npcomp/Dialect/Basicpy/BasicpyDialect.td @@ -38,11 +38,19 @@ class Basicpy_Op traits = []> : // Dialect types //===----------------------------------------------------------------------===// -def Basicpy_UnknownType : DialectType()">, - "Unknown type"> { +def Basicpy_BoolType : DialectType()">, "Bool type">, + BuildableType<"$_builder.getType<::mlir::NPCOMP::Basicpy::BoolType>()"> { let typeDescription = [{ - An unknown type (for the current phase of processing). + Type for 'True' and 'False' values. + }]; +} + +def Basicpy_EllipsisType : DialectType()">, "Ellipsis type">, + BuildableType<"$_builder.getType<::mlir::NPCOMP::Basicpy::EllipsisType>()"> { + let typeDescription = [{ + Type of the Python 'Ellipsis' value. }]; } @@ -54,14 +62,6 @@ def Basicpy_NoneType : DialectType()">, "Bool type">, - BuildableType<"$_builder.getType<::mlir::NPCOMP::Basicpy::BoolType>()"> { - let typeDescription = [{ - Type for 'True' and 'False' values. - }]; -} - def Basicpy_SlotObjectType : DialectType()">, "Slot object"> { @@ -72,11 +72,19 @@ def Basicpy_SlotObjectType : DialectType()">, "Ellipsis type">, - BuildableType<"$_builder.getType<::mlir::NPCOMP::Basicpy::EllipsisType>()"> { +def Basicpy_StrType : DialectType()">,"String type">, + BuildableType<"$_builder.getType<::mlir::NPCOMP::Basicpy::StrType>()"> { let typeDescription = [{ - Type of the Python 'Ellipsis' value. + Represents values of the python 'str' type. + }]; +} + +def Basicpy_UnknownType : DialectType()">, + "Unknown type"> { + let typeDescription = [{ + An unknown type (for the current phase of processing). }]; } diff --git a/include/npcomp/Dialect/Basicpy/BasicpyOps.td b/include/npcomp/Dialect/Basicpy/BasicpyOps.td index 33232f005..597d278cf 100644 --- a/include/npcomp/Dialect/Basicpy/BasicpyOps.td +++ b/include/npcomp/Dialect/Basicpy/BasicpyOps.td @@ -13,27 +13,6 @@ include "BasicpyDialect.td" include "mlir/Interfaces/SideEffectInterfaces.td" include "mlir/IR/SymbolInterfaces.td" -def Basicpy_UnknownCastOp : Basicpy_Op<"unknown_cast", [NoSideEffect]> { - let summary = "Casts to and from the UnknownType"; - let arguments = (ins AnyType:$input); - let results = (outs AnyType:$result); - let assemblyFormat = "operands attr-dict `:` type(operands) `->` type(results)"; -} - -def Basicpy_SingletonOp : Basicpy_Op<"singleton", [ - ConstantLike, NoSideEffect]> { - let summary = "Constant value for a singleton type"; - let description = [{ - Some types only have a single possible value, represented by the - SingletonAttr. This op allows creating constants of these types. - }]; - let arguments = (ins); - let results = (outs - Basicpy_SingletonType:$result - ); - let assemblyFormat = "attr-dict `:` type($result)"; -} - def Basicpy_BoolConstantOp : Basicpy_Op<"bool_constant", [ ConstantLike, NoSideEffect]> { let summary = "A boolean constant"; @@ -92,4 +71,41 @@ def Basicpy_SlotObjectGetOp : Basicpy_Op<"slot_object_get", [ ); } +def Basicpy_StrConstantOp : Basicpy_Op<"str_constant", [ + ConstantLike, NoSideEffect]> { + let summary = "Constant string value"; + let description = [{ + A string value of StrType. The value is represented by a StringAttr + that is UTF-8 encoded. + }]; + let arguments = (ins + StrAttr:$value + ); + let results = (outs + Basicpy_StrType:$result + ); + let assemblyFormat = "$value attr-dict"; +} + +def Basicpy_SingletonOp : Basicpy_Op<"singleton", [ + ConstantLike, NoSideEffect]> { + let summary = "Constant value for a singleton type"; + let description = [{ + Some types only have a single possible value, represented by the + SingletonAttr. This op allows creating constants of these types. + }]; + let arguments = (ins); + let results = (outs + Basicpy_SingletonType:$result + ); + let assemblyFormat = "attr-dict `:` type($result)"; +} + +def Basicpy_UnknownCastOp : Basicpy_Op<"unknown_cast", [NoSideEffect]> { + let summary = "Casts to and from the UnknownType"; + let arguments = (ins AnyType:$input); + let results = (outs AnyType:$result); + let assemblyFormat = "operands attr-dict `:` type(operands) `->` type(results)"; +} + #endif // NPCOMP_DIALECT_BASICPY_BASICPY_OPS diff --git a/lib/Dialect/Basicpy/BasicpyDialect.cpp b/lib/Dialect/Basicpy/BasicpyDialect.cpp index 8bbcbffa4..73d020cd8 100644 --- a/lib/Dialect/Basicpy/BasicpyDialect.cpp +++ b/lib/Dialect/Basicpy/BasicpyDialect.cpp @@ -19,7 +19,8 @@ BasicpyDialect::BasicpyDialect(MLIRContext *context) #define GET_OP_LIST #include "npcomp/Dialect/Basicpy/BasicpyOps.cpp.inc" >(); - addTypes(); + addTypes(); // TODO: Make real ops for everything we need. allowUnknownOperations(); @@ -30,14 +31,12 @@ Type BasicpyDialect::parseType(DialectAsmParser &parser) const { if (parser.parseKeyword(&keyword)) return Type(); - if (keyword == "UnknownType") - return UnknownType::get(getContext()); - if (keyword == "NoneType") - return NoneType::get(getContext()); if (keyword == "BoolType") return BoolType::get(getContext()); if (keyword == "EllipsisType") return EllipsisType::get(getContext()); + if (keyword == "NoneType") + return NoneType::get(getContext()); if (keyword == "SlotObject") { StringRef className; if (parser.parseLess() || parser.parseKeyword(&className)) { @@ -56,6 +55,10 @@ Type BasicpyDialect::parseType(DialectAsmParser &parser) const { return SlotObjectType::get(StringAttr::get(className, getContext()), slotTypes); } + if (keyword == "StrType") + return StrType::get(getContext()); + if (keyword == "UnknownType") + return UnknownType::get(getContext()); parser.emitError(parser.getNameLoc(), "unknown basicpy type"); return Type(); @@ -63,18 +66,15 @@ Type BasicpyDialect::parseType(DialectAsmParser &parser) const { void BasicpyDialect::printType(Type type, DialectAsmPrinter &os) const { switch (type.getKind()) { - case BasicpyTypes::UnknownType: - os << "UnknownType"; - return; - case BasicpyTypes::NoneType: - os << "NoneType"; - return; case BasicpyTypes::BoolType: os << "BoolType"; return; case BasicpyTypes::EllipsisType: os << "EllipsisType"; return; + case BasicpyTypes::NoneType: + os << "NoneType"; + return; case BasicpyTypes::SlotObjectType: { auto slotObject = type.cast(); auto slotTypes = slotObject.getSlotTypes(); @@ -86,6 +86,12 @@ void BasicpyDialect::printType(Type type, DialectAsmPrinter &os) const { os << ">"; return; } + case BasicpyTypes::StrType: + os << "StrType"; + return; + case BasicpyTypes::UnknownType: + os << "UnknownType"; + return; default: llvm_unreachable("unexpected 'basicpy' type kind"); } diff --git a/pytest/Compiler/constants.py b/pytest/Compiler/constants.py index 706871a87..b6196cc99 100644 --- a/pytest/Compiler/constants.py +++ b/pytest/Compiler/constants.py @@ -11,7 +11,7 @@ def import_global(f): return f -# CHECK: func @integer_constants +# CHECK-LABEL: func @integer_constants @import_global def integer_constants(): # CHECK: %[[A:.*]] = constant 100 : i64 @@ -20,7 +20,7 @@ def integer_constants(): # CHECK: return %[[A_CAST]] return a -# CHECK: func @float_constants +# CHECK-LABEL: func @float_constants @import_global def float_constants(): # CHECK: %[[A:.*]] = constant 2.200000e+00 : f64 @@ -29,7 +29,7 @@ def float_constants(): # CHECK: return %[[A_CAST]] return a -# CHECK: func @bool_true_constant +# CHECK-LABEL: func @bool_true_constant @import_global def bool_true_constant(): # CHECK: %[[A:.*]] = basicpy.bool_constant 1 @@ -37,10 +37,27 @@ def bool_true_constant(): a = True return a -# CHECK: func @bool_false_constant +# CHECK-LABEL: func @bool_false_constant @import_global def bool_false_constant(): # CHECK: %[[A:.*]] = basicpy.bool_constant 0 # CHECK: basicpy.unknown_cast %[[A]] a = False return a + +# CHECK-LABEL: func @string_constant +@import_global +def string_constant(): + # CHECK: %[[A:.*]] = basicpy.str_constant "foobar" + # CHECK: basicpy.unknown_cast %[[A]] + a = "foobar" + return a + +# CHECK-LABEL: func @joined_string_constant +@import_global +def joined_string_constant(): + # CHECK: %[[A:.*]] = basicpy.str_constant "I am still here" + # CHECK: basicpy.unknown_cast %[[A]] + a = "I am" " still here" + return a + diff --git a/python/npcomp/compiler/frontend.py b/python/npcomp/compiler/frontend.py index 1410477fd..669ea58eb 100644 --- a/python/npcomp/compiler/frontend.py +++ b/python/npcomp/compiler/frontend.py @@ -167,7 +167,7 @@ class FunctionDefImporter(BaseNodeVisitor): ir_h = self.fctx.ir_h expr = ExpressionImporter(self.fctx) expr.visit(ast_node.value) - casted = ir_h.basicpy_unknown_cast(ir_h.basicpy_UnknownType, + casted = ir_h.basicpy_unknown_cast_op(ir_h.basicpy_UnknownType, expr.value).result ir_h.return_op([casted]) @@ -208,6 +208,8 @@ class ExpressionImporter(BaseNodeVisitor): self.value = ir_h.basicpy_bool_constant_op(False).result else: self.fctx.abort("unknown named constant '%r'" % (ast_node.value,)) + elif isinstance(ast_node, ast.Str): + self.value = ir_h.basicpy_str_constant_op(ast_node.s).result else: self.fctx.abort("unknown constant type %s" % (ast_node.__class__.__name__)) diff --git a/python/npcomp/dialect/Basicpy.py b/python/npcomp/dialect/Basicpy.py index 0bd93e460..4c66b8b8f 100644 --- a/python/npcomp/dialect/Basicpy.py +++ b/python/npcomp/dialect/Basicpy.py @@ -47,9 +47,6 @@ class DialectHelper(_BaseDialectHelper): """ - def basicpy_unknown_cast(self, result_type, operand): - return self.op("basicpy.unknown_cast", [result_type], [operand]) - def basicpy_bool_constant_op(self, value): c = self.context ival = 1 if value else 0 @@ -57,7 +54,7 @@ class DialectHelper(_BaseDialectHelper): "value": c.integer_attr(self.i1_type, ival) }) return self.op("basicpy.bool_constant", [self.basicpy_BoolType], [], attrs) - + def basicpy_singleton_op(self, singleton_type): return self.op("basicpy.singleton", [singleton_type], []) @@ -70,6 +67,16 @@ class DialectHelper(_BaseDialectHelper): return self.op("basicpy.slot_object_make", [object_type], slot_values, attrs) + def basicpy_str_constant_op(self, value): + c = self.context + attrs = c.dictionary_attr({ + "value": c.string_attr(value.encode("utf-8")) + }) + return self.op("basicpy.str_constant", [self.basicpy_StrType], [], attrs) + + def basicpy_unknown_cast_op(self, result_type, operand): + return self.op("basicpy.unknown_cast", [result_type], [operand]) + if __name__ == "__main__": import doctest diff --git a/python_native/NpcompDialect.cpp b/python_native/NpcompDialect.cpp index 22d678f75..73a3fc434 100644 --- a/python_native/NpcompDialect.cpp +++ b/python_native/NpcompDialect.cpp @@ -21,16 +21,6 @@ public: static void bind(py::module m) { py::class_(m, "BasicpyDialectHelper") .def(py::init>()) - .def_property_readonly("basicpy_UnknownType", - [](BasicpyDialectHelper &self) -> PyType { - return Basicpy::UnknownType::get( - &self.context->context); - }) - .def_property_readonly("basicpy_NoneType", - [](BasicpyDialectHelper &self) -> PyType { - return Basicpy::NoneType::get( - &self.context->context); - }) .def_property_readonly("basicpy_BoolType", [](BasicpyDialectHelper &self) -> PyType { return Basicpy::BoolType::get( @@ -41,6 +31,11 @@ public: return Basicpy::EllipsisType::get( &self.context->context); }) + .def_property_readonly("basicpy_NoneType", + [](BasicpyDialectHelper &self) -> PyType { + return Basicpy::NoneType::get( + &self.context->context); + }) .def("basicpy_SlotObject_type", [](BasicpyDialectHelper &self, std::string className, py::args pySlotTypes) -> PyType { @@ -53,6 +48,16 @@ public: return Basicpy::SlotObjectType::get(classNameAttr, slotTypes); }, py::arg("className")) + .def_property_readonly("basicpy_StrType", + [](BasicpyDialectHelper &self) -> PyType { + return Basicpy::StrType::get( + &self.context->context); + }) + .def_property_readonly("basicpy_UnknownType", + [](BasicpyDialectHelper &self) -> PyType { + return Basicpy::UnknownType::get( + &self.context->context); + }) .def("basicpy_slot_object_get_op", [](BasicpyDialectHelper &self, PyValue slotObject, unsigned index) -> PyOperationRef {