My point is not that the table has to be implemetned necessarily as a hash with a vector indexed by hash(key) containing pointers/references to keys and collision pointers (or a randomization function like adding some prime modulo N where N is the size of the hash) plus a pointer/refernce to values, and a separate store for keys and for values. There are lot of ways to implement tables including the possibility for some kinds of keys or values to reduce it to a simple integer-indexed vector (i.e. an array).
So the alledged cost of using a table for upvalues is not true: this is here also implementation detail. As well as the way you implement the stack (not necessarily as an array, as it could be as well a double-linked list), so te implementation can also be "stackless" (i.e. the Lua VM engine could be used with a small constant maximum use of the native thread stack, all the rest only needing a head, and even all Lua objects could have a constant maximum native size, allowing efficient reuse of the native heap with low level of fragmentation, and faster use of the native heap, and low footprint on the native heap).
It's important to remember that the Lua table is a generalization of several wellknown structures: lists, arrays, vectors, associative arrays, sets, trees... it can be used in fact for all kinds of collections (including collections of collections). Lua also allows tables to be linked to another table to form a chain of tables (so it also natively allows ternary trees of collections with all orders/ranks).
If we view the language this way, there's large freedom of implementation, and much more possibilities to implement not just interpreters, but as well compilers (including JIT compilers and static compilers) than what the current Lua.org's implementation currently uses.: Even Lua.org's has changed between versions how this implementation was done. Even the opcodes where considerably changed (i.e. the VMs themselves were necessarily incompatible, but not the language itself and programs written in it). This allowed multiple implementations to appear, and allowed the language to be integrated in many more environments, including the smallest ones (e.g. on IoT devices, or even small 8-bit microcontrolers, or within other VMs for languages like Java or _javascript_, or within networked environments, or within SQL engines for example in stored procedures and triggers). Lua is extremely flexible nad much less dependant than other languages like C/C++ which require a much more complex base environement and setting up a processing model with harder to support.
The concept of "registers" in the Lua.org's implemetnation is nothing else than a conceptualization of an integer-indexed array which is used in hope to improve data locality, it is not necessarily bound to native registers. It is used only because of the way its opcodes are encoded in its VM instruction set (but the same can be said as well in C/C++ stack based implementations using [BP+offset] indexed arrays for local variables, and *caching* a few of them in native registers. The main difference being that "words" used in the Lua.org's implementation is not just n-bit integers (like in native CPUs) but can hold any Lua's datatype (i.e. it is a tagged TV value which can be represented by a fixed-size object, even if some bits are actually references to other "words").