Commit 5071e380 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #755 from kmod/perf3

Allow passing NULL for empty kwargs
parents 0867bb4c f4b8ab11
...@@ -1977,7 +1977,7 @@ static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) ...@@ -1977,7 +1977,7 @@ static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds)
// ASSERT(self->tp_new != Py_CallPythonNew, "going to get in an infinite loop"); // ASSERT(self->tp_new != Py_CallPythonNew, "going to get in an infinite loop");
RELEASE_ASSERT(args->cls == tuple_cls, ""); RELEASE_ASSERT(args->cls == tuple_cls, "");
RELEASE_ASSERT(kwds->cls == dict_cls, ""); RELEASE_ASSERT(!kwds || kwds->cls == dict_cls, "");
RELEASE_ASSERT(args->size() >= 1, ""); RELEASE_ASSERT(args->size() >= 1, "");
BoxedClass* subtype = static_cast<BoxedClass*>(args->elts[0]); BoxedClass* subtype = static_cast<BoxedClass*>(args->elts[0]);
......
...@@ -315,8 +315,12 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener ...@@ -315,8 +315,12 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener
if (param_names.vararg_name) if (param_names.vararg_name)
doStore(param_names.vararg_name, Value(getArg(i++, arg1, arg2, arg3, args), 0)); doStore(param_names.vararg_name, Value(getArg(i++, arg1, arg2, arg3, args), 0));
if (param_names.kwarg_name) if (param_names.kwarg_name) {
doStore(param_names.kwarg_name, Value(getArg(i++, arg1, arg2, arg3, args), 0)); Box* val = getArg(i++, arg1, arg2, arg3, args);
if (!val)
val = createDict();
doStore(param_names.kwarg_name, Value(val, 0));
}
assert(nargs == i); assert(nargs == i);
} }
...@@ -1695,7 +1699,8 @@ Box* astInterpretFunction(CLFunction* clfunc, int nargs, Box* closure, Box* gene ...@@ -1695,7 +1699,8 @@ Box* astInterpretFunction(CLFunction* clfunc, int nargs, Box* closure, Box* gene
std::vector<ConcreteCompilerType*> arg_types; std::vector<ConcreteCompilerType*> arg_types;
for (int i = 0; i < nargs; i++) { for (int i = 0; i < nargs; i++) {
Box* arg = getArg(i, arg1, arg2, arg3, args); Box* arg = getArg(i, arg1, arg2, arg3, args);
assert(arg); // only builtin functions can pass NULL args
assert(arg || i == clfunc->param_names.kwargsIndex()); // only builtin functions can pass NULL args
// TODO: reenable argument-type specialization // TODO: reenable argument-type specialization
arg_types.push_back(UNKNOWN); arg_types.push_back(UNKNOWN);
......
...@@ -2614,7 +2614,24 @@ public: ...@@ -2614,7 +2614,24 @@ public:
} }
if (param_names.kwarg.size()) { if (param_names.kwarg.size()) {
loadArgument(internString(param_names.kwarg), arg_types[i], python_parameters[i], UnwindInfo::cantUnwind()); llvm::BasicBlock* starting_block = emitter.currentBasicBlock();
llvm::BasicBlock* isnull_bb = emitter.createBasicBlock("isnull");
llvm::BasicBlock* continue_bb = emitter.createBasicBlock("kwargs_join");
llvm::Value* kwargs_null
= emitter.getBuilder()->CreateICmpEQ(python_parameters[i], getNullPtr(g.llvm_value_type_ptr));
llvm::BranchInst* null_check = emitter.getBuilder()->CreateCondBr(kwargs_null, isnull_bb, continue_bb);
emitter.setCurrentBasicBlock(isnull_bb);
llvm::Value* created_dict = emitter.getBuilder()->CreateCall(g.funcs.createDict);
emitter.getBuilder()->CreateBr(continue_bb);
emitter.setCurrentBasicBlock(continue_bb);
llvm::PHINode* phi = emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, 2);
phi->addIncoming(python_parameters[i], starting_block);
phi->addIncoming(created_dict, isnull_bb);
loadArgument(internString(param_names.kwarg), arg_types[i], phi, UnwindInfo::cantUnwind());
i++; i++;
} }
......
...@@ -133,6 +133,11 @@ struct ArgPassSpec { ...@@ -133,6 +133,11 @@ struct ArgPassSpec {
int totalPassed() { return num_args + num_keywords + (has_starargs ? 1 : 0) + (has_kwargs ? 1 : 0); } int totalPassed() { return num_args + num_keywords + (has_starargs ? 1 : 0) + (has_kwargs ? 1 : 0); }
int kwargsIndex() const {
assert(has_kwargs);
return num_args + num_keywords + (has_starargs ? 1 : 0);
}
uint32_t asInt() const { return *reinterpret_cast<const uint32_t*>(this); } uint32_t asInt() const { return *reinterpret_cast<const uint32_t*>(this); }
void dump() { void dump() {
...@@ -162,6 +167,11 @@ struct ParamNames { ...@@ -162,6 +167,11 @@ struct ParamNames {
return args.size() + (vararg.str().size() == 0 ? 0 : 1) + (kwarg.str().size() == 0 ? 0 : 1); return args.size() + (vararg.str().size() == 0 ? 0 : 1) + (kwarg.str().size() == 0 ? 0 : 1);
} }
int kwargsIndex() const {
assert(kwarg.str().size());
return args.size() + (vararg.str().size() == 0 ? 0 : 1);
}
private: private:
ParamNames() : takes_param_names(false), vararg_name(NULL), kwarg_name(NULL) {} ParamNames() : takes_param_names(false), vararg_name(NULL), kwarg_name(NULL) {}
}; };
......
...@@ -555,7 +555,7 @@ static void callPendingFinalizers() { ...@@ -555,7 +555,7 @@ static void callPendingFinalizers() {
Box* box = pending_finalization_list.front(); Box* box = pending_finalization_list.front();
pending_finalization_list.pop_front(); pending_finalization_list.pop_front();
RELEASE_ASSERT(isValidGCObject(box), "objects to be finalized should still be alive"); ASSERT(isValidGCObject(box), "objects to be finalized should still be alive");
if (isWeaklyReferenced(box)) { if (isWeaklyReferenced(box)) {
// Callbacks for weakly-referenced objects with finalizers (if any), followed by call to finalizers. // Callbacks for weakly-referenced objects with finalizers (if any), followed by call to finalizers.
...@@ -572,7 +572,7 @@ static void callPendingFinalizers() { ...@@ -572,7 +572,7 @@ static void callPendingFinalizers() {
} }
finalize(box); finalize(box);
RELEASE_ASSERT(isValidGCObject(box), "finalizing an object should not free the object"); ASSERT(isValidGCObject(box), "finalizing an object should not free the object");
} }
if (!initially_empty) { if (!initially_empty) {
......
...@@ -128,7 +128,7 @@ void _bytesAllocatedTripped() { ...@@ -128,7 +128,7 @@ void _bytesAllocatedTripped() {
bool hasOrderedFinalizer(BoxedClass* cls) { bool hasOrderedFinalizer(BoxedClass* cls) {
if (cls->has_safe_tp_dealloc) { if (cls->has_safe_tp_dealloc) {
assert(!cls->tp_del); ASSERT(!cls->tp_del, "class \"%s\" with safe tp_dealloc also has tp_del?", cls->tp_name);
return false; return false;
} else if (cls->hasNonDefaultTpDealloc()) { } else if (cls->hasNonDefaultTpDealloc()) {
return true; return true;
......
...@@ -865,7 +865,7 @@ Box* execfile(Box* _fn) { ...@@ -865,7 +865,7 @@ Box* execfile(Box* _fn) {
Box* print(BoxedTuple* args, BoxedDict* kwargs) { Box* print(BoxedTuple* args, BoxedDict* kwargs) {
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwargs->cls == dict_cls); assert(!kwargs || kwargs->cls == dict_cls);
Box* dest, *end; Box* dest, *end;
...@@ -873,23 +873,22 @@ Box* print(BoxedTuple* args, BoxedDict* kwargs) { ...@@ -873,23 +873,22 @@ Box* print(BoxedTuple* args, BoxedDict* kwargs) {
static BoxedString* end_str = internStringImmortal("end"); static BoxedString* end_str = internStringImmortal("end");
static BoxedString* space_str = internStringImmortal(" "); static BoxedString* space_str = internStringImmortal(" ");
auto it = kwargs->d.find(file_str); BoxedDict::DictMap::iterator it;
if (it != kwargs->d.end()) { if (kwargs && ((it = kwargs->d.find(file_str)) != kwargs->d.end())) {
dest = it->second; dest = it->second;
kwargs->d.erase(it); kwargs->d.erase(it);
} else { } else {
dest = getSysStdout(); dest = getSysStdout();
} }
it = kwargs->d.find(end_str); if (kwargs && ((it = kwargs->d.find(end_str)) != kwargs->d.end())) {
if (it != kwargs->d.end()) {
end = it->second; end = it->second;
kwargs->d.erase(it); kwargs->d.erase(it);
} else { } else {
end = boxString("\n"); end = boxString("\n");
} }
RELEASE_ASSERT(kwargs->d.size() == 0, "print() got unexpected keyword arguments"); RELEASE_ASSERT(!kwargs || kwargs->d.size() == 0, "print() got unexpected keyword arguments");
static BoxedString* write_str = internStringImmortal("write"); static BoxedString* write_str = internStringImmortal("write");
CallattrFlags callattr_flags{.cls_only = false, .null_on_nonexistent = false, .argspec = ArgPassSpec(1) }; CallattrFlags callattr_flags{.cls_only = false, .null_on_nonexistent = false, .argspec = ArgPassSpec(1) };
......
...@@ -1448,7 +1448,7 @@ Box* BoxedCApiFunction::__call__(BoxedCApiFunction* self, BoxedTuple* varargs, B ...@@ -1448,7 +1448,7 @@ Box* BoxedCApiFunction::__call__(BoxedCApiFunction* self, BoxedTuple* varargs, B
STAT_TIMER(t0, "us_timer_boxedcapifunction__call__", (self->cls->is_user_defined ? 10 : 20)); STAT_TIMER(t0, "us_timer_boxedcapifunction__call__", (self->cls->is_user_defined ? 10 : 20));
assert(self->cls == capifunc_cls); assert(self->cls == capifunc_cls);
assert(varargs->cls == tuple_cls); assert(varargs->cls == tuple_cls);
assert(kwargs->cls == dict_cls); assert(!kwargs || kwargs->cls == dict_cls);
// Kind of silly to have asked callFunc to rearrange the arguments for us, just to pass things // Kind of silly to have asked callFunc to rearrange the arguments for us, just to pass things
// off to tppCall, but this case should be very uncommon (people explicitly asking for __call__) // off to tppCall, but this case should be very uncommon (people explicitly asking for __call__)
......
...@@ -125,7 +125,7 @@ Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) { ...@@ -125,7 +125,7 @@ Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) {
assert(_args->cls == tuple_cls); assert(_args->cls == tuple_cls);
BoxedTuple* args = static_cast<BoxedTuple*>(_args); BoxedTuple* args = static_cast<BoxedTuple*>(_args);
assert(_kwargs->cls == dict_cls); assert(!_kwargs || _kwargs->cls == dict_cls);
BoxedDict* kwargs = static_cast<BoxedDict*>(_kwargs); BoxedDict* kwargs = static_cast<BoxedDict*>(_kwargs);
BoxedInstance* made = new BoxedInstance(cls); BoxedInstance* made = new BoxedInstance(cls);
...@@ -138,7 +138,7 @@ Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) { ...@@ -138,7 +138,7 @@ Box* classobjCall(Box* _cls, Box* _args, Box* _kwargs) {
if (init_rtn != None) if (init_rtn != None)
raiseExcHelper(TypeError, "__init__() should return None"); raiseExcHelper(TypeError, "__init__() should return None");
} else { } else {
if (args->size() || kwargs->d.size()) if (args->size() || (kwargs && kwargs->d.size()))
raiseExcHelper(TypeError, "this constructor takes no arguments"); raiseExcHelper(TypeError, "this constructor takes no arguments");
} }
return made; return made;
......
...@@ -480,7 +480,7 @@ Box* BoxedWrapperObject::__call__(BoxedWrapperObject* self, Box* args, Box* kwds ...@@ -480,7 +480,7 @@ Box* BoxedWrapperObject::__call__(BoxedWrapperObject* self, Box* args, Box* kwds
assert(self->cls == wrapperobject_cls); assert(self->cls == wrapperobject_cls);
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwds->cls == dict_cls); assert(!kwds || kwds->cls == dict_cls);
int flags = self->descr->wrapper->flags; int flags = self->descr->wrapper->flags;
wrapperfunc wrapper = self->descr->wrapper->wrapper; wrapperfunc wrapper = self->descr->wrapper->wrapper;
......
...@@ -582,8 +582,7 @@ extern "C" int PyDict_Merge(PyObject* a, PyObject* b, int override_) noexcept { ...@@ -582,8 +582,7 @@ extern "C" int PyDict_Merge(PyObject* a, PyObject* b, int override_) noexcept {
Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) { Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) {
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwargs); assert(!kwargs || kwargs->cls == dict_cls);
assert(kwargs->cls == dict_cls);
RELEASE_ASSERT(args->size() <= 1, ""); // should throw a TypeError RELEASE_ASSERT(args->size() <= 1, ""); // should throw a TypeError
if (args->size()) { if (args->size()) {
...@@ -596,7 +595,7 @@ Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) { ...@@ -596,7 +595,7 @@ Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) {
} }
} }
if (kwargs->d.size()) if (kwargs && kwargs->d.size())
dictMerge(self, kwargs); dictMerge(self, kwargs);
return None; return None;
...@@ -604,7 +603,7 @@ Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) { ...@@ -604,7 +603,7 @@ Box* dictUpdate(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) {
extern "C" Box* dictInit(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) { extern "C" Box* dictInit(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) {
int args_sz = args->size(); int args_sz = args->size();
int kwargs_sz = kwargs->d.size(); int kwargs_sz = kwargs ? kwargs->d.size() : 0;
// CPython accepts a single positional and keyword arguments, in any combination // CPython accepts a single positional and keyword arguments, in any combination
if (args_sz > 1) if (args_sz > 1)
...@@ -612,11 +611,13 @@ extern "C" Box* dictInit(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) { ...@@ -612,11 +611,13 @@ extern "C" Box* dictInit(BoxedDict* self, BoxedTuple* args, BoxedDict* kwargs) {
dictUpdate(self, args, kwargs); dictUpdate(self, args, kwargs);
// handle keyword arguments by merging (possibly over positional entries per CPy) if (kwargs) {
assert(kwargs->cls == dict_cls); // handle keyword arguments by merging (possibly over positional entries per CPy)
assert(kwargs->cls == dict_cls);
for (const auto& p : kwargs->d) for (const auto& p : kwargs->d)
self->d[p.first] = p.second; self->d[p.first] = p.second;
}
return None; return None;
} }
......
...@@ -3361,7 +3361,7 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name ...@@ -3361,7 +3361,7 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
if (paramspec.takes_kwargs) { if (paramspec.takes_kwargs) {
int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0); int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0);
if (rewrite_args) { if (rewrite_args) {
RewriterVar* r_kwargs = rewrite_args->rewriter->call(true, (void*)createDict); RewriterVar* r_kwargs = rewrite_args->rewriter->loadConst(0);
if (kwargs_idx == 0) if (kwargs_idx == 0)
rewrite_args->arg1 = r_kwargs; rewrite_args->arg1 = r_kwargs;
...@@ -3408,7 +3408,10 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name ...@@ -3408,7 +3408,10 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
Box* kwargs Box* kwargs
= getArg(argspec.num_args + argspec.num_keywords + (argspec.has_starargs ? 1 : 0), arg1, arg2, arg3, args); = getArg(argspec.num_args + argspec.num_keywords + (argspec.has_starargs ? 1 : 0), arg1, arg2, arg3, args);
if (!isSubclass(kwargs->cls, dict_cls)) { if (!kwargs) {
// TODO could try to avoid creating this
kwargs = new BoxedDict();
} else if (!isSubclass(kwargs->cls, dict_cls)) {
BoxedDict* d = new BoxedDict(); BoxedDict* d = new BoxedDict();
dictMerge(d, kwargs); dictMerge(d, kwargs);
kwargs = d; kwargs = d;
...@@ -3755,15 +3758,26 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar ...@@ -3755,15 +3758,26 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
// already fit, either since the type inferencer could determine that, // already fit, either since the type inferencer could determine that,
// or because they only need to fit into an UNKNOWN slot. // or because they only need to fit into an UNKNOWN slot.
if (npassed_args >= 1) int kwargs_index = -1;
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls); if (argspec.has_kwargs)
if (npassed_args >= 2) kwargs_index = argspec.kwargsIndex();
rewrite_args->arg2->addAttrGuard(offsetof(Box, cls), (intptr_t)arg2->cls);
if (npassed_args >= 3) for (int i = 0; i < npassed_args; i++) {
rewrite_args->arg3->addAttrGuard(offsetof(Box, cls), (intptr_t)arg3->cls); Box* v = getArg(i, arg1, arg2, arg3, args);
for (int i = 3; i < npassed_args; i++) {
RewriterVar* v = rewrite_args->args->getAttr((i - 3) * sizeof(Box*), Location::any()); if (i == kwargs_index) {
v->addAttrGuard(offsetof(Box, cls), (intptr_t)args[i - 3]->cls); if (v == NULL) {
// I don't think this case should ever get hit currently -- the only places
// we offer rewriting are places that don't have the ability to pass a NULL
// kwargs.
getArg(i, rewrite_args)->addGuard(0);
} else {
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
}
} else {
assert(v);
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
}
} }
rewrite_args->args_guarded = true; rewrite_args->args_guarded = true;
} }
......
...@@ -1827,7 +1827,7 @@ extern "C" PyObject* _do_string_format(PyObject* self, PyObject* args, PyObject* ...@@ -1827,7 +1827,7 @@ extern "C" PyObject* _do_string_format(PyObject* self, PyObject* args, PyObject*
Box* strFormat(BoxedString* self, BoxedTuple* args, BoxedDict* kwargs) { Box* strFormat(BoxedString* self, BoxedTuple* args, BoxedDict* kwargs) {
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwargs->cls == dict_cls); assert(!kwargs || kwargs->cls == dict_cls);
Box* rtn = _do_string_format(self, args, kwargs); Box* rtn = _do_string_format(self, args, kwargs);
checkAndThrowCAPIException(); checkAndThrowCAPIException();
......
...@@ -262,7 +262,7 @@ extern "C" Box* tupleNew(Box* _cls, BoxedTuple* args, BoxedDict* kwargs) { ...@@ -262,7 +262,7 @@ extern "C" Box* tupleNew(Box* _cls, BoxedTuple* args, BoxedDict* kwargs) {
getNameOfClass(cls)); getNameOfClass(cls));
int args_sz = args->size(); int args_sz = args->size();
int kwargs_sz = kwargs->d.size(); int kwargs_sz = kwargs ? kwargs->d.size() : 0;
if (args_sz + kwargs_sz > 1) if (args_sz + kwargs_sz > 1)
raiseExcHelper(TypeError, "tuple() takes at most 1 argument (%d given)", args_sz + kwargs_sz); raiseExcHelper(TypeError, "tuple() takes at most 1 argument (%d given)", args_sz + kwargs_sz);
......
...@@ -1358,7 +1358,7 @@ static Box* functionCall(BoxedFunction* self, Box* args, Box* kwargs) { ...@@ -1358,7 +1358,7 @@ static Box* functionCall(BoxedFunction* self, Box* args, Box* kwargs) {
// disallow it but it's good to know. // disallow it but it's good to know.
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwargs->cls == dict_cls); assert(!kwargs || kwargs->cls == dict_cls);
return runtimeCall(self, ArgPassSpec(0, 0, true, true), args, kwargs, NULL, NULL, NULL); return runtimeCall(self, ArgPassSpec(0, 0, true, true), args, kwargs, NULL, NULL, NULL);
} }
...@@ -2156,8 +2156,7 @@ public: ...@@ -2156,8 +2156,7 @@ public:
AttrWrapper* self = static_cast<AttrWrapper*>(_self); AttrWrapper* self = static_cast<AttrWrapper*>(_self);
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
assert(kwargs); assert(!kwargs || kwargs->cls == dict_cls);
assert(kwargs->cls == dict_cls);
RELEASE_ASSERT(args->size() <= 1, ""); // should throw a TypeError RELEASE_ASSERT(args->size() <= 1, ""); // should throw a TypeError
...@@ -2192,7 +2191,8 @@ public: ...@@ -2192,7 +2191,8 @@ public:
for (auto e : *args) { for (auto e : *args) {
handle(e); handle(e);
} }
handle(kwargs); if (kwargs)
handle(kwargs);
return None; return None;
} }
......
...@@ -50,3 +50,5 @@ try: ...@@ -50,3 +50,5 @@ try:
except TypeError: except TypeError:
pass pass
for i in xrange(10):
print()
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