The generic "prefer equalities with zero" rules rewrite an ordered
comparison against zero into an equality, so the many NE/EQ-keyed
rewrites downstream -- the bit-test recognizers among them -- see one
canonical spelling. Two cells of that table were missing: the unsigned
"0 < x", and the signed "x <= 0" for a provably non-negative x. The new
signed rule subsumes the existing signed "x < 1" rule, since "x < 1"
canonicalizes to "x <= 0"; remove it.
The unsigned one is what matters in practice. The "x & mask > 0"
spelling of a bit test previously survived to the backends as an
unsigned-order comparison and so missed every NE/EQ-keyed rewrite, the
bit-test recognizers included, on any architecture without its own
arch-specific fixup. arm64 has one ("((ULE|UGT) (CMPconst [0] x))");
amd64, 386, riscv64, loong64, s390x, ppc64 and wasm do not.
Canonicalizing at the generic level reaches all of them at once.
The canonicalization broke fuseIntInRange, which recognized only the
raw Less...U(0, x) shape as the open lower bound of an unsigned range
check ("x > 0 && x <= n"). Teach isUnsignedInequality to also accept
the canonical Neq(x, 0) form, and add the matching AndB combine rule so
the fused comparison still folds to a single subtracted compare;
test/codegen/fuse.go guards that.
The signed "x <= 0" rule converts no power-of-two TEST site in std
today; it is here so the zero-comparison table is closed rather than
half-filled, and it does fire on ordinary non-negative quantities such
as "len(b) <= 0".
ixdiff on linux/amd64 binaries built with the compiler before and after
this change, payload source held fixed:
cmd/compile -1312 bytes text, 424 functions changed
tsgo -1088 bytes text, 244 functions changed
kubectl -1440 bytes text, 381 functions changed
Change-Id: Ic9ee9b77fb3631063a9ca97bb7e5d4f40b5950d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/813840
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Jorropo <jorropo.pgm@gmail.com>
Auto-Submit: Jorropo <jorropo.pgm@gmail.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
440 lines
10 KiB
Go
440 lines
10 KiB
Go
// asmcheck
|
|
|
|
// Copyright 2019 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 "math"
|
|
|
|
// Notes:
|
|
// - these examples use channels to provide a source of
|
|
// unknown values that cannot be optimized away
|
|
// - these examples use for loops to force branches
|
|
// backward (predicted taken)
|
|
|
|
// ---------------------------------- //
|
|
// signed integer range (conjunction) //
|
|
// ---------------------------------- //
|
|
|
|
func si1c(c <-chan int64) {
|
|
// amd64:"CMPQ .+, [$]256"
|
|
// s390x:"CLGIJ [$]12, R[0-9]+, [$]255"
|
|
for x := <-c; x >= 0 && x < 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si2c(c <-chan int32) {
|
|
// amd64:"CMPL .+, [$]256"
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]255"
|
|
for x := <-c; x >= 0 && x < 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si3c(c <-chan int16) {
|
|
// amd64:"CMPW .+, [$]256"
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]255"
|
|
for x := <-c; x >= 0 && x < 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si4c(c <-chan int8) {
|
|
// amd64:"CMPB .+, [$]10"
|
|
// s390x:"CLIJ [$]4, R[0-9]+, [$]10"
|
|
for x := <-c; x >= 0 && x < 10; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si5c(c <-chan int64) {
|
|
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
|
|
// s390x:"CLGIJ [$]4, R[0-9]+, [$]251" "ADD [$]-5,"
|
|
for x := <-c; x < 256 && x > 4; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si6c(c <-chan int32) {
|
|
// amd64:"CMPL .+, [$]255" "DECL "
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]255" "ADDW [$]-1,"
|
|
for x := <-c; x > 0 && x <= 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si7c(c <-chan int16) {
|
|
// amd64:"CMPW .+, [$]60" "ADDL [$]10,"
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]60" "ADDW [$]10,"
|
|
for x := <-c; x >= -10 && x <= 50; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si8c(c <-chan int8) {
|
|
// amd64:"CMPB .+, [$]126" "ADDL [$]126,"
|
|
// s390x:"CLIJ [$]4, R[0-9]+, [$]126" "ADDW [$]126,"
|
|
for x := <-c; x >= -126 && x < 0; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ---------------------------------- //
|
|
// signed integer range (disjunction) //
|
|
// ---------------------------------- //
|
|
|
|
func si1d(c <-chan int64) {
|
|
// amd64:"CMPQ .+, [$]256"
|
|
// s390x:"CLGIJ [$]2, R[0-9]+, [$]255"
|
|
for x := <-c; x < 0 || x >= 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si2d(c <-chan int32) {
|
|
// amd64:"CMPL .+, [$]256"
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]255"
|
|
for x := <-c; x < 0 || x >= 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si3d(c <-chan int16) {
|
|
// amd64:"CMPW .+, [$]256"
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]255"
|
|
for x := <-c; x < 0 || x >= 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si4d(c <-chan int8) {
|
|
// amd64:"CMPB .+, [$]10"
|
|
// s390x:"CLIJ [$]10, R[0-9]+, [$]10"
|
|
for x := <-c; x < 0 || x >= 10; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si5d(c <-chan int64) {
|
|
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
|
|
// s390x:"CLGIJ [$]10, R[0-9]+, [$]251" "ADD [$]-5,"
|
|
for x := <-c; x >= 256 || x <= 4; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si6d(c <-chan int32) {
|
|
// amd64:"CMPL .+, [$]255" "DECL "
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]255" "ADDW [$]-1,"
|
|
for x := <-c; x <= 0 || x > 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si7d(c <-chan int16) {
|
|
// amd64:"CMPW .+, [$]60" "ADDL [$]10,"
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]60" "ADDW [$]10,"
|
|
for x := <-c; x < -10 || x > 50; x = <-c {
|
|
}
|
|
}
|
|
|
|
func si8d(c <-chan int8) {
|
|
// amd64:"CMPB .+, [$]126" "ADDL [$]126,"
|
|
// s390x:"CLIJ [$]10, R[0-9]+, [$]126" "ADDW [$]126,"
|
|
for x := <-c; x < -126 || x >= 0; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ------------------------------------ //
|
|
// unsigned integer range (conjunction) //
|
|
// ------------------------------------ //
|
|
|
|
func ui1c(c <-chan uint64) {
|
|
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
|
|
// s390x:"CLGIJ [$]4, R[0-9]+, [$]251" "ADD [$]-5,"
|
|
for x := <-c; x < 256 && x > 4; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui2c(c <-chan uint32) {
|
|
// amd64:"CMPL .+, [$]255" "DECL "
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]255" "ADDW [$]-1,"
|
|
for x := <-c; x > 0 && x <= 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui3c(c <-chan uint16) {
|
|
// amd64:"CMPW .+, [$]40" "ADDL [$]-10,"
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]40" "ADDW [$]-10,"
|
|
for x := <-c; x >= 10 && x <= 50; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui4c(c <-chan uint8) {
|
|
// amd64:"CMPB .+, [$]2" "ADDL [$]-126,"
|
|
// s390x:"CLIJ [$]4, R[0-9]+, [$]2" "ADDW [$]-126,"
|
|
for x := <-c; x >= 126 && x < 128; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui5c(c <-chan uint64) {
|
|
// amd64:"CMPQ .+, [$]255" "DECQ "
|
|
// s390x:"CLGIJ [$]4, R[0-9]+, [$]255" "ADD [$]-1,"
|
|
for x := <-c; x > 0 && x < 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui6c(c <-chan uint16) {
|
|
// amd64:"CMPW .+, [$]39" "DECL "
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]39" "ADDW [$]-1,"
|
|
for x := <-c; x > 0 && x <= 40; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui7c(c <-chan uint8) {
|
|
// amd64:"CMPB .+, [$]39" "DECL "
|
|
// s390x:"CLIJ [$]12, R[0-9]+, [$]39" "ADDW [$]-1,"
|
|
for x := <-c; x > 0 && x <= 40; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ------------------------------------ //
|
|
// unsigned integer range (disjunction) //
|
|
// ------------------------------------ //
|
|
|
|
func ui1d(c <-chan uint64) {
|
|
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
|
|
// s390x:"CLGIJ [$]10, R[0-9]+, [$]251" "ADD [$]-5,"
|
|
for x := <-c; x >= 256 || x <= 4; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui2d(c <-chan uint32) {
|
|
// amd64:"CMPL .+, [$]254" "ADDL [$]-2,"
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]254" "ADDW [$]-2,"
|
|
for x := <-c; x <= 1 || x > 256; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui3d(c <-chan uint16) {
|
|
// amd64:"CMPW .+, [$]40" "ADDL [$]-10,"
|
|
// s390x:"CLIJ [$]2, R[0-9]+, [$]40" "ADDW [$]-10,"
|
|
for x := <-c; x < 10 || x > 50; x = <-c {
|
|
}
|
|
}
|
|
|
|
func ui4d(c <-chan uint8) {
|
|
// amd64:"CMPB .+, [$]2" "ADDL [$]-126,"
|
|
// s390x:"CLIJ [$]10, R[0-9]+, [$]2" "ADDW [$]-126,"
|
|
for x := <-c; x < 126 || x >= 128; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ------------------------------------ //
|
|
// single bit difference (conjunction) //
|
|
// ------------------------------------ //
|
|
|
|
func sisbc64(c <-chan int64) {
|
|
// amd64: "ORQ [$]2,"
|
|
// riscv64: "ORI [$]2,"
|
|
for x := <-c; x != 4 && x != 6; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbc32(c <-chan int32) {
|
|
// amd64: "ORL [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x != -1 && x != -5; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbc16(c <-chan int16) {
|
|
// amd64: "ORL [$]32,"
|
|
// riscv64: "ORI [$]32,"
|
|
for x := <-c; x != 16 && x != 48; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbc8(c <-chan int8) {
|
|
// amd64: "ORL [$]16,"
|
|
// riscv64: "ORI [$]16,"
|
|
for x := <-c; x != -15 && x != -31; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbc64(c <-chan uint64) {
|
|
// amd64: "ORQ [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x != 1 && x != 5; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbc32(c <-chan uint32) {
|
|
// amd64: "ORL [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x != 2 && x != 6; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbc16(c <-chan uint16) {
|
|
// amd64: "ORL [$]32,"
|
|
// riscv64: "ORI [$]32,"
|
|
for x := <-c; x != 16 && x != 48; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbc8(c <-chan uint8) {
|
|
// amd64: "ORL [$]64,"
|
|
// riscv64: "ORI [$]64,"
|
|
for x := <-c; x != 64 && x != 0; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ------------------------------------ //
|
|
// single bit difference (disjunction) //
|
|
// ------------------------------------ //
|
|
|
|
func sisbd64(c <-chan int64) {
|
|
// amd64: "ORQ [$]2,"
|
|
// riscv64: "ORI [$]2,"
|
|
for x := <-c; x == 4 || x == 6; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbd32(c <-chan int32) {
|
|
// amd64: "ORL [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x == -1 || x == -5; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbd16(c <-chan int16) {
|
|
// amd64: "ORL [$]32,"
|
|
// riscv64: "ORI [$]32,"
|
|
for x := <-c; x == 16 || x == 48; x = <-c {
|
|
}
|
|
}
|
|
|
|
func sisbd8(c <-chan int8) {
|
|
// amd64: "ORL [$]16,"
|
|
// riscv64: "ORI [$]16,"
|
|
for x := <-c; x == -15 || x == -31; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbd64(c <-chan uint64) {
|
|
// amd64: "ORQ [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x == 1 || x == 5; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbd32(c <-chan uint32) {
|
|
// amd64: "ORL [$]4,"
|
|
// riscv64: "ORI [$]4,"
|
|
for x := <-c; x == 2 || x == 6; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbd16(c <-chan uint16) {
|
|
// amd64: "ORL [$]32,"
|
|
// riscv64: "ORI [$]32,"
|
|
for x := <-c; x == 16 || x == 48; x = <-c {
|
|
}
|
|
}
|
|
|
|
func uisbd8(c <-chan uint8) {
|
|
// amd64: "ORL [$]64,"
|
|
// riscv64: "ORI [$]64,"
|
|
for x := <-c; x == 64 || x == 0; x = <-c {
|
|
}
|
|
}
|
|
|
|
// -------------------------------------//
|
|
// merge NaN checks //
|
|
// ------------------------------------ //
|
|
|
|
func f64NaNOrPosInf(c <-chan float64) {
|
|
// This test assumes IsInf(x, 1) is implemented as x > MaxFloat rather than x == Inf(1).
|
|
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FCLASSD" -"FLED" -"FLTD" -"FNED" -"FEQD"
|
|
for x := <-c; math.IsNaN(x) || math.IsInf(x, 1); x = <-c {
|
|
}
|
|
}
|
|
|
|
func f64NaNOrNegInf(c <-chan float64) {
|
|
// This test assumes IsInf(x, -1) is implemented as x < -MaxFloat rather than x == Inf(-1).
|
|
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FCLASSD" -"FLED" -"FLTD" -"FNED" -"FEQD"
|
|
for x := <-c; math.IsNaN(x) || math.IsInf(x, -1); x = <-c {
|
|
}
|
|
}
|
|
|
|
func f64NaNOrLtOne(c <-chan float64) {
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLED" -"FLTD" -"FNED" -"FEQD"
|
|
for x := <-c; math.IsNaN(x) || x < 1; x = <-c {
|
|
}
|
|
}
|
|
|
|
func f64NaNOrLteOne(c <-chan float64) {
|
|
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLTD" -"FLED" -"FNED" -"FEQD"
|
|
for x := <-c; x <= 1 || math.IsNaN(x); x = <-c {
|
|
}
|
|
}
|
|
|
|
func f64NaNOrGtOne(c <-chan float64) {
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLED" -"FLTD" -"FNED" -"FEQD"
|
|
for x := <-c; math.IsNaN(x) || x > 1; x = <-c {
|
|
}
|
|
}
|
|
|
|
func f64NaNOrGteOne(c <-chan float64) {
|
|
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLTD" -"FLED" -"FNED" -"FEQD"
|
|
for x := <-c; x >= 1 || math.IsNaN(x); x = <-c {
|
|
}
|
|
}
|
|
|
|
func f32NaNOrLtOne(c <-chan float32) {
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLES" -"FLTS" -"FNES" -"FEQS"
|
|
for x := <-c; x < 1 || x != x; x = <-c {
|
|
}
|
|
}
|
|
|
|
func f32NaNOrLteOne(c <-chan float32) {
|
|
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLTS" -"FLES" -"FNES" -"FEQS"
|
|
for x := <-c; x != x || x <= 1; x = <-c {
|
|
}
|
|
}
|
|
|
|
func f32NaNOrGtOne(c <-chan float32) {
|
|
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLES" -"FLTS" -"FNES" -"FEQS"
|
|
for x := <-c; x > 1 || x != x; x = <-c {
|
|
}
|
|
}
|
|
|
|
func f32NaNOrGteOne(c <-chan float32) {
|
|
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
|
|
// riscv64:"FLTS" -"FLES" -"FNES" -"FEQS"
|
|
for x := <-c; x != x || x >= 1; x = <-c {
|
|
}
|
|
}
|
|
|
|
// ------------------------------------ //
|
|
// regressions //
|
|
// ------------------------------------ //
|
|
|
|
func gte4(x uint64) bool {
|
|
return x >= 4
|
|
}
|
|
|
|
func lt20(x uint64) bool {
|
|
return x < 20
|
|
}
|
|
|
|
func issue74915(c <-chan uint64) {
|
|
// Check that the optimization is not blocked by function inlining.
|
|
|
|
// amd64:"CMPQ .+, [$]16" "ADDQ [$]-4,"
|
|
// s390x:"CLGIJ [$]4, R[0-9]+, [$]16" "ADD [$]-4,"
|
|
for x := <-c; gte4(x) && lt20(x); x = <-c {
|
|
}
|
|
}
|