olofson / audiality2

A realtime scripted modular audio engine for video games and musical applications.
http://audiality.org/
zlib License
79 stars 5 forks source link

Proper functions and return values #59

Open olofson opened 11 years ago

olofson commented 11 years ago

For any non-trivial programs, we need to be able to define functions, to avoid having to duplicate and inline complicated expressions.

It would be nice to also support calls to native code this way, and we could provide an API for injecting application callbacks as functions, as another simplified alternative to writing voice units, similar to the a2_Tap()/a2_Insert() API.

Actually, we already have a kind of functions, declared with the same syntax as programs, except inside other programs. However, those differ from what we want in three ways:

  1. They have direct access to local variables and control registers of the program they're defined in, and as a result...
  2. They can only ever be local to a program.
  3. They cannot return values.

From a technical point of view, 1 and 2 are closely related to the fact that these functions are compiled in the context of their parent program, relying on the compiler register allocation logic to keep function arguments and local variables from colliding with registers used by the parent program.

To call functions external to the program, we need to completely preserves all VM registers, to avoid corrupting the caller VM state. Perhaps we can just use a2_VoiceCall()? (Currently used for local functions and message/interrupt handlers.) A non-local function would just have 0 (probably) in its 'argv' field, since there are no hardwired registers or control registers in the context they run in.

Further, we need a syntax for calling functions from within expressions. Maybe LISP syntax? A TK_FUNCTION directly following the opening '(' can trivially be detected in a2c_SimplExp(), as that's currently not legal syntax.

We could apply return values and the corresponding call-from-within-expression syntax to program local functions while we're at it, essentially making local functions a variant of functions with access to the state of the calling parent program. I think it would make sense to use the same 'func' keyword for these, basically just saying that there's a return value.

olofson commented 8 years ago

Here's another take on the low level implementation of this: Isolate the execution context of functions completely from the caller's VM context! That eliminates the register save/restore problem, and it also makes a lot more sense in the first place, as these functions can't have access to any caller VM registers anyway.

Also, this makes it much easier to implement native functions. Both native and A2S functions would be called with an array of arguments (the argument stack used by PUSH and CALL), and a pointer to a space for the return value. Native functions just get the pointers and get on with the work, whereas A2S functions would run in a "scratchpad" VM state in the engine, result slot and arguments already in the VM registers upon entry. (Much like how a program is started on a new VM instance, that is.)