Commit 1a5e6053 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Change rearrangeArgs to taking a callback

This function is complicated because it has so many return values (out-parameters).
It also returns a decent amount of information about what the caller has to do
after it is done with the args (decref them, decref the rewritten ones, etc), and
the contract was getting very complicated.  It also had some complicated rules
about how the caller had to set up certain input arguments.

I also tried adding some optimizations to it, where it would sometimes not incref all
of the returned args; I tried continuing the current scheme by passing back some information
about which args needed to be decref'd or not.  This was really messy and was also a
perf hit.

So instead, switch it to being callback-based.  I think this should clean it up quite a bit
and also open up some room for more optimizations.

This commit is just a refactor -- it changes the name to rearrangeArgumentsAndCall, which takes
a "continuation" callback.  For now it just calls rearrangeArguments under the hood.
parent 69d40057
......@@ -3411,26 +3411,9 @@ 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_DECREF(arg1);
auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
if (!paramspec.takes_kwargs)
arg2 = NULL;
AUTO_XDECREF(arg2);
if (!rewrite_success)
rewrite_args = NULL;
if (rewrite_args) {
if (!paramspec.takes_kwargs)
......@@ -3453,9 +3436,21 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp
}
Box* r = self->cls->tp_call(self, arg1, arg2);
if (!r && S == CXX)
if (!r)
throwCAPIException();
return r;
};
try {
return rearrangeArgumentsAndCall(paramspec, NULL, "", NULL, rewrite_args, argspec, arg1, arg2, arg3, args,
keyword_names, continuation);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
return NULL;
} else
throw e;
}
}
extern "C" void PyType_RequestHcAttrs(PyTypeObject* cls, int offset) noexcept {
......
......@@ -262,15 +262,15 @@ 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 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
......
......@@ -553,20 +553,12 @@ 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;
auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
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
......@@ -586,16 +578,10 @@ Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args,
}
}
_str = coerceUnicodeToStr<S>(_str);
if (S == CAPI && !_str)
return NULL;
_str = coerceUnicodeToStr<CXX>(_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");
}
......@@ -648,9 +634,21 @@ Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args,
}
Box* r = getattrFuncHelper(rtn, obj, str, default_value);
if (S == CXX && !r)
if (!r)
throwCAPIException();
return r;
};
try {
return rearrangeArgumentsAndCall(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args,
argspec, arg1, arg2, arg3, args, keyword_names, continuation);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
return NULL;
}
throw e;
}
}
Box* setattrFunc(Box* obj, Box* _str, Box* value) {
......@@ -688,15 +686,7 @@ 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);
auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
Box* obj = arg1;
Box* _str = arg2;
......@@ -719,16 +709,10 @@ Box* hasattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args,
}
}
_str = coerceUnicodeToStr<S>(_str);
if (S == CAPI && !_str)
return NULL;
_str = coerceUnicodeToStr<CXX>(_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");
}
......@@ -776,9 +760,21 @@ Box* hasattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args,
}
Box* r = hasattrFuncHelper(rtn);
if (S == CXX && !r)
if (!r)
throwCAPIException();
return r;
};
try {
return rearrangeArgumentsAndCall(ParamReceiveSpec(2, 0, false, false), NULL, "hasattr", NULL, rewrite_args,
argspec, arg1, arg2, arg3, args, keyword_names, continuation);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
return NULL;
}
throw e;
}
}
Box* map2(Box* f, Box* container) {
......
......@@ -1797,15 +1797,7 @@ 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;
auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
RewriterVar* r_passthrough = NULL;
if (rewrite_args)
r_passthrough = rewrite_args->rewriter->loadConst((intptr_t)self->passthrough, Location::forArg(0));
......@@ -1814,12 +1806,13 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
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);
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->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);
......@@ -1831,8 +1824,8 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
} 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);
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);
......@@ -1872,10 +1865,14 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
rewrite_args->out_success = true;
}
if (!rtn)
if (S == CXX && !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
......
......@@ -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,15 +318,7 @@ 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;
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))
......@@ -343,8 +326,9 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
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());
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) {
......@@ -368,7 +352,8 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
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->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)) {
{
......@@ -385,12 +370,13 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
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->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);
rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, args);
}
if (rewrite_args) {
if (paramspec.totalReceived() == 2)
......@@ -416,16 +402,18 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
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;
}
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,19 +634,11 @@ 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_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs);
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);
if (!rewrite_success)
rewrite_args = NULL;
#endif
Box* rtn;
if (flags == PyWrapperFlag_KEYWORDS) {
......@@ -667,7 +647,8 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
rewrite_args->out_rtn = rewriter->call(true, (void*)wk, rewrite_args->arg1, rewrite_args->arg2,
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);
......@@ -678,7 +659,8 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
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);
......@@ -690,7 +672,8 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1,
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);
......@@ -701,7 +684,8 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
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);
......@@ -714,6 +698,10 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
if (!rtn)
throwCAPIException();
return rtn;
};
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*) {
......
......@@ -429,11 +429,14 @@ extern "C" BoxedGenerator::BoxedGenerator(BoxedFunctionBase* function, Box* arg1
#endif
{
Py_INCREF(function);
int numArgs = function->md->numReceivedArgs();
if (numArgs > 0)
Py_XINCREF(arg1);
if (numArgs > 1)
Py_XINCREF(arg2);
if (numArgs > 2)
Py_XINCREF(arg3);
int numArgs = function->md->numReceivedArgs();
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);
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);
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);
......
......@@ -4131,7 +4131,7 @@ void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int num_oargs) {
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,
Box** defaults, CallRewriteArgs* 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) {
......@@ -4610,23 +4610,76 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
cleanup.cancel();
}
// TODO: implement this for real
template <Rewritable rewritable, typename FuncNameCB>
Box* rearrangeArgumentsAndCallInternal(ParamReceiveSpec paramspec, const ParamNames* param_names,
FuncNameCB 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) {
Box** oargs = NULL;
bool* oargs_owned = NULL;
if (paramspec.totalReceived() > 3) {
oargs = (Box**)alloca(sizeof(Box*) * (paramspec.totalReceived() - 3));
oargs_owned = (bool*)alloca(sizeof(bool) * (paramspec.totalReceived() - 3));
}
bool rewrite_success = false;
rearrangeArgumentsInternal<rewritable>(paramspec, param_names, func_name_cb, 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;
Box* r = continuation(rewrite_args, arg1, arg2, arg3, oargs);
if (rewrite_args)
decrefOargs(rewrite_args->args, oargs_owned, paramspec.totalReceived() - 3);
return r;
}
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,
Box** defaults, CallRewriteArgs* 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) {
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);
argspec, arg1, arg2, arg3, 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<REWRITABLE>(ParamReceiveSpec, const ParamNames*, const char*, Box**, CallRewriteArgs*,
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**,
CallRewriteArgs*, bool&, ArgPassSpec, Box*&, Box*&, Box*&, Box**,
Box**, const std::vector<BoxedString*>*, bool*);
template <Rewritable rewritable>
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) {
auto func = [func_name]() { return func_name; };
return rearrangeArgumentsAndCallInternal<rewritable>(paramspec, param_names, func, defaults, rewrite_args, argspec,
arg1, arg2, arg3, args, keyword_names, continuation);
}
template Box* rearrangeArgumentsAndCall<REWRITABLE>(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);
template Box* rearrangeArgumentsAndCall<NOT_REWRITABLE>(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);
static StatCounter slowpath_callfunc("slowpath_callfunc");
template <ExceptionStyle S, Rewritable rewritable>
Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
......@@ -4658,26 +4711,49 @@ 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;
// 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);
rewrite_args->out_success = true;
}
} else {
res = callCLFunc<S, rewritable>(md, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2,
arg3, args);
}
return res;
};
Box* r;
try {
auto func_name_cb = [md]() { return getFunctionName(md).data(); };
rearrangeArgumentsInternal<rewritable>(
r = rearrangeArgumentsAndCallInternal<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);
rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names, continuation);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
......@@ -4686,22 +4762,12 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
throw e;
}
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);
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.
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 +4826,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>
......
......@@ -292,26 +292,58 @@ 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.
typedef Box* (*rearrange_target_t)(void*, CallRewriteArgs*, Box*, Box*, Box*, Box**);
// This is a magical helper class that converts a functor (in particular, lambdas) into a [function pointer, void* data]
// pair. This lets us pass the functor efficiently: we can avoid doing an allocation (by storing the functor on the
// stack),
// and also avoid making the receiving function have to be a template.
//
// In effect, it does what you would have to do to create a function pointer + void* pair, like one would pass to
// C-style functions.
class FunctorPointer {
private:
template <typename Functor> class ConversionHelper {
public:
static Box* call(void* f, CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
// static_assert(decltypeD
return (*(Functor*)f)(rewrite_args, arg1, arg2, arg3, args);
}
};
rearrange_target_t function_pointer;
void* functor;
public:
template <typename Functor>
FunctorPointer(Functor& f)
: function_pointer(ConversionHelper<Functor>::call), functor(&f) {}
Box* operator()(CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
return function_pointer(functor, rewrite_args, arg1, arg2, arg3, 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.
// 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.
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);
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
......@@ -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, &param_names, "unicode", defaults, rewrite_args, rewrite_success, argspec, arg1,
arg2, arg3, args, oargs, keyword_names, oargs_owned);
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);
decrefOargs(rewrite_args->args, oargs_owned, 1);
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);
rewrite_args->out_success = true;
}
return unicodeNewHelper(cls, arg2, arg3, oargs);
return unicodeNewHelper(static_cast<BoxedClass*>(arg1), arg2, arg3, args);
};
return rearrangeArgumentsAndCall(paramspec, &param_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,18 +863,9 @@ 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
......@@ -891,6 +875,9 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
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,6 +1200,9 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
};
// 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);
......@@ -1245,6 +1214,18 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
rewrite_args->out_success = true;
}
return InitHelper::call(made, cls, arg2, arg3);
};
try {
return rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, argspec,
made, arg2, arg3, args, keyword_names, continuation);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
return NULL;
} else
throw e;
}
}
// If __new__ returns a subclass, supposed to call that subclass's __init__.
......@@ -1343,10 +1324,22 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
assert(tpinit == cls->tp_init);
}
bool rewrite_success = false;
auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
assert(arg2->cls == tuple_cls);
assert(!arg3 || arg3->cls == dict_cls);
int err = tpinit(made, arg2, arg3);
if (err == -1) {
throwCAPIException();
}
return (Box*)NULL;
};
try {
rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success,
argspec, made, arg2, arg3, args, NULL, keyword_names, NULL);
Box* _t = rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args,
argspec, made, arg2, arg3, args, keyword_names, continuation);
assert(_t == NULL);
} catch (ExcInfo e) {
Py_DECREF(made);
if (S == CAPI) {
......@@ -1356,22 +1349,11 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
throw e;
}
if (!rewrite_success)
if (rewrite_args) {
if (!rewrite_args->out_success)
rewrite_args = NULL;
assert(arg2->cls == tuple_cls);
assert(!arg3 || arg3->cls == dict_cls);
int err = tpinit(made, arg2, arg3);
Py_DECREF(made);
Py_DECREF(arg2);
Py_XDECREF(arg3);
if (err == -1) {
Py_DECREF(made);
if (S == CAPI)
return NULL;
else
throwCAPIException();
rewrite_args->out_success = false;
}
if (rewrite_args) {
......
......@@ -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;
......
# 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())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment