self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops)
self.assertNotIn("_CHECK_METHOD_VERSION", uops)
+ def test_record_bound_method_general(self):
+ class MyClass:
+ def method(self, *args):
+ return args[0] + 1
+
+ def testfunc(n):
+ obj = MyClass()
+ bound = obj.method
+ result = 0
+ for i in range(n):
+ result += bound(i)
+ return result
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(
+ res, sum(i + 1 for i in range(TIER2_THRESHOLD))
+ )
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_PUSH_FRAME", uops)
+
+ def test_record_bound_method_exact_args(self):
+ class MyClass:
+ def method(self, x):
+ return x + 1
+
+ def testfunc(n):
+ obj = MyClass()
+ bound = obj.method
+ result = 0
+ for i in range(n):
+ result += bound(i)
+ return result
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(
+ res, sum(i + 1 for i in range(TIER2_THRESHOLD))
+ )
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_PUSH_FRAME", uops)
+ self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops)
+
def test_jit_error_pops(self):
"""
Tests that the correct number of pops are inserted into the
tier2 op(_RECORD_BOUND_METHOD, (callable, self, args[oparg] -- callable, self, args[oparg])) {
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
if (Py_TYPE(callable_o) == &PyMethod_Type) {
- PyObject *func = ((PyMethodObject *)callable_o)->im_func;
- RECORD_VALUE(func);
+ RECORD_VALUE(callable_o);
}
}
}
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
- callable = sym_new_not_null(ctx);
- self_or_null = sym_new_not_null(ctx);
+ PyObject *bound_method = sym_get_probable_value(callable);
+ if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) {
+ PyMethodObject *method = (PyMethodObject *)bound_method;
+ callable = sym_new_not_null(ctx);
+ sym_set_recorded_value(callable, method->im_func);
+ self_or_null = sym_new_not_null(ctx);
+ sym_set_recorded_value(self_or_null, method->im_self);
+ }
+ else {
+ callable = sym_new_not_null(ctx);
+ self_or_null = sym_new_not_null(ctx);
+ }
}
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func;
}
+ else {
+ // Guarding on the bound method, safe to promote.
+ PyObject *bound_method = sym_get_probable_value(callable);
+ if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) {
+ PyMethodObject *method = (PyMethodObject *)bound_method;
+ PyObject *func = method->im_func;
+ if (PyFunction_Check(func) &&
+ ((PyFunctionObject *)func)->func_version == func_version) {
+ _Py_BloomFilter_Add(dependencies, func);
+ sym_set_const(callable, bound_method);
+ }
+ }
+ }
sym_set_type(callable, &PyMethod_Type);
}
}
}
+ op(_EXPAND_METHOD, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
+ if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) {
+ PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable);
+ callable = sym_new_const(ctx, method->im_func);
+ self_or_null = sym_new_const(ctx, method->im_self);
+ }
+ else {
+ callable = sym_new_not_null(ctx);
+ self_or_null = sym_new_not_null(ctx);
+ }
+ }
+
op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) {
(void)args;
callable = sym_new_not_null(ctx);
sym_set_recorded_value(func, (PyObject *)this_instr->operand0);
}
+ op(_RECORD_BOUND_METHOD, (callable, self, args[oparg] -- callable, self, args[oparg])) {
+ sym_set_recorded_value(callable, (PyObject *)this_instr->operand0);
+ }
+
op(_RECORD_NOS_GEN_FUNC, (nos, tos -- nos, tos)) {
PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0;
assert(func == NULL || PyFunction_Check(func));
ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func;
}
+ else {
+ PyObject *bound_method = sym_get_probable_value(callable);
+ if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) {
+ PyMethodObject *method = (PyMethodObject *)bound_method;
+ PyObject *func = method->im_func;
+ if (PyFunction_Check(func) &&
+ ((PyFunctionObject *)func)->func_version == func_version) {
+ _Py_BloomFilter_Add(dependencies, func);
+ sym_set_const(callable, bound_method);
+ }
+ }
+ }
sym_set_type(callable, &PyMethod_Type);
break;
}
case _EXPAND_METHOD: {
+ JitOptRef self_or_null;
+ JitOptRef callable;
+ self_or_null = stack_pointer[-1 - oparg];
+ callable = stack_pointer[-2 - oparg];
+ if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) {
+ PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable);
+ callable = sym_new_const(ctx, method->im_func);
+ self_or_null = sym_new_const(ctx, method->im_self);
+ }
+ else {
+ callable = sym_new_not_null(ctx);
+ self_or_null = sym_new_not_null(ctx);
+ }
+ stack_pointer[-2 - oparg] = callable;
+ stack_pointer[-1 - oparg] = self_or_null;
break;
}
JitOptRef callable;
self_or_null = stack_pointer[-1 - oparg];
callable = stack_pointer[-2 - oparg];
- callable = sym_new_not_null(ctx);
- self_or_null = sym_new_not_null(ctx);
+ PyObject *bound_method = sym_get_probable_value(callable);
+ if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) {
+ PyMethodObject *method = (PyMethodObject *)bound_method;
+ callable = sym_new_not_null(ctx);
+ sym_set_recorded_value(callable, method->im_func);
+ self_or_null = sym_new_not_null(ctx);
+ sym_set_recorded_value(self_or_null, method->im_self);
+ }
+ else {
+ callable = sym_new_not_null(ctx);
+ self_or_null = sym_new_not_null(ctx);
+ }
stack_pointer[-2 - oparg] = callable;
stack_pointer[-1 - oparg] = self_or_null;
break;
}
case _RECORD_BOUND_METHOD: {
+ JitOptRef callable;
+ callable = stack_pointer[-2 - oparg];
+ sym_set_recorded_value(callable, (PyObject *)this_instr->operand0);
break;
}
callable = stack_pointer[-2 - oparg];
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
if (Py_TYPE(callable_o) == &PyMethod_Type) {
- PyObject *func = ((PyMethodObject *)callable_o)->im_func;
- *recorded_value = (PyObject *)func;
+ *recorded_value = (PyObject *)callable_o;
Py_INCREF(*recorded_value);
}
}