-----------------------------------------------------------------------------
--
-- Cmm optimisation
--
-- (c) The University of Glasgow 2006
--
-----------------------------------------------------------------------------

module GHC.Cmm.Opt (
        constantFoldNode,
        constantFoldExpr,
        cmmMachOpFold,
        cmmMachOpFoldM
 ) where

import GHC.Prelude

import GHC.Cmm.Utils
import GHC.Cmm
import GHC.Utils.Misc

import GHC.Utils.Outputable
import GHC.Platform

import Data.Bits
import Data.Maybe


constantFoldNode :: Platform -> CmmNode e x -> CmmNode e x
constantFoldNode :: forall (e :: Extensibility) (x :: Extensibility).
Platform -> CmmNode e x -> CmmNode e x
constantFoldNode Platform
platform = (CmmExpr -> CmmExpr) -> CmmNode e x -> CmmNode e x
forall (e :: Extensibility) (x :: Extensibility).
(CmmExpr -> CmmExpr) -> CmmNode e x -> CmmNode e x
mapExp (Platform -> CmmExpr -> CmmExpr
constantFoldExpr Platform
platform)

constantFoldExpr :: Platform -> CmmExpr -> CmmExpr
constantFoldExpr :: Platform -> CmmExpr -> CmmExpr
constantFoldExpr Platform
platform = (CmmExpr -> CmmExpr) -> CmmExpr -> CmmExpr
wrapRecExp CmmExpr -> CmmExpr
f
  where f :: CmmExpr -> CmmExpr
f (CmmMachOp MachOp
op [CmmExpr]
args) = Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
op [CmmExpr]
args
        f (CmmRegOff CmmReg
r Int
0) = CmmReg -> CmmExpr
CmmReg CmmReg
r
        f CmmExpr
e = CmmExpr
e

-- -----------------------------------------------------------------------------
-- MachOp constant folder

-- Now, try to constant-fold the MachOps.  The arguments have already
-- been optimized and folded.

cmmMachOpFold
    :: Platform
    -> MachOp       -- The operation from an CmmMachOp
    -> [CmmExpr]    -- The optimized arguments
    -> CmmExpr

cmmMachOpFold :: Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
op [CmmExpr]
args = CmmExpr -> Maybe CmmExpr -> CmmExpr
forall a. a -> Maybe a -> a
fromMaybe (MachOp -> [CmmExpr] -> CmmExpr
CmmMachOp MachOp
op [CmmExpr]
args) (Platform -> MachOp -> [CmmExpr] -> Maybe CmmExpr
cmmMachOpFoldM Platform
platform MachOp
op [CmmExpr]
args)

-- Returns Nothing if no changes, useful for Hoopl, also reduces
-- allocation!
cmmMachOpFoldM
    :: Platform
    -> MachOp
    -> [CmmExpr]
    -> Maybe CmmExpr

cmmMachOpFoldM :: Platform -> MachOp -> [CmmExpr] -> Maybe CmmExpr
cmmMachOpFoldM Platform
_ MachOp
op [CmmLit (CmmInt Integer
x Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ case MachOp
op of
      MO_S_Neg Width
_ -> CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (-Integer
x) Width
rep)
      MO_Not Width
_   -> CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer -> Integer
forall a. Bits a => a -> a
complement Integer
x) Width
rep)

        -- these are interesting: we must first narrow to the
        -- "from" type, in order to truncate to the correct size.
        -- The final narrow/widen to the destination type
        -- is implicit in the CmmLit.
      MO_SF_Conv Width
_from Width
to -> CmmLit -> CmmExpr
CmmLit (Rational -> Width -> CmmLit
CmmFloat (Integer -> Rational
forall a. Num a => Integer -> a
fromInteger Integer
x) Width
to)
      MO_SS_Conv  Width
from Width
to -> CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Width -> Integer -> Integer
narrowS Width
from Integer
x) Width
to)
      MO_UU_Conv  Width
