packages feed

qute-0.1.0: src/Language/QBE/Simulator/Expression.hs

-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
--
-- SPDX-License-Identifier: GPL-3.0-only

-- | This module provides a generic expression language used to describe
-- arithmetic and logic operations on instruction operands in the abstract
-- 'Language.QBE.Simulator' description of QBE semantics. Therefore, in
-- addition to the 'Language.QBE.Simulator.State.Simulator' monad, it is the
-- central component for the abstract description of QBE's semantics.
module Language.QBE.Simulator.Expression
  ( -- * Expression Abstraction
    ValueRepr (..),

    -- * Conversion Functions,
    fromString,
    toString,
    boolToValue,

    -- * Comparision
    compareIntExpr,
    compareFloatExpr,
  )
where

import Data.Char qualified as C
import Data.Word (Word64)
import Language.QBE.Types qualified as QBE

-- | Generic expression abstraction operating on values of type 'QBE.ExtType'.
-- Values are either fixed-size bitvectors (8-, 16, 32-, or 64-bit) or
-- single-precision or double-precision floating point values. The value type
-- must be tracked internally by the 'ValueRepr' instance. Operations on the
-- value must return 'Nothing' if the operation is performed on values of
-- different types.
class ValueRepr v where
  -- | Create a 'ValueRepr' from an integer literal.
  --
  -- TODO: rename fromLit to fromInt
  fromLit :: QBE.ExtType -> Word64 -> v

  fromFloat :: Float -> v
  fromDouble :: Double -> v
  toWord64 :: v -> Word64
  getType :: v -> QBE.ExtType

  floatToInt :: QBE.ExtType -> Bool -> v -> Maybe v
  intToFloat :: QBE.ExtType -> Bool -> v -> Maybe v
  extendFloat :: v -> Maybe v
  truncFloat :: v -> Maybe v

  -- | Extend a value to the given 'QBE.ExtType'. The 'Bool' is true if
  -- the value should be sign-extended, otherwise it is zero-extended.
  -- If the @v@ is a float or if the current size exceeds (or is equal to)
  -- the size of 'QBE.ExtType', then 'Nothing' is returned.
  extend :: QBE.ExtType -> Bool -> v -> Maybe v

  -- | Extract the least significant bits of a @v@. The bits to extract
  -- are deduced from the given 'QBE.ExtType'. Returns 'Nothing' if the
  -- 'QBE.ExtType' is a float type, if the value is a float, or if the size
  -- of 'QBE.ExtType' exceeds the size of @v@.
  extract :: QBE.ExtType -> v -> Maybe v

  -- | Addition.
  add :: v -> v -> Maybe v

  -- | Subtraction.
  sub :: v -> v -> Maybe v

  -- | Multiplication.
  mul :: v -> v -> Maybe v

  -- | Unsigned division.
  div :: v -> v -> Maybe v

  -- | Unsigned remainder.
  urem :: v -> v -> Maybe v

  -- | Signed remainder.
  srem :: v -> v -> Maybe v

  -- | Unsigned division.
  udiv :: v -> v -> Maybe v

  -- | Bitwise or.
  or :: v -> v -> Maybe v

  -- | Bitwise xor.
  xor :: v -> v -> Maybe v

  -- | Bitwise and.
  and :: v -> v -> Maybe v

  -- | Unary negation.
  neg :: v -> Maybe v

  -- | Arithmetic right shift, preserving the sign bit of the shifted value.
  -- Shift amount must always be a 32-bit value, the shifted value must be 32- or 64-bit.
  sar :: v -> v -> Maybe v

  -- | Logical shift right, filling the newly freed bits with zeroes.
  -- Shift amount must always be a 32-bit value, the shifted value must be 32- or 64-bit.
  shr :: v -> v -> Maybe v

  -- | Logical shift left, always fills the freed bits with zeroes.
  -- Shift amount must always be a 32-bit value, the shifted value must be 32- or 64-bit.
  shl :: v -> v -> Maybe v

  -- | Check for equality.
  eq :: v -> v -> Maybe v

  -- | Check if two values are not equal.
  ne :: v -> v -> Maybe v

  -- | Signed less than or equal to.
  sle :: v -> v -> Maybe v

  -- | Signed less than.
  slt :: v -> v -> Maybe v

  -- | Signed greater than or equal to.
  sge :: v -> v -> Maybe v

  -- | Signed greater than.
  sgt :: v -> v -> Maybe v

  -- | Unsigned less than or equal to.
  ule :: v -> v -> Maybe v

  -- | Unsigned less than.
  ult :: v -> v -> Maybe v

  -- | Unsigned greater than or equal to.
  uge :: v -> v -> Maybe v

  -- | Unsigned greater then.
  ugt :: v -> v -> Maybe v

  -- | Ordered, no operand is a NaN.
  -- Only defined for floating points, must return 'Nothing' otherwise.
  ord :: v -> v -> Maybe v

  -- | Unordered, at least one operand is a NaN.
  -- Only defined for floating points, must return 'Nothing' otherwise.
  unord :: v -> v -> Maybe v
  unord lhs rhs = ord lhs rhs >>= neg

-- | Convert a string to a list of 8-bit values represented through 'ValueRepr'.
fromString :: (ValueRepr v) => String -> [v]
fromString = map (\c -> fromLit QBE.Byte (fromIntegral $ C.ord c))

-- | Inverse of 'fromString'.
toString :: (ValueRepr v) => [v] -> String
toString = map (\b -> C.chr (fromIntegral $ toWord64 b))

-- | Convert a Boolean value to a 64-bit value in 'ValueRepr'.
boolToValue :: (ValueRepr v) => Bool -> v
boolToValue True = fromLit (QBE.Base QBE.Long) 1
boolToValue False = fromLit (QBE.Base QBE.Long) 0

-- | Map a 'QBE.IntCmpOp' to the corresponding function from 'ValueRepr'.
compareIntExpr :: (ValueRepr v) => QBE.IntCmpOp -> (v -> v -> Maybe v)
compareIntExpr QBE.IEq = eq
compareIntExpr QBE.INe = ne
compareIntExpr QBE.ISle = sle
compareIntExpr QBE.ISlt = slt
compareIntExpr QBE.ISge = sge
compareIntExpr QBE.ISgt = sgt
compareIntExpr QBE.IUle = ule
compareIntExpr QBE.IUlt = ult
compareIntExpr QBE.IUge = uge
compareIntExpr QBE.IUgt = ugt
{-# INLINE compareIntExpr #-}

-- | Map a 'QBE.FloatCmpOp' to the corresponding function from 'ValueRepr'.
compareFloatExpr :: (ValueRepr v) => QBE.FloatCmpOp -> (v -> v -> Maybe v)
compareFloatExpr QBE.FEq = eq
compareFloatExpr QBE.FNe = ne
compareFloatExpr QBE.FLe = sle
compareFloatExpr QBE.FLt = slt
compareFloatExpr QBE.FGe = sge
compareFloatExpr QBE.FGt = sgt
compareFloatExpr QBE.FOrd = ord
compareFloatExpr QBE.FUnord = unord
{-# INLINE compareFloatExpr #-}