When compiling source code to bytecode or modifying bytecode for profiling or debugging,
an intermediate form is required. By limiting operands to 20 bits,
instructions can be represented in a compact 64 bit form allowing
very fast passes over the instruction sequence.
Having 20 bit operands (21 bits for relative branches) allows instructions
to fit into 32 bits without needing additional ``EXTENDED_ARG`` instructions.
This improves dispatch, as the operand is strictly local to the instruction.
Using super-instructions would make that the 32 bit format
almost as compact as the 16 bit format, and significantly faster.
The benefit of restricting the number of lines in a module is primarily the implied limit on bytecodes.
It is more important for implementations that it is instructions per code object, not lines per module, that is limited to one million,
but it is much easier to explain a one million line limit. Having a consistent limit of one million is just easier to remember.
It is mostly likely, although not guaranteed, that the line limit will be hit first and thus provide a simpler to understand error message to the developer.
Total number of classes in a running interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This limit has to the potential to reduce the size of object headers considerably.
Currently objects have a two word header, for objects without references
(int, float, str, etc.) or a four word header for objects with references.
By reducing the maximum number of classes, the space for the class reference
can be reduced from 64 bits to fewer than 32 bits allowing a much more compact header.
For example, a super-compact header format might look like this:
..code-block::
struct header {
uint32_t gc_flags:6; /* Needs finalisation, might be part of a cycle, etc. */
uint32_t class_id:26; /* Can be efficiently mapped to address by ensuring suitable alignment of classes */
uint32_t refcount; /* Limited memory or saturating */
}
This format would reduce the size of a Python object without slots, on a 64 bit machine, from 40 to 16 bytes.
Note that there are two ways to use a 32 bit refcount on a 64 bit machine.
One is to limit each sub-interpreter to 32Gb of memory.
The other is to use a saturating reference count, which would be a little bit slower, but allow unlimited memory allocation.
Enforcement
-----------
Python implementations are not obliged to enforce the limits.
However, if a limit can be enforced without hurting performance, then it should be.
It is anticipated that CPython will enforce the limits as follows:
* The number of source code lines in a module: 3.9 onwards.
* The number of bytecode instructions in a code object: 3.9 onwards.
* The sum of local variables and stack usage for a code object: 3.9 onwards.
* The number of classes in a running interpreter: probably 3.10 onwards, maybe warning in 3.9.
Backwards Compatibility
=======================
It is hypothetically possible that some machine generated code exceeds one or more of the above limits.
The author believes that to be highly unlikely and easily fixed by modifying the output stage of the code generator.
We would like to gain the benefit from the above limits for performance as soon as possible.
To that end, CPython will start applying limits from 3.9 onward.
To ease the transition and mimimize breakage, the initial limits would be much larger than one million
and will be gradually reduced to one million over several versions.
The actual limits enforced by CPython will be:
============= ===============
Version Limit
============= ===============
3.9 8 million
3.10 4 million
3.11 2 million
3.12 onward 1 million
============= ===============
Given the rarity of code genenerators that would exceed the one million limits,
and the environments in which they are typically used, it seems reasonable
to start issuing warnings in 3.9 if any limited quantity exceeds one million.
Other implementations
=====================
Implementations of Python other than CPython have different purposes, so different limits might be appropriate.
This is acceptable, provided the limits are clearly documented.
General purpose implementations
-------------------------------
General purpose implementations, such as PyPy, should use the one million limit.
If maximum compatibility is a goal, then they should also follow CPython's behaviour for 3.9 to 3.11.
Special purpose implementations
-------------------------------
Special purpose implementations may use lower limits, as long as they are clearly documented.
For example, Jython might need to use a lower class limit of fifty or sixty thousand becuase of JVM limits.
An implementation designed for embedded systems, for example MicroPython, might impose limits as low as a few thousand.
Security Implications
=====================
Minimal. This reduces the attack surface of any Python virtual machine by a small amount.
Reference Implementation
========================
None, as yet. This will be implemented in CPython, once the PEP has been accepted.
Rejected Ideas
==============
Being able to modify the limits upwards at compile time was suggested by Tal Einat.
This is rejected as the current limits of 2\ :sup:`32` have not been an issue, and the practical
advantages of allowing limits between 2\ :sup:`20` and 2\ :sup:`32` seem slight compared to the additional