Skip to content

Commit

Permalink
[Builtins] Inline costing monoids
Browse files Browse the repository at this point in the history
  • Loading branch information
effectfully committed Mar 6, 2023
1 parent bb6e4ff commit aa0995c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ data ExBudget = ExBudget { exBudgetCPU :: ExCPU, exBudgetMemory :: ExMemory }
-- | Subract one 'ExBudget' from another. Does not guarantee that the result is positive.
minusExBudget :: ExBudget -> ExBudget -> ExBudget
minusExBudget (ExBudget c1 m1) (ExBudget c2 m2) = ExBudget (c1-c2) (m1-m2)
{-# INLINE minusExBudget #-}

-- These functions are performance critical, so we can't use GenericSemigroupMonoid, and we insist that they be inlined.
instance Semigroup ExBudget where
Expand All @@ -195,6 +196,7 @@ instance Semigroup ExBudget where

instance Monoid ExBudget where
mempty = ExBudget mempty mempty
{-# INLINE mempty #-}

instance Pretty ExBudget where
pretty (ExBudget cpu memory) = parens $ braces $ vsep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import PlutusCore.Pretty
import PlutusPrelude

import Codec.Serialise (Serialise)
import Control.Monad.RWS.Strict
import Data.Aeson
import Data.ByteString qualified as BS
import Data.Proxy
Expand Down Expand Up @@ -105,7 +104,6 @@ type CostingInteger = SatInt
newtype ExMemory = ExMemory CostingInteger
deriving stock (Eq, Ord, Show, Generic, Lift)
deriving newtype (Num, NFData, Read, Bounded)
deriving (Semigroup, Monoid) via (Sum CostingInteger)
deriving (FromJSON, ToJSON) via CostingInteger
deriving Serialise via CostingInteger
deriving anyclass NoThunks
Expand All @@ -114,12 +112,19 @@ instance Pretty ExMemory where
instance PrettyBy config ExMemory where
prettyBy _ m = pretty m

instance Semigroup ExMemory where
(<>) = coerce $ (+) @CostingInteger
{-# INLINE (<>) #-}

instance Monoid ExMemory where
mempty = coerce (0 :: CostingInteger)
{-# INLINE mempty #-}

-- | Counts CPU units in picoseconds: maximum value for SatInt is 2^63 ps, or
-- appproximately 106 days.
newtype ExCPU = ExCPU CostingInteger
deriving stock (Eq, Ord, Show, Generic, Lift)
deriving newtype (Num, NFData, Read, Bounded)
deriving (Semigroup, Monoid) via (Sum CostingInteger)
deriving (FromJSON, ToJSON) via CostingInteger
deriving Serialise via CostingInteger
deriving anyclass NoThunks
Expand All @@ -128,6 +133,14 @@ instance Pretty ExCPU where
instance PrettyBy config ExCPU where
prettyBy _ m = pretty m

instance Semigroup ExCPU where
(<>) = coerce $ (+) @CostingInteger
{-# INLINE (<>) #-}

instance Monoid ExCPU where
mempty = coerce (0 :: CostingInteger)
{-# INLINE mempty #-}

{- Note [ExMemoryUsage instances for non-constants]
In order to calculate the cost of a built-in function we need to feed the 'ExMemory' of each
argument to the costing function associated with the builtin. For a polymorphic builtin this means
Expand Down
81 changes: 64 additions & 17 deletions plutus-core/satint/src/Data/SatInt.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,49 +49,83 @@ instance Read SatInt where
readsPrec p xs = [ (SI x, r) | (x, r) <- readsPrec p xs ]

instance Eq SatInt where
SI x == SI y = eqInt x y
SI x /= SI y = neInt x y
{-# INLINE (==) #-}
(==) = coerce eqInt

{-# INLINE (/=) #-}
(/=) = coerce neInt

instance Ord SatInt where
SI x < SI y = ltInt x y
SI x <= SI y = leInt x y
SI x > SI y = gtInt x y
SI x >= SI y = geInt x y
{-# INLINE (<) #-}
(<) = coerce ltInt

{-# INLINE (<=) #-}
(<=) = coerce leInt

{-# INLINE (>) #-}
(>) = coerce gtInt

{-# INLINE (>=) #-}
(>=) = coerce geInt

-- | In the `Num' instance, we plug in our own addition, multiplication
-- and subtraction function that perform overflow-checking.
instance Num SatInt where
(+) = plusSI
(*) = timesSI
(-) = minusSI
{-# INLINE (+) #-}
(+) = plusSI

{-# INLINE (*) #-}
(*) = timesSI

{-# INLINE (-) #-}
(-) = minusSI

{-# INLINE negate #-}
negate (SI y)
| y == minBound = maxBound
| otherwise = SI (negate y)

{-# INLINE abs #-}
abs x
| x >= 0 = x
| otherwise = negate x
signum (SI x) = SI (signum x)
| x >= 0 = x
| otherwise = negate x

{-# INLINE signum #-}
signum = coerce (signum :: Int -> Int)

{-# INLINE fromInteger #-}
fromInteger x
| x > maxBoundInteger = maxBound
| x < minBoundInteger = minBound
| otherwise = SI (fromInteger x)

{-# INLINABLE maxBoundInteger #-}
maxBoundInteger :: Integer
maxBoundInteger = toInteger maxInt

{-# INLINABLE minBoundInteger #-}
minBoundInteger :: Integer
minBoundInteger = toInteger minInt

instance Bounded SatInt where

{-# INLINABLE minBound #-}
minBound = SI minInt

{-# INLINABLE maxBound #-}
maxBound = SI maxInt

instance Enum SatInt where
succ (SI x) = SI (succ x)
pred (SI x) = SI (pred x)
toEnum = SI
fromEnum = unSatInt
{-# INLINE succ #-}
succ = coerce (succ :: Int -> Int)

{-# INLINE pred #-}
pred = coerce (pred :: Int -> Int)

{-# INLINE toEnum #-}
toEnum = SI

{-# INLINE fromEnum #-}
fromEnum = unSatInt

{-# INLINE enumFrom #-}
enumFrom (SI (I# x)) = eftInt x maxInt#
Expand Down Expand Up @@ -223,54 +257,64 @@ efdtIntDnFB c n x1 x2 y -- Be careful about underflow!
-- The following code is copied/adapted from GHC.Real.

instance Real SatInt where
{-# INLINE toRational #-}
toRational (SI x) = toInteger x % 1

instance Integral SatInt where
{-# INLINE toInteger #-}
toInteger (SI (I# i)) = smallInteger i

{-# INLINE quot #-}
SI a `quot` SI b
| b == 0 = divZeroError
-- a/-1 = -a, -minBound = maxBound
-- We can't just fall though since `quotInt` would overflow instead
| a == minBound && b == (-1) = maxBound
| otherwise = SI (a `quotInt` b)

{-# INLINE rem #-}
SI a `rem` SI b
| b == 0 = divZeroError
-- a/-1 = -a, with no remainder
-- We can't just fall though since `remInt` would overflow instead
| a == minBound && b == (-1) = 0
| otherwise = SI (a `remInt` b)

{-# INLINE div #-}
SI a `div` SI b
| b == 0 = divZeroError
-- a/-1 = -a, -minBound = maxBound
-- We can't just fall though since `divInt` would overflow instead
| a == minBound && b == (-1) = maxBound
| otherwise = SI (a `divInt` b)

{-# INLINE mod #-}
SI a `mod` SI b
| b == 0 = divZeroError
-- a/-1 = -a, with no remainder
-- We can't just fall though since `modInt` would overflow instead
| a == minBound && b == (-1) = 0
| otherwise = SI (a `modInt` b)

{-# INLINE quotRem #-}
SI a `quotRem` SI b
| b == 0 = divZeroError
-- See cases for `quot` and `rem`
| a == minBound && b == (-1) = (maxBound, 0)
| otherwise = a `quotRemSI` b

{-# INLINE divMod #-}
SI a `divMod` SI b
| b == 0 = divZeroError
-- See cases for `div` and `mod`
| a == minBound && b == (-1) = (maxBound, 0)
| otherwise = a `divModSI` b

{-# INLINE quotRemSI #-}
quotRemSI :: Int -> Int -> (SatInt, SatInt)
quotRemSI a@(I# _) b@(I# _) = (SI (a `quotInt` b), SI (a `remInt` b))

{-# INLINE divModSI #-}
divModSI :: Int -> Int -> (SatInt, SatInt)
divModSI x@(I# _) y@(I# _) = (SI (x `divInt` y), SI (x `modInt` y))

Expand All @@ -285,6 +329,7 @@ So we have to case on the result, and then do some logic to work out what
kind of overflow we're facing, and pick the correct result accordingly.
-}

{-# INLINE plusSI #-}
plusSI :: SatInt -> SatInt -> SatInt
plusSI (SI (I# x#)) (SI (I# y#)) =
case addIntC# x# y# of
Expand All @@ -297,6 +342,7 @@ plusSI (SI (I# x#)) (SI (I# y#)) =
-- be impossible
else overflowError

{-# INLINE minusSI #-}
minusSI :: SatInt -> SatInt -> SatInt
minusSI (SI (I# x#)) (SI (I# y#)) =
case subIntC# x# y# of
Expand All @@ -309,6 +355,7 @@ minusSI (SI (I# x#)) (SI (I# y#)) =
-- be impossible
else overflowError

{-# INLINE timesSI #-}
timesSI :: SatInt -> SatInt -> SatInt
timesSI (SI (I# x#)) (SI (I# y#)) =
case mulIntMayOflo# x# y# of
Expand Down

0 comments on commit aa0995c

Please sign in to comment.