Menu

scScript Alias Operator: =&

The alias operator was originally designed to assign a local name to a global variable:

   Var MyLocal =& SomeGlobal;

Bytecodes represent variables with a single-byte index, so are limited to only 254 available local variables (0x00 indicates a value on the stack while 0xFF signifies a reference on the stack). But scScript supports up to 65K globals, so bytecodes cannot directly manipulate global variables. Instead, the compiler sets aside a fixed number (currently 7) of implicit local variables as registers and writes bytecodes to set them (at runtime) to the same storage as the global variable. This way bytecodes can directly access registers as a proxy for globals. But this automatic register aliasing is often sub-optimal, especially in loops and with goto. So =& was devised as a simple optimization to allow the script writer to explicitly localize global variables in performance-critical functions, eliminating automatic bytecode generation to set/reset/recycle the few available registers.

The =& operator was later expanded to alias from (right side) array and dict elements too, saving the effort of repeated indexing and dict lookup. Once CurElt is aliased to MyArr[12] or MyD.Weight, it will have exactly the same storage and can be read, set, and otherwise manipulated just like the array elt or dict pair but without the indexing and key lookup. This is a significant step in runtime efficiency and programmer convenience.

Note: In the above example, if entry 12 is missing from MyArr or if Weight is missing from MyD when aliased, they will be automatically created first!

Finally, the alias operator can also be used to pack an out array just as Sys.GetArgs would. This special alias array will get the same elt storage as the target vars (or array elts and dict pairs) and can be directly unpacked into the out list of another call.

So the sequence:

   MyArr = Array(3);
   MyArr[0] =& OtherVar;
   MyArr[1] =& MyD.Size;
   MyArr[2] =& MyD.Weight;
   SomeFunction(3)(@MyArr);

Is the same as calling SomeFunction with:

  SomeFunction(3)(OtherVar, MyD.Size, MyD.Weight);

The args passed to a function call are usually set at compile time. But using an array along with the unpack operator allows scScript to set the arg list of a function call at runtime.

As powerful as the alias operator is, it has two, somewhat obscure, limitations:

  1. The left-side cannot be a global. Globals may already be automatically aliased (set) to a register, and the compiler relies on the validity of these registers for code correctness. If the global variable itself assumes another storage location, its pre-existing register will have the wrong storage. While the compiler may find ways to invalidate such registers, nothing can be done if there are yielded coroutines that have already set the register!
  2. The left-side cannot be a dict element. Array elts are simple and use the same storage format as variables. But pairs in dicts have much more information. Therefore, a pair cannot get the same storage as a simple variable. (But a simple variable can happily use the larger storage of a dict pair.) So pairs are allowed on the right side, but not the left side, of an alias.

The main body of a script runs as an implicit (nameless) main function. This function, by virtue of its top position, cannot directly declare any local vars to alias critical globals (everything it declares becomes another global). While a current limitation, this can be remedied by introducing a {} block in the main function for the express purpose of introducing one or more local vars and aliasing globals.