Can someone explain CALL_FUNCTION and RETURN_VALUE from python bytecode

Question:

I’m trying to understand python bytecode and I’m caught on CALL_FUNCTION and RETURN_VALUE.

Does a function have its own stack? If not, what does the documentation mean by "Returns TOS to the caller of the function"?

Asked By: Jam

||

Answers:

In CPython every function gets its own stack, it’s called a frame in CPython and it’s an implementation-specific detail(very old one) and other implementation of Python like IronPython1 and Jython doesn’t have this functionality or implement it differently.

To clarify when we say stack there are multiple stacks involved:

  1. Python stack: The stack of frame objects
  2. Function values stack: The values in each frame object are stored in this stack to be operated on within the scope of this frame2
  3. C stack: For C function calls

When a function is called a new frame object is created first and placed on the Python stack. This frame object contains the code object of the function, global variables the function has access to, and also the local variables defined in the function get stored in the frame object.

You can get the current frames in Python stack and current frame using the utilities provided in the inspect module.

The issue with this is that it is a Python object, it has its own type PyFrame_Type, it gets reference count(gets all headers from PyVarObject) and consumes some memory and if we have a chain of function calls, each time we will be creating these frame objects in memory all over the heap.

In Python 3.11, the frame object will be replaced by an array of structs that won’t have an object header. The frame objects will still be available, but only if we request for it using inspect.currentframe() or sys._get_frame().


2 Function values stack

We can check stacksize of a function by accessing co_stacksize attribute of function’s code object, this value is determined during the compilation time:

>>> def func():
...     a = 1
...     b = 2
...     c = 3
...     d = a + b + c
...
>>> func.__code__.co_stacksize
2

Here the value is 2 because to sum a + b + c, it first loads a and b on the stack(LOAD_FAST) and performs the sum(BINARY_ADD) and puts the result back on top of stack, now c is loaded and it’s summed with the result of the sum of a and b. Hence, a maximum size of 2 is required for the stack specific to this function.


1: The flag X:Frames can be used in IronPython to enable CPython like frame objects.

Answered By: Ashwini Chaudhary
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.