Closed
Description
gc 1.6.2 generates incorrect linux/amd64 code for the following program: the final print statement prints the wrong answer.
% cat ~/a.go
package main
type S []S
func main() {
var s S
println(s == nil) // "true"
s = append(s, s) // append a nil value to s
println(s[0] == nil) // "false" (!)
}
From the assembly, it appears that the register r8, which holds the old value of s.ptr and is used in the s[0]==nil check, gets set to the the new value of s.ptr during the growslice operation.
By default, the tip gc generates completely different code that does not exhibit the problem, but with -ssa=0
the same problem reoccurs (with r9). See comments in listing below.
"".main t=1 size=335 args=0x0 locals=0x68
...
0x0021 00033 (a.go:7) MOVQ $0, AX
0x0023 00035 (a.go:7) MOVQ AX, "".s+80(SP)
0x0028 00040 (a.go:7) MOVQ AX, "".s+88(SP)
0x002d 00045 (a.go:8) MOVQ AX, "".s+72(SP)
0x0032 00050 (a.go:8) CMPQ AX, $0
0x0036 00054 (a.go:8) SETEQ "".autotmp_0+71(SP)
0x003b 00059 (a.go:8) PCDATA $0, $1
0x003b 00059 (a.go:8) CALL runtime.printlock(SB)
0x0040 00064 (a.go:8) MOVBQZX "".autotmp_0+71(SP), BX
0x0045 00069 (a.go:8) MOVB BL, (SP)
0x0048 00072 (a.go:8) PCDATA $0, $1
0x0048 00072 (a.go:8) CALL runtime.printbool(SB)
0x004d 00077 (a.go:8) PCDATA $0, $1
0x004d 00077 (a.go:8) CALL runtime.printnl(SB)
0x0052 00082 (a.go:8) PCDATA $0, $1
0x0052 00082 (a.go:8) CALL runtime.printunlock(SB)
0x0057 00087 (a.go:8) MOVQ "".s+72(SP), CX
0x005c 00092 (a.go:8) MOVQ "".s+88(SP), DI
0x0061 00097 (a.go:9) MOVQ "".s+80(SP), BX
0x0066 00102 (a.go:9) MOVQ CX, R9 ;;; load old s.ptr into R9
0x0069 00105 (a.go:9) MOVQ BX, AX
0x006c 00108 (a.go:9) INCQ BX
0x006f 00111 (a.go:9) CMPQ BX, DI
0x0072 00114 (a.go:9) JHI $1, 260 ;;; uh oh, time to grow the slice
0x0078 00120 (a.go:9) MOVQ BX, SI
0x007b 00123 (a.go:9) MOVQ CX, BX
0x007e 00126 (a.go:9) MOVQ AX, R8
0x0081 00129 (a.go:9) IMULQ $24, R8
0x0085 00133 (a.go:9) ADDQ R8, BX
0x0088 00136 (a.go:9) MOVQ SI, "".s+80(SP)
0x008d 00141 (a.go:9) MOVQ SI, 8(BX)
0x0091 00145 (a.go:9) MOVQ DI, "".s+88(SP)
0x0096 00150 (a.go:9) MOVQ DI, 16(BX)
0x009a 00154 (a.go:9) MOVQ R9, "".s+72(SP)
0x009f 00159 (a.go:9) CMPB runtime.writeBarrier(SB), $0
0x00a6 00166 (a.go:9) JNE $0, 234
0x00a8 00168 (a.go:9) MOVQ R9, (BX) ;;; check s[0]==nil using new not old s.ptr
0x00ab 00171 (a.go:10) CMPQ SI, $0
0x00af 00175 (a.go:10) JLS $1, 227
0x00b1 00177 (a.go:10) MOVQ (R9), R8
0x00b4 00180 (a.go:10) CMPQ R8, $0
0x00b8 00184 (a.go:10) SETEQ "".autotmp_1+71(SP)
0x00bd 00189 (a.go:10) PCDATA $0, $0
0x00bd 00189 (a.go:10) CALL runtime.printlock(SB)
0x00c2 00194 (a.go:10) MOVBQZX "".autotmp_1+71(SP), BX
0x00c7 00199 (a.go:10) MOVB BL, (SP)
0x00ca 00202 (a.go:10) PCDATA $0, $0
0x00ca 00202 (a.go:10) CALL runtime.printbool(SB)
...
0x00e2 00226 (a.go:12) RET
...
0x0104 00260 (a.go:9) LEAQ type."".S(SB), R8
0x010b 00267 (a.go:9) MOVQ R8, (SP)
0x010f 00271 (a.go:9) MOVQ CX, 8(SP)
0x0114 00276 (a.go:9) MOVQ AX, 16(SP)
0x0119 00281 (a.go:9) MOVQ DI, 24(SP)
0x011e 00286 (a.go:9) MOVQ BX, 32(SP)
0x0123 00291 (a.go:9) PCDATA $0, $0
0x0123 00291 (a.go:9) CALL runtime.growslice(SB)
0x0128 00296 (a.go:9) MOVQ 40(SP), R9 ;;; update R9 with new, larger s.ptr
0x012d 00301 (a.go:9) MOVQ 48(SP), SI
0x0132 00306 (a.go:9) MOVQ 56(SP), DI
0x0137 00311 (a.go:9) MOVQ SI, AX
0x013a 00314 (a.go:9) INCQ SI
0x013d 00317 (a.go:9) MOVQ R9, CX
0x0140 00320 (a.go:9) JMP 123