Permalink
Browse files

cmd/compile: optimize signed non-negative div/mod by a power of 2

This CL optimizes assembly for len() or cap() division
by a power of 2 constants:

    func lenDiv(s []int) int {
        return len(s) / 16
    }

amd64 assembly before the CL:

    MOVQ    "".s+16(SP), AX
    MOVQ    AX, CX
    SARQ    $63, AX
    SHRQ    $60, AX
    ADDQ    CX, AX
    SARQ    $4, AX
    MOVQ    AX, "".~r1+32(SP)
    RET

amd64 assembly after the CL:

    MOVQ    "".s+16(SP), AX
    SHRQ    $4, AX
    MOVQ    AX, "".~r1+32(SP)
    RET

The CL relies on the fact that len() and cap() result cannot
be negative.

Trigger stats for the added SSA rules on linux/amd64 when running
make.bash:

     46 Div64
     12 Mod64

The added SSA rules may trigger on more cases in the future
when SSA values will be populated with the info on their
lower bounds.

For instance:

    func f(i int16) int16 {
        if i < 3 {
            return -1
        }

        // Lower bound of i is 3 here -> i is non-negative,
        // so unsigned arithmetics may be used here.
        return i % 16
    }

Change-Id: I8bc6be5a03e71157ced533c01416451ff6f1a7f0
Reviewed-on: https://go-review.googlesource.com/65530
Reviewed-by: Keith Randall <khr@golang.org>
  • Loading branch information...
valyala authored and randall77 committed Sep 22, 2017
1 parent 2e85455 commit 0011cfbe2b57b385bac25a3daf9de581ee263661
@@ -1140,6 +1140,58 @@ var linuxAMD64Tests = []*asmTest{
`,
pos: []string{"\tSETHI\t\\("},
},
// Check that len() and cap() div by a constant power of two
// are compiled into SHRQ.
{
fn: `
func $(a []int) int {
return len(a) / 1024
}
`,
pos: []string{"\tSHRQ\t\\$10,"},
},
{
fn: `
func $(s string) int {
return len(s) / (4097 >> 1)
}
`,
pos: []string{"\tSHRQ\t\\$11,"},
},
{
fn: `
func $(a []int) int {
return cap(a) / ((1 << 11) + 2048)
}
`,
pos: []string{"\tSHRQ\t\\$12,"},
},
// Check that len() and cap() mod by a constant power of two
// are compiled into ANDQ.
{
fn: `
func $(a []int) int {
return len(a) % 1024
}
`,
pos: []string{"\tANDQ\t\\$1023,"},
},
{
fn: `
func $(s string) int {
return len(s) % (4097 >> 1)
}
`,
pos: []string{"\tANDQ\t\\$2047,"},
},
{
fn: `
func $(a []int) int {
return cap(a) % ((1 << 11) + 2048)
}
`,
pos: []string{"\tANDQ\t\\$4095,"},
},
}
var linux386Tests = []*asmTest{
@@ -1219,6 +1271,58 @@ var linux386Tests = []*asmTest{
}`,
pos: []string{"\tADDL\t[$]-19", "\tIMULL"}, // (n-19)*a
},
// Check that len() and cap() div by a constant power of two
// are compiled into SHRL.
{
fn: `
func $(a []int) int {
return len(a) / 1024
}
`,
pos: []string{"\tSHRL\t\\$10,"},
},
{
fn: `
func $(s string) int {
return len(s) / (4097 >> 1)
}
`,
pos: []string{"\tSHRL\t\\$11,"},
},
{
fn: `
func $(a []int) int {
return cap(a) / ((1 << 11) + 2048)
}
`,
pos: []string{"\tSHRL\t\\$12,"},
},
// Check that len() and cap() mod by a constant power of two
// are compiled into ANDL.
{
fn: `
func $(a []int) int {
return len(a) % 1024
}
`,
pos: []string{"\tANDL\t\\$1023,"},
},
{
fn: `
func $(s string) int {
return len(s) % (4097 >> 1)
}
`,
pos: []string{"\tANDL\t\\$2047,"},
},
{
fn: `
func $(a []int) int {
return cap(a) % ((1 << 11) + 2048)
}
`,
pos: []string{"\tANDL\t\\$4095,"},
},
}
var linuxS390XTests = []*asmTest{
@@ -1003,6 +1003,13 @@
(Div64u n (Const64 [c])) && isPowerOfTwo(c) -> (Rsh64Ux64 n (Const64 <typ.UInt64> [log2(c)]))
(Div64u n (Const64 [-1<<63])) -> (Rsh64Ux64 n (Const64 <typ.UInt64> [63]))
// Signed non-negative divide by power of 2.
(Div8 n (Const8 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xff) -> (Rsh8Ux64 n (Const64 <typ.UInt64> [log2(c&0xff)]))
(Div16 n (Const16 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xffff) -> (Rsh16Ux64 n (Const64 <typ.UInt64> [log2(c&0xffff)]))
(Div32 n (Const32 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xffffffff) -> (Rsh32Ux64 n (Const64 <typ.UInt64> [log2(c&0xffffffff)]))
(Div64 n (Const64 [c])) && isNonNegative(n) && isPowerOfTwo(c) -> (Rsh64Ux64 n (Const64 <typ.UInt64> [log2(c)]))
(Div64 n (Const64 [-1<<63])) && isNonNegative(n) -> (Const64 [0])
// Unsigned divide, not a power of 2. Strength reduce to a multiply.
// For 8-bit divides, we just do a direct 9-bit by 8-bit multiply.
(Div8u x (Const8 [c])) && umagicOK(8, c) ->
@@ -1236,6 +1243,13 @@
(Mod64u <t> n (Const64 [c])) && isPowerOfTwo(c) -> (And64 n (Const64 <t> [c-1]))
(Mod64u <t> n (Const64 [-1<<63])) -> (And64 n (Const64 <t> [1<<63-1]))
// Signed non-negative mod by power of 2 constant.
(Mod8 <t> n (Const8 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xff) -> (And8 n (Const8 <t> [(c&0xff)-1]))
(Mod16 <t> n (Const16 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xffff) -> (And16 n (Const16 <t> [(c&0xffff)-1]))
(Mod32 <t> n (Const32 [c])) && isNonNegative(n) && isPowerOfTwo(c&0xffffffff) -> (And32 n (Const32 <t> [(c&0xffffffff)-1]))
(Mod64 <t> n (Const64 [c])) && isNonNegative(n) && isPowerOfTwo(c) -> (And64 n (Const64 <t> [c-1]))
(Mod64 n (Const64 [-1<<63])) && isNonNegative(n) -> n
// Signed mod by negative constant.
(Mod8 <t> n (Const8 [c])) && c < 0 && c != -1<<7 -> (Mod8 <t> n (Const8 <t> [-c]))
(Mod16 <t> n (Const16 [c])) && c < 0 && c != -1<<15 -> (Mod16 <t> n (Const16 <t> [-c]))
Oops, something went wrong.

0 comments on commit 0011cfb

Please sign in to comment.