|Code, Local Variables, and Temporary Values Outside Functions firstname.lastname@example.org (Robbert Haarman) (2010-11-21)|
|From:||Robbert Haarman <email@example.com>|
|Date:||Sun, 21 Nov 2010 19:15:56 +0100|
|Posted-Date:||21 Nov 2010 23:07:29 EST|
In making Voodoo more suitable for expressing various kinds of program,
one issue I run up against is that not everything necessarily maps
nicely to the calling conventions of a given platform. For example,
continuations are not necessarily implemented most naturally or
efficiently on top of functions that have stack-allocated activation
frames. The problem here is that a stack is last in, first out,
whereas continuations require a more flexible way of managing
activation frame lifetime.
Similarly, automatic memory management is hard to implement without
an interface for inspecting activation frames. The problem here is
that it can be hard to find out which objects are live at a given
point in the execution of a program.
In assembly languages I am familiar with, one can get around these
issues by, essentially, implementing one's own calling convention
and activation frames. This allows activation frame lifetime and
inspection of active frames to be implemented as needed. From the
start, the plan has been to provide the same capability in Voodoo.
Since functions are implemented (in my Voodoo compiler, in any case)
using the target platform's calling conventions, code that uses
a different calling convention will have to be written outside of
functions. This is already possible in Voodoo, and has been since
The challenge here is allowing for things like local variables when
not inside a function. Inside a function, there is an activation frame
in which such variables can be created. Outside a function, such a
frame may not exist, or it may be in a layout unknown to the Voodoo
compiler. What code, then, is the Voodoo compiler to generate for
"let x add y z"? Should let be declared invalid outside functions?
If that is done, how should a language compiling to Voodoo deal with
this, for example, when it needs to store temporary values somewhere?
A possible solution I have implemented in an experimental branch of
the Voodoo compiler is by using blocks. A block is started by
the keyword "block" and ended by "end block". Local variables
can be declared inside a block and will go out of scope at the
end of the block. Blocks can also be nested. For example:
let x 12
call print_int x
let x 42
call print_int x
call print_int x
would print 12, 42, and 12. I think that these blocks are useful
in their own right, and they will likely be in an upcoming release
Blocks can be used to solve the "no frame" problem by specifying
that a frame that allows for the addition of local variables is to
be created when a block is started. That means that the above code
could be written at top level (i.e. outside of any functions or
blocks), and it would create its own frame.
That still leaves the question of how a block can be left, and
what should happen in the various cases. One way a block can be
left is when control reaches the end of the block. It seems
sensible to de-allocate any variables that were created inside
the block at that point.
In Voodoo, another way to leave a block would be to execute a goto with
a target outside the block. What should happen in that case? Currently,
as in machine code, nothing special is actually done with the block
when a goto is executed. Perhaps it would be better to also de-allocate
the frame and its contents at that point. And what if one jumps
right into the middle of a block? Should an appropriate frame be set
up? Currently, a goto is just a simple goto, and Voodoo leaves the
responsibility for maintaining/smashing the stack up to the programmer.
I feel this is appropriate for a low-level language like Voodoo.
I am throwing this out here for discussion because many minds know more
than one. Perhaps there is a better idea out there that I haven't though
of yet. If you have such an idea, I would be most interested to learn
about it. :-) Keep in mind that Voodoo's goal is to basically expose the
hardware's raw capabilities, albeit in a platform-independent way. Type
safety is not a concern, efficiency, flexibility, and portability are.
Return to the
Search the comp.compilers archives again.