haskelloptimizationcompiler-construction

how to do constant folding algorithm for an optmizing compiler in haskell?


so this question might seem too newbie, but I'm already reading the book "Advanced Compiler Design and Implementation" by Steven Muchnick and in the first chapter of optimization it talks about constant folding.

first attempt was something like below, but it was almost impossible to implement considering it needed typeclasses Integral and Bits.

data Value
  = BoolV Bool
  | IntegerV Integer
  | FloatV Double
  | StringV String
  deriving (Show, Eq)

data Expression
  = Const Value
  | Temp Label
  | List [Expression]
  | Call Label [Expression]
  | BinOp Operator Expression Expression
  deriving (Show, Eq)

rerunOverIR :: Expression -> Expression
rerunOverIR = \case
  Const constant -> Const constant
  Temp temporary -> Temp temporary
  List list -> List list
  Call label args -> Call label (map rerunOverIR args)
  BinOp operator lhs rhs ->
    case operator of
      Addition -> folder (+)
      Subtraction -> folder (-)
      Multiplication -> folder (*)
      Modulo -> folder mod
      Division -> folder div
      BitwiseAnd -> folder (.&.)
      BitwiseOr -> folder (.|.)
      BitwiseXor -> folder xor
      ShiftRight -> folder shiftR
      ShiftLeft -> folder shiftL
      Equal -> folder (==)
      NotEqual -> folder (/=)
      Greater -> folder (>)
      GreaterEqual -> folder (>=)
      Less -> folder (<)
      LessEqual -> folder (<=)
      LogicalAnd -> folder (&&)
      LogicalOr -> folder (||)
      _ -> error $ "this operator doesn't exist " ++ show operator
    where
      folder op =
        case lhs of
          Const c1 -> case rhs of
            Const c2 -> Const $ op c1 c2
            expr -> rerunOverIR $ BinOp operator (Const c1) (rerunOverIR expr)
          e1 -> case rhs of
            Const c2 -> rerunOverIR $ BinOp operator (rerunOverIR e1) (Const c2)
            e2 -> rerunOverIR $ BinOp operator (rerunOverIR e1) (rerunOverIR e2)

so I tried to this time change expressions to below, but it's even worse.

data Expression
  = Bool Bool
  | Integer Integer
  | Float Double
  | String String
  | Temp Label
  | List [Expression]
  | Call Label [Expression]
  | BinOp Operator Expression Expression
  deriving (Show, Eq)

so my question is that, how compilers or interpreters written in Haskell properly handle constant folding at the late stage? I'm pretty sure I'm on the wrong track...


Solution

  • It looks like you should certainly be able to do it by doing, for example,

    Addition -> folderNum (+)
    ...
    where folderNum :: (forall a. Num a => a -> a -> a) -> Expression
          folderNum op = case (lhs, rhs) of
            (Const (IntegerV a), Const (IntegerV b)) -> Const (IntegerV (op a b))
            (Const (FloatV a), Const (FloatV b)) -> Const (FloatV (op a b)) 
            else -> ...
    

    You're on the right track, but you're going to have to do the detailed case work to match which binary operations apply to which constant types, presumably for each distinct type class in Haskell that supports the operations you'll need.