from Width
to -> CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Width -> Integer -> Integer
narrowU Width
from Integer
x) Width
to)
      MO_XX_Conv  Width
from Width
to -> CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Width -> Integer -> Integer
narrowS Width
from Integer
x) Width
to)

      MachOp
_ -> String -> CmmExpr
forall a. String -> a
panic (String -> CmmExpr) -> String -> CmmExpr
forall a b. (a -> b) -> a -> b
$ String
"cmmMachOpFoldM: unknown unary op: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ MachOp -> String
forall a. Show a => a -> String
show MachOp
op


-- Eliminate conversion NOPs
cmmMachOpFoldM Platform
_ (MO_SS_Conv Width
rep1 Width
rep2) [CmmExpr
x] | Width
rep1 Width -> Width -> Bool
forall a. Eq a => a -> a -> Bool
== Width
rep2 = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
cmmMachOpFoldM Platform
_ (MO_UU_Conv Width
rep1 Width
rep2) [CmmExpr
x] | Width
rep1 Width -> Width -> Bool
forall a. Eq a => a -> a -> Bool
== Width
rep2 = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
cmmMachOpFoldM Platform
_ (MO_XX_Conv Width
rep1 Width
rep2) [CmmExpr
x] | Width
rep1 Width -> Width -> Bool
forall a. Eq a => a -> a -> Bool
== Width
rep2 = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x

-- Eliminate nested conversions where possible
cmmMachOpFoldM Platform
platform MachOp
conv_outer [CmmMachOp MachOp
conv_inner [CmmExpr
x]]
  | Just (Width
rep1,Width
rep2,Bool
signed1) <- MachOp -> Maybe (Width, Width, Bool)
isIntConversion MachOp
conv_inner,
    Just (Width
_,   Width
rep3,Bool
signed2) <- MachOp -> Maybe (Width, Width, Bool)
isIntConversion MachOp
conv_outer
  = case () of
        -- widen then narrow to the same size is a nop
      ()
_ | Width
rep1 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
< Width
rep2 Bool -> Bool -> Bool
&& Width
rep1 Width -> Width -> Bool
forall a. Eq a => a -> a -> Bool
== Width
rep3 -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
        -- Widen then narrow to different size: collapse to single conversion
        -- but remember to use the signedness from the widening, just in case
        -- the final conversion is a widen.
        | Width
rep1 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
< Width
rep2 Bool -> Bool -> Bool
&& Width
rep2 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
> Width
rep3 ->
            CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Bool -> Width -> Width -> MachOp
intconv Bool
signed1 Width
rep1 Width
rep3) [CmmExpr
x]
        -- Nested widenings: collapse if the signedness is the same
        | Width
rep1 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
< Width
rep2 Bool -> Bool -> Bool
&& Width
rep2 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
< Width
rep3 Bool -> Bool -> Bool
&& Bool
signed1 Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
signed2 ->
            CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Bool -> Width -> Width -> MachOp
intconv Bool
signed1 Width
rep1 Width
rep3) [CmmExpr
x]
        -- Nested narrowings: collapse
        | Width
rep1 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
> Width
rep2 Bool -> Bool -> Bool
&& Width
rep2 Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
> Width
rep3 ->
            CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> Width -> MachOp
MO_UU_Conv Width
rep1 Width
rep3) [CmmExpr
x]
        | Bool
otherwise ->
            Maybe CmmExpr
forall a. Maybe a
Nothing
  where
        isIntConversion :: MachOp -> Maybe (Width, Width, Bool)
isIntConversion (MO_UU_Conv Width
rep1 Width
rep2)
          = (Width, Width, Bool) -> Maybe (Width, Width, Bool)
forall a. a -> Maybe a
Just (Width
rep1,Width
rep2,Bool
False)
        isIntConversion (MO_SS_Conv Width
rep1 Width
rep2)
          = (Width, Width, Bool) -> Maybe (Width, Width, Bool)
forall a. a -> Maybe a
Just (Width
rep1,Width
rep2,Bool
True)
        isIntConversion MachOp
_ = Maybe (Width, Width, Bool)
forall a. Maybe a
Nothing

        intconv :: Bool -> Width -> Width -> MachOp
intconv Bool
True  = Width -> Width -> MachOp
MO_SS_Conv
        intconv Bool
False = Width -> Width -> MachOp
MO_UU_Conv

-- ToDo: a narrow of a load can be collapsed into a narrow load, right?
-- but what if the architecture only supports word-sized loads, should
-- we do the transformation anyway?

cmmMachOpFoldM Platform
platform MachOp
mop [CmmLit (CmmInt Integer
x Width
xrep), CmmLit (CmmInt Integer
y Width
_)]
  = case MachOp
mop of
        -- for comparisons: don't forget to narrow the arguments before
        -- comparing, since they might be out of range.
        MO_Eq Width
_   -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_Ne Width
_   -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))

        MO_U_Gt Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>  Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_U_Ge Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_U_Lt Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<  Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_U_Le Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_u Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
y_u then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))

        MO_S_Gt Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_s Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>  Integer
y_s then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_S_Ge Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_s Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
y_s then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_S_Lt Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_s Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<  Integer
y_s then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))
        MO_S_Le Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (if Integer
x_s Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
y_s then Integer
1 else Integer
0) (Platform -> Width
wordWidth Platform
platform))

        MO_Add Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
y) Width
r)
        MO_Sub Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
y) Width
r)
        MO_Mul Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
y) Width
r)
        MO_U_Quot Width
r | Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x_u Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
y_u) Width
r)
        MO_U_Rem  Width
r | Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x_u Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`rem`  Integer
y_u) Width
r)
        MO_S_Quot Width
r | Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
y) Width
r)
        MO_S_Rem  Width
r | Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
/= Integer
0 -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`rem` Integer
y) Width
r)

        MO_And   Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Bits a => a -> a -> a
.&. Integer
y) Width
r)
        MO_Or    Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Bits a => a -> a -> a
.|. Integer
y) Width
r)
        MO_Xor   Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Integer -> Integer
forall a. Bits a => a -> a -> a
`xor` Integer
y) Width
r)

        MO_Shl   Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Int -> Integer
forall a. Bits a => a -> Int -> a
`shiftL` Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
y) Width
r)
        MO_U_Shr Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x_u Integer -> Int -> Integer
forall a. Bits a => a -> Int -> a
`shiftR` Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
y) Width
r)
        MO_S_Shr Width
r -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
x Integer -> Int -> Integer
forall a. Bits a => a -> Int -> a
`shiftR` Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
y) Width
r)

        MachOp
_          -> Maybe CmmExpr
forall a. Maybe a
Nothing

   where
        x_u :: Integer
x_u = Width -> Integer -> Integer
narrowU Width
xrep Integer
x
        y_u :: Integer
y_u = Width -> Integer -> Integer
narrowU Width
xrep Integer
y
        x_s :: Integer
x_s = Width -> Integer -> Integer
narrowS Width
xrep Integer
x
        y_s :: Integer
y_s = Width -> Integer -> Integer
narrowS Width
xrep Integer
y


-- When possible, shift the constants to the right-hand side, so that we
-- can match for strength reductions.  Note that the code generator will
-- also assume that constants have been shifted to the right when
-- possible.

cmmMachOpFoldM Platform
platform MachOp
op [x :: CmmExpr
x@(CmmLit CmmLit
_), CmmExpr
y]
   | Bool -> Bool
not (CmmExpr -> Bool
isLit CmmExpr
y) Bool -> Bool -> Bool
&& MachOp -> Bool
isCommutableMachOp MachOp
op
   = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
op [CmmExpr
y, CmmExpr
x])

-- Turn (a+b)+c into a+(b+c) where possible.  Because literals are
-- moved to the right, it is more likely that we will find
-- opportunities for constant folding when the expression is
-- right-associated.
--
-- ToDo: this appears to introduce a quadratic behaviour due to the
-- nested cmmMachOpFold.  Can we fix this?
--
-- Why do we check isLit arg1?  If arg1 is a lit, it means that arg2
-- is also a lit (otherwise arg1 would be on the right).  If we
-- put arg1 on the left of the rearranged expression, we'll get into a
-- loop:  (x1+x2)+x3 => x1+(x2+x3)  => (x2+x3)+x1 => x2+(x3+x1) ...
--
-- Also don't do it if arg1 is PicBaseReg, so that we don't separate the
-- PicBaseReg from the corresponding label (or label difference).
--
cmmMachOpFoldM Platform
platform MachOp
mop1 [CmmMachOp MachOp
mop2 [CmmExpr
arg1,CmmExpr
arg2], CmmExpr
arg3]
   | MachOp
mop2 MachOp -> MachOp -> Bool
`associates_with` MachOp
mop1
     Bool -> Bool -> Bool
&& Bool -> Bool
not (CmmExpr -> Bool
isLit CmmExpr
arg1) Bool -> Bool -> Bool
&& Bool -> Bool
not (CmmExpr -> Bool
isPicReg CmmExpr
arg1)
   = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
mop2 [CmmExpr
arg1, Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
mop1 [CmmExpr
arg2,CmmExpr
arg3]])
   where
     MO_Add{} associates_with :: MachOp -> MachOp -> Bool
`associates_with` MO_Sub{} = Bool
True
     MachOp
mop1 `associates_with` MachOp
mop2 =
        MachOp
mop1 MachOp -> MachOp -> Bool
forall a. Eq a => a -> a -> Bool
== MachOp
mop2 Bool -> Bool -> Bool
&& MachOp -> Bool
isAssociativeMachOp MachOp
mop1

-- special case: (a - b) + c  ==>  a + (c - b)
cmmMachOpFoldM Platform
platform mop1 :: MachOp
mop1@(MO_Add{}) [CmmMachOp mop2 :: MachOp
mop2@(MO_Sub{}) [CmmExpr
arg1,CmmExpr
arg2], CmmExpr
arg3]
   | Bool -> Bool
not (CmmExpr -> Bool
isLit CmmExpr
arg1) Bool -> Bool -> Bool
&& Bool -> Bool
not (CmmExpr -> Bool
isPicReg CmmExpr
arg1)
   = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
mop1 [CmmExpr
arg1, Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
mop2 [CmmExpr
arg3,CmmExpr
arg2]])

-- special case: (PicBaseReg + lit) + N  ==>  PicBaseReg + (lit+N)
--
-- this is better because lit+N is a single link-time constant (e.g. a
-- CmmLabelOff), so the right-hand expression needs only one
-- instruction, whereas the left needs two.  This happens when pointer
-- tagging gives us label+offset, and PIC turns the label into
-- PicBaseReg + label.
--
cmmMachOpFoldM Platform
_ MO_Add{} [ CmmMachOp op :: MachOp
op@MO_Add{} [CmmExpr
pic, CmmLit CmmLit
lit]
                          , CmmLit (CmmInt Integer
n Width
rep) ]
  | CmmExpr -> Bool
isPicReg CmmExpr
pic
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ MachOp -> [CmmExpr] -> CmmExpr
CmmMachOp MachOp
op [CmmExpr
pic, CmmLit -> CmmExpr
CmmLit (CmmLit -> CmmExpr) -> CmmLit -> CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> Int -> CmmLit
cmmOffsetLit CmmLit
lit Int
off ]
  where off :: Int
off = Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowS Width
rep Integer
n)

-- Make a RegOff if we can
cmmMachOpFoldM Platform
_ (MO_Add Width
_) [CmmReg CmmReg
reg, CmmLit (CmmInt Integer
n Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmReg -> Int -> CmmExpr
cmmRegOff CmmReg
reg (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowS Width
rep Integer
n))
cmmMachOpFoldM Platform
_ (MO_Add Width
_) [CmmRegOff CmmReg
reg Int
off, CmmLit (CmmInt Integer
n Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmReg -> Int -> CmmExpr
cmmRegOff CmmReg
reg (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowS Width
rep Integer
n))
cmmMachOpFoldM Platform
_ (MO_Sub Width
_) [CmmReg CmmReg
reg, CmmLit (CmmInt Integer
n Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmReg -> Int -> CmmExpr
cmmRegOff CmmReg
reg (- Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowS Width
rep Integer
n))
cmmMachOpFoldM Platform
_ (MO_Sub Width
_) [CmmRegOff CmmReg
reg Int
off, CmmLit (CmmInt Integer
n Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmReg -> Int -> CmmExpr
cmmRegOff CmmReg
reg (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
- Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowS Width
rep Integer
n))

-- Fold label(+/-)offset into a CmmLit where possible

cmmMachOpFoldM Platform
_ (MO_Add Width
_) [CmmLit CmmLit
lit, CmmLit (CmmInt Integer
i Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (CmmLit -> Int -> CmmLit
cmmOffsetLit CmmLit
lit (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowU Width
rep Integer
i)))
cmmMachOpFoldM Platform
_ (MO_Add Width
_) [CmmLit (CmmInt Integer
i Width
rep), CmmLit CmmLit
lit]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (CmmLit -> Int -> CmmLit
cmmOffsetLit CmmLit
lit (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Integer -> Integer
narrowU Width
rep Integer
i)))
cmmMachOpFoldM Platform
_ (MO_Sub Width
_) [CmmLit CmmLit
lit, CmmLit (CmmInt Integer
i Width
rep)]
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (CmmLit -> Int -> CmmLit
cmmOffsetLit CmmLit
lit (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Integer
forall a. Num a => a -> a
negate (Width -> Integer -> Integer
narrowU Width
rep Integer
i))))


-- Comparison of literal with widened operand: perform the comparison
-- at the smaller width, as long as the literal is within range.

-- We can't do the reverse trick, when the operand is narrowed:
-- narrowing throws away bits from the operand, there's no way to do
-- the same comparison at the larger size.

cmmMachOpFoldM Platform
platform MachOp
cmp [CmmMachOp MachOp
conv [CmmExpr
x], CmmLit (CmmInt Integer
i Width
_)]
  |     -- powerPC NCG has a TODO for I8/I16 comparisons, so don't try
    Platform -> Arch
platformArch Platform
platform Arch -> [Arch] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Arch
ArchX86, Arch
ArchX86_64],
        -- if the operand is widened:
    Just (Width
rep, Bool
signed, Width -> Integer -> Integer
narrow_fn) <- MachOp -> Maybe (Width, Bool, Width -> Integer -> Integer)
maybe_conversion MachOp
conv,
        -- and this is a comparison operation:
    Just MachOp
narrow_cmp <- MachOp -> Width -> Bool -> Maybe MachOp
maybe_comparison MachOp
cmp Width
rep Bool
signed,
        -- and the literal fits in the smaller size:
    Integer
i Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Width -> Integer -> Integer
narrow_fn Width
rep Integer
i
        -- then we can do the comparison at the smaller size
  = CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform MachOp
narrow_cmp [CmmExpr
x, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
i Width
rep)])
 where
    maybe_conversion :: MachOp -> Maybe (Width, Bool, Width -> Integer -> Integer)
maybe_conversion (MO_UU_Conv Width
from Width
to)
        | Width
to Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
> Width
from
        = (Width, Bool, Width -> Integer -> Integer)
-> Maybe (Width, Bool, Width -> Integer -> Integer)
forall a. a -> Maybe a
Just (Width
from, Bool
False, Width -> Integer -> Integer
narrowU)
    maybe_conversion (MO_SS_Conv Width
from Width
to)
        | Width
to Width -> Width -> Bool
forall a. Ord a => a -> a -> Bool
> Width
from
        = (Width, Bool, Width -> Integer -> Integer)
-> Maybe (Width, Bool, Width -> Integer -> Integer)
forall a. a -> Maybe a
Just (Width
from, Bool
True, Width -> Integer -> Integer
narrowS)

        -- don't attempt to apply this optimisation when the source
        -- is a float; see #1916
    maybe_conversion MachOp
_ = Maybe (Width, Bool, Width -> Integer -> Integer)
forall a. Maybe a
Nothing

        -- careful (#2080): if the original comparison was signed, but
        -- we were doing an unsigned widen, then we must do an
        -- unsigned comparison at the smaller size.
    maybe_comparison :: MachOp -> Width -> Bool -> Maybe MachOp
maybe_comparison (MO_U_Gt Width
_) Width
rep Bool
_     = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Gt Width
rep)
    maybe_comparison (MO_U_Ge Width
_) Width
rep Bool
_     = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Ge Width
rep)
    maybe_comparison (MO_U_Lt Width
_) Width
rep Bool
_     = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Lt Width
rep)
    maybe_comparison (MO_U_Le Width
_) Width
rep Bool
_     = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Le Width
rep)
    maybe_comparison (MO_Eq   Width
_) Width
rep Bool
_     = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_Eq   Width
rep)
    maybe_comparison (MO_S_Gt Width
_) Width
rep Bool
True  = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_S_Gt Width
rep)
    maybe_comparison (MO_S_Ge Width
_) Width
rep Bool
True  = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_S_Ge Width
rep)
    maybe_comparison (MO_S_Lt Width
_) Width
rep Bool
True  = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_S_Lt Width
rep)
    maybe_comparison (MO_S_Le Width
_) Width
rep Bool
True  = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_S_Le Width
rep)
    maybe_comparison (MO_S_Gt Width
_) Width
rep Bool
False = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Gt Width
rep)
    maybe_comparison (MO_S_Ge Width
_) Width
rep Bool
False = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Ge Width
rep)
    maybe_comparison (MO_S_Lt Width
_) Width
rep Bool
False = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Lt Width
rep)
    maybe_comparison (MO_S_Le Width
_) Width
rep Bool
False = MachOp -> Maybe MachOp
forall a. a -> Maybe a
Just (Width -> MachOp
MO_U_Le Width
rep)
    maybe_comparison MachOp
_ Width
_ Bool
_ = Maybe MachOp
forall a. Maybe a
Nothing

-- We can often do something with constants of 0 and 1 ...
-- See Note [Comparison operators]

cmmMachOpFoldM Platform
platform MachOp
mop [CmmExpr
x, y :: CmmExpr
y@(CmmLit (CmmInt Integer
0 Width
_))]
  = case MachOp
mop of
        -- Arithmetic
        MO_Add   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- x + 0 = x
        MO_Sub   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- x - 0 = x
        MO_Mul   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
y   -- x * 0 = 0

        -- Logical operations
        MO_And   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
y   -- x &     0 = 0
        MO_Or    Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- x |     0 = x
        MO_Xor   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- x `xor` 0 = x

        -- Shifts
        MO_Shl   Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- x << 0 = x
        MO_S_Shr Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x   -- ditto shift-right
        MO_U_Shr Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x

        -- Comparisons; these ones are trickier
        -- See Note [Comparison operators]
        MO_Ne    Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x                -- (x > y) != 0  =  x > y
        MO_Eq    Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'  -- (x > y) == 0  =  x <= y
        MO_U_Gt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x                -- (x > y) > 0   =  x > y
        MO_S_Gt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x                -- ditto
        MO_U_Lt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
zero             -- (x > y) < 0  =  0
        MO_S_Lt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
zero
        MO_U_Ge  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
one              -- (x > y) >= 0  =  1
        MO_S_Ge  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
one

        MO_U_Le  Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'  -- (x > y) <= 0  =  x <= y
        MO_S_Le  Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'
        MachOp
_ -> Maybe CmmExpr
forall a. Maybe a
Nothing
  where
    zero :: CmmExpr
zero = CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
0 (Platform -> Width
wordWidth Platform
platform))
    one :: CmmExpr
one  = CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
1 (Platform -> Width
wordWidth Platform
platform))

cmmMachOpFoldM Platform
platform MachOp
mop [CmmExpr
x, (CmmLit (CmmInt Integer
1 Width
rep))]
  = case MachOp
mop of
        -- Arithmetic: x*1 = x, etc
        MO_Mul    Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
        MO_S_Quot Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
        MO_U_Quot Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
        MO_S_Rem  Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
0 Width
rep)
        MO_U_Rem  Width
_ -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (CmmExpr -> Maybe CmmExpr) -> CmmExpr -> Maybe CmmExpr
forall a b. (a -> b) -> a -> b
$ CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
0 Width
rep)

        -- Comparisons; trickier
        -- See Note [Comparison operators]
        MO_Ne    Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'  -- (x>y) != 1  =  x<=y
        MO_Eq    Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x                -- (x>y) == 1  =  x>y
        MO_U_Lt  Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'  -- (x>y) < 1   =  x<=y
        MO_S_Lt  Width
_ | Just CmmExpr
x' <- CmmExpr -> Maybe CmmExpr
maybeInvertCmmExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x'  -- ditto
        MO_U_Gt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
zero             -- (x>y) > 1   = 0
        MO_S_Gt  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
zero
        MO_U_Le  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
one              -- (x>y) <= 1  = 1
        MO_S_Le  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
one
        MO_U_Ge  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x                -- (x>y) >= 1  = x>y
        MO_S_Ge  Width
_ | CmmExpr -> Bool
isComparisonExpr CmmExpr
x -> CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just CmmExpr
x
        MachOp
_ -> Maybe CmmExpr
forall a. Maybe a
Nothing
  where
    zero :: CmmExpr
zero = CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
0 (Platform -> Width
wordWidth Platform
platform))
    one :: CmmExpr
one  = CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
1 (Platform -> Width
wordWidth Platform
platform))

-- Now look for multiplication/division by powers of 2 (integers).

cmmMachOpFoldM Platform
platform MachOp
mop [CmmExpr
x, (CmmLit (CmmInt Integer
n Width
_))]
  = case MachOp
mop of
        MO_Mul Width
rep
           | Just Integer
p <- Integer -> Maybe Integer
exactLog2 Integer
n ->
                 CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_Shl Width
rep) [CmmExpr
x, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
p Width
rep)])
        MO_U_Quot Width
rep
           | Just Integer
p <- Integer -> Maybe Integer
exactLog2 Integer
n ->
                 CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_U_Shr Width
rep) [CmmExpr
x, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
p Width
rep)])
        MO_U_Rem Width
rep
           | Just Integer
_ <- Integer -> Maybe Integer
exactLog2 Integer
n ->
                 CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_And Width
rep) [CmmExpr
x, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1) Width
rep)])
        MO_S_Quot Width
rep
           | Just Integer
p <- Integer -> Maybe Integer
exactLog2 Integer
n,
             CmmReg CmmReg
_ <- CmmExpr
x ->   -- We duplicate x in signedQuotRemHelper, hence require
                                -- it is a reg.  FIXME: remove this restriction.
                CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_S_Shr Width
rep)
                  [Width -> Integer -> CmmExpr
signedQuotRemHelper Width
rep Integer
p, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
p Width
rep)])
        MO_S_Rem Width
rep
           | Just Integer
p <- Integer -> Maybe Integer
exactLog2 Integer
n,
             CmmReg CmmReg
_ <- CmmExpr
x ->   -- We duplicate x in signedQuotRemHelper, hence require
                                -- it is a reg.  FIXME: remove this restriction.
                -- We replace (x `rem` 2^p) by (x - (x `quot` 2^p) * 2^p).
                -- Moreover, we fuse MO_S_Shr (last operation of MO_S_Quot)
                -- and MO_S_Shl (multiplication by 2^p) into a single MO_And operation.
                CmmExpr -> Maybe CmmExpr
forall a. a -> Maybe a
Just (Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_Sub Width
rep)
                    [CmmExpr
x, Platform -> MachOp -> [CmmExpr] -> CmmExpr
cmmMachOpFold Platform
platform (Width -> MachOp
MO_And Width
rep)
                      [Width -> Integer -> CmmExpr
signedQuotRemHelper Width
rep Integer
p, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (- Integer
n) Width
rep)]])
        MachOp
_ -> Maybe CmmExpr
forall a. Maybe a
Nothing
  where
    -- In contrast with unsigned integers, for signed ones
    -- shift right is not the same as quot, because it rounds
    -- to minus infinity, whereas quot rounds toward zero.
    -- To fix this up, we add one less than the divisor to the
    -- dividend if it is a negative number.
    --
    -- to avoid a test/jump, we use the following sequence:
    --      x1 = x >> word_size-1  (all 1s if -ve, all 0s if +ve)
    --      x2 = y & (divisor-1)
    --      result = x + x2
    -- this could be done a bit more simply using conditional moves,
    -- but we're processor independent here.
    --
    -- we optimise the divide by 2 case slightly, generating
    --      x1 = x >> word_size-1  (unsigned)
    --      return = x + x1
    signedQuotRemHelper :: Width -> Integer -> CmmExpr
    signedQuotRemHelper :: Width -> Integer -> CmmExpr
signedQuotRemHelper Width
rep Integer
p = MachOp -> [CmmExpr] -> CmmExpr
CmmMachOp (Width -> MachOp
MO_Add Width
rep) [CmmExpr
x, CmmExpr
x2]
      where
        bits :: Integer
bits = Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Width -> Int
widthInBits Width
rep) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1
        shr :: MachOp
shr = if Integer
p Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
1 then Width -> MachOp
MO_U_Shr Width
rep else Width -> MachOp
MO_S_Shr Width
rep
        x1 :: CmmExpr
x1 = MachOp -> [CmmExpr] -> CmmExpr
CmmMachOp MachOp
shr [CmmExpr
x, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt Integer
bits Width
rep)]
        x2 :: CmmExpr
x2 = if Integer
p Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
1 then CmmExpr
x1 else
             MachOp -> [CmmExpr] -> CmmExpr
CmmMachOp (Width -> MachOp
MO_And Width
rep) [CmmExpr
x1, CmmLit -> CmmExpr
CmmLit (Integer -> Width -> CmmLit
CmmInt (Integer
nInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-Integer
1) Width
rep)]

-- ToDo (#7116): optimise floating-point multiplication, e.g. x*2.0 -> x+x
-- Unfortunately this needs a unique supply because x might not be a
-- register.  See #2253 (program 6) for an example.


-- Anything else is just too hard.

cmmMachOpFoldM Platform
_ MachOp
_ [CmmExpr]
_ = Maybe CmmExpr
forall a. Maybe a
Nothing

{- Note [Comparison operators]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If we have
   CmmCondBranch ((x>#y) == 1) t f
we really want to convert to
   CmmCondBranch (x>#y) t f

That's what the constant-folding operations on comparison operators do above.
-}


-- -----------------------------------------------------------------------------
-- Utils

isPicReg :: CmmExpr -> Bool
isPicReg :: CmmExpr -> Bool
isPicReg (CmmReg (CmmGlobal GlobalReg
PicBaseReg)) = Bool
True
isPicReg CmmExpr
_ = Bool
False