/
githubmirror
/
go
Обзор
Документация
Войти
/
githubmirror
/
go
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/codegen/comparisons.go
1 053 строки
19 KB
Egon Elbre
cmd/compile/internal/ssa: strip redundant byte/halfword zero-extensions on arm64
28 июл 2026, 01:03
28 июл 2026, 01:03
5de5a2e
Код
Авторство
О чём код?
// asmcheck // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package codegen import ( "cmp" "unsafe" ) // This file contains code generation tests related to the comparison // operators. // -------------- // // Equality // // -------------- // // Check that compare to constant string use 2/4/8 byte compares func CompareString1(s string) bool { // amd64:`CMPW \(.*\), [$]` // arm64:`MOVHU \(.*\), [R]` `MOVD [$]` `CMPW R` // ppc64le:`MOVHZ \(.*\), [R]` `CMPW .*, [$]` // s390x:`MOVHBR \(.*\), [R]` `CMPW .*, [$]` return s == "xx" } func CompareString2(s string) bool { // amd64:`CMPL \(.*\), [$]` // arm64:`MOVWU \(.*\), [R]` `CMPW .*, [R]` // ppc64le:`MOVWZ \(.*\), [R]` `CMPW .*, [R]` // s390x:`MOVWBR \(.*\), [R]` `CMPW .*, [$]` return s == "xxxx" } func CompareString3(s string) bool { // amd64:`CMPQ \(.*\), [A-Z]` // arm64:-`CMPW ` // ppc64x:-`CMPW ` // s390x:-`CMPW ` return s == "xxxxxxxx" } // Check that arrays compare use 2/4/8 byte compares func CompareArray1(a, b [2]byte) bool { // amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // arm64:-`MOVBU ` // ppc64le:-`MOVBZ ` // s390x:-`MOVBZ ` return a == b } func CompareArray2(a, b [3]uint16) bool { // amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray3(a, b [3]int16) bool { // amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray4(a, b [12]int8) bool { // amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray5(a, b [15]byte) bool { // amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } // This was a TODO in mapaccess1_faststr func CompareArray6(a, b unsafe.Pointer) bool { // amd64:`CMPL \(.*\), [A-Z]` // arm64:`MOVWU \(.*\), [R]` `CMPW .*, [R]` // ppc64le:`MOVWZ \(.*\), [R]` `CMPW .*, [R]` // s390x:`MOVWBR \(.*\), [R]` `CMPW .*, [R]` return *((*[4]byte)(a)) != *((*[4]byte)(b)) } // Check that some structs generate 2/4/8 byte compares. type T1 struct { a [8]byte } func CompareStruct1(s1, s2 T1) bool { // amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:-`CALL` return s1 == s2 } type T2 struct { a [16]byte } func CompareStruct2(s1, s2 T2) bool { // amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:-`CALL` return s1 == s2 } // Assert that a memequal call is still generated when // inlining would increase binary size too much. type T3 struct { a [24]byte } func CompareStruct3(s1, s2 T3) bool { // amd64:-`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:`CALL` return s1 == s2 } type T4 struct { a [32]byte } func CompareStruct4(s1, s2 T4) bool { // amd64:-`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // amd64:`CALL` return s1 == s2 } // -------------- // // Ordering // // -------------- // // Test that LEAQ/ADDQconst are folded into SETx ops var r bool func CmpFold(x uint32) { // amd64:`SETHI .*\(SB\)` r = x > 4 } // Test that direct comparisons with memory are generated when // possible func CmpMem1(p int, q *int) bool { // amd64:`CMPQ \(.*\), [A-Z]` return p < *q } func CmpMem2(p *int, q int) bool { // amd64:`CMPQ \(.*\), [A-Z]` return *p < q } func CmpMem3(p *int) bool { // amd64:`CMPQ \(.*\), [$]7` return *p < 7 } func CmpMem4(p *int) bool { // amd64:`CMPQ \(.*\), [$]7` return 7 < *p } func CmpMem5(p **int) { // amd64:`CMPL runtime.writeBarrier\(SB\), [$]0` *p = nil } func CmpMem6(a []int) int { // 386:`CMPL 8\([A-Z]+\),` // amd64:`CMPQ 16\([A-Z]+\),` if a[1] > a[2] { return 1 } else { return 2 } } // Check tbz/tbnz are generated when comparing against zero on arm64 func CmpZero1(a int32, ptr *int) { if a < 0 { // arm64:"TBZ" *ptr = 0 } } func CmpZero2(a int64, ptr *int) { if a < 0 { // arm64:"TBZ" *ptr = 0 } } func CmpZero3(a int32, ptr *int) { if a >= 0 { // arm64:"TBNZ" *ptr = 0 } } func CmpZero4(a int64, ptr *int) { if a >= 0 { // arm64:"TBNZ" *ptr = 0 } } func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 { // arm:`TST` -`AND` // arm64:`TSTW` -`AND` // 386:`TESTL` -`ANDL` // amd64:`TESTL` -`ANDL` c0 := a&b < 0 // arm:`CMN` -`ADD` // arm64:`CMNW` -`ADD` c1 := a+b < 0 // arm:`TEQ` -`XOR` c2 := a^b < 0 // arm64:`TST` -`AND` // amd64:`TESTQ` -`ANDQ` c3 := e&f < 0 // arm64:`CMN` -`ADD` c4 := e+f < 0 // not optimized to single CMNW/CMN due to further use of b+d // arm64:`ADD` -`CMNW` // arm:`ADD` -`CMN` c5 := b+d == 0 // not optimized to single TSTW/TST due to further use of a&d // arm64:`AND` -`TSTW` // arm:`AND` -`TST` // 386:`ANDL` c6 := a&d >= 0 // For arm64, could be TST+BGE or AND+TBZ c7 := e&(f<<3) < 0 // For arm64, could be CMN+BPL or ADD+TBZ c8 := e+(f<<3) < 0 // arm64:`TST R[0-9], R[0-9]+` c9 := e&(-19) < 0 if c0 { return 1 } else if c1 { return 2 } else if c2 { return 3 } else if c3 { return 4 } else if c4 { return 5 } else if c5 { return 6 } else if c6 { return 7 } else if c7 { return 9 } else if c8 { return 10 } else if c9 { return 11 } else if deOptC0 { return b + d } else if deOptC1 { return a & d } else { return 0 } } func CmpLogicalToZero(a, b, c uint32, d, e, f, g uint64) uint64 { // ppc64x:"ANDCC" -"CMPW" // wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64" if a&63 == 0 { return 1 } // ppc64x:"ANDCC" -"CMP" // wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64" if d&255 == 0 { return 1 } // ppc64x:"ANDCC" -"CMP" // wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64" if d&e == 0 { return 1 } // ppc64x:"ORCC" -"CMP" // wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64" if f|g == 0 { return 1 } // ppc64x:"XORCC" -"CMP" // wasm:"I64Eqz" "I32Eqz" -"I64ExtendI32U" -"I32WrapI64" if e^d == 0 { return 1 } return 0 } // The following CmpToZero_ex* check that cmp|cmn with bmi|bpl are generated for // 'comparing to zero' expressions // var + const // 'x-const' might be canonicalized to 'x+(-const)', so we check both // CMN and CMP for subtraction expressions to make the pattern robust. func CmpToZero_ex1(a int64, e int32) int { // arm64:`CMN` -`ADD` `(BMI|BPL)` if a+3 < 0 { return 1 } // arm64:`CMN` -`ADD` `BEQ` `(BMI|BPL)` if a+5 <= 0 { return 1 } // arm64:`CMN` -`ADD` `(BMI|BPL)` if a+13 >= 0 { return 2 } // arm64:`CMP|CMN` -`(ADD|SUB)` `(BMI|BPL)` if a-7 < 0 { return 3 } // arm64:`SUB` `TBZ` if a-11 >= 0 { return 4 } // arm64:`SUB` `CMP` `BGT` if a-19 > 0 { return 4 } // arm64:`CMNW` -`ADDW` `(BMI|BPL)` // arm:`CMN` -`ADD` `(BMI|BPL)` if e+3 < 0 { return 5 } // arm64:`CMNW` -`ADDW` `(BMI|BPL)` // arm:`CMN` -`ADD` `(BMI|BPL)` if e+13 >= 0 { return 6 } // arm64:`CMPW|CMNW` `(BMI|BPL)` // arm:`CMP|CMN` -`(ADD|SUB)` `(BMI|BPL)` if e-7 < 0 { return 7 } // arm64:`SUB` `TBNZ` // arm:`SUB` -`(BMI|BPL)` if e-11 >= 0 { return 8 } return 0 } // var + var // TODO: optimize 'var - var' func CmpToZero_ex2(a, b, c int64, e, f, g int32) int { // arm64:`CMN` -`ADD` `(BMI|BPL)` if a+b < 0 { return 1 } // arm64:`CMN` -`ADD` `BEQ` `(BMI|BPL)` if a+c <= 0 { return 1 } // arm64:`CMN` -`ADD` `(BMI|BPL)` if b+c >= 0 { return 2 } // arm64:`CMNW` -`ADDW` `(BMI|BPL)` // arm:`CMN` -`ADD` `(BMI|BPL)` if e+f < 0 { return 5 } // arm64:`CMNW` -`ADDW` `(BMI|BPL)` // arm:`CMN` -`ADD` `(BMI|BPL)` if f+g >= 0 { return 6 } return 0 } // var + var*var func CmpToZero_ex3(a, b, c, d int64, e, f, g, h int32) int { // arm64:`CMN` -`MADD` `MUL` `(BMI|BPL)` if a+b*c < 0 { return 1 } // arm64:`CMN` -`MADD` `MUL` `(BMI|BPL)` if b+c*d >= 0 { return 2 } // arm64:`CMNW` -`MADDW` `MULW` `BEQ` `(BMI|BPL)` // arm:`CMN` -`MULA` `MUL` `BEQ` `(BMI|BPL)` if e+f*g > 0 { return 5 } // arm64:`CMNW` -`MADDW` `MULW` `BEQ` `(BMI|BPL)` // arm:`CMN` -`MULA` `MUL` `BEQ` `(BMI|BPL)` if f+g*h <= 0 { return 6 } return 0 } // var - var*var func CmpToZero_ex4(a, b, c, d int64, e, f, g, h int32) int { // arm64:`MSUB` if a-b*c > 0 { return 1 } // arm64:`MSUB` if b-c*d >= 0 { return 2 } // arm64:`MSUBW` if e-f*g < 0 { return 5 } // arm64:`MSUBW` if f-g*h >= 0 { return 6 } return 0 } func CmpToZero_ex5(e, f int32, u uint32) int { // arm:`CMN` -`ADD` `BEQ` `(BMI|BPL)` if e+f<<1 > 0 { return 1 } // arm:`SUB` -`(BMI|BPL)` if f-int32(u>>2) >= 0 { return 2 } return 0 } func UintLtZero(a uint8, b uint16, c uint32, d uint64) int { // amd64: -`(TESTB|TESTW|TESTL|TESTQ|JCC|JCS)` // arm64: -`(CMPW|CMP|BHS|BLO)` if a < 0 || b < 0 || c < 0 || d < 0 { return 1 } return 0 } func UintGeqZero(a uint8, b uint16, c uint32, d uint64) int { // amd64: -`(TESTB|TESTW|TESTL|TESTQ|JCS|JCC)` // arm64: -`(CMPW|CMP|BLO|BHS)` if a >= 0 || b >= 0 || c >= 0 || d >= 0 { return 1 } return 0 } func UintGtZero(a uint8, b uint16, c uint32, d uint64) int { // arm64: `(CBN?ZW)` `(CBN?Z[^W])` -`(CMPW|CMP|BLS|BHI)` if a > 0 || b > 0 || c > 0 || d > 0 { return 1 } return 0 } func UintLeqZero(a uint8, b uint16, c uint32, d uint64) int { // arm64: `(CBN?ZW)` `(CBN?Z[^W])` -`(CMPW|CMP|BHI|BLS)` if a <= 0 || b <= 0 || c <= 0 || d <= 0 { return 1 } return 0 } func UintLtOne(a uint8, b uint16, c uint32, d uint64) int { // arm64: `(CBN?ZW)` `(CBN?Z[^W])` -`(CMPW|CMP|BHS|BLO)` if a < 1 || b < 1 || c < 1 || d < 1 { return 1 } return 0 } func UintGeqOne(a uint8, b uint16, c uint32, d uint64) int { // arm64: `(CBN?ZW)` `(CBN?Z[^W])` -`(CMPW|CMP|BLO|BHS)` if a >= 1 || b >= 1 || c >= 1 || d >= 1 { return 1 } return 0 } func CmpToZeroU_ex1(a uint8, b uint16, c uint32, d uint64) int { // wasm:"I64Eqz" -"I64LtU" if 0 < a { return 1 } // wasm:"I64Eqz" -"I64LtU" if 0 < b { return 1 } // wasm:"I64Eqz" -"I64LtU" if 0 < c { return 1 } // wasm:"I64Eqz" -"I64LtU" if 0 < d { return 1 } return 0 } func CmpToZeroU_ex2(a uint8, b uint16, c uint32, d uint64) int { // wasm:"I64Eqz" -"I64LeU" if a <= 0 { return 1 } // wasm:"I64Eqz" -"I64LeU" if b <= 0 { return 1 } // wasm:"I64Eqz" -"I64LeU" if c <= 0 { return 1 } // wasm:"I64Eqz" -"I64LeU" if d <= 0 { return 1 } return 0 } func CmpToOneU_ex1(a uint8, b uint16, c uint32, d uint64) int { // wasm:"I64Eqz" -"I64LtU" if a < 1 { return 1 } // wasm:"I64Eqz" -"I64LtU" if b < 1 { return 1 } // wasm:"I64Eqz" -"I64LtU" if c < 1 { return 1 } // wasm:"I64Eqz" -"I64LtU" if d < 1 { return 1 } return 0 } func CmpToOneU_ex2(a uint8, b uint16, c uint32, d uint64) int { // wasm:"I64Eqz" -"I64LeU" if 1 <= a { return 1 } // wasm:"I64Eqz" -"I64LeU" if 1 <= b { return 1 } // wasm:"I64Eqz" -"I64LeU" if 1 <= c { return 1 } // wasm:"I64Eqz" -"I64LeU" if 1 <= d { return 1 } return 0 } func int64LtZero(x int64) bool { // arm64: `LSR [$]63` return x < 0 } func int64GeZero(x int64) bool { // arm64: `LSR [$]63` `EOR [$]1` return x >= 0 } func int32LtZero(x int32) bool { // arm64: `UBFX [$]31, R[0-9]+, [$]1,` return x < 0 } func int32GeZero(x int32) bool { // arm64: `UBFX [$]31, R[0-9]+, [$]1,` `EOR [$]1` return x >= 0 } // Check that small memequals are replaced with eq instructions func equalConstString1() bool { a := string("A") b := string("Z") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a == b } func equalVarString1(a string) bool { b := string("Z") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a[:1] == b } func equalConstString2() bool { a := string("AA") b := string("ZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a == b } func equalVarString2(a string) bool { b := string("ZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a[:2] == b } func equalConstString4() bool { a := string("AAAA") b := string("ZZZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a == b } func equalVarString4(a string) bool { b := string("ZZZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a[:4] == b } func equalConstString8() bool { a := string("AAAAAAAA") b := string("ZZZZZZZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a == b } func equalVarString8(a string) bool { b := string("ZZZZZZZZ") // amd64:-".*memequal" // arm64:-".*memequal" // loong64:-".*memequal" // ppc64x:-".*memequal" return a[:8] == b } func equalVarStringNoSpill(a, b string) bool { s := string("123456789012345678901234567890123") // arm64:".*memequal" memeq1 := a[:33] == s // arm64:-".*" memeq2 := s == a[:33] // arm64:-"MOVB R0,.*SP" ".*memequal" memeq3 := s == b[:33] return memeq1 && memeq2 && memeq3 } func equalVarString17(a string) bool { b := string("12345678901234567") // arm64:-".*memequal" "CMPW [$]55," "MOVD [$]3906085646303834169," "MOVD [$]4050765991979987505," return a[:17] == b } func cmpToCmn(a, b, c, d int) int { var c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11 int // arm64:`CMN` -`CMP` if a < -8 { c1 = 1 } // arm64:`CMN` -`CMP` if a+1 == 0 { c2 = 1 } // arm64:`CMN` -`CMP` if a+3 != 0 { c3 = 1 } // arm64:`CMN` -`CMP` if a+b == 0 { c4 = 1 } // arm64:`CMN` -`CMP` if b+c != 0 { c5 = 1 } // arm64:`CMN` -`CMP` if a == -c { c6 = 1 } // arm64:`CMN` -`CMP` if b != -d { c7 = 1 } // arm64:`CMN` -`CMP` if a*b+c == 0 { c8 = 1 } // arm64:`CMN` -`CMP` if a*c+b != 0 { c9 = 1 } // arm64:`CMP` -`CMN` if b*c-a == 0 { c10 = 1 } // arm64:`CMP` -`CMN` if a*d-b != 0 { c11 = 1 } return c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 } func cmpToCmnLessThan(a, b, c, d int) int { var c1, c2, c3, c4 int // arm64:`CMN` `CSET MI` -`CMP` if a+1 < 0 { c1 = 1 } // arm64:`CMN` `CSET MI` -`CMP` if a+b < 0 { c2 = 1 } // arm64:`CMN` `CSET MI` -`CMP` if a*b+c < 0 { c3 = 1 } // arm64:`MSUB` `LSR` if a-b*c < 0 { c4 = 1 } return c1 + c2 + c3 + c4 } func less128Signed32(x int32) bool { // amd64:`CMPL.*127` // amd64:`SETLE` return x < 128 } func less128Signed64(x int64) bool { // amd64:`CMPQ.*127` // amd64:`SETLE` return x < 128 } func less128Unsigned32(x uint32) bool { // amd64:`CMPL.*127` // amd64:`SETLS` return x < 128 } func less128Unsigned64(x uint64) bool { // amd64:`CMPQ.*127` // amd64:`SETLS` return x < 128 } func ge128Unsigned32(x uint32) bool { // amd64:`CMPL.*127` // amd64:`SETHI` return x >= 128 } func ge128Unsigned64(x uint64) bool { // amd64:`CMPQ.*127` // amd64:`SETHI` return x >= 128 } func ge128Signed32(x int32) bool { // amd64:`CMPL.*127` // amd64:`SETGT` return x >= 128 } func ge128Signed64(x int64) bool { // amd64:`CMPQ.*127` // amd64:`SETGT` return x >= 128 } func cmpToCmnGreaterThanEqual(a, b, c, d int) int { var c1, c2, c3, c4 int // arm64:`CMN` `CSET PL` -`CMP` if a+1 >= 0 { c1 = 1 } // arm64:`CMN` `CSET PL` -`CMP` if a+b >= 0 { c2 = 1 } // arm64:`CMN` `CSET PL` -`CMP` if a*b+c >= 0 { c3 = 1 } // arm64:`MSUB` `LSR` `EOR [$]1` if a-b*c >= 0 { c4 = 1 } return c1 + c2 + c3 + c4 } func cmp1(val string) bool { var z string // amd64:-".*memequal" return z == val } func cmp2(val string) bool { var z string // amd64:-".*memequal" return val == z } func cmp3(val string) bool { z := "food" // amd64:-".*memequal" return z == val } func cmp4(val string) bool { z := "food" // amd64:-".*memequal" return val == z } func cmp5[T comparable](val T) bool { var z T // amd64:-".*memequal" return z == val } func cmp6[T comparable](val T) bool { var z T // amd64:-".*memequal" return val == z } func cmp7() { cmp5[string]("") // force instantiation cmp6[string]("") // force instantiation } func cmpstring1(x, y string) int { // amd64:".*cmpstring" if x < y { return -1 } // amd64:-".*cmpstring" if x > y { return +1 } return 0 } func cmpstring2(x, y string) int { // We want to fail if there are two calls to cmpstring. // They will both have the same line number, so a test // like in cmpstring1 will not work. Instead, we // look for spill/restore instructions, which only // need to exist if there are 2 calls. //amd64:-`MOVQ .*\(SP\)` return cmp.Compare(x, y) } func bijectiveAdd(x uint) bool { // amd64: -"ADD" // arm64: -"ADD" return x+1337 == 42 } func bijectiveSub1(x uint) bool { // amd64: -"SUB" // arm64: -"SUB" return x-1337 == 42 } func bijectiveSub2(x uint) bool { // amd64: -"SUB" // arm64: -"SUB" return 1337-x == 42 } func bijectiveXor(x uint) bool { // amd64: -"XOR" // arm64: -"EOR" return x^1337 == 42 } func bijectiveCom(x uint) bool { // amd64: -"NOT" // arm64: -"MVN" return ^x == 42 } func bijectiveNeg(x int) bool { // amd64: -"NEG" // arm64: -"NEG" return -x == 42 } func bijectiveMul(x uint) bool { // amd64: -"MUL" // arm64: -"MUL" return x*1337 == 42 } func scanASCIILess(b []byte) int { for i := range b { // arm64:"TBNZ [$]7" -"CMPW [$]128" if b[i] < 128 { return i } } return -1 } func scanASCIIGeq(b []byte) int { for i := range b { // arm64:"TBZ [$]7" -"CMPW [$]128" if b[i] >= 128 { return i } } return -1 } func scanASCIILess64(b []byte) int { for i := range b { // arm64:"TBNZ [$]7" -"CMP [$]128" if uint64(b[i]) < 128 { return i } } return -1 } func scanASCIIGeq64(b []byte) int { for i := range b { // arm64:"TBZ [$]7" -"CMP [$]128" if uint64(b[i]) >= 128 { return i } } return -1 } func scanASCIILessSigned32(b []byte) int { for i := range b { // arm64:"TBNZ [$]7" -"CMPW [$]128" if int32(b[i]) < 128 { return i } } return -1 } func scanASCIIGeqSigned64(b []byte) int { for i := range b { // arm64:"TBZ [$]7" -"CMP [$]128" if int64(b[i]) >= 128 { return i } } return -1 } // A loaded byte that is both compared and used a second time (here as a table // index) must not be copied with a MOVD register move just to feed the // comparison: the compare reads the loaded register directly // (go.dev/issue/43357). Before the fix, arm64's unsigned narrow comparison // inserted a ZeroExt8to32 that the register allocator realized as a redundant // MOVD Rx,Ry copy whenever the loaded byte had a second consumer. These are // LEAF, bool-return loops so the only reg-reg MOVD that could appear is that // copy: a single-consumer compare emits no copy (the extension is elided to a // MOVDnop), and a return-value or morestack MOVD would mask the assertion, // which is why the natural a[i] < a[j] shape does not exercise this. var escByteTab [256]bool // c is compared for equality against a byte constant and then used again as a // table index (the second consumer keeps c live). The compare must read the // loaded register, not a copy of it. func scanByteCmpReuse(b []byte) bool { for i := range b { c := b[i] // arm64:-"MOVD R[0-9]+, R[0-9]+" if c == '"' { return true } if escByteTab[c] { return true } } return false } // The scanner ASCII fast path: c is range-compared (c >= 128) and then reused // as a table index. The unsigned compare must read the loaded register // directly rather than a copy of it. func scanByteHighReuse(b []byte) bool { for i := range b { c := b[i] // arm64:-"MOVD R[0-9]+, R[0-9]+" if c >= 128 { return true } if escByteTab[c] { return true } } return false }