In a C program, inlining a function is a fairly intuitive optimization. If the inlined function's body is sufficiently small, you end up saving the jump to the function and creation of the stack frame, and you store the return value wherever the function's result would have been stored, jumping to the end of the inlined function's "body" rather than long-jumping to the return pointer.
I'm interested in doing the same thing in Python, converting two python functions into another valid python function where the first got "inlined" into the second. An ideal solution to this might look something like the following:
def g(x):
return x ** 2
def f(y):
return g(y + 3)
# ... Becomes ...
def inlined_f(y):
return (y + 3) ** 2
Clearly, in a language as dynamic as Python, this isn't trivial to do automatically. The best generic solution I have come up with is to use dict
to capture the arguments passed to the function, wrap the function body in a one-iteration for
loop, use break
to jump to the end of the function, and replace uses of arguments with indexes into the argument dictionary. The result looks something like the following:
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
_g['return'] = _g['x'] ** 2
break
_g_return = _g.get('return', None)
del _g
return _g_return
I don't care that it's ugly, but I do care that it doesn't support returns from within loops. E.g.:
def g(x):
for i in range(x + 1):
if i == x:
return i ** 2
print("Woops, you shouldn't get here")
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
for _g['i'] in range(_g['x'] + 1):
if _g['i'] == _g['x']:
_g['return'] _g['i'] ** 2
break # <-- Doesn't exit function, just innermost loop
print("Woops, you shouldn't get here")
_g_return = _g.get('return', None)
del _g
return _g_return
What approach could I take to this problem that avoids needing to use break
to "jump" out of the inlined function's body? I'd also be open to an overall better, generic approach could I take to inline one Python function into another.
For reference, I'm working at the AST (abstract syntax tree) level, so using parsed Python code; clearly, outside of literal values, I don't know what value or type anything will have while performing this transformation. The resulting inlined function must behave identically to the original functions, and must support all features typically available when calling a function. Is this even possible in Python?
EDIT: I should clarify since I used the tag "optimization", that I'm not actually interested in a performance boost. The resulting code does not need to be faster, it just must not call the inlined function while still behaving identically. You can assume that both functions' source code is available as valid Python.
numba.jit
andtangent
. Hence why I need the result to be a valid Python function. If I can dynamically produce equivalent Python functions that don't use certain code features, then I don't need to rely on those libraries to support those code features (such as function calls). – Stellular#pragma inline
in C. Could you provide some reference/article/tutorial regarding the ideas you mention (alpha-conversion, etc.) that would clarify the distinction between what I'm asking for and how actual code inlining works? – Stellularnumba
that additionally doesn't support function calls; any idea how to solve this question in that case? – Stellularreturn
at the end of the function. I know you're trying to reduce limitations, but still. Probably the least invasive option if you want to supportreturn
in other positions is to use exception handling, but that has its own limitations if the called function useswith
ortry
, or if one of the exceptions you use bubbles out of somewhere unexpected. – Alowhile
loop with a giant branching construct inside. – Alof
with argumenta
-- i.e.f(a)
, wheref
is defined bydef f(x): ..
withE[x->a] :- code-of-f
, meaning execute the code of f with f's x-variable bound to a. Alpha-conversion simply means that variables in f needs to be renamed so they don't conflict with existing variables in the new location. Wikipedia has a general example (en.wikipedia.org/wiki/Inline_expansion#Implementation). LISP macros would be a better model than C-type#define
s (cs.cmu.edu/Groups/AI/html/cltl/clm/node97.html) – Jamisonjammal