[LLVMdev] RFC: GSoC Project
Sanjoy Das
sanjoy at playingwithpointers.com
Mon Apr 11 06:07:32 PDT 2011
Hi!
Thanks for the feedback. For context, my implementation plan is here:
http://pastebin.com/e9JMZNCE
First, about unwinding:
In architectures like x86-64, where unwinding based on DWARF info, there
shouldn't be any problems; since the DWARF info will be emitted
correctly. Otherwise, if the unwinding is done by following BP, it
should still be possible to have BP de-reference correctly (ref. "Frame
Pointers" section in the implementation plan). SP will not always have a
correct value - I don't know if this is problem.
About co-routines:
Here is a sketch of how I think co-routines can be implemented (I'll
merge this with the main implementation plan after I get some feedback):
Have a new instruction, called "yield" to return a value from a
co-routine, preserving the state. Thus, we immediately know which
functions are co-routines. Each co-routine will have a new stack.
Associate each co-routine with a thread-local global variable (called
saved_stack here, will have to be mangled with the name of the
co-routine) which points to the start stack block for that co-routine.
This will be the first block in the chain of blocks to follow.
The structure of the block will be similar to the structure of a regular
stack block, except that it will also have space to store two registers
- this_ip and this_sp.
The prologue of a co-routine will jump to a function similar to
setup_new_block (setup_new_block_coroutine) which will work like
setup_new_block, except:
1. It will first check if saved_stack is NULL. If it is NULL, it will
allocate a new block and save it to saved_stack. It if isn't, it'll
simply restore saved_sp, saved_ip.
2. In case a new block was allocated, it will pretty much do what
setup_block does, after which it will adjust the SP to make space for
the saved registers.
The destroy_block procedure will also have to be a little different
(mentioned below).
There are four things (relevant to this discussion) a co-routine can do:
Yield
This returns control to the calling function, without forgetting the
current state of the function. To do this, we save saved_ip and
saved_sp. Every yield be padded with instructions pushing and popping
the registers live at that point. Then we set the return value (register
or memory), and restore saved_sp and saved_ip from the current block. We
can't simply return because the actual return value has been hijacked to
provide for block cleanup.
Call to regular function
Just a simple call - the caller's prologue will handle setting up a it's
own stack space etc.
Call to Co-routine
This too should "just work", since all the heavy-lifting is done in the
co-routine's prologue. However, the above approach will not work for
nested co-routines (i.e. calling the same co-routine body with one call
is still active, recursively). I'm not sure if having support for nested
co-routines will add any value.
Return
This will be a regular return. Since the return value has been hijacked
to point to a another block of code (destroy_block_coroutine), control
will jump there instead.
destroy_block_coroutine will free the linked-list of stack blocks (we
have to free this, since we will won't have a reference to this list
anymore), set saved_stack for this co-routine to NULL, and restore
saved_sp and saved_ip.
--
Sanjoy Das
http://playingwithpointers.com
More information about the llvm-dev
mailing list