-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Currently, gollvm stores the current g in tls. The runtime.getg () function returns the current g. This function will be inlined for better performance and the inlining will be disabled by GoSafeGetg pass in some situations. Cherry described this situation in this pass:
within a function,
//
// load g
// call mcall(...)
// load g
//
// may be compiled to
//
// leaq g@TLS, %rdi
// call __tls_get_addr
// movq %rax, %rbx // cache in a callee-save register %rbx
// ... use g in %rax ...
// call foo
// ... use g in %rbx ...
// This is incorrect if a thread switch happens at the call of foo.
A practical example of this situation: gofrontend/chan.go#154
By removing the inlining of the second and subsequent getg functions in a block, GoSafeGetg pass fixed this issue on linux/amd64. But on Linux / arm64, llvm performs cache optimization on the tls base address across the entire function range, so the g obtained by the second and subsequent getg in the above situation may still be wrong.
As I know, this kind of optimization is common in llvm and gcc, and it seems to be correct and very good for c / c ++. Before c / c ++ introduced a concept like goroutine, I think this optimization will not be changed. Please refer to similar issues: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=26461, https://bugs.llvm.org/show_bug.cgi?id=19177.
Currently gollvm only supports linux / amd64 and linux / arm64, but as more platforms are supported in the future, I think this issue will be a problem on more platforms, so I think we need to find a better way to store the current g.
At present, the methods I can think of are as follows:
- Keep the current practice, store g in tls, try to remove the cache of tls base address.
- Follow the practice of main go, reserve a register to store g.
- Store g in a suitable location on the stack.
@thanm @cherrymui @ianlancetaylor Any suggestions ?