New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: useless autotmp variables for slice literal #29547

dsnet opened this Issue Jan 4, 2019 · 1 comment


None yet
2 participants
Copy link

dsnet commented Jan 4, 2019

Using go1.11.2.

Consider the following snippet:

 var sink []V 

 func main() {
 	sink = []V{
-		(*tar.Reader)(nil),
+		VOf((*tar.Reader)(nil)),

 type V = interface{}

 func VOf(t interface{}) V { return t }

The VOf function is a trivially useless function does nothing. If VOf function is missing, the main function is 176 bytes long, but if it is present, the function is 188 bytes long.

The assembly output without use of VOf is:

 "".main STEXT size=176 args=0x0 locals=0x18
 	(main.go:9)	TEXT	"".main(SB), $24-0
 	(main.go:9)	MOVQ	(TLS), CX
 	(main.go:9)	CMPQ	SP, 16(CX)
 	(main.go:9)	JLS	166
 	(main.go:9)	SUBQ	$24, SP
 	(main.go:9)	MOVQ	BP, 16(SP)
 	(main.go:9)	LEAQ	16(SP), BP
 	(main.go:9)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
 	(main.go:9)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
 	(main.go:9)	FUNCDATA	$3, gclocals·f6aec3988379d2bd21c69c093370a150(SB)
 	(main.go:10)	LEAQ	type.[1]interface {}(SB), AX
 	(main.go:10)	MOVQ	AX, (SP)
 	(main.go:10)	CALL	runtime.newobject(SB)
 	(main.go:10)	MOVQ	8(SP), AX
 	(main.go:11)	MOVQ	"".statictmp_0(SB), CX
 	(main.go:11)	LEAQ	type.*archive/tar.Reader(SB), DX
 	(main.go:11)	MOVQ	DX, (AX)
 	(main.go:11)	CMPL	runtime.writeBarrier(SB), $0
 	(main.go:11)	JNE	146
 	(main.go:11)	MOVQ	CX, 8(AX)
 	(main.go:10)	MOVQ	$1, "".sink+8(SB)
 	(main.go:10)	MOVQ	$1, "".sink+16(SB)
 	(main.go:10)	CMPL	runtime.writeBarrier(SB), $0
 	(main.go:10)	JNE	132
 	(main.go:10)	MOVQ	AX, "".sink(SB)
 	(main.go:13)	MOVQ	16(SP), BP
 	(main.go:13)	ADDQ	$24, SP
 	(main.go:13)	RET
 	(main.go:10)	LEAQ	"".sink(SB), DI
 	(main.go:10)	CALL	runtime.gcWriteBarrier(SB)
 	(main.go:10)	JMP	122
 	(main.go:11)	LEAQ	8(AX), DI
 	(main.go:10)	MOVQ	AX, DX
 	(main.go:11)	MOVQ	CX, AX
 	(main.go:11)	CALL	runtime.gcWriteBarrier(SB)
 	(main.go:10)	MOVQ	DX, AX
 	(main.go:11)	JMP	84
 	(main.go:11)	NOP
 	(main.go:9)	CALL	runtime.morestack_noctxt(SB)
 	(main.go:9)	JMP	0

However, calling VOf results in the generation of an autotmp variables.

 "".main STEXT size=188 args=0x0 locals=0x20
 	(main.go:9)	TEXT	"".main(SB), $32-0
 	(main.go:9)	MOVQ	(TLS), CX
 	(main.go:9)	CMPQ	SP, 16(CX)
 	(main.go:9)	JLS	178
 	(main.go:9)	SUBQ	$32, SP
 	(main.go:9)	MOVQ	BP, 24(SP)
 	(main.go:9)	LEAQ	24(SP), BP
 	(main.go:9)	FUNCDATA	$0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
 	(main.go:9)	FUNCDATA	$1, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
 	(main.go:9)	FUNCDATA	$3, gclocals·ba192201ac73f95fb90b551d9176d705(SB)
 	(main.go:11)	MOVQ	"".statictmp_0(SB), AX
+	(main.go:11)	MOVQ	AX, ""..autotmp_9+16(SP)
 	(main.go:10)	LEAQ	type.[1]interface {}(SB), CX
 	(main.go:10)	MOVQ	CX, (SP)
 	(main.go:10)	CALL	runtime.newobject(SB)
 	(main.go:10)	MOVQ	8(SP), AX
 	(main.go:10)	LEAQ	type.*archive/tar.Reader(SB), CX
 	(main.go:10)	MOVQ	CX, (AX)
 	(main.go:10)	CMPL	runtime.writeBarrier(SB), $0
 	(main.go:10)	JNE	156
+	(main.go:10)	MOVQ	""..autotmp_9+16(SP), CX
 	(main.go:10)	MOVQ	CX, 8(AX)
 	(main.go:10)	MOVQ	$1, "".sink+8(SB)
 	(main.go:10)	MOVQ	$1, "".sink+16(SB)
 	(main.go:10)	CMPL	runtime.writeBarrier(SB), $0
 	(main.go:10)	JNE	142
 	(main.go:10)	MOVQ	AX, "".sink(SB)
 	(main.go:13)	MOVQ	24(SP), BP
 	(main.go:13)	ADDQ	$32, SP
 	(main.go:13)	RET
 	(main.go:10)	LEAQ	"".sink(SB), DI
 	(main.go:10)	CALL	runtime.gcWriteBarrier(SB)
 	(main.go:10)	JMP	132
 	(main.go:10)	LEAQ	8(AX), DI
 	(main.go:10)	MOVQ	AX, CX
+	(main.go:10)	MOVQ	""..autotmp_9+16(SP), AX
 	(main.go:10)	CALL	runtime.gcWriteBarrier(SB)
 	(main.go:10)	MOVQ	CX, AX
 	(main.go:10)	JMP	94
 	(main.go:10)	NOP
 	(main.go:9)	CALL	runtime.morestack_noctxt(SB)
 	(main.go:9)	JMP	0

An additional autotmp variable is added for every element in the slice, so large slice literals are noticeably more bloated with VOf than without.

@dsnet dsnet added the Performance label Jan 4, 2019


This comment has been minimized.

Copy link

randall77 commented Jan 4, 2019

This is no longer directly a bug at tip, because we are smarter about generating nil using MOVQ $0, ... instead of MOVQ "".statictmp_0(SB), AX ; MOVQ AX, .... (CL 141118)

The reason the temp was used is that the compiler didn't realize the load (of 0) could not be reordered with the newobject call. So the value needed to be loaded, spilled, restored, and then put in the desired destination.

If you instead do:

var tr tar.Reader

func main() {
	sink = []V{

There still isn't an issue at tip.

If you do:

var tr *tar.Reader

func main() {
	sink = []V{

then an autotmp is used, because the compiler doesn't know that newobject won't modify tr.

We could teach the compiler that newobject won't modify globals, but the compiler doesn't have any infrastructure for taking advantage of that knowledge. Yet.

I'm going to close this as fixed. The more general case will probably be solved if and when we do some sort of alias analysis in the compiler.

@randall77 randall77 closed this Jan 4, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment