Currently, the tree inlining is done very early, during the evaluation phase. This means that the inlining is done even if the corresponding call belong to a sub-expression that will be discarded during the expansion phase. Usually this is not a problem but in some pathological cases it can lead to a huge waste of memory and CPU time. So, move this inline expansion to ... the expansion phase. Also, re-expand the resulting expression since constant arguments may create new opportunities for simplification. Note: the motivation for thsi is a pathological case in the kernel where a combination of max_t() + const_ilog2() + roundup_pow_of_two() + cpumask_weight() + __const_hweight*() caused Sparse to use 2.3Gb of memory. With this patch the memory consumption is down to 247Mb. Link: https://marc.info/?l=linux-sparse&m=158098958501220 Link: https://lore.kernel.org/netdev/CAHk-=whvS9x5NKtOqcUgJeTY7dfdAHc Reported-by: Randy Dunlap <rdunlap@xxxxxxxxxxxxx> Originally-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- evaluate.c | 16 ---------------- expand.c | 19 +++++++++++++++++++ inline.c | 5 ----- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/evaluate.c b/evaluate.c index f1a266beccfd..b7bb1f52aa91 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3107,22 +3107,6 @@ static int evaluate_symbol_call(struct expression *expr) if (ctype->op && ctype->op->evaluate) return ctype->op->evaluate(expr); - if (ctype->ctype.modifiers & MOD_INLINE) { - int ret; - struct symbol *curr = current_fn; - - if (ctype->definition) - ctype = ctype->definition; - - current_fn = ctype->ctype.base_type; - - ret = inline_function(expr, ctype); - - /* restore the old function */ - current_fn = curr; - return ret; - } - return 0; } diff --git a/expand.c b/expand.c index 36612c8672dd..e75598781b6c 100644 --- a/expand.c +++ b/expand.c @@ -910,6 +910,25 @@ static int expand_symbol_call(struct expression *expr, int cost) if (fn->type != EXPR_PREOP) return SIDE_EFFECTS; + if (ctype->ctype.modifiers & MOD_INLINE) { + struct symbol *def; + + def = ctype->definition ? ctype->definition : ctype; + if (inline_function(expr, def)) { + struct symbol *fn = def->ctype.base_type; + struct symbol *curr = current_fn; + + current_fn = fn; + evaluate_statement(expr->statement); + current_fn = curr; + + fn->expanding = 1; + cost = expand_expression(expr); + fn->expanding = 0; + return cost; + } + } + if (ctype->op && ctype->op->expand) return ctype->op->expand(expr, cost); diff --git a/inline.c b/inline.c index 6f73a30556bc..a959728013f1 100644 --- a/inline.c +++ b/inline.c @@ -519,8 +519,6 @@ int inline_function(struct expression *expr, struct symbol *sym) if (fn->expanding) return 0; - fn->expanding = 1; - name_list = fn->arguments; expr->type = EXPR_STATEMENT; @@ -558,9 +556,6 @@ int inline_function(struct expression *expr, struct symbol *sym) unset_replace_list(fn_symbol_list); - evaluate_statement(stmt); - - fn->expanding = 0; return 1; } -- 2.25.0