Repository navigation
[mypyc] Specialize .real and .imag on complex, float, int and bool #22099
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -54,8 +54,10 @@ | |||||||||||||||||
| Instance, | ||||||||||||||||||
| ProperType, | ||||||||||||||||||
| TupleType, | ||||||||||||||||||
| Type, | ||||||||||||||||||
| TypeOfAny, | ||||||||||||||||||
| TypeType, | ||||||||||||||||||
| UnionType, | ||||||||||||||||||
| get_proper_type, | ||||||||||||||||||
| ) | ||||||||||||||||||
| from mypyc.common import ( | ||||||||||||||||||
|
|
@@ -73,6 +75,7 @@ | |||||||||||||||||
| CallC, | ||||||||||||||||||
| Cast, | ||||||||||||||||||
| ComparisonOp, | ||||||||||||||||||
| Float, | ||||||||||||||||||
| GetAttr, | ||||||||||||||||||
| Integer, | ||||||||||||||||||
| LoadAddress, | ||||||||||||||||||
|
|
@@ -88,14 +91,17 @@ | |||||||||||||||||
| from mypyc.ir.rtypes import ( | ||||||||||||||||||
| RInstance, | ||||||||||||||||||
| RTuple, | ||||||||||||||||||
| RType, | ||||||||||||||||||
| RVec, | ||||||||||||||||||
| bool_rprimitive, | ||||||||||||||||||
| int64_rprimitive, | ||||||||||||||||||
| int_rprimitive, | ||||||||||||||||||
| is_any_int, | ||||||||||||||||||
| is_bool_or_bit_rprimitive, | ||||||||||||||||||
| is_bytearray_rprimitive, | ||||||||||||||||||
| is_bytes_rprimitive, | ||||||||||||||||||
| is_fixed_width_rtype, | ||||||||||||||||||
| is_float_rprimitive, | ||||||||||||||||||
| is_int64_rprimitive, | ||||||||||||||||||
| is_int_rprimitive, | ||||||||||||||||||
| is_list_rprimitive, | ||||||||||||||||||
|
|
@@ -145,6 +151,12 @@ | |||||||||||||||||
| ) | ||||||||||||||||||
| from mypyc.primitives.bytes_ops import bytes_slice_op | ||||||||||||||||||
| from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op | ||||||||||||||||||
| from mypyc.primitives.float_ops import ( | ||||||||||||||||||
| complex_imag_op, | ||||||||||||||||||
| complex_real_op, | ||||||||||||||||||
| number_imag_op, | ||||||||||||||||||
| number_real_op, | ||||||||||||||||||
| ) | ||||||||||||||||||
| from mypyc.primitives.generic_ops import iter_op, name_op | ||||||||||||||||||
| from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op | ||||||||||||||||||
| from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op | ||||||||||||||||||
|
|
@@ -303,6 +315,9 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value: | |||||||||||||||||
| # only apply to RInstance types. | ||||||||||||||||||
| return builder.primitive_op(type_op, [obj], expr.line) | ||||||||||||||||||
|
|
||||||||||||||||||
| if expr.name in ("real", "imag") and is_builtin_number_type(builder.types.get(expr.expr)): | ||||||||||||||||||
| return transform_real_imag(builder, obj, expr.name == "real", rtype, expr.line) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Special case: for named tuples transform attribute access to faster index access. | ||||||||||||||||||
| typ = get_proper_type(builder.types.get(expr.expr)) | ||||||||||||||||||
| if isinstance(typ, TupleType) and typ.partial_fallback.type.is_named_tuple: | ||||||||||||||||||
|
|
@@ -335,6 +350,37 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value: | |||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def is_builtin_number_type(typ: Type | None) -> bool: | ||||||||||||||||||
| """Is typ int, bool, float, complex or a union of them (and not a subclass)?""" | ||||||||||||||||||
| typ = get_proper_type(typ) | ||||||||||||||||||
| if isinstance(typ, UnionType): | ||||||||||||||||||
| return all(is_builtin_number_type(item) for item in typ.items) | ||||||||||||||||||
| return isinstance(typ, Instance) and typ.type.fullname in ( | ||||||||||||||||||
| "builtins.int", | ||||||||||||||||||
| "builtins.bool", | ||||||||||||||||||
| "builtins.float", | ||||||||||||||||||
| "builtins.complex", | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def transform_real_imag( | ||||||||||||||||||
| builder: IRBuilder, obj: Value, is_real: bool, rtype: RType, line: int | ||||||||||||||||||
| ) -> Value: | ||||||||||||||||||
| """Get obj.real or obj.imag, where obj is an int, a float or a complex.""" | ||||||||||||||||||
| if is_tagged(obj.type) or is_bool_or_bit_rprimitive(obj.type): | ||||||||||||||||||
| # Unboxed ints are always exact ints, and the real part of a bool is an int | ||||||||||||||||||
|
Comment on lines
+370
to
+371
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a tagged integer can still be a generic python object for large ints so i think we'd need a runtime check here because the generic object might be a subclass that overrides
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call.
|
||||||||||||||||||
| return builder.coerce(obj, int_rprimitive, line) if is_real else builder.load_int(0, line) | ||||||||||||||||||
| if is_float_rprimitive(obj.type): | ||||||||||||||||||
| # Unboxed floats are always exact floats, so these can't be overridden | ||||||||||||||||||
| return obj if is_real else Float(0.0, line) | ||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. found by codex: reusing the x = 1.0
print(x.real + (x := 2.0)) # 4.0might also need a new register for the return value of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The walrus keeps tripping me up. On a float, Speed (ns per value):
EDIT: Also note that I was having trouble pushing my changes to Github; now it is not showing my new commits and instead saying "Processing updates". Once Github shakes off it's Microsoft chills, I'll make sure my changes are in fact here, and checks ran. |
||||||||||||||||||
| if is_float_rprimitive(rtype): | ||||||||||||||||||
| op = complex_real_op if is_real else complex_imag_op | ||||||||||||||||||
| return builder.primitive_op(op, [obj], line) | ||||||||||||||||||
| # The result can be an int, for example with int | float | complex | ||||||||||||||||||
| op = number_real_op if is_real else number_imag_op | ||||||||||||||||||
| return builder.primitive_op(op, [obj], line, result_type=rtype) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def value_borrow_scope(builder: IRBuilder, v: Value) -> int: | ||||||||||||||||||
| """Compute how long an existing borrowed value can safely be kept alive. | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -237,3 +237,30 @@ double CPyFloat_Pow(double x, double y) { | |
| } | ||
| return r; | ||
| } | ||
|
|
||
| // Look up o.real or o.imag and convert it to a C double (see CPyComplex_Real) | ||
| double CPyComplex_GetPartSlow(PyObject *o, PyObject *name) { | ||
| PyObject *part = PyObject_GetAttr(o, name); | ||
| if (part == NULL) { | ||
| return CPY_FLOAT_ERROR; | ||
| } | ||
| double result = PyFloat_AsDouble(part); | ||
| if (result == -1.0 && PyErr_Occurred()) { | ||
| // Same error as unboxing the attribute value would produce | ||
| CPy_TypeError("float", part); | ||
| result = CPY_FLOAT_ERROR; | ||
| } | ||
| Py_DECREF(part); | ||
| return result; | ||
| } | ||
|
|
||
| // Convert the real part of an int to a C double (see CPyComplex_Real) | ||
| double CPyComplex_LongAsDouble(PyObject *o) { | ||
|
Comment on lines
+257
to
+258
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this function is more general than the name/comment suggest, maybe rename to something like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I agree. I also thought it seemed more generic than the name suggested and was hoping there was a method I could just use... I'll think of a better name. To your point around "real part of an int" doesn't make sense; I completely agree, and it makes even less sense for a bool, however this is valid interpreted Python (as uncomfortable as it makes me): >>> a = int(10)
>>> a.real
10
>>> a.imag
0
>>>
>>> b = True
>>> b.real
1
>>> b.imag
0EDIT: Obviously the |
||
| double result = PyLong_AsDouble(o); | ||
| if (result == -1.0 && PyErr_Occurred()) { | ||
| // Same error as unboxing the attribute value would produce | ||
| CPy_TypeError("float", o); | ||
| return CPY_FLOAT_ERROR; | ||
| } | ||
| return result; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.