packages feed

fir-0.1.0.0: src/FIR/Prim/Atomic.hs

{-# LANGUAGE ConstraintKinds          #-}
{-# LANGUAGE DataKinds                #-}
{-# LANGUAGE DerivingStrategies       #-}
{-# LANGUAGE GADTs                    #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeFamilies             #-}
{-# LANGUAGE UndecidableInstances     #-}

{-|
Module: FIR.Prim.Atomic

Atomic operations (loads, stores, exchanges, and arithmetic and bitwise
read-modify-write operations), and the scalar types that support them.
-}

module FIR.Prim.Atomic
  ( AtomicOp(..), SAtomicOp(..), atomicOp
  , AtomicShape(..), SAtomicShape(..), AtomicOpShape, atomicShape
  , AtomicTy, AtomicIntegralTy, AtomicOpTy
  )
  where

-- base
import Data.Int
  ( Int32, Int64 )
import Data.Kind
  ( Type, Constraint )
import Data.Word
  ( Word32, Word64 )

-- half
import Numeric.Half
  ( Half )

-- fir
import FIR.Prim.Types
  ( ScalarTy, IntegralTy )

------------------------------------------------------------

-- | Atomic operations on the scalar a pointer points to.
data AtomicOp
  = AtomicLoad            -- ^ Read the value.
  | AtomicStore           -- ^ Write a value.
  | AtomicExchange        -- ^ Write a value, returning the previous value.
  | AtomicCompareExchange -- ^ Write a value if the current value equals
                          --   an expected value.
  | AtomicIncrement       -- ^ Add one.
  | AtomicDecrement       -- ^ Subtract one.
  | AtomicAdd             -- ^ Add a value.
  | AtomicSub             -- ^ Subtract a value.
  | AtomicMin             -- ^ Take the minimum with a value.
  | AtomicMax             -- ^ Take the maximum with a value.
  | AtomicAnd             -- ^ Bitwise and with a value.
  | AtomicOr              -- ^ Bitwise or with a value.
  | AtomicXor             -- ^ Bitwise exclusive or with a value.
  deriving stock ( Eq, Show )

-- | Singleton for 'AtomicOp'.
data SAtomicOp ( op :: AtomicOp ) where
  SAtomicLoad            :: SAtomicOp 'AtomicLoad
  SAtomicStore           :: SAtomicOp 'AtomicStore
  SAtomicExchange        :: SAtomicOp 'AtomicExchange
  SAtomicCompareExchange :: SAtomicOp 'AtomicCompareExchange
  SAtomicIncrement       :: SAtomicOp 'AtomicIncrement
  SAtomicDecrement       :: SAtomicOp 'AtomicDecrement
  SAtomicAdd             :: SAtomicOp 'AtomicAdd
  SAtomicSub             :: SAtomicOp 'AtomicSub
  SAtomicMin             :: SAtomicOp 'AtomicMin
  SAtomicMax             :: SAtomicOp 'AtomicMax
  SAtomicAnd             :: SAtomicOp 'AtomicAnd
  SAtomicOr              :: SAtomicOp 'AtomicOr
  SAtomicXor             :: SAtomicOp 'AtomicXor

atomicOp :: SAtomicOp op -> AtomicOp
atomicOp SAtomicLoad            = AtomicLoad
atomicOp SAtomicStore           = AtomicStore
atomicOp SAtomicExchange        = AtomicExchange
atomicOp SAtomicCompareExchange = AtomicCompareExchange
atomicOp SAtomicIncrement       = AtomicIncrement
atomicOp SAtomicDecrement       = AtomicDecrement
atomicOp SAtomicAdd             = AtomicAdd
atomicOp SAtomicSub             = AtomicSub
atomicOp SAtomicMin             = AtomicMin
atomicOp SAtomicMax             = AtomicMax
atomicOp SAtomicAnd             = AtomicAnd
atomicOp SAtomicOr              = AtomicOr
atomicOp SAtomicXor             = AtomicXor

-- | The shape of an atomic operation: its operands besides the pointer,
-- and its result.
data AtomicShape
  = Reads    -- ^ No operand; returns the value.
  | Writes   -- ^ One operand; returns nothing.
  | Modifies -- ^ One operand; returns the previous value.
  | Compares -- ^ Two operands (the expected value, then the new value);
             --   returns the previous value.

-- | Singleton for 'AtomicShape'.
data SAtomicShape ( sh :: AtomicShape ) where
  SReads    :: SAtomicShape 'Reads
  SWrites   :: SAtomicShape 'Writes
  SModifies :: SAtomicShape 'Modifies
  SCompares :: SAtomicShape 'Compares

type AtomicOpShape :: AtomicOp -> AtomicShape
type family AtomicOpShape op where
  AtomicOpShape 'AtomicLoad            = 'Reads
  AtomicOpShape 'AtomicIncrement       = 'Reads
  AtomicOpShape 'AtomicDecrement       = 'Reads
  AtomicOpShape 'AtomicStore           = 'Writes
  AtomicOpShape 'AtomicCompareExchange = 'Compares
  AtomicOpShape _                      = 'Modifies

atomicShape :: SAtomicOp op -> SAtomicShape ( AtomicOpShape op )
atomicShape SAtomicLoad            = SReads
atomicShape SAtomicIncrement       = SReads
atomicShape SAtomicDecrement       = SReads
atomicShape SAtomicStore           = SWrites
atomicShape SAtomicCompareExchange = SCompares
atomicShape SAtomicExchange        = SModifies
atomicShape SAtomicAdd             = SModifies
atomicShape SAtomicSub             = SModifies
atomicShape SAtomicMin             = SModifies
atomicShape SAtomicMax             = SModifies
atomicShape SAtomicAnd             = SModifies
atomicShape SAtomicOr              = SModifies
atomicShape SAtomicXor             = SModifies

-- | Scalar types on which atomic operations are supported:
-- 32-bit and 64-bit integers, and floating-point numbers.
--
-- Under Vulkan, 64-bit integer atomics require the @shaderBufferInt64Atomics@
-- or @shaderSharedInt64Atomics@ features, and floating-point atomics require
-- features of @VK_EXT_shader_atomic_float@ (and of @VK_EXT_shader_atomic_float2@
-- for 16-bit floating-point numbers, and for minimum and maximum).
class ScalarTy ty => AtomicTy ty
instance AtomicTy Word32
instance AtomicTy Word64
instance AtomicTy Int32
instance AtomicTy Int64
instance AtomicTy Half
instance AtomicTy Float
instance AtomicTy Double

-- | Integral types on which atomic operations are supported:
-- 32-bit and 64-bit integers.
class ( AtomicTy ty, IntegralTy ty ) => AtomicIntegralTy ty
instance AtomicIntegralTy Word32
instance AtomicIntegralTy Word64
instance AtomicIntegralTy Int32
instance AtomicIntegralTy Int64

-- | Scalar types on which a given atomic operation is supported: bitwise
-- operations, subtraction, increment, decrement and compare-exchange are only
-- supported on integers.
type AtomicOpTy :: AtomicOp -> Type -> Constraint
type family AtomicOpTy op ty where
  AtomicOpTy 'AtomicCompareExchange ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicIncrement       ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicDecrement       ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicSub             ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicAnd             ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicOr              ty = AtomicIntegralTy ty
  AtomicOpTy 'AtomicXor             ty = AtomicIntegralTy ty
  AtomicOpTy _                      ty = AtomicTy ty