@@ -1757,7 +1757,9 @@ func (ft *factsTable) flowLimit(v *Value) bool {
1757
1757
case OpSub64 , OpSub32 , OpSub16 , OpSub8 :
1758
1758
a := ft .limits [v .Args [0 ].ID ]
1759
1759
b := ft .limits [v .Args [1 ].ID ]
1760
- return ft .newLimit (v , a .sub (b , uint (v .Type .Size ())* 8 ))
1760
+ sub := ft .newLimit (v , a .sub (b , uint (v .Type .Size ())* 8 ))
1761
+ mod := ft .detectSignedMod (v )
1762
+ return sub || mod
1761
1763
case OpNeg64 , OpNeg32 , OpNeg16 , OpNeg8 :
1762
1764
a := ft .limits [v .Args [0 ].ID ]
1763
1765
bitsize := uint (v .Type .Size ()) * 8
@@ -1913,6 +1915,122 @@ func (ft *factsTable) flowLimit(v *Value) bool {
1913
1915
return false
1914
1916
}
1915
1917
1918
+ // See if we can get any facts because v is the result of signed mod by a constant.
1919
+ // The mod operation has already been rewritten, so we have to try and reconstruct it.
1920
+ // x % d
1921
+ // is rewritten as
1922
+ // x - (x / d) * d
1923
+ // furthermore, the divide itself gets rewritten. If d is a power of 2 (d == 1<<k), we do
1924
+ // (x / d) * d = ((x + adj) >> k) << k
1925
+ // = (x + adj) & (-1<<k)
1926
+ // with adj being an adjustment in case x is negative (see below).
1927
+ // if d is not a power of 2, we do
1928
+ // x / d = ... TODO ...
1929
+ func (ft * factsTable ) detectSignedMod (v * Value ) bool {
1930
+ if ft .detectSignedModByPowerOfTwo (v ) {
1931
+ return true
1932
+ }
1933
+ // TODO: non-powers-of-2
1934
+ return false
1935
+ }
1936
+ func (ft * factsTable ) detectSignedModByPowerOfTwo (v * Value ) bool {
1937
+ // We're looking for:
1938
+ //
1939
+ // x % d ==
1940
+ // x - (x / d) * d
1941
+ //
1942
+ // which for d a power of 2, d == 1<<k, is done as
1943
+ //
1944
+ // x - ((x + (x>>(w-1))>>>(w-k)) & (-1<<k))
1945
+ //
1946
+ // w = bit width of x.
1947
+ // (>> = signed shift, >>> = unsigned shift).
1948
+ // See ./_gen/generic.rules, search for "Signed divide by power of 2".
1949
+
1950
+ var w int64
1951
+ var addOp , andOp , constOp , sshiftOp , ushiftOp Op
1952
+ switch v .Op {
1953
+ case OpSub64 :
1954
+ w = 64
1955
+ addOp = OpAdd64
1956
+ andOp = OpAnd64
1957
+ constOp = OpConst64
1958
+ sshiftOp = OpRsh64x64
1959
+ ushiftOp = OpRsh64Ux64
1960
+ case OpSub32 :
1961
+ w = 32
1962
+ addOp = OpAdd32
1963
+ andOp = OpAnd32
1964
+ constOp = OpConst32
1965
+ sshiftOp = OpRsh32x64
1966
+ ushiftOp = OpRsh32Ux64
1967
+ case OpSub16 :
1968
+ w = 16
1969
+ addOp = OpAdd16
1970
+ andOp = OpAnd16
1971
+ constOp = OpConst16
1972
+ sshiftOp = OpRsh16x64
1973
+ ushiftOp = OpRsh16Ux64
1974
+ case OpSub8 :
1975
+ w = 8
1976
+ addOp = OpAdd8
1977
+ andOp = OpAnd8
1978
+ constOp = OpConst8
1979
+ sshiftOp = OpRsh8x64
1980
+ ushiftOp = OpRsh8Ux64
1981
+ default :
1982
+ return false
1983
+ }
1984
+
1985
+ x := v .Args [0 ]
1986
+ and := v .Args [1 ]
1987
+ if and .Op != andOp {
1988
+ return false
1989
+ }
1990
+ var add , mask * Value
1991
+ if and .Args [0 ].Op == addOp && and .Args [1 ].Op == constOp {
1992
+ add = and .Args [0 ]
1993
+ mask = and .Args [1 ]
1994
+ } else if and .Args [1 ].Op == addOp && and .Args [0 ].Op == constOp {
1995
+ add = and .Args [1 ]
1996
+ mask = and .Args [0 ]
1997
+ } else {
1998
+ return false
1999
+ }
2000
+ var ushift * Value
2001
+ if add .Args [0 ] == x {
2002
+ ushift = add .Args [1 ]
2003
+ } else if add .Args [1 ] == x {
2004
+ ushift = add .Args [0 ]
2005
+ } else {
2006
+ return false
2007
+ }
2008
+ if ushift .Op != ushiftOp {
2009
+ return false
2010
+ }
2011
+ if ushift .Args [1 ].Op != OpConst64 {
2012
+ return false
2013
+ }
2014
+ k := w - ushift .Args [1 ].AuxInt // Now we know k!
2015
+ d := int64 (1 ) << k // divisor
2016
+ sshift := ushift .Args [0 ]
2017
+ if sshift .Op != sshiftOp {
2018
+ return false
2019
+ }
2020
+ if sshift .Args [0 ] != x {
2021
+ return false
2022
+ }
2023
+ if sshift .Args [1 ].Op != OpConst64 || sshift .Args [1 ].AuxInt != w - 1 {
2024
+ return false
2025
+ }
2026
+ if mask .AuxInt != - d {
2027
+ return false
2028
+ }
2029
+
2030
+ // All looks ok. x % d is at most +/- d-1.
2031
+ return ft .signedMinMax (v , - d + 1 , d - 1 )
2032
+ }
2033
+
1916
2034
// getBranch returns the range restrictions added by p
1917
2035
// when reaching b. p is the immediate dominator of b.
1918
2036
func getBranch (sdom SparseTree , p * Block , b * Block ) branch {
0 commit comments