One approach is to maintain a shadow stack holding the pointers in a place 
the GC already knows about, like an array allocated in the heap. This can 
be done in Go, the language. Dereferences would use a level of indirection. 
Perhaps one would pass an index into the array instead of the pointer and 
somehow know the location of the shadow stack from a VM structures. This 
way the stack contains no pointers _into the heap_ so the _GC_ is happy. 
You might still have to deal with pointers to stack allocated objects since 
stacks can be moved and so forth but that is not the problem being 
discussed.

Go the implementation, such as the Go 1.13, has a GC that does not move 
heap objects. This means that to keep a heap object live the GC only needs 
to know about a single pointer. That’s sort of handy since now you can push 
the pointer onto the shadow stack and also onto the call stack since as 
long as the shadow stack is visible the object will not be collected. I 
note that this involves a barrier on all pointer writes so it is more than 
just a change to the calling conventions. Reads on the other hand would be 
full speed and not require a level of indirection or barrier unless and 
until Go the implementation moved to a moving collector.

I would explore this approach first since all of the pieces are under your 
control. Developing an ABI for stack maps would include other people with 
differing agendas and would likely slow you down. Likewise forking would 
come with the usual maintenance/merge headaches.



On Wednesday, October 23, 2019 at 1:50:51 PM UTC+1, Max wrote:
>
> Hello gophers,
>
> My recent attempt at creating a JIT compiler in Go to speed up my 
> interpreter https://github.com/cosmos72/gomacro hit an early roadblock.
>
> In its current status, it can compile integer arithmetic and 
> struct/array/slice/pointer access for amd64 and arm64, but it cannot 
> allocate memory or call other functions, which severely limits its 
> usefulness (and is thus not yet used by gomacro).
>
> The reason is: there is a requirement that Go functions must have a "stack 
> frame descriptors registered with the runtime", in brief a "stack map" that 
> tells which bits on the stack are pointers and which ones are not.
> See https://github.com/golang/go/issues/20123 for details.
>
> But there is no API to associate a stack map to functions generated at 
> runtime and running on the Go stack - currently the only supported 
> mechanism to load Go code at runtime is to open a shared library file with 
> `plugin.Open()`
>
> Thus JIT-generated functions must avoid triggering the garbage collector, 
> as it would panic as described in the link above.
> In turn, this means they cannot:
> * allocate memory
> * call other functions
> * grow the stack
> or do anything else that may start the GC.
>
> Now, I understand *why* Go functions must currently have a stack map, and 
> I see at least two possible solutions:
>
> 1. implement an API to associate a stack map to functions generated at 
> runtime - possibly by forking the Go compiler and/or standard library
> 2. replace Go GC and allocator with an alternative that does not require 
> stack maps - for example Boehm GC https://www.hboehm.info/gc/
>     Here too, forking the Go compiler and/or standard library if needed.
>
> My questions are:
>
> a. which one of the two solutions above is easier, and how long could it 
> take to a full-time expert?
> b. does anyone have an easier solution or workaround to achieve the same 
> goal?
>
> Regards,
> cosmos72
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/b5fed315-2e30-4c63-8f7e-75fd21feb853%40googlegroups.com.

Reply via email to