You can't efficiently address 21 bit values. You can efficiently address 8, 16, 32, and 64 bit values, some architectures can efficiently address 24 bit values. No architectures make it efficient to address 21 bit values. If you wanted to pack stuff more efficiently you can pack 8 24 bit values in 192 bits. All of those are multiples of one byte. But even that is dumb.
On the scale of a program with one million lines of code, those extra 11 bits constitute a space savings of .00001% at a non inconsequential time cost to access weirdly aligned values. Thanks for the byte? Am I allowed to spend it all in one place?
Honestly, were I charged with implementing this feature I would ignore it in the name of performance and just stick those line refs in a 64 bit pointer to the line of code/bytecode. I'm honestly not even seeing that the proposed advantages to this idea are advantageous, even in the absence of the catastrophic negatives.
I don't think the packing part was talking just about line numbers. They propose a number of things that would be limited to 1e6 elements. The question about performance is a good one and needs to be studied. This proposal is arguing from a different perspective though, one that focuses on reducing ambiguity rather than optimizing for performance. Python as a language is an exercise in sacrificing performance for other objectives, so I think that's fine for a language like this to consider this question even if it means performance could be impacted negatively.
If we made the limit 2^32 the total size of those 3 values would only be 96 bits, and you would only be saving 32 bits.