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.