-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
Proposal Details
Add go:readonly comment directive for body-less functions indicating the following arguments and any pointers exclusively accessed through a readonly argument wont be modified:
// writeBlocks will update vector (read-hash-write) and read extra and bytes.
//
//go:nescape
//go:readonly extra, bytes
func writeBlocks(vector *[4]uint64, extra *[32]byte, bytes []byte)The point of this is to hook up with 925d2fb this would allow []byte(str) conversions to be elided when the byte string is passed as a readonly argument to an assembly optimized subroutine.
github.com/cespare/xxhash implements Sum64String(string) uint64 and (*Digest).WriteString(string) using unsafe which allows to skip the allocation either by using (*strings.Reader).WriteTo or io.WriteString.
Other hashing libraries such as crypto do not implement this optimization, this could allow to implement this using compiler optimizations rather than duplicating APIs for []byte and string. (*note: this assume you are using a devirtualized hash.Hash or a concrete hasher implementation, which is now possible and not unrealistic, particularly for h := sha256.New(); h.Write([]byte(str)) patterns)
In the future this could improve register allocation in some rare edge cases so I don't expect this to be implemented.
Other stronger compilers like GCC and LLVM might already implement such logic in their register allocators, then work in gofrontend could be much easier (as I assume this would amount to setting up metadata flags on the function call nodes passed to gcc or llvm) so who knows.
The body-less function is allowed to read from memory, which is arguably a side effect when using -race altho I don't think very many body-less functions implement any kind of race tracking.
For slices the body-less function would assume to not be allowed to write past capacity.
If some memory is accessible through an unknown and a readonly pointer the function is allowed to write to it:
//go:readonly b
func doSomething(a, b []byte)
func f() {
var x [64]byte
doSomething(x[:32:32], x[:])
}Here the compiler would be allowed to assume only the last 32 bytes of the x array are readonly (I expect compilers to not implement this or to assume x maybe completely overwritten).
If a pointer to a pointer is readonly then both memory are readonly:
type s struct{
v *int
}
//go:readonly a
func doSomething(a *s)
func f() {
var x int // here the compiler is allowed to assume `x` will never be modified by `doSomething`
doSomething(&S{&x})
}Note: this apply transitively to any amount of level.
Combining the two edge cases above, if a pointer to a pointer is accessible both through unknown and readonly paths, the body-less function may modify it:
type s struct{
v *int
}
//go:readonly b
func doSomething(a, b *s)
func f() {
var x int // x maybe modified through a
doSomething(&S{&x}, &S{&x})
}The body-less function is assumed to maybe write to anything part of the global scope:
var x int
//go:readonly a
func doSomething(a *int)
func f() {
doSomething(&x) // doSomething is allowed to modify x
}Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Status