diff --git a/Makefile b/Makefile index be91dcc18afedb7fc1f2ec09b34c9514b1fcd8cd..f7fca75754bda43a7ad87b65bee31b807f5338ca 100644 --- a/Makefile +++ b/Makefile @@ -776,7 +776,7 @@ check$1 test$1: $(PYTHON_EXE_DEPS) pyston$1 @# we pass -I to cpython tests and skip failing ones because they are sloooow otherwise $(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -a=-S -k --exit-code-only --skip-failing -t50 $(TEST_DIR)/cpython $(ARGS) $(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -k -a=-S --exit-code-only --skip-failing -t600 $(TEST_DIR)/integration $(ARGS) - $(PYTHON) $(TOOLS_DIR)/tester.py -a=-X -R pyston$1 -j$(TEST_THREADS) -a=-n -a=-S -k $(TESTS_DIR) $(ARGS) + $(PYTHON) $(TOOLS_DIR)/tester.py -a=-X -R pyston$1 -j$(TEST_THREADS) -a=-n -a=-S -t50 -k $(TESTS_DIR) $(ARGS) $(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -a=-O -a=-S -k $(TESTS_DIR) $(ARGS) .PHONY: run$1 dbg$1 diff --git a/src/capi/typeobject.cpp b/src/capi/typeobject.cpp index 6b76e182e8a6908fb59c647fa9cd991089aac883..b02e0f4628fc84efc4bf508445092f0143aa1597 100644 --- a/src/capi/typeobject.cpp +++ b/src/capi/typeobject.cpp @@ -3411,51 +3411,40 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp paramspec.takes_kwargs = false; } - bool rewrite_success = false; - Box** oargs = NULL; - try { - rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args, - oargs, keyword_names, NULL); - } catch (ExcInfo e) { - if (S == CAPI) { - setCAPIException(e); - return NULL; - } else - throw e; - } + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + if (!paramspec.takes_kwargs) + arg2 = NULL; - AUTO_DECREF(arg1); - if (!paramspec.takes_kwargs) - arg2 = NULL; - AUTO_XDECREF(arg2); + if (rewrite_args) { + if (!paramspec.takes_kwargs) + rewrite_args->arg2 = rewrite_args->rewriter->loadConst(0, Location::forArg(2)); + + // Currently, guard that the value of tp_call didn't change, and then + // emit a call to the current function address. + // It might be better to just load the current value of tp_call and call it + // (after guarding it's not null), or maybe not. But the rewriter doesn't currently + // support calling a RewriterVar (can only call fixed function addresses). + RewriterVar* r_cls = rewrite_args->obj->getAttr(offsetof(Box, cls)); + r_cls->addAttrGuard(offsetof(BoxedClass, tp_call), (intptr_t)self->cls->tp_call); + + rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->cls->tp_call, rewrite_args->obj, + rewrite_args->arg1, rewrite_args->arg2); + rewrite_args->out_rtn->setType(RefType::OWNED); + if (S == CXX) + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } - if (!rewrite_success) - rewrite_args = NULL; + Box* r = self->cls->tp_call(self, arg1, arg2); + if (S == CXX && !r) + throwCAPIException(); + return r; + }; - if (rewrite_args) { - if (!paramspec.takes_kwargs) - rewrite_args->arg2 = rewrite_args->rewriter->loadConst(0, Location::forArg(2)); - - // Currently, guard that the value of tp_call didn't change, and then - // emit a call to the current function address. - // It might be better to just load the current value of tp_call and call it - // (after guarding it's not null), or maybe not. But the rewriter doesn't currently - // support calling a RewriterVar (can only call fixed function addresses). - RewriterVar* r_cls = rewrite_args->obj->getAttr(offsetof(Box, cls)); - r_cls->addAttrGuard(offsetof(BoxedClass, tp_call), (intptr_t)self->cls->tp_call); - - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->cls->tp_call, rewrite_args->obj, - rewrite_args->arg1, rewrite_args->arg2); - rewrite_args->out_rtn->setType(RefType::OWNED); - if (S == CXX) - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } - - Box* r = self->cls->tp_call(self, arg1, arg2); - if (!r && S == CXX) - throwCAPIException(); - return r; + return callCXXFromStyle<S>([&]() { + return rearrangeArgumentsAndCall(paramspec, NULL, "", NULL, rewrite_args, argspec, arg1, arg2, arg3, args, + keyword_names, continuation); + }); } extern "C" void PyType_RequestHcAttrs(PyTypeObject* cls, int offset) noexcept { diff --git a/src/core/types.h b/src/core/types.h index 5e10250b63480e3589a82bfa169a3d683756a827..6d70a060da1d519baf3444c110027cf4bf4ffa29 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -262,15 +262,16 @@ struct ParamReceiveSpec { assert(num_defaults <= MAX_DEFAULTS); } - bool operator==(ParamReceiveSpec rhs) { + bool operator==(ParamReceiveSpec rhs) const { return takes_varargs == rhs.takes_varargs && takes_kwargs == rhs.takes_kwargs && num_defaults == rhs.num_defaults && num_args == rhs.num_args; } - bool operator!=(ParamReceiveSpec rhs) { return !(*this == rhs); } + bool operator!=(ParamReceiveSpec rhs) const { return !(*this == rhs); } - int totalReceived() { return num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0); } - int kwargsIndex() { return num_args + (takes_varargs ? 1 : 0); } + int totalReceived() const { return num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0); } + int varargsIndex() const { return num_args; } + int kwargsIndex() const { return num_args + (takes_varargs ? 1 : 0); } }; // Inline-caches contain fastpath code, and need to know that their fastpath is valid for a particular set diff --git a/src/runtime/builtin_modules/builtins.cpp b/src/runtime/builtin_modules/builtins.cpp index 06695cc9b5c85dc2e3772d77ede3372ea8f8cd6f..bb00bfdddae0ac969a8dbf6165106248dfa10a92 100644 --- a/src/runtime/builtin_modules/builtins.cpp +++ b/src/runtime/builtin_modules/builtins.cpp @@ -553,104 +553,103 @@ template <ExceptionStyle S> Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) { static Box* defaults[] = { NULL }; - bool rewrite_success = false; - rearrangeArguments(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args, rewrite_success, - argspec, arg1, arg2, arg3, args, NULL, keyword_names, NULL); - if (!rewrite_success) - rewrite_args = NULL; - - Box* obj = arg1; - Box* _str = arg2; - Box* default_value = arg3; - - AUTO_DECREF(obj); - AUTO_DECREF(_str); - AUTO_XDECREF(default_value); - - if (rewrite_args) { - // We need to make sure that the attribute string will be the same. - // Even though the passed string might not be the exact attribute name - // that we end up looking up (because we need to encode it or intern it), - // guarding on that object means (for strings and unicode) that the string - // value is fixed. - if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str)) - rewrite_args = NULL; - else { - if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) { - // can avoid keeping the extra gc reference - } else { - rewrite_args->rewriter->addGCReference(_str); - } - rewrite_args->arg2->addGuard((intptr_t)arg2); + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + Box* obj = arg1; + Box* _str = arg2; + Box* default_value = arg3; + + if (rewrite_args) { + // We need to make sure that the attribute string will be the same. + // Even though the passed string might not be the exact attribute name + // that we end up looking up (because we need to encode it or intern it), + // guarding on that object means (for strings and unicode) that the string + // value is fixed. + if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str)) + rewrite_args = NULL; + else { + if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) { + // can avoid keeping the extra gc reference + } else { + rewrite_args->rewriter->addGCReference(_str); + } + + rewrite_args->arg2->addGuard((intptr_t)arg2); + } } - } - _str = coerceUnicodeToStr<S>(_str); - if (S == CAPI && !_str) - return NULL; + _str = coerceUnicodeToStr<S>(_str); - if (!PyString_Check(_str)) { - Py_DECREF(_str); - if (S == CAPI) { - PyErr_SetString(TypeError, "getattr(): attribute name must be string"); - return NULL; - } else - raiseExcHelper(TypeError, "getattr(): attribute name must be string"); - } + if (S == CAPI && !_str) + return (Box*)NULL; - BoxedString* str = static_cast<BoxedString*>(_str); - if (!PyString_CHECK_INTERNED(str)) - internStringMortalInplace(str); - AUTO_DECREF(str); + if (!PyString_Check(_str)) { + Py_DECREF(_str); + if (S == CAPI) { + PyErr_SetString(TypeError, "getattr(): attribute name must be string"); + return (Box*)NULL; + } else + raiseExcHelper(TypeError, "getattr(): attribute name must be string"); + } - Box* rtn; - RewriterVar* r_rtn; - if (rewrite_args) { - GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination); - rtn = getattrInternal<CAPI>(obj, str, &grewrite_args); - // TODO could make the return valid in the NOEXC_POSSIBLE case via a helper - if (!grewrite_args.isSuccessful()) - rewrite_args = NULL; - else { - ReturnConvention return_convention; - std::tie(r_rtn, return_convention) = grewrite_args.getReturn(); - - // Convert to NOEXC_POSSIBLE: - if (return_convention == ReturnConvention::NO_RETURN) { - return_convention = ReturnConvention::NOEXC_POSSIBLE; - r_rtn = rewrite_args->rewriter->loadConst(0); - } else if (return_convention == ReturnConvention::MAYBE_EXC) { - if (default_value) - rewrite_args = NULL; + BoxedString* str = static_cast<BoxedString*>(_str); + if (!PyString_CHECK_INTERNED(str)) + internStringMortalInplace(str); + AUTO_DECREF(str); + + Box* rtn; + RewriterVar* r_rtn; + if (rewrite_args) { + GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination); + rtn = getattrInternal<CAPI>(obj, str, &grewrite_args); + // TODO could make the return valid in the NOEXC_POSSIBLE case via a helper + if (!grewrite_args.isSuccessful()) + rewrite_args = NULL; + else { + ReturnConvention return_convention; + std::tie(r_rtn, return_convention) = grewrite_args.getReturn(); + + // Convert to NOEXC_POSSIBLE: + if (return_convention == ReturnConvention::NO_RETURN) { + return_convention = ReturnConvention::NOEXC_POSSIBLE; + r_rtn = rewrite_args->rewriter->loadConst(0); + } else if (return_convention == ReturnConvention::MAYBE_EXC) { + if (default_value) + rewrite_args = NULL; + } + assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE + || return_convention == ReturnConvention::HAS_RETURN + || return_convention == ReturnConvention::CAPI_RETURN + || (default_value == NULL && return_convention == ReturnConvention::MAYBE_EXC)); } - assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE - || return_convention == ReturnConvention::HAS_RETURN - || return_convention == ReturnConvention::CAPI_RETURN - || (default_value == NULL && return_convention == ReturnConvention::MAYBE_EXC)); + } else { + rtn = getattrInternal<CAPI>(obj, str); } - } else { - rtn = getattrInternal<CAPI>(obj, str); - } - if (rewrite_args) { - assert(PyString_CHECK_INTERNED(str) == SSTATE_INTERNED_IMMORTAL); - RewriterVar* r_str = rewrite_args->rewriter->loadConst((intptr_t)str, Location::forArg(2)); - RewriterVar* final_rtn - = rewrite_args->rewriter->call(false, (void*)getattrFuncHelper, r_rtn, rewrite_args->arg1, r_str, - rewrite_args->arg3)->setType(RefType::OWNED); - r_rtn->refConsumed(); + if (rewrite_args) { + assert(PyString_CHECK_INTERNED(str) == SSTATE_INTERNED_IMMORTAL); + RewriterVar* r_str = rewrite_args->rewriter->loadConst((intptr_t)str, Location::forArg(2)); + RewriterVar* final_rtn + = rewrite_args->rewriter->call(false, (void*)getattrFuncHelper, r_rtn, rewrite_args->arg1, r_str, + rewrite_args->arg3)->setType(RefType::OWNED); + r_rtn->refConsumed(); + + if (S == CXX) + rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn); + rewrite_args->out_success = true; + rewrite_args->out_rtn = final_rtn; + } - if (S == CXX) - rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn); - rewrite_args->out_success = true; - rewrite_args->out_rtn = final_rtn; - } + Box* r = getattrFuncHelper(rtn, obj, str, default_value); + if (S == CXX && !r) + throwCAPIException(); + return r; + }; - Box* r = getattrFuncHelper(rtn, obj, str, default_value); - if (S == CXX && !r) - throwCAPIException(); - return r; + return callCXXFromStyle<S>([&]() { + return rearrangeArgumentsAndCall(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args, + argspec, arg1, arg2, arg3, args, keyword_names, continuation); + }); } Box* setattrFunc(Box* obj, Box* _str, Box* value) { @@ -688,97 +687,96 @@ static Box* hasattrFuncHelper(STOLEN(Box*) return_val) noexcept { template <ExceptionStyle S> Box* hasattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) { - bool rewrite_success = false; - rearrangeArguments(ParamReceiveSpec(2, 0, false, false), NULL, "hasattr", NULL, rewrite_args, rewrite_success, - argspec, arg1, arg2, arg3, args, NULL, keyword_names, NULL); - if (!rewrite_success) - rewrite_args = NULL; - - AUTO_DECREF(arg1); - AUTO_DECREF(arg2); - - Box* obj = arg1; - Box* _str = arg2; - - if (rewrite_args) { - // We need to make sure that the attribute string will be the same. - // Even though the passed string might not be the exact attribute name - // that we end up looking up (because we need to encode it or intern it), - // guarding on that object means (for strings and unicode) that the string - // value is fixed. - if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str)) - rewrite_args = NULL; - else { - if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) { - // can avoid keeping the extra gc reference - } else { - rewrite_args->rewriter->addGCReference(_str); - } + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + Box* obj = arg1; + Box* _str = arg2; + + if (rewrite_args) { + // We need to make sure that the attribute string will be the same. + // Even though the passed string might not be the exact attribute name + // that we end up looking up (because we need to encode it or intern it), + // guarding on that object means (for strings and unicode) that the string + // value is fixed. + if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str)) + rewrite_args = NULL; + else { + if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) { + // can avoid keeping the extra gc reference + } else { + rewrite_args->rewriter->addGCReference(_str); + } - rewrite_args->arg2->addGuard((intptr_t)arg2); + rewrite_args->arg2->addGuard((intptr_t)arg2); + } } - } - _str = coerceUnicodeToStr<S>(_str); - if (S == CAPI && !_str) - return NULL; + _str = coerceUnicodeToStr<S>(_str); - if (!PyString_Check(_str)) { - Py_DECREF(_str); - if (S == CAPI) { - PyErr_SetString(TypeError, "hasattr(): attribute name must be string"); - return NULL; - } else - raiseExcHelper(TypeError, "hasattr(): attribute name must be string"); - } + if (S == CAPI && !_str) + return (Box*)NULL; - BoxedString* str = static_cast<BoxedString*>(_str); + if (!PyString_Check(_str)) { + Py_DECREF(_str); + if (S == CAPI) { + PyErr_SetString(TypeError, "hasattr(): attribute name must be string"); + return (Box*)NULL; + } else + raiseExcHelper(TypeError, "hasattr(): attribute name must be string"); + } - if (!PyString_CHECK_INTERNED(str)) - internStringMortalInplace(str); - AUTO_DECREF(str); + BoxedString* str = static_cast<BoxedString*>(_str); - Box* rtn; - RewriterVar* r_rtn; - if (rewrite_args) { - GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination); - rtn = getattrInternal<CAPI>(obj, str, &grewrite_args); - if (!grewrite_args.isSuccessful()) - rewrite_args = NULL; - else { - ReturnConvention return_convention; - std::tie(r_rtn, return_convention) = grewrite_args.getReturn(); - - // Convert to NOEXC_POSSIBLE: - if (return_convention == ReturnConvention::NO_RETURN) { - return_convention = ReturnConvention::NOEXC_POSSIBLE; - r_rtn = rewrite_args->rewriter->loadConst(0); - } else if (return_convention == ReturnConvention::MAYBE_EXC) { + if (!PyString_CHECK_INTERNED(str)) + internStringMortalInplace(str); + AUTO_DECREF(str); + + Box* rtn; + RewriterVar* r_rtn; + if (rewrite_args) { + GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination); + rtn = getattrInternal<CAPI>(obj, str, &grewrite_args); + if (!grewrite_args.isSuccessful()) rewrite_args = NULL; + else { + ReturnConvention return_convention; + std::tie(r_rtn, return_convention) = grewrite_args.getReturn(); + + // Convert to NOEXC_POSSIBLE: + if (return_convention == ReturnConvention::NO_RETURN) { + return_convention = ReturnConvention::NOEXC_POSSIBLE; + r_rtn = rewrite_args->rewriter->loadConst(0); + } else if (return_convention == ReturnConvention::MAYBE_EXC) { + rewrite_args = NULL; + } + assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE + || return_convention == ReturnConvention::HAS_RETURN + || return_convention == ReturnConvention::CAPI_RETURN); } - assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE - || return_convention == ReturnConvention::HAS_RETURN - || return_convention == ReturnConvention::CAPI_RETURN); + } else { + rtn = getattrInternal<CAPI>(obj, str); } - } else { - rtn = getattrInternal<CAPI>(obj, str); - } - if (rewrite_args) { - RewriterVar* final_rtn - = rewrite_args->rewriter->call(false, (void*)hasattrFuncHelper, r_rtn)->setType(RefType::OWNED); - r_rtn->refConsumed(); + if (rewrite_args) { + RewriterVar* final_rtn + = rewrite_args->rewriter->call(false, (void*)hasattrFuncHelper, r_rtn)->setType(RefType::OWNED); + r_rtn->refConsumed(); - if (S == CXX) - rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn); - rewrite_args->out_success = true; - rewrite_args->out_rtn = final_rtn; - } + if (S == CXX) + rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn); + rewrite_args->out_success = true; + rewrite_args->out_rtn = final_rtn; + } - Box* r = hasattrFuncHelper(rtn); - if (S == CXX && !r) - throwCAPIException(); - return r; + Box* r = hasattrFuncHelper(rtn); + if (S == CXX && !r) + throwCAPIException(); + return r; + }; + + return callCXXFromStyle<S>([&]() { + return rearrangeArgumentsAndCall(ParamReceiveSpec(2, 0, false, false), NULL, "hasattr", NULL, rewrite_args, + argspec, arg1, arg2, arg3, args, keyword_names, continuation); + }); } Box* map2(Box* f, Box* container) { diff --git a/src/runtime/capi.cpp b/src/runtime/capi.cpp index 9af73cc7141535d4f3d891f9959c071f388dd90a..1a0dc47fad85872eccbb11b671f718e4e81baa0e 100644 --- a/src/runtime/capi.cpp +++ b/src/runtime/capi.cpp @@ -1797,85 +1797,82 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa // so we could just rearrangeArguments to the form that it wants and then call tp_new directly. } - bool rewrite_success = false; - rearrangeArguments(paramspec, NULL, self->method_def->ml_name, defaults, rewrite_args, rewrite_success, argspec, - arg1, arg2, arg3, args, oargs, keyword_names, NULL); - - AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs); - - if (!rewrite_success) - rewrite_args = NULL; - - RewriterVar* r_passthrough = NULL; - if (rewrite_args) - r_passthrough = rewrite_args->rewriter->loadConst((intptr_t)self->passthrough, Location::forArg(0)); - - Box* rtn; - if (flags == METH_VARARGS) { - rtn = (Box*)func(self->passthrough, arg1); - if (rewrite_args) - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1) - ->setType(RefType::OWNED); - } else if (flags == (METH_VARARGS | METH_KEYWORDS)) { - rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, arg1, arg2); - if (rewrite_args) - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1, - rewrite_args->arg2)->setType(RefType::OWNED); - } else if (flags == METH_NOARGS) { - rtn = (Box*)func(self->passthrough, NULL); - if (rewrite_args) - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, - rewrite_args->rewriter->loadConst(0, Location::forArg(1))) - ->setType(RefType::OWNED); - } else if (flags == METH_O) { - rtn = (Box*)func(self->passthrough, arg1); + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + RewriterVar* r_passthrough = NULL; if (rewrite_args) - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1) - ->setType(RefType::OWNED); - } else if ((flags & ~(METH_O3 | METH_D3)) == 0) { - assert(paramspec.totalReceived() <= 3); // would need to pass through oargs - rtn = ((Box * (*)(Box*, Box*, Box*, Box*))func)(self->passthrough, arg1, arg2, arg3); - if (rewrite_args) { - if (paramspec.totalReceived() == 1) + r_passthrough = rewrite_args->rewriter->loadConst((intptr_t)self->passthrough, Location::forArg(0)); + + Box* rtn; + if (flags == METH_VARARGS) { + rtn = (Box*)func(self->passthrough, arg1); + if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1)->setType(RefType::OWNED); - else if (paramspec.totalReceived() == 2) + } else if (flags == (METH_VARARGS | METH_KEYWORDS)) { + rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, arg1, arg2); + if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1, rewrite_args->arg2)->setType(RefType::OWNED); - else if (paramspec.totalReceived() == 3) + } else if (flags == METH_NOARGS) { + rtn = (Box*)func(self->passthrough, NULL); + if (rewrite_args) rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1, - rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED); - else - abort(); + = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, + rewrite_args->rewriter->loadConst(0, Location::forArg(1))) + ->setType(RefType::OWNED); + } else if (flags == METH_O) { + rtn = (Box*)func(self->passthrough, arg1); + if (rewrite_args) + rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, + rewrite_args->arg1)->setType(RefType::OWNED); + } else if ((flags & ~(METH_O3 | METH_D3)) == 0) { + assert(paramspec.totalReceived() <= 3); // would need to pass through oargs + rtn = ((Box * (*)(Box*, Box*, Box*, Box*))func)(self->passthrough, arg1, arg2, arg3); + if (rewrite_args) { + if (paramspec.totalReceived() == 1) + rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, + rewrite_args->arg1)->setType(RefType::OWNED); + else if (paramspec.totalReceived() == 2) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1, + rewrite_args->arg2)->setType(RefType::OWNED); + else if (paramspec.totalReceived() == 3) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1, + rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED); + else + abort(); + } + } else if (flags == METH_OLDARGS) { + /* the really old style */ + + rewrite_args = NULL; + + int size = PyTuple_GET_SIZE(arg1); + Box* arg = arg1; + if (size == 1) + arg = PyTuple_GET_ITEM(arg1, 0); + else if (size == 0) + arg = NULL; + rtn = func(self->passthrough, arg); + } else { + RELEASE_ASSERT(0, "0x%x", flags); } - } else if (flags == METH_OLDARGS) { - /* the really old style */ - rewrite_args = NULL; - - int size = PyTuple_GET_SIZE(arg1); - Box* arg = arg1; - if (size == 1) - arg = PyTuple_GET_ITEM(arg1, 0); - else if (size == 0) - arg = NULL; - rtn = func(self->passthrough, arg); - } else { - RELEASE_ASSERT(0, "0x%x", flags); - } + if (rewrite_args) { + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } - if (rewrite_args) { - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } + if (S == CXX && !rtn) + throwCAPIException(); + assert(rtn && "should have set + thrown an exception!"); + return rtn; + }; - if (!rtn) - throwCAPIException(); - assert(rtn && "should have set + thrown an exception!"); - return rtn; + return rearrangeArgumentsAndCall(paramspec, NULL, self->method_def->ml_name, defaults, rewrite_args, argspec, arg1, + arg2, arg3, args, keyword_names, continuation); } /* extension modules might be compiled with GC support so these diff --git a/src/runtime/descr.cpp b/src/runtime/descr.cpp index 777ca32d7fda9557fe2b3ba8b3f02979f52e63b8..2eec041d87807d71305aeb5b29267798e6ae3751 100644 --- a/src/runtime/descr.cpp +++ b/src/runtime/descr.cpp @@ -311,15 +311,6 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A RELEASE_ASSERT(0, "0x%x", call_flags); } - Box** oargs = NULL; - - Box* oargs_array[1] = { NULL }; - if (paramspec.totalReceived() > 3) { - assert((paramspec.totalReceived() - 3) <= sizeof(oargs_array) / sizeof(oargs_array[0])); - oargs = oargs_array; - } - bool oargs_owned[1]; - bool arg1_class_guarded = false; if (rewrite_args && argspec.num_args >= 1) { // Try to do the guard before rearrangeArguments if possible: @@ -327,105 +318,102 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A arg1_class_guarded = true; } - bool rewrite_success = false; - rearrangeArguments(paramspec, NULL, self->method->ml_name, defaults, rewrite_args, rewrite_success, argspec, arg1, - arg2, arg3, args, oargs, keyword_names, oargs_owned); - - AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs); - - if (!rewrite_success) - rewrite_args = NULL; - - if (ml_flags & METH_CLASS) { - rewrite_args = NULL; - if (!PyType_Check(arg1)) - raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->method->ml_name, - getFullTypeName(arg1).c_str()); - } else { - if (!isSubclass(arg1->cls, self->type)) - raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'", self->method->ml_name, - getFullNameOfClass(self->type).c_str(), getFullTypeName(arg1).c_str()); - } - - if (rewrite_args && !arg1_class_guarded) { - rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls); - } - - Box* rtn; - if (call_flags == METH_NOARGS) { - { - UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); - rtn = (Box*)self->method->ml_meth(arg1, NULL); - } - if (rewrite_args) - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, - rewrite_args->rewriter->loadConst(0, Location::forArg(1))) - ->setType(RefType::OWNED); - } else if (call_flags == METH_VARARGS) { - { - UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); - rtn = (Box*)self->method->ml_meth(arg1, arg2); - } - if (rewrite_args) - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, - rewrite_args->arg2)->setType(RefType::OWNED); - } else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) { - { - UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); - rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(arg1, arg2, arg3); - } - if (rewrite_args) - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, - rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED); - } else if (call_flags == METH_O) { - { - UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); - rtn = (Box*)self->method->ml_meth(arg1, arg2); + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + if (ml_flags & METH_CLASS) { + rewrite_args = NULL; + if (!PyType_Check(arg1)) + raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->method->ml_name, + getFullTypeName(arg1).c_str()); + } else { + if (!isSubclass(arg1->cls, self->type)) + raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'", + self->method->ml_name, getFullNameOfClass(self->type).c_str(), + getFullTypeName(arg1).c_str()); } - if (rewrite_args) - rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, - rewrite_args->arg2)->setType(RefType::OWNED); - } else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) { - { - UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); - rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, oargs); + + if (rewrite_args && !arg1_class_guarded) { + rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls); } - if (rewrite_args) { - if (paramspec.totalReceived() == 2) + + Box* rtn; + if (call_flags == METH_NOARGS) { + { + UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); + rtn = (Box*)self->method->ml_meth(arg1, NULL); + } + if (rewrite_args) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, + rewrite_args->rewriter->loadConst(0, Location::forArg(1))) + ->setType(RefType::OWNED); + } else if (call_flags == METH_VARARGS) { + { + UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); + rtn = (Box*)self->method->ml_meth(arg1, arg2); + } + if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, rewrite_args->arg2)->setType(RefType::OWNED); - else if (paramspec.totalReceived() == 3) + } else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) { + { + UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); + rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(arg1, arg2, arg3); + } + if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED); - else if (paramspec.totalReceived() > 3) + } else if (call_flags == METH_O) { + { + UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); + rtn = (Box*)self->method->ml_meth(arg1, arg2); + } + if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, - rewrite_args->arg2, rewrite_args->arg3, - rewrite_args->args)->setType(RefType::OWNED); - else - abort(); + rewrite_args->arg2)->setType(RefType::OWNED); + } else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) { + { + UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins"); + rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, args); + } + if (rewrite_args) { + if (paramspec.totalReceived() == 2) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, + rewrite_args->arg2)->setType(RefType::OWNED); + else if (paramspec.totalReceived() == 3) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, + rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED); + else if (paramspec.totalReceived() > 3) + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1, + rewrite_args->arg2, rewrite_args->arg3, + rewrite_args->args)->setType(RefType::OWNED); + else + abort(); + } + } else { + RELEASE_ASSERT(0, "0x%x", call_flags); } - } else { - RELEASE_ASSERT(0, "0x%x", call_flags); - } - if (!rtn) - throwCAPIException(); + if (!rtn) + throwCAPIException(); - if (rewrite_args && oargs) - decrefOargs(rewrite_args->args, oargs_owned, 1); + if (rewrite_args) { + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } - if (rewrite_args) { - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } + return rtn; + }; - return rtn; + return rearrangeArgumentsAndCall(paramspec, NULL, self->method->ml_name, defaults, rewrite_args, argspec, arg1, + arg2, arg3, args, keyword_names, continuation); } + static Box* methodGetName(Box* b, void*) { assert(b->cls == method_cls); const char* s = static_cast<BoxedMethodDescriptor*>(b)->method->ml_name; @@ -646,74 +634,76 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, RELEASE_ASSERT(0, "%d", flags); } - Box** oargs = NULL; - - bool rewrite_success = false; - rearrangeArguments(paramspec, NULL, self->wrapper->name.data(), NULL, rewrite_args, rewrite_success, argspec, arg1, - arg2, arg3, args, oargs, keyword_names, NULL); + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { +#ifndef NDEBUG + if (paramspec.takes_varargs) + assert(arg2 && arg2->cls == tuple_cls); +#endif - AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs); + Box* rtn; + if (flags == PyWrapperFlag_KEYWORDS) { + wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper; + rtn = (*wk)(arg1, arg2, self->wrapped, arg3); - if (paramspec.takes_varargs) - assert(arg2 && arg2->cls == tuple_cls); - - if (!rewrite_success) - rewrite_args = NULL; - - Box* rtn; - if (flags == PyWrapperFlag_KEYWORDS) { - wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper; - rtn = (*wk)(arg1, arg2, self->wrapped, arg3); - - if (rewrite_args) { - auto rewriter = rewrite_args->rewriter; - rewrite_args->out_rtn = rewriter->call(true, (void*)wk, rewrite_args->arg1, rewrite_args->arg2, - rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)), - rewrite_args->arg3)->setType(RefType::OWNED); - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } - } else if (flags == PyWrapperFlag_PYSTON || flags == 0) { - rtn = (*wrapper)(arg1, arg2, self->wrapped); - - if (rewrite_args) { - auto rewriter = rewrite_args->rewriter; - rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2, - rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2))) - ->setType(RefType::OWNED); - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } - } else if (flags == PyWrapperFlag_1ARG) { - wrapperfunc_1arg wrapper_1arg = (wrapperfunc_1arg)wrapper; - rtn = (*wrapper_1arg)(arg1, self->wrapped); - - if (rewrite_args) { - auto rewriter = rewrite_args->rewriter; - rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, - rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(1))) - ->setType(RefType::OWNED); - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; + if (rewrite_args) { + auto rewriter = rewrite_args->rewriter; + rewrite_args->out_rtn + = rewriter->call(true, (void*)wk, rewrite_args->arg1, rewrite_args->arg2, + rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)), + rewrite_args->arg3)->setType(RefType::OWNED); + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } + } else if (flags == PyWrapperFlag_PYSTON || flags == 0) { + rtn = (*wrapper)(arg1, arg2, self->wrapped); + + if (rewrite_args) { + auto rewriter = rewrite_args->rewriter; + rewrite_args->out_rtn + = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2, + rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2))) + ->setType(RefType::OWNED); + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } + } else if (flags == PyWrapperFlag_1ARG) { + wrapperfunc_1arg wrapper_1arg = (wrapperfunc_1arg)wrapper; + rtn = (*wrapper_1arg)(arg1, self->wrapped); + + if (rewrite_args) { + auto rewriter = rewrite_args->rewriter; + rewrite_args->out_rtn + = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, + rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(1))) + ->setType(RefType::OWNED); + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } + } else if (flags == PyWrapperFlag_2ARG) { + rtn = (*wrapper)(arg1, arg2, self->wrapped); + + if (rewrite_args) { + auto rewriter = rewrite_args->rewriter; + rewrite_args->out_rtn + = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2, + rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2))) + ->setType(RefType::OWNED); + rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); + rewrite_args->out_success = true; + } + } else { + RELEASE_ASSERT(0, "%d", flags); } - } else if (flags == PyWrapperFlag_2ARG) { - rtn = (*wrapper)(arg1, arg2, self->wrapped); - if (rewrite_args) { - auto rewriter = rewrite_args->rewriter; - rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2, - rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2))) - ->setType(RefType::OWNED); - rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); - rewrite_args->out_success = true; - } - } else { - RELEASE_ASSERT(0, "%d", flags); - } + if (S == CXX && !rtn) + throwCAPIException(); + return rtn; + }; - if (!rtn) - throwCAPIException(); - return rtn; + return callCXXFromStyle<S>([&]() { + return rearrangeArgumentsAndCall(paramspec, NULL, self->wrapper->name.data(), NULL, rewrite_args, argspec, arg1, + arg2, arg3, args, keyword_names, continuation); + }); } static Box* wrapperdescrGetDoc(Box* b, void*) { diff --git a/src/runtime/generator.cpp b/src/runtime/generator.cpp index 11aaf9218da656cd9819815e22bc281055c84440..08660d44c9e77cf28a45a0f2101389bc755bff62 100644 --- a/src/runtime/generator.cpp +++ b/src/runtime/generator.cpp @@ -429,11 +429,14 @@ extern "C" BoxedGenerator::BoxedGenerator(BoxedFunctionBase* function, Box* arg1 #endif { Py_INCREF(function); - Py_XINCREF(arg1); - Py_XINCREF(arg2); - Py_XINCREF(arg3); int numArgs = function->md->numReceivedArgs(); + if (numArgs > 0) + Py_XINCREF(arg1); + if (numArgs > 1) + Py_XINCREF(arg2); + if (numArgs > 2) + Py_XINCREF(arg3); if (numArgs > 3) { numArgs -= 3; this->args = new (numArgs) GCdArray(); @@ -631,15 +634,16 @@ static void generator_dealloc(BoxedGenerator* self) noexcept { int numArgs = self->function->md->numReceivedArgs(); if (numArgs > 3) { - numArgs -= 3; - for (int i = 0; i < numArgs; i++) { + for (int i = 0; i < numArgs - 3; i++) { Py_CLEAR(self->args->elts[i]); } } - - Py_CLEAR(self->arg1); - Py_CLEAR(self->arg2); - Py_CLEAR(self->arg3); + if (numArgs > 2) + Py_CLEAR(self->arg3); + if (numArgs > 1) + Py_CLEAR(self->arg2); + if (numArgs > 0) + Py_CLEAR(self->arg1); Py_CLEAR(self->function); @@ -667,15 +671,16 @@ static int generator_traverse(BoxedGenerator* self, visitproc visit, void* arg) int numArgs = self->function->md->numReceivedArgs(); if (numArgs > 3) { - numArgs -= 3; - for (int i = 0; i < numArgs; i++) { + for (int i = 0; i < numArgs - 3; i++) { Py_VISIT(self->args->elts[i]); } } - - Py_VISIT(self->arg1); - Py_VISIT(self->arg2); - Py_VISIT(self->arg3); + if (numArgs > 2) + Py_VISIT(self->arg3); + if (numArgs > 1) + Py_VISIT(self->arg2); + if (numArgs > 0) + Py_VISIT(self->arg1); Py_VISIT(self->function); diff --git a/src/runtime/objmodel.cpp b/src/runtime/objmodel.cpp index 12b16d4395a77a0cf8ae32cb0144ca1420c0065d..26fa113d58583be1894c006c98902a08cf1f0aff 100644 --- a/src/runtime/objmodel.cpp +++ b/src/runtime/objmodel.cpp @@ -4032,10 +4032,34 @@ static llvm::StringRef getFunctionName(FunctionMetadata* f) { return "<unknown function>"; } -template <typename FuncNameCB> +// A hacky little class that lets us save on some parameter size. +// We want to pass enough information to rearrangeArgumentsAndCallInternal so that +// it knows how to get the function name when it needs to (only when an exception is thrown), +// and there are a couple different ways that it might need to do that. So we pass through this +// class that steals some bits from the pointers. +class FuncNameGetter { +private: + uint64_t data; + + // Can't be the lsb since a const char* is only 1-byte aligned. + static constexpr uint64_t BIT = (1L << 63); + +public: + FuncNameGetter(const char* name) : data((uint64_t)name) { assert(!(data & BIT)); } + FuncNameGetter(FunctionMetadata* md) : data(((uint64_t)md) ^ BIT) { assert(data & BIT); } + + const char* operator()() { + if (data & BIT) { + FunctionMetadata* md = (FunctionMetadata*)(data ^ BIT); + return getFunctionName(md).data(); + } else { + return (const char*)data; + } + } +}; static int placeKeyword(const ParamNames* param_names, llvm::SmallVector<bool, 8>& params_filled, BoxedString* kw_name, Box* kw_val, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs, BoxedDict* okwargs, - FuncNameCB func_name_cb) { + FuncNameGetter func_name_cb) { assert(kw_val); assert(kw_name); @@ -4120,25 +4144,10 @@ public: } }; -void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int num_oargs) { - for (int i = 0; i < num_oargs; i++) { - if (oargs_owned[i]) { - oargs->deregisterOwnedAttr(i * sizeof(Box*)); - oargs->getAttr(i * sizeof(Box*))->setType(RefType::OWNED); - } - } -} - -template <Rewritable rewritable, typename FuncNameCB> -void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* param_names, FuncNameCB func_name_cb, - Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success, - ArgPassSpec argspec, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** args, Box** oargs, - const std::vector<BoxedString*>* keyword_names, bool* oargs_owned) { - if (rewritable == NOT_REWRITABLE) { - assert(!rewrite_args); - rewrite_args = NULL; - } - +Box* rearrangeArgumentsAndCallInternal(ParamReceiveSpec paramspec, const ParamNames* param_names, + FuncNameGetter func_name_cb, Box** defaults, CallRewriteArgs* rewrite_args, + ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args, + const std::vector<BoxedString*>* keyword_names, FunctorPointer continuation) { /* * Procedure: * - First match up positional arguments; any extra go to varargs. error if too many. @@ -4147,44 +4156,17 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa * - error about missing parameters */ - int num_output_args = paramspec.totalReceived(); - int num_passed_args = argspec.totalPassed(); - - assert((oargs != NULL) == (num_output_args > 3)); assert((defaults != NULL) == (paramspec.num_defaults != 0)); - if (rewrite_args && oargs) { - assert(oargs_owned); - memset(oargs_owned, 0, (num_output_args - 3) * sizeof(bool)); - } - - if (rewrite_args) { - rewrite_success = false; // default case - } - - auto propagate_args = [&]() { - if (num_output_args >= 1) - Py_XINCREF(oarg1); - if (num_output_args >= 2) - Py_XINCREF(oarg2); - if (num_output_args >= 3) - Py_XINCREF(oarg3); - if (num_output_args >= 3) { - memcpy(oargs, args, sizeof(Box*) * (num_output_args - 3)); - for (int i = 0; i < num_output_args - 3; i++) { - Py_XINCREF(oargs[i]); - } - } - }; - // Super fast path: if (argspec.num_keywords == 0 && !argspec.has_starargs && !paramspec.takes_varargs && !argspec.has_kwargs && argspec.num_args == paramspec.num_args && !paramspec.takes_kwargs) { - rewrite_success = true; - propagate_args(); - return; + return continuation(rewrite_args, arg1, arg2, arg3, args); } + int num_output_args = paramspec.totalReceived(); + int num_passed_args = argspec.totalPassed(); + // Fast path: if it's a simple-enough call, we don't have to do anything special. On a simple // django-admin test this covers something like 93% of all calls to callFunc. if (argspec.num_keywords == 0 && argspec.has_starargs == paramspec.takes_varargs && !argspec.has_kwargs @@ -4195,7 +4177,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa assert(num_output_args == num_passed_args + 1); int idx = paramspec.kwargsIndex(); assert(idx < 3); - getArg(idx, oarg1, oarg2, oarg3, NULL) = NULL; // pass NULL for kwargs + getArg(idx, arg1, arg2, arg3, NULL) = NULL; // pass NULL for kwargs if (rewrite_args) { if (idx == 0) rewrite_args->arg1 = rewrite_args->rewriter->loadConst(0)->setType(RefType::BORROWED); @@ -4215,31 +4197,28 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa // received by the caller are always tuples). // This is why we can't pass kwargs here. if (argspec.has_starargs) { - Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, oarg1, oarg2, oarg3, args); + Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, arg1, arg2, arg3, args); if (given_varargs->cls == tuple_cls) { if (rewrite_args) { getArg(argspec.num_args + argspec.num_keywords, rewrite_args) ->addAttrGuard(offsetof(Box, cls), (intptr_t)tuple_cls); } - rewrite_success = true; - propagate_args(); - return; + return continuation(rewrite_args, arg1, arg2, arg3, args); } } else { - rewrite_success = true; - propagate_args(); - return; + return continuation(rewrite_args, arg1, arg2, arg3, args); } } - // Save the original values: - Box* arg1 = oarg1; - Box* arg2 = oarg2; - Box* arg3 = oarg3; + Box* oarg1, *oarg2, *oarg3; oarg1 = oarg2 = oarg3 = NULL; - if (oargs) + Box** oargs = NULL; + // TODO: could we reuse the args array? + if (num_output_args > 3) { + oargs = (Box**)alloca(sizeof(Box*) * (num_output_args - 3)); memset(oargs, 0, sizeof(Box*) * (num_output_args - 3)); + } // Clear any increfs we did for when we throw an exception: auto clear_refs = [&]() { @@ -4268,9 +4247,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa // At this point we are not allowed to abort the rewrite any more, since we will start // modifying rewrite_args. - if (rewrite_args) - rewrite_success = true; - if (rewrite_args) { // We might have trouble if we have more output args than input args, // such as if we need more space to pass defaults. @@ -4346,6 +4322,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa unused_positional.push_back(PySequence_Fast_GET_ITEM(varargs, i)); } + bool varargs_owned = false; if (paramspec.takes_varargs) { int varargs_idx = paramspec.num_args; if (rewrite_args) { @@ -4354,7 +4331,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa RewriterVar* varargs_val; int varargs_size = unused_positional_rvars.size(); - bool is_owned = false; if (varargs_size == 0) { varargs_val @@ -4374,7 +4350,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa }; varargs_val = rewrite_args->rewriter->call(true, create_ptrs[varargs_size], unused_positional_rvars) ->setType(RefType::OWNED); - is_owned = true; + varargs_owned = true; } if (varargs_val) { @@ -4385,13 +4361,12 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa if (varargs_idx == 2) rewrite_args->arg3 = varargs_val; if (varargs_idx >= 3) { - if (is_owned) { + if (varargs_owned) { rewrite_args->args->registerOwnedAttr((varargs_idx - 3) * sizeof(Box*)); rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val, RewriterVar::SetattrType::HANDED_OFF); - oargs_owned[varargs_idx - 3] = true; varargs_val->refConsumed(); } else { rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val); @@ -4607,25 +4582,28 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa getArg(arg_idx, oarg1, oarg2, oarg3, oargs) = xincref(default_obj); } - cleanup.cancel(); + if (rewrite_args && varargs_owned) { + Box* r = continuation(rewrite_args, oarg1, oarg2, oarg3, oargs); + + int varargs_idx = paramspec.varargsIndex(); + if (varargs_idx >= 3) { + rewrite_args->args->deregisterOwnedAttr((varargs_idx - 3) * sizeof(Box*)); + rewrite_args->args->getAttr((varargs_idx - 3) * sizeof(Box*))->setType(RefType::OWNED); + } + + return r; + } else { + return continuation(rewrite_args, oarg1, oarg2, oarg3, oargs); + } } -template <Rewritable rewritable> -void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name, - Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success, ArgPassSpec argspec, - Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** args, Box** oargs, - const std::vector<BoxedString*>* keyword_names, bool* oargs_owned) { - auto func = [func_name]() { return func_name; }; - return rearrangeArgumentsInternal<rewritable>(paramspec, param_names, func, defaults, rewrite_args, rewrite_success, - argspec, oarg1, oarg2, oarg3, args, oargs, keyword_names, - oargs_owned); -} -template void rearrangeArguments<REWRITABLE>(ParamReceiveSpec, const ParamNames*, const char*, Box**, - _CallRewriteArgsBase*, bool&, ArgPassSpec, Box*&, Box*&, Box*&, Box**, - Box**, const std::vector<BoxedString*>*, bool*); -template void rearrangeArguments<NOT_REWRITABLE>(ParamReceiveSpec, const ParamNames*, const char*, Box**, - _CallRewriteArgsBase*, bool&, ArgPassSpec, Box*&, Box*&, Box*&, Box**, - Box**, const std::vector<BoxedString*>*, bool*); +Box* rearrangeArgumentsAndCall(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name, + Box** defaults, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, + Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names, + FunctorPointer continuation) { + return rearrangeArgumentsAndCallInternal(paramspec, param_names, func_name, defaults, rewrite_args, argspec, arg1, + arg2, arg3, args, keyword_names, continuation); +} static StatCounter slowpath_callfunc("slowpath_callfunc"); template <ExceptionStyle S, Rewritable rewritable> @@ -4658,50 +4636,57 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe rewrite_args->rewriter->addDependenceOn(func->dependent_ics); } - Box** oargs = NULL; - bool* oargs_owned = NULL; - bool rewrite_success = false; - int num_output_args = paramspec.totalReceived(); int num_passed_args = argspec.totalPassed(); - if (num_output_args > 3) { - int size = (num_output_args - 3) * sizeof(Box*); - oargs = (Box**)alloca(size); - memset(&oargs[0], 0, size); + bool rearrange_rewrite_failed = false; + auto orig_rewrite_args = rewrite_args; + auto continuation = [=, &rearrange_rewrite_failed](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, + Box** args) { + if (orig_rewrite_args && !rewrite_args) + rearrange_rewrite_failed = true; - oargs_owned = (bool*)alloca((num_output_args - 3) * sizeof(bool)); - } + BoxedClosure* closure = func->closure; - try { - auto func_name_cb = [md]() { return getFunctionName(md).data(); }; - rearrangeArgumentsInternal<rewritable>( - paramspec, &md->param_names, func_name_cb, paramspec.num_defaults ? func->defaults->elts : NULL, - rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args, oargs, keyword_names, oargs_owned); - } catch (ExcInfo e) { - if (S == CAPI) { - setCAPIException(e); - return NULL; - } else - throw e; - } + // special handling for generators: + // the call to function containing a yield should just create a new generator object. + Box* res; + if (md->isGenerator()) { + res = createGenerator(func, arg1, arg2, arg3, args); + + if (rewrite_args) { + RewriterVar* r_arg1 = num_output_args >= 1 ? rewrite_args->arg1 : rewrite_args->rewriter->loadConst(0); + RewriterVar* r_arg2 = num_output_args >= 2 ? rewrite_args->arg2 : rewrite_args->rewriter->loadConst(0); + RewriterVar* r_arg3 = num_output_args >= 3 ? rewrite_args->arg3 : rewrite_args->rewriter->loadConst(0); + RewriterVar* r_args = num_output_args >= 4 ? rewrite_args->args : rewrite_args->rewriter->loadConst(0); + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)createGenerator, rewrite_args->obj, r_arg1, r_arg2, + r_arg3, r_args)->setType(RefType::OWNED); - if (num_output_args < 1) - arg1 = NULL; - if (num_output_args < 2) - arg2 = NULL; - if (num_output_args < 3) - arg3 = NULL; - AUTO_XDECREF(arg1); - AUTO_XDECREF(arg2); - AUTO_XDECREF(arg3); - AUTO_XDECREF_ARRAY(oargs, num_output_args - 3); + rewrite_args->out_success = true; + } + } else { + res = callCLFunc<S, rewritable>(md, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2, + arg3, args); + } - if (rewrite_args && !rewrite_success) { -// These are the cases that we weren't able to rewrite. -// So instead, just rewrite them to be a call to callFunc, which helps a little bit. -// TODO we should extract the rest of this function from the end of this block, -// put it in a different function, and have the rewrites target that. + return res; + }; + + Box* r = callCXXFromStyle<S>([&] { + return rearrangeArgumentsAndCallInternal(paramspec, &md->param_names, md, + paramspec.num_defaults ? func->defaults->elts : NULL, rewrite_args, + argspec, arg1, arg2, arg3, args, keyword_names, continuation); + }); + if (S == CAPI && !r) + return NULL; + + if (rearrange_rewrite_failed) { +// If we weren't able to rewrite, at least rewrite to callFunc, which helps a little bit. +// Only do this if rearrangeArguments was the reason we couldn't rewrite, since it is one +// of the few functions that is careful to not write anything out if it can't rewrite. +// +// TODO we should extract the continuation, put it in a different function, and have the rewrites target that. // Note(kmod): I tried moving this section to runtimeCallInternal, ie to the place that calls // callFunc. The thought was that this would let us apply this same optimization to other @@ -4760,34 +4745,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe } } - BoxedClosure* closure = func->closure; - - // special handling for generators: - // the call to function containing a yield should just create a new generator object. - Box* res; - if (md->isGenerator()) { - res = createGenerator(func, arg1, arg2, arg3, oargs); - - if (rewrite_args) { - RewriterVar* r_arg1 = num_output_args >= 1 ? rewrite_args->arg1 : rewrite_args->rewriter->loadConst(0); - RewriterVar* r_arg2 = num_output_args >= 2 ? rewrite_args->arg2 : rewrite_args->rewriter->loadConst(0); - RewriterVar* r_arg3 = num_output_args >= 3 ? rewrite_args->arg3 : rewrite_args->rewriter->loadConst(0); - RewriterVar* r_args = num_output_args >= 4 ? rewrite_args->args : rewrite_args->rewriter->loadConst(0); - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)createGenerator, rewrite_args->obj, r_arg1, r_arg2, r_arg3, - r_args)->setType(RefType::OWNED); - - rewrite_args->out_success = true; - } - } else { - res = callCLFunc<S, rewritable>(md, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2, - arg3, oargs); - } - - if (rewrite_args && num_output_args > 3) - decrefOargs(rewrite_args->args, oargs_owned, num_output_args - 3); - - return res; + return r; } template <ExceptionStyle S> diff --git a/src/runtime/rewrite_args.h b/src/runtime/rewrite_args.h index 912d032d7b0c583a7682d43fb7c7e1b46ab40476..b936ab00d3dc632e8a312a0948fc8195a8562d95 100644 --- a/src/runtime/rewrite_args.h +++ b/src/runtime/rewrite_args.h @@ -292,26 +292,28 @@ struct CompareRewriteArgs { : rewriter(rewriter), lhs(lhs), rhs(rhs), destination(destination), out_success(false), out_rtn(NULL) {} }; -// Passes the output arguments back through oarg. Passes the rewrite success by setting rewrite_success. -// Directly modifies rewrite_args args in place, but only if rewrite_success got set. -// oargs needs to be pre-allocated by the caller, since it's assumed that they will want to use alloca. +using FunctorPointer + = llvm::function_ref<Box*(CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args)>; + +// rearrangeArgumentsAndCall maps from a given set of arguments (the structure specified by an ArgPassSpec) to the +// parameter form than the receiving function expects (given by the ParamReceiveSpec). After it does this, it will +// call `continuation` and return the result. +// // The caller is responsible for guarding for paramspec, argspec, param_names, and defaults. -// TODO Fix this function's signature. should we pass back out through args? the common case is that they -// match anyway. Or maybe it should call a callback function, which could save on the common case. // -// Reference semantics: takes borrowed references, and everything written out is an owned reference. -template <Rewritable rewritable = REWRITABLE> -void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name, - Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success, ArgPassSpec argspec, - Box*& arg1, Box*& arg2, Box*& arg3, Box** args, Box** oargs, - const std::vector<BoxedString*>* keyword_names, bool* oargs_owned); +// rearrangeArgumentsAndCall supports both CAPI- and CXX- exception styles for continuation, and will propagate them +// back to the caller. For now, it can also throw its own exceptions such as "not enough arguments", and will throw +// them in the CXX style. +Box* rearrangeArgumentsAndCall(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name, + Box** defaults, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, + Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names, + FunctorPointer continuation); // new_args should be allocated by the caller if at least three args get passed in. // rewrite_args will get modified in place. ArgPassSpec bindObjIntoArgs(Box* bind_obj, RewriterVar* r_bind_obj, _CallRewriteArgsBase* rewrite_args, ArgPassSpec argspec, Box*& arg1, Box*& arg2, Box*& arg3, Box** args, Box** new_args); -void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int oargs_size); } // namespace pyston #endif diff --git a/src/runtime/types.cpp b/src/runtime/types.cpp index 20988c4bafd6720c050d89360b11396101c32fc8..ba1d5e5052911cdc9bd1290a6111faa9f833a923 100644 --- a/src/runtime/types.cpp +++ b/src/runtime/types.cpp @@ -833,32 +833,25 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo assert(S == CXX && "implement me"); ParamReceiveSpec paramspec(4, 3, false, false); - bool rewrite_success = false; static ParamNames param_names({ "", "string", "encoding", "errors" }, "", ""); static Box* defaults[3] = { NULL, NULL, NULL }; - Box* oargs[1] = { NULL }; - bool oargs_owned[1]; - rearrangeArguments(paramspec, ¶m_names, "unicode", defaults, rewrite_args, rewrite_success, argspec, arg1, - arg2, arg3, args, oargs, keyword_names, oargs_owned); - assert(arg1 == cls); + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + assert(arg1 == cls); - AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs); - - if (!rewrite_success) - rewrite_args = NULL; - - if (rewrite_args) { - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)unicodeNewHelper, rewrite_args->arg1, rewrite_args->arg2, - rewrite_args->arg3, rewrite_args->args)->setType(RefType::OWNED); + if (rewrite_args) { + rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)unicodeNewHelper, rewrite_args->arg1, + rewrite_args->arg2, rewrite_args->arg3, + rewrite_args->args)->setType(RefType::OWNED); - decrefOargs(rewrite_args->args, oargs_owned, 1); + rewrite_args->out_success = true; + } - rewrite_args->out_success = true; - } + return unicodeNewHelper(static_cast<BoxedClass*>(arg1), arg2, arg3, args); + }; - return unicodeNewHelper(cls, arg2, arg3, oargs); + return rearrangeArgumentsAndCall(paramspec, ¶m_names, "unicode", defaults, rewrite_args, argspec, arg1, + arg2, arg3, args, keyword_names, continuation); } if (cls->tp_new != object_cls->tp_new && cls->tp_new != slot_tp_new && cls->tp_new != BaseException->tp_new @@ -870,27 +863,21 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo assert(S == CXX && "implement me"); ParamReceiveSpec paramspec(1, false, true, true); - bool rewrite_success = false; - Box** oargs = NULL; - rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args, - oargs, keyword_names, NULL); - assert(arg1 == cls); - AUTO_DECREF(arg1); - AUTO_DECREF(arg2); - AUTO_XDECREF(arg3); - - if (!rewrite_success) - rewrite_args = NULL; + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + assert(arg1 == cls); - if (rewrite_args) { - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)cpythonTypeCall, rewrite_args->arg1, rewrite_args->arg2, - rewrite_args->arg3)->setType(RefType::OWNED); - rewrite_args->out_success = true; - } + if (rewrite_args) { + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)cpythonTypeCall, rewrite_args->arg1, rewrite_args->arg2, + rewrite_args->arg3)->setType(RefType::OWNED); + rewrite_args->out_success = true; + } - return cpythonTypeCall(cls, arg2, arg3); + return cpythonTypeCall(cls, arg2, arg3); + }; + return rearrangeArgumentsAndCall(paramspec, NULL, "", NULL, rewrite_args, argspec, arg1, arg2, arg3, args, + keyword_names, continuation); } if (argspec.has_starargs || argspec.has_kwargs) @@ -1196,27 +1183,6 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo // will be, whereas this block can't; I'm not sure how to merge the functionality. That's // probably just evidence of the overall convolutedness of this function. - // TODO: instead of rewriting to the capi-format, maybe we should do the rearrangearguments - // inside the helper? - bool rewrite_success = false; - try { - rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success, - argspec, made, arg2, arg3, args, NULL, keyword_names, NULL); - } catch (ExcInfo e) { - if (S == CAPI) { - setCAPIException(e); - return NULL; - } else - throw e; - } - - AUTO_DECREF(made); - AUTO_DECREF(arg2); - AUTO_XDECREF(arg3); - - if (!rewrite_success) - rewrite_args = NULL; - class InitHelper { public: static Box* call(STOLEN(Box*) made, BoxedClass* cls, Box* args, Box* kwargs) noexcept(S == CAPI) { @@ -1234,17 +1200,26 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo } }; - assert(arg2->cls == tuple_cls); - assert(!arg3 || arg3->cls == dict_cls); + // TODO: instead of rewriting to the capi-format, maybe we should do the rearrangearguments + // inside the helper? + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + assert(arg2->cls == tuple_cls); + assert(!arg3 || arg3->cls == dict_cls); - if (rewrite_args) { - rewrite_args->out_rtn - = rewrite_args->rewriter->call(true, (void*)InitHelper::call, r_made, r_ccls, rewrite_args->arg2, - rewrite_args->arg3)->setType(RefType::OWNED); - r_made->refConsumed(); - rewrite_args->out_success = true; - } - return InitHelper::call(made, cls, arg2, arg3); + if (rewrite_args) { + rewrite_args->out_rtn + = rewrite_args->rewriter->call(true, (void*)InitHelper::call, r_made, r_ccls, rewrite_args->arg2, + rewrite_args->arg3)->setType(RefType::OWNED); + r_made->refConsumed(); + rewrite_args->out_success = true; + } + return InitHelper::call(made, cls, arg2, arg3); + }; + + return callCXXFromStyle<S>([&]() { + return rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, argspec, + made, arg2, arg3, args, keyword_names, continuation); + }); } // If __new__ returns a subclass, supposed to call that subclass's __init__. @@ -1343,30 +1318,35 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo assert(tpinit == cls->tp_init); } - bool rewrite_success = false; - try { - rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success, - argspec, made, arg2, arg3, args, NULL, keyword_names, NULL); - } catch (ExcInfo e) { - Py_DECREF(made); - if (S == CAPI) { - setCAPIException(e); - return NULL; - } else - throw e; - } + auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { + assert(arg2->cls == tuple_cls); + assert(!arg3 || arg3->cls == dict_cls); - if (!rewrite_success) - rewrite_args = NULL; + int err = tpinit(made, arg2, arg3); - assert(arg2->cls == tuple_cls); - assert(!arg3 || arg3->cls == dict_cls); + if (err == -1) + return (Box*)NULL; + + if (rewrite_args) { + auto r_err = rewrite_args->rewriter->call(true, (void*)tpinit, r_made, rewrite_args->arg2, + rewrite_args->arg3); + + assert(S == CXX && "this need to be converted"); + rewrite_args->rewriter->checkAndThrowCAPIException(r_err, -1, assembler::MovType::L); + rewrite_args->out_success = true; + } + return (Box*)1; + }; - int err = tpinit(made, arg2, arg3); - Py_DECREF(made); - Py_DECREF(arg2); - Py_XDECREF(arg3); - if (err == -1) { + Box* _t; + try { + _t = rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, + argspec, made, arg2, arg3, args, keyword_names, continuation); + } catch (ExcInfo e) { + setCAPIException(e); + _t = NULL; + } + if (_t == NULL) { Py_DECREF(made); if (S == CAPI) return NULL; @@ -1375,11 +1355,10 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo } if (rewrite_args) { - auto r_err - = rewrite_args->rewriter->call(true, (void*)tpinit, r_made, rewrite_args->arg2, rewrite_args->arg3); - - assert(S == CXX && "this need to be converted"); - rewrite_args->rewriter->checkAndThrowCAPIException(r_err, -1, assembler::MovType::L); + if (!rewrite_args->out_success) + rewrite_args = NULL; + else + rewrite_args->out_success = false; } } } else { diff --git a/src/runtime/types.h b/src/runtime/types.h index 51a402045192e31725e40c53bbdbabdfce2db902..9b975b4263cf13f789823b813d8c84299497ecac 100644 --- a/src/runtime/types.h +++ b/src/runtime/types.h @@ -413,41 +413,6 @@ public: #define AUTO_DECREF_ARRAY(x, size) AutoDecrefArray<false> CAT(_autodecref_, __LINE__)((x), (size)) #define AUTO_XDECREF_ARRAY(x, size) AutoDecrefArray<true> CAT(_autodecref_, __LINE__)((x), (size)) -class AutoDecrefArgs { -private: - int num_args; - Box* arg1, *arg2, *arg3; - Box** args; - -public: - AutoDecrefArgs(int num_args, Box* arg1, Box* arg2, Box* arg3, Box** args) - : num_args(num_args), arg1(arg1), arg2(arg2), arg3(arg3), args(args) {} - AutoDecrefArgs(ParamReceiveSpec paramspec, Box* arg1, Box* arg2, Box* arg3, Box** args) - : num_args(paramspec.totalReceived()), arg1(arg1), arg2(arg2), arg3(arg3), args(args) {} - - ~AutoDecrefArgs() { - // TODO Minor optimization: only the last arg (kwargs) is allowed to be NULL. - switch (num_args) { - default: - for (int i = 0; i < num_args - 3; i++) { - Py_XDECREF(args[i]); - } - case 3: - Py_XDECREF(arg3); - case 2: - Py_XDECREF(arg2); - case 1: - Py_XDECREF(arg1); - case 0: - break; - } - } -}; -// Note: this captures the first three args by value (like AUTO_DECREF) but the array by reference. -// You can also pass a ParamReceiveSpec instead of an int for num_args -#define AUTO_DECREF_ARGS(num_args, arg1, arg2, arg3, args) \ - AutoDecrefArgs CAT(_autodecref_, __LINE__)((num_args), (arg1), (arg2), (arg3), (args)) - template <typename B> B* incref(B* b) { Py_INCREF(b); return b; @@ -457,6 +422,32 @@ template <typename B> B* xincref(B* b) { return b; } +// Helper function: calls a CXX-style function from a templated function. This is more efficient than the +// easier-to-type version: +// +// try { +// return f(); +// } catch (ExcInfo e) { +// if (S == CAPI) { +// setCAPIException(e); +// return NULL; +// } else +// throw e; +// } +// +// since this version does not need the try-catch block when called from a CXX-style function +template <ExceptionStyle S, typename Functor> Box* callCXXFromStyle(Functor f) { + if (S == CAPI) { + try { + return f(); + } catch (ExcInfo e) { + setCAPIException(e); + return NULL; + } + } else + return f(); +} + //#define DISABLE_INT_FREELIST extern "C" int PyInt_ClearFreeList() noexcept; diff --git a/test/tests/arg_refs.py b/test/tests/arg_refs.py new file mode 100644 index 0000000000000000000000000000000000000000..787115aceffc28d851b95a5996f0cdcc1c82b00d --- /dev/null +++ b/test/tests/arg_refs.py @@ -0,0 +1,10 @@ +# Tests to see if we add any extra refs to function arguments. + +import sys +print sys.getrefcount(object()) + +def f(o): + print sys.getrefcount(o) + +# This gives 3 for CPython and our interpreter, but 2 for the llvm tier: +# f(object())