cmd/compile, bytes: bootstrap array causes bytes.Buffer to always be heap-allocated #7921
Open
Description
What steps reproduce the problem? 1. Download playground.zip 2. Run go build -gcflags=-m bytes.go 3. Run go build -gcflags=-m bytes2.go playground/bytes: Resembles a simplified version of Go's bytes.Buffer, with the exception that NewBuffer returns Buffer{} instead of &Buffer{}. playground/bytes2: Different implementation that avoids reslicing What happened? $ go build -gcflags=-m bytes.go # playground/bytes bytes/buffer.go:12: can inline NewBuffer bytes/buffer.go:57: can inline (*Buffer).Read bytes/buffer.go:69: can inline (*Buffer).Bytes bytes/buffer.go:12: leaking param: b to result ~anon1 bytes/buffer.go:16: leaking param: b bytes/buffer.go:16: leaking param: b bytes/buffer.go:34: make([]byte, 2 * cap(b.buf) + n) escapes to heap bytes/buffer.go:16: leaking param: b bytes/buffer.go:44: leaking param: b bytes/buffer.go:44: leaking param: b bytes/buffer.go:52: leaking param: b bytes/buffer.go:52: (*Buffer).Write p does not escape bytes/buffer.go:57: (*Buffer).Read b does not escape bytes/buffer.go:57: (*Buffer).Read p does not escape bytes/buffer.go:69: (*Buffer).Bytes b does not escape # command-line-arguments ./bytes.go:9: inlining call to bytes.NewBuffer ./bytes.go:13: inlining call to Read ./bytes.go:18: inlining call to Bytes ./bytes.go:9: moved to heap: myBuf ./bytes.go:11: myBuf escapes to heap ./bytes.go:8: make([]byte, 4) escapes to heap ./bytes.go:14: myBuf escapes to heap ./bytes.go:8: make([]byte, 4) escapes to heap ./bytes.go:11: main []byte literal does not escape ./bytes.go:12: main make([]byte, 2) does not escape ./bytes.go:13: main myBuf does not escape ./bytes.go:14: main []byte literal does not escape ./bytes.go:18: main myBuf does not escape $ go build -gcflags=-m bytes2.go # playground/bytes2 bytes2/buffer.go:13: can inline NewBuffer bytes2/buffer.go:61: can inline (*Buffer).Read bytes2/buffer.go:73: can inline (*Buffer).Bytes bytes2/buffer.go:13: leaking param: b to result ~anon1 bytes2/buffer.go:38: make([]byte, size) escapes to heap bytes2/buffer.go:17: (*Buffer).grow b does not escape bytes2/buffer.go:47: (*Buffer).Grow b does not escape bytes2/buffer.go:54: (*Buffer).Write b does not escape bytes2/buffer.go:54: (*Buffer).Write p does not escape bytes2/buffer.go:61: (*Buffer).Read b does not escape bytes2/buffer.go:61: (*Buffer).Read p does not escape bytes2/buffer.go:73: (*Buffer).Bytes b does not escape # command-line-arguments ./bytes2.go:9: inlining call to bytes2.NewBuffer ./bytes2.go:13: inlining call to Read ./bytes2.go:18: inlining call to Bytes ./bytes2.go:8: main make([]byte, 4) does not escape ./bytes2.go:11: main myBuf does not escape ./bytes2.go:11: main []byte literal does not escape ./bytes2.go:12: main make([]byte, 2) does not escape ./bytes2.go:13: main myBuf does not escape ./bytes2.go:14: main myBuf does not escape ./bytes2.go:14: main []byte literal does not escape ./bytes2.go:18: main myBuf does not escape # command-line-arguments What should have happened instead? This shouldn't be happening with playground/bytes: ./bytes.go:9: moved to heap: myBuf ./bytes.go:11: myBuf escapes to heap ./bytes.go:8: make([]byte, 4) escapes to heap ./bytes.go:14: myBuf escapes to heap ./bytes.go:8: make([]byte, 4) escapes to heap A re-slicing shouldn't cause playground/bytes's Buffer to always be heap allocated. It should be like playground/bytes2, which avoids heap allocation until a resize happens: bytes2/buffer.go:38: make([]byte, size) escapes to heap Please provide any additional information below. If this is working as intended, the implementation of bytes.Buffer should change to allow it to be completely stack allocated. The playground/bytes2 implementation is completely stack allocated until the initial buffer needs to be resized. This allows it to operate on the stack, avoiding a heap allocation if possible.
Attachments:
- playground.zip (5470 bytes)