Consider the following program:
//go:nosplit
func use(z, x []int) int {
if len(x) == 0 {
return 0
} else if len(x) > cap(z) {
z = make([]int, len(x))
} else {
z = z[:len(x)]
}
return z[len(x)-1]
}
There is no input which can make it panic, but it emits a bounds check for z[len(x)-1], despite len(z) == len(x) > 0:
TEXT .use(SB), NOSPLIT|ABIInternal, $64-48
PUSHQ BP
MOVQ SP, BP
SUBQ $56, SP
MOVQ AX, 72(FP)
MOVQ DI, 96(FP)
TESTQ SI, SI
JEQ pc112
CMPQ CX, SI
JGE pc87
NOP
CMPQ SI, $4
JHI pc59
LEAQ 24(SP), CX
MOVUPS X15, (CX)
MOVUPS X15, 16(CX)
LEAQ 24(SP), AX
JMP pc87
pc59:
MOVQ SI, 104(SP)
LEAQ type:int(SB), AX
MOVQ SI, BX
MOVQ BX, CX
CALL runtime.makeslice(SB)
MOVQ 104(SP), SI
pc87:
LEAQ -1(SI), CX
NOP
CMPQ SI, CX
JLS pc120
MOVQ -8(AX)(SI*8), AX
ADDQ $56, SP
POPQ BP
RET
pc112:
XORL AX, AX
ADDQ $56, SP
POPQ BP
RET
pc120:
CALL runtime.panicBounds(SB)
After much poking at the structure of this code, I've concluded that the issue is the fact that the bounds information comes from non-dominating blocks which collectively dominate the load. This is unfortunate, because this notably affects math/big.nat.make, which wants to be append(z[:0], make(nat, n)), but in the case where n <= cap(z) results in a redundant call to memclr. This has the knock-on effect that other code in math/big needs to (redundantly) teach the optimizer about bounds checks in the "usual way".
Maybe I'm getting something wrong here, but it looks like a genuine missed optimization to me.
Consider the following program:
There is no input which can make it panic, but it emits a bounds check for
z[len(x)-1], despitelen(z) == len(x) > 0:After much poking at the structure of this code, I've concluded that the issue is the fact that the bounds information comes from non-dominating blocks which collectively dominate the load. This is unfortunate, because this notably affects
math/big.nat.make, which wants to beappend(z[:0], make(nat, n)), but in the case wheren <= cap(z)results in a redundant call to memclr. This has the knock-on effect that other code in math/big needs to (redundantly) teach the optimizer about bounds checks in the "usual way".Maybe I'm getting something wrong here, but it looks like a genuine missed optimization to me.