Skip to content

Commit 476b91a

Browse files
committed
ICs: don't emit guards for callattr and runtimeCall ICs when the called function excepts all args
1 parent a6b4559 commit 476b91a

File tree

2 files changed

+61
-65
lines changed

2 files changed

+61
-65
lines changed

src/runtime/objmodel.cpp

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3674,31 +3674,6 @@ Box* callattrInternal(Box* obj, BoxedString* attr, LookupScope scope, CallattrRe
36743674

36753675
int npassed_args = argspec.totalPassed();
36763676

3677-
if (rewrite_args && !rewrite_args->args_guarded) {
3678-
// TODO duplication with runtime_call
3679-
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
3680-
// already fit, either since the type inferencer could determine that,
3681-
// or because they only need to fit into an UNKNOWN slot.
3682-
3683-
if (npassed_args >= 1)
3684-
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls);
3685-
if (npassed_args >= 2)
3686-
rewrite_args->arg2->addAttrGuard(offsetof(Box, cls), (intptr_t)arg2->cls);
3687-
if (npassed_args >= 3)
3688-
rewrite_args->arg3->addAttrGuard(offsetof(Box, cls), (intptr_t)arg3->cls);
3689-
3690-
if (npassed_args > 3) {
3691-
for (int i = 3; i < npassed_args; i++) {
3692-
// TODO if there are a lot of args (>16), might be better to increment a pointer
3693-
// rather index them directly?
3694-
RewriterVar* v = rewrite_args->args->getAttr((i - 3) * sizeof(Box*), Location::any());
3695-
v->addAttrGuard(offsetof(Box, cls), (intptr_t)args[i - 3]->cls);
3696-
}
3697-
}
3698-
3699-
rewrite_args->args_guarded = true;
3700-
}
3701-
37023677
// right now I don't think this is ever called with INST_ONLY?
37033678
assert(scope != INST_ONLY);
37043679

@@ -5083,6 +5058,36 @@ const char* PyEval_GetFuncDesc(PyObject* func) noexcept {
50835058
}
50845059
}
50855060

5061+
static void addArgGuards(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
5062+
Box** args) {
5063+
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
5064+
// already fit, either since the type inferencer could determine that,
5065+
// or because they only need to fit into an UNKNOWN slot.
5066+
5067+
int kwargs_index = -1;
5068+
if (argspec.has_kwargs)
5069+
kwargs_index = argspec.kwargsIndex();
5070+
5071+
for (int i = 0; i < argspec.totalPassed(); i++) {
5072+
Box* v = getArg(i, arg1, arg2, arg3, args);
5073+
5074+
if (i == kwargs_index) {
5075+
if (v == NULL) {
5076+
// I don't think this case should ever get hit currently -- the only places
5077+
// we offer rewriting are places that don't have the ability to pass a NULL
5078+
// kwargs.
5079+
getArg(i, rewrite_args)->addGuard(0);
5080+
} else {
5081+
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
5082+
}
5083+
} else {
5084+
assert(v);
5085+
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
5086+
}
5087+
}
5088+
rewrite_args->args_guarded = true;
5089+
}
5090+
50865091

50875092
template <ExceptionStyle S, Rewritable rewritable>
50885093
Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
@@ -5173,65 +5178,46 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
51735178
return rtn;
51745179
}
51755180

5176-
if (rewrite_args) {
5177-
if (!rewrite_args->args_guarded) {
5178-
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
5179-
// already fit, either since the type inferencer could determine that,
5180-
// or because they only need to fit into an UNKNOWN slot.
5181-
5182-
int kwargs_index = -1;
5183-
if (argspec.has_kwargs)
5184-
kwargs_index = argspec.kwargsIndex();
5185-
5186-
for (int i = 0; i < npassed_args; i++) {
5187-
Box* v = getArg(i, arg1, arg2, arg3, args);
5188-
5189-
if (i == kwargs_index) {
5190-
if (v == NULL) {
5191-
// I don't think this case should ever get hit currently -- the only places
5192-
// we offer rewriting are places that don't have the ability to pass a NULL
5193-
// kwargs.
5194-
getArg(i, rewrite_args)->addGuard(0);
5195-
} else {
5196-
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
5197-
}
5198-
} else {
5199-
assert(v);
5200-
getArg(i, rewrite_args)->addAttrGuard(offsetof(Box, cls), (intptr_t)v->cls);
5201-
}
5202-
}
5203-
rewrite_args->args_guarded = true;
5204-
}
5205-
}
52065181

52075182
if (obj->cls == function_cls || obj->cls == builtin_function_or_method_cls) {
52085183
BoxedFunctionBase* f = static_cast<BoxedFunctionBase*>(obj);
5209-
5210-
if (rewrite_args && !rewrite_args->func_guarded) {
5211-
rewrite_args->obj->addGuard((intptr_t)f);
5212-
rewrite_args->func_guarded = true;
5213-
rewrite_args->rewriter->addDependenceOn(f->dependent_ics);
5214-
}
5184+
auto md = f->md;
52155185

52165186
// Some functions are sufficiently important that we want them to be able to patchpoint themselves;
52175187
// they can do this by setting the "internal_callable" field:
5218-
auto callable = f->md->internal_callable.get<S>();
5219-
5188+
auto callable = md->internal_callable.get<S>();
52205189
if (S == CAPI)
5221-
assert((bool(f->md->internal_callable.get(CXX)) == bool(callable))
5190+
assert((bool(md->internal_callable.get(CXX)) == bool(callable))
52225191
&& "too many opportunities for mistakes unless both CXX and CAPI versions are implemented");
52235192
else
5224-
assert((bool(f->md->internal_callable.get(CAPI)) == bool(callable))
5193+
assert((bool(md->internal_callable.get(CAPI)) == bool(callable))
52255194
&& "too many opportunities for mistake unless both CXX and CAPI versions are implementeds");
52265195

52275196
if (callable == NULL) {
52285197
callable = callFunc<S>;
52295198
}
52305199

5200+
if (rewrite_args && !rewrite_args->args_guarded) {
5201+
bool does_not_need_guards = callable == &callFunc<S> && md->always_use_version;
5202+
if (!does_not_need_guards)
5203+
addArgGuards(rewrite_args, argspec, arg1, arg2, arg3, args);
5204+
}
5205+
5206+
if (rewrite_args && !rewrite_args->func_guarded) {
5207+
rewrite_args->obj->addGuard((intptr_t)f);
5208+
rewrite_args->func_guarded = true;
5209+
// callFunc will add the invalidator
5210+
if (callable != &callFunc<S>)
5211+
rewrite_args->rewriter->addDependenceOn(f->dependent_ics);
5212+
}
5213+
52315214
KEEP_ALIVE(f);
52325215
Box* res = callable(f, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
52335216
return res;
52345217
} else if (obj->cls == instancemethod_cls) {
5218+
if (rewrite_args && !rewrite_args->args_guarded)
5219+
addArgGuards(rewrite_args, argspec, arg1, arg2, arg3, args);
5220+
52355221
BoxedInstanceMethod* im = static_cast<BoxedInstanceMethod*>(obj);
52365222

52375223
RewriterVar* r_im_func = NULL;

test/tests/func_ics.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# statcheck: stats['ic_attempts'] <= 2000
2+
# statcheck: stats['ic_attempts_skipped_megamorphic'] == 0
3+
ins = (1, 1.0, 1l, "", u"")
4+
def f():
5+
for x in ins:
6+
isinstance(x, int)
7+
[].append(x)
8+
9+
for i in xrange(30000):
10+
f()

0 commit comments

Comments
 (0)