Skip to content

Commit bdd320e

Browse files
committed
feat: specialize
1 parent a7151dd commit bdd320e

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

mypyc/irbuild/specialize.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,12 @@
9494
isinstance_dict,
9595
)
9696
from mypyc.primitives.float_ops import isinstance_float
97-
from mypyc.primitives.int_ops import isinstance_int
97+
from mypyc.primitives.int_ops import (
98+
isinstance_int,
99+
int_to_big_endian_op,
100+
int_to_bytes_op,
101+
int_to_little_endian_op,
102+
)
98103
from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op
99104
from mypyc.primitives.misc_ops import isinstance_bool
100105
from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set
@@ -1000,3 +1005,24 @@ def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value
10001005
if isinstance(arg, (StrExpr, BytesExpr)) and len(arg.value) == 1:
10011006
return Integer(ord(arg.value))
10021007
return None
1008+
1009+
@specialize_function("to_bytes", int_rprimitive)
1010+
def specialize_int_to_bytes(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
1011+
# int.to_bytes(length, byteorder, signed=False)
1012+
# args: [self, length, byteorder, (optional) signed]
1013+
if len(expr.args) < 3 or len(expr.args) > 4:
1014+
return None
1015+
self_arg = builder.accept(expr.args[0])
1016+
length_arg = builder.accept(expr.args[1])
1017+
byteorder_expr = expr.args[2]
1018+
signed_arg = builder.accept(expr.args[3]) if len(expr.args) == 4 else builder.false()
1019+
if isinstance(byteorder_expr, StrExpr):
1020+
if byteorder_expr.value == "little":
1021+
return builder.call_c(int_to_little_endian_op, [self_arg, length_arg, signed_arg], expr.line)
1022+
elif byteorder_expr.value == "big":
1023+
return builder.call_c(int_to_big_endian_op, [self_arg, length_arg, signed_arg], expr.line)
1024+
# Fallback to generic primitive op
1025+
byteorder_arg = builder.accept(byteorder_expr)
1026+
return builder.primitive_op(
1027+
int_to_bytes_op, [self_arg, length_arg, byteorder_arg, signed_arg], expr.line
1028+
)

mypyc/lib-rt/CPy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op);
149149
CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right);
150150
CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right);
151151
PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteorder, int signed_flag);
152+
PyObject *CPyTagged_ToBigEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag);
153+
PyObject *CPyTagged_ToLittleEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag);
152154

153155
PyObject *CPyTagged_Str(CPyTagged n);
154156
CPyTagged CPyTagged_FromFloat(double f);

mypyc/lib-rt/int_ops.c

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -582,17 +582,8 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) {
582582
return 1.0;
583583
}
584584

585-
static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, const char *byteorder, int signed_flag) {
585+
static PyObject *CPyLong_ToBytes(PyObject *v, Py_ssize_t length, int little_endian, int signed_flag) {
586586
// This is a wrapper for PyLong_AsByteArray and PyBytes_FromStringAndSize
587-
int little_endian;
588-
if (strcmp(byteorder, "big") == 0) {
589-
little_endian = 0;
590-
} else if (strcmp(byteorder, "little") == 0) {
591-
little_endian = 1;
592-
} else {
593-
PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'");
594-
return NULL;
595-
}
596587
PyObject *result = PyBytes_FromStringAndSize(NULL, length);
597588
if (!result) {
598589
return NULL;
@@ -623,7 +614,31 @@ PyObject *CPyTagged_ToBytes(CPyTagged self, Py_ssize_t length, PyObject *byteord
623614
Py_DECREF(pyint);
624615
return NULL;
625616
}
626-
PyObject *result = CPyLong_ToBytes(pyint, length, order, signed_flag);
617+
int little_endian;
618+
if (strcmp(order, "big") == 0) {
619+
little_endian = 0;
620+
} else if (strcmp(order, "little") == 0) {
621+
little_endian = 1;
622+
} else {
623+
PyErr_SetString(PyExc_ValueError, "byteorder must be either 'little' or 'big'");
624+
return NULL;
625+
}
626+
PyObject *result = CPyLong_ToBytes(pyint, length, little_endian, signed_flag);
627+
Py_DECREF(pyint);
628+
return result;
629+
630+
// int.to_bytes(length, byteorder="little", signed=False)
631+
PyObject *CPyTagged_ToLittleEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag) {
632+
PyObject *pyint = CPyTagged_AsObject(self);
633+
PyObject *result = CPyLong_ToBytes(pyint, length, 1, signed_flag);
634+
Py_DECREF(pyint);
635+
return result;
636+
}
637+
638+
// int.to_bytes(length, "big", signed=False)
639+
PyObject *CPyTagged_ToBigEndianBytes(CPyTagged self, Py_ssize_t length, int signed_flag) {
640+
PyObject *pyint = CPyTagged_AsObject(self);
641+
PyObject *result = CPyLong_ToBytes(pyint, length, 0, signed_flag);
627642
Py_DECREF(pyint);
628643
return result;
629644
}

mypyc/primitives/int_ops.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,31 @@ def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription:
324324
error_kind=ERR_MAGIC,
325325
)
326326

327-
# int.to_bytes(length, byteorder, signed)
328-
method_op(
327+
# int.to_bytes(length, byteorder, signed=...)
328+
int_to_bytes_op = method_op(
329329
name="to_bytes",
330330
arg_types=[int_rprimitive, int_rprimitive, str_rprimitive, bool_rprimitive],
331331
return_type=bytes_rprimitive,
332332
c_function_name="CPyTagged_ToBytes",
333333
error_kind=ERR_MAGIC,
334334
)
335+
336+
# specialized custom_op cases for int.to_bytes:
337+
338+
# int.to_bytes(length, "big")
339+
# int.to_bytes(length, "big", signed=...)
340+
int_to_big_endian_op = custom_op(
341+
arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive],
342+
return_type=bytes_rprimitive,
343+
c_function_name="CPyTagged_ToBigEndianBytes",
344+
error_kind=ERR_MAGIC,
345+
)
346+
347+
# int.to_bytes(length, "little")
348+
# int.to_bytes(length, "little", signed=...)
349+
int_to_little_endian_op = custom_op(
350+
arg_types=[int_rprimitive, int_rprimitive, bool_rprimitive],
351+
return_type=bytes_rprimitive,
352+
c_function_name="CPyTagged_ToLittleEndianBytes",
353+
error_kind=ERR_MAGIC,
354+
)

0 commit comments

Comments
 (0)