packages feed

copilot-bluespec-4.2: src/Copilot/Compile/Bluespec/FloatingPoint.hs

-- | Generate definitions that allow importing C implementations of
-- floating-point operations into Bluespec.
module Copilot.Compile.Bluespec.FloatingPoint
  ( copilotBluespecFloatingPointBSV
  , copilotBluespecFloatingPointC
  ) where

-- | The contents of the generated @BluespecFP.bsv@ file, which contains the
-- @import \"BDPI\"@ declarations needed to use imported C functions in
-- Bluespec.
copilotBluespecFloatingPointBSV :: String
copilotBluespecFloatingPointBSV =
  unlines $
    [ "import FloatingPoint::*;"
    , ""
    ] ++
    concatMap
      (\funName -> [importOp1 Float funName, importOp1 Double funName])
      unaryFloatOpNames ++
    concatMap
      (\funName -> [importOp2 Float funName, importOp2 Double funName])
      [ "pow"
      , "atan2"
      , "logb"
      ]
  where
    importOp1 :: FloatType -> String -> String
    importOp1 ft funName =
      "import \"BDPI\" function " ++ floatTypeName ft ++ " " ++ funNamePrefix
        ++ funName ++ floatTypeSuffix ft ++ " (" ++ floatTypeName ft ++ " x);"

    importOp2 :: FloatType -> String -> String
    importOp2 ft funName =
      "import \"BDPI\" function " ++ floatTypeName ft ++ " " ++ funNamePrefix
        ++ funName ++ floatTypeSuffix ft ++ " (" ++ floatTypeName ft ++ " x, "
        ++ floatTypeName ft ++ " y);"

    floatTypeName :: FloatType -> String
    floatTypeName Float  = "Float"
    floatTypeName Double = "Double"

-- | The contents of the generated @bs_fp.c@ file, which contains the C wrapper
-- functions that Bluespec imports.
copilotBluespecFloatingPointC :: String
copilotBluespecFloatingPointC =
  unlines $
    [ "#include <math.h>"
    , ""
    , defineUnionType Float
    , defineUnionType Double
    ] ++
    concatMap
      (\funName -> [defineOp1 Float funName, defineOp1 Double funName])
      unaryFloatOpNames ++
    concatMap
      (\funName -> [defineOp2 Float funName, defineOp2 Double funName])
      [ "pow"
      , "atan2"
      ] ++
    -- There is no direct C counterpart to the `logb` function, so we implement
    -- it in terms of `log`.
    map
      (\ft ->
        defineOp2Skeleton ft "logb" $
        \_cFunName x y -> "log" ++ floatTypeSuffix ft ++ "(" ++ y ++ ") / log"
                            ++ floatTypeSuffix ft ++ "( " ++ x ++ ")")
      [Float, Double]
  where
    defineUnionType :: FloatType -> String
    defineUnionType ft =
      unlines
        [ "union " ++ unionTypeName ft ++ " {"
        , "  " ++ integerTypeName ft ++ " i;"
        , "  " ++ floatTypeName ft ++ " f;"
        , "};"
        ]

    defineOp1Skeleton ::
      FloatType -> String -> (String -> String -> String) -> String
    defineOp1Skeleton ft funName mkFloatOp =
      let cFunName = funName ++ floatTypeSuffix ft in
      unlines
        [ integerTypeName ft ++ " "
          ++ funNamePrefix ++ cFunName
          ++ "(" ++ integerTypeName ft ++ " x) {"
        , "  union " ++ unionTypeName ft ++ " x_u;"
        , "  union " ++ unionTypeName ft ++ " r_u;"
        , "  x_u.i = x;"
        , "  r_u.f = " ++ mkFloatOp cFunName "x_u.f" ++ ";"
        , "  return r_u.i;"
        , "}"
        ]

    defineOp1 :: FloatType -> String -> String
    defineOp1 ft funName =
      defineOp1Skeleton ft funName $
      \cFunName x -> cFunName ++ "(" ++ x ++ ")"

    defineOp2Skeleton ::
      FloatType -> String -> (String -> String -> String -> String) -> String
    defineOp2Skeleton ft funName mkFloatOp =
      let cFunName = funName ++ floatTypeSuffix ft in
      unlines
        [ integerTypeName ft ++ " "
          ++ funNamePrefix ++ cFunName
          ++ "(" ++ integerTypeName ft ++ " x, " ++ integerTypeName ft
          ++ " y) {"
        , "  union " ++ unionTypeName ft ++ " x_u;"
        , "  union " ++ unionTypeName ft ++ " y_u;"
        , "  union " ++ unionTypeName ft ++ " r_u;"
        , "  x_u.i = x;"
        , "  y_u.i = y;"
        , "  r_u.f = " ++ mkFloatOp cFunName "x_u.f" "y_u.f" ++ ";"
        , "  return r_u.i;"
        , "}"
        ]

    defineOp2 :: FloatType -> String -> String
    defineOp2 ft funName =
      defineOp2Skeleton ft funName $
      \cFunName x y -> cFunName ++ "(" ++ x ++ ", " ++ y ++ ")"

    integerTypeName :: FloatType -> String
    integerTypeName Float  = "unsigned int"
    integerTypeName Double = "unsigned long long"

    floatTypeName :: FloatType -> String
    floatTypeName Float  = "float"
    floatTypeName Double = "double"

    unionTypeName :: FloatType -> String
    unionTypeName Float  = "ui_float"
    unionTypeName Double = "ull_double"

-- * Internals

-- | Are we generating a function for a @float@ or @double@ operation?
data FloatType
  = Float
  | Double

-- | The suffix to use in the generated function.
floatTypeSuffix :: FloatType -> String
floatTypeSuffix Float  = "f"
floatTypeSuffix Double = ""

-- | The prefix to use in the generated function.
funNamePrefix :: String
funNamePrefix = "bs_fp_"

-- | The names of unary floating-point operations.
unaryFloatOpNames :: [String]
unaryFloatOpNames =
  [ "exp"
  , "log"
  , "acos"
  , "asin"
  , "atan"
  , "cos"
  , "sin"
  , "tan"
  , "acosh"
  , "asinh"
  , "atanh"
  , "cosh"
  , "sinh"
  , "tanh"
  , "ceil"
  , "floor"
  , "sqrt"
  ]