{-# LANGUAGE BangPatterns #-}
module Unison.Runtime.TypeNumbers
( typeNumbers,
typeNumber,
typeReference,
typeReferences,
typeNumberMap,
seedTypeNumbers,
)
where
import Control.Exception (evaluate)
import Control.Monad.ST (RealWorld)
import Data.Bits (shiftR, (.&.))
import Data.IORef
import Data.Map.Strict qualified as Map
import Data.Primitive.Array
import Data.Primitive.SmallArray
import Data.Set qualified as Set
import GHC.IO (unsafeDupablePerformIO, unsafePerformIO)
import Unison.Prelude
import Unison.Reference (Reference)
import Unison.Runtime.Builtin.Types (builtinTypeNumbering)
import Unison.Runtime.InternalError (internalBug)
import Unison.Util.EnumContainers (EnumMap)
import Unison.Util.EnumContainers qualified as EC
data Maps = Maps !(Map Reference Word64) !(EnumMap Word64 Reference)
data Registry = Registry
{
Registry -> IORef Word64
next :: !(IORef Word64),
Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory :: !(SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))),
Registry -> IORef Maps
maps :: !(IORef Maps)
}
chunkBits :: Int
chunkBits :: Int
chunkBits = Int
12
chunkSize :: Int
chunkSize :: Int
chunkSize = Int
4096
directorySize :: Int
directorySize :: Int
directorySize = Int
1024
registry :: Registry
registry :: Registry
registry = IO Registry -> Registry
forall a. IO a -> a
unsafePerformIO do
IORef Word64
next <- Word64 -> IO (IORef Word64)
forall a. a -> IO (IORef a)
newIORef (Word64
1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Map Reference Word64 -> Word64
forall a. Ord a => Map Reference a -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum Map Reference Word64
builtinTypeNumbering)
SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory <- Int
-> [IORef (Maybe (MutableArray RealWorld Reference))]
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
forall a. Int -> [a] -> SmallArray a
smallArrayFromListN Int
directorySize ([IORef (Maybe (MutableArray RealWorld Reference))]
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference))))
-> IO [IORef (Maybe (MutableArray RealWorld Reference))]
-> IO
(SmallArray (IORef (Maybe (MutableArray RealWorld Reference))))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int
-> IO (IORef (Maybe (MutableArray RealWorld Reference)))
-> IO [IORef (Maybe (MutableArray RealWorld Reference))]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
directorySize (Maybe (MutableArray RealWorld Reference)
-> IO (IORef (Maybe (MutableArray RealWorld Reference)))
forall a. a -> IO (IORef a)
newIORef Maybe (MutableArray RealWorld Reference)
forall a. Maybe a
Nothing)
IORef Maps
maps <- Maps -> IO (IORef Maps)
forall a. a -> IO (IORef a)
newIORef (Map Reference Word64 -> EnumMap Word64 Reference -> Maps
Maps Map Reference Word64
forall k a. Map k a
Map.empty EnumMap Word64 Reference
forall a. Monoid a => a
mempty)
let reg :: Registry
reg = IORef Word64
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> IORef Maps
-> Registry
Registry IORef Word64
next SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory IORef Maps
maps
[(Reference, Word64)]
-> ((Reference, Word64) -> IO Word64) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (Map Reference Word64 -> [(Reference, Word64)]
forall k a. Map k a -> [(k, a)]
Map.toList Map Reference Word64
builtinTypeNumbering) \(Reference
r, Word64
n) -> Registry -> Word64 -> Reference -> IO Word64
assign Registry
reg Word64
n Reference
r
pure Registry
reg
{-# NOINLINE registry #-}
chunkFor :: Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor :: Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor Registry
reg Word64
n
| Int
ci Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
directorySize = [Word] -> [Char] -> IO (MutableArray RealWorld Reference)
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: number out of range: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)
| Bool
otherwise =
IORef (Maybe (MutableArray RealWorld Reference))
-> IO (Maybe (MutableArray RealWorld Reference))
forall a. IORef a -> IO a
readIORef IORef (Maybe (MutableArray RealWorld Reference))
ref IO (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference)
-> IO (MutableArray RealWorld Reference))
-> IO (MutableArray RealWorld Reference)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just MutableArray RealWorld Reference
c -> MutableArray RealWorld Reference
-> IO (MutableArray RealWorld Reference)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure MutableArray RealWorld Reference
c
Maybe (MutableArray RealWorld Reference)
Nothing -> do
MutableArray RealWorld Reference
c <- Int -> Reference -> IO (MutableArray (PrimState IO) Reference)
forall (m :: * -> *) a.
PrimMonad m =>
Int -> a -> m (MutableArray (PrimState m) a)
newArray Int
chunkSize ([Word] -> [Char] -> Reference
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: unassigned type number near " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n))
IORef (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference)
-> (Maybe (MutableArray RealWorld Reference),
MutableArray RealWorld Reference))
-> IO (MutableArray RealWorld Reference)
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (Maybe (MutableArray RealWorld Reference))
ref \case
Just MutableArray RealWorld Reference
c' -> (MutableArray RealWorld Reference
-> Maybe (MutableArray RealWorld Reference)
forall a. a -> Maybe a
Just MutableArray RealWorld Reference
c', MutableArray RealWorld Reference
c')
Maybe (MutableArray RealWorld Reference)
Nothing -> (MutableArray RealWorld Reference
-> Maybe (MutableArray RealWorld Reference)
forall a. a -> Maybe a
Just MutableArray RealWorld Reference
c, MutableArray RealWorld Reference
c)
where
ci :: Int
ci = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
n Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
chunkBits)
ref :: IORef (Maybe (MutableArray RealWorld Reference))
ref = SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> Int -> IORef (Maybe (MutableArray RealWorld Reference))
forall a. SmallArray a -> Int -> a
indexSmallArray (Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory Registry
reg) Int
ci
assign :: Registry -> Word64 -> Reference -> IO Word64
assign :: Registry -> Word64 -> Reference -> IO Word64
assign Registry
reg Word64
n Reference
r = do
MutableArray RealWorld Reference
c <- Registry -> Word64 -> IO (MutableArray RealWorld Reference)
chunkFor Registry
reg Word64
n
MutableArray (PrimState IO) Reference -> Int -> Reference -> IO ()
forall (m :: * -> *) a.
PrimMonad m =>
MutableArray (PrimState m) a -> Int -> a -> m ()
writeArray MutableArray RealWorld Reference
MutableArray (PrimState IO) Reference
c (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. (Int
chunkSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) Reference
r
Either Reference Word64
outcome <- IORef Maps
-> (Maps -> (Maps, Either Reference Word64))
-> IO (Either Reference Word64)
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Maps
maps Registry
reg) \ms :: Maps
ms@(Maps Map Reference Word64
fwd EnumMap Word64 Reference
bwd) -> case Reference -> Map Reference Word64 -> Maybe Word64
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Reference
r Map Reference Word64
fwd of
Just Word64
n' -> (Maps
ms, Word64 -> Either Reference Word64
forall a b. b -> Either a b
Right Word64
n')
Maybe Word64
Nothing -> case Word64 -> EnumMap Word64 Reference -> Maybe Reference
forall k a. EnumKey k => k -> EnumMap k a -> Maybe a
EC.lookup Word64
n EnumMap Word64 Reference
bwd of
Just Reference
r' -> (Maps
ms, Reference -> Either Reference Word64
forall a b. a -> Either a b
Left Reference
r')
Maybe Reference
Nothing -> (Map Reference Word64 -> EnumMap Word64 Reference -> Maps
Maps (Reference -> Word64 -> Map Reference Word64 -> Map Reference Word64
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Reference
r Word64
n Map Reference Word64
fwd) (Word64
-> Reference
-> EnumMap Word64 Reference
-> EnumMap Word64 Reference
forall k a. EnumKey k => k -> a -> EnumMap k a -> EnumMap k a
EC.mapInsert Word64
n Reference
r EnumMap Word64 Reference
bwd), Word64 -> Either Reference Word64
forall a b. b -> Either a b
Right Word64
n)
case Either Reference Word64
outcome of
Right Word64
n' -> Word64 -> IO Word64
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
n'
Left Reference
r' -> [Word] -> [Char] -> IO Word64
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" is " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r' [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", cannot seed it as " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r)
typeNumbers :: Set Reference -> IO (Map Reference Word64)
typeNumbers :: Set Reference -> IO (Map Reference Word64)
typeNumbers Set Reference
rs = do
Maps Map Reference Word64
fwd EnumMap Word64 Reference
_ <- IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
let new :: Set Reference
new = (Reference -> Bool) -> Set Reference -> Set Reference
forall a. (a -> Bool) -> Set a -> Set a
Set.filter (Reference -> Map Reference Word64 -> Bool
forall k a. Ord k => k -> Map k a -> Bool
`Map.notMember` Map Reference Word64
fwd) Set Reference
rs
[(Reference, Word64)]
assigned <- [Reference]
-> (Reference -> IO (Reference, Word64))
-> IO [(Reference, Word64)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for (Set Reference -> [Reference]
forall a. Set a -> [a]
Set.toList Set Reference
new) \Reference
r -> do
Word64
n <- IORef Word64 -> (Word64 -> (Word64, Word64)) -> IO Word64
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Word64
next Registry
registry) \Word64
i -> (Word64
i Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1, Word64
i)
(Reference
r,) (Word64 -> (Reference, Word64))
-> IO Word64 -> IO (Reference, Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Registry -> Word64 -> Reference -> IO Word64
assign Registry
registry Word64
n Reference
r
pure ([(Reference, Word64)] -> Map Reference Word64
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Reference, Word64)]
assigned Map Reference Word64
-> Map Reference Word64 -> Map Reference Word64
forall a. Semigroup a => a -> a -> a
<> Map Reference Word64 -> Set Reference -> Map Reference Word64
forall k a. Ord k => Map k a -> Set k -> Map k a
Map.restrictKeys Map Reference Word64
fwd Set Reference
rs)
typeNumber :: Reference -> IO Word64
typeNumber :: Reference -> IO Word64
typeNumber Reference
r = do
Maps Map Reference Word64
fwd EnumMap Word64 Reference
_ <- IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
case Reference -> Map Reference Word64 -> Maybe Word64
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Reference
r Map Reference Word64
fwd of
Just Word64
n -> Word64 -> IO Word64
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word64
n
Maybe Word64
Nothing -> (Map Reference Word64 -> Reference -> Word64
forall k a. Ord k => Map k a -> k -> a
Map.! Reference
r) (Map Reference Word64 -> Word64)
-> IO (Map Reference Word64) -> IO Word64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Set Reference -> IO (Map Reference Word64)
typeNumbers (Reference -> Set Reference
forall a. a -> Set a
Set.singleton Reference
r)
typeReference :: Word64 -> Reference
typeReference :: Word64 -> Reference
typeReference Word64
n = IO Reference -> Reference
forall a. IO a -> a
unsafeDupablePerformIO do
IORef (Maybe (MutableArray RealWorld Reference))
-> IO (Maybe (MutableArray RealWorld Reference))
forall a. IORef a -> IO a
readIORef (SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
-> Int -> IORef (Maybe (MutableArray RealWorld Reference))
forall a. SmallArray a -> Int -> a
indexSmallArray (Registry
-> SmallArray (IORef (Maybe (MutableArray RealWorld Reference)))
directory Registry
registry) (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64
n Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
chunkBits))) IO (Maybe (MutableArray RealWorld Reference))
-> (Maybe (MutableArray RealWorld Reference) -> IO Reference)
-> IO Reference
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just MutableArray RealWorld Reference
c -> MutableArray (PrimState IO) Reference -> Int -> IO Reference
forall (m :: * -> *) a.
PrimMonad m =>
MutableArray (PrimState m) a -> Int -> m a
readArray MutableArray RealWorld Reference
MutableArray (PrimState IO) Reference
c (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. (Int
chunkSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) IO Reference -> (Reference -> IO Reference) -> IO Reference
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Reference -> IO Reference
forall a. a -> IO a
evaluate
Maybe (MutableArray RealWorld Reference)
Nothing -> [Word] -> [Char] -> IO Reference
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: unassigned type number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)
{-# NOINLINE typeReference #-}
typeReferences :: IO (EnumMap Word64 Reference)
typeReferences :: IO (EnumMap Word64 Reference)
typeReferences = (\(Maps Map Reference Word64
_ EnumMap Word64 Reference
bwd) -> EnumMap Word64 Reference
bwd) (Maps -> EnumMap Word64 Reference)
-> IO Maps -> IO (EnumMap Word64 Reference)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
typeNumberMap :: IO (Map Reference Word64)
typeNumberMap :: IO (Map Reference Word64)
typeNumberMap = (\(Maps Map Reference Word64
fwd EnumMap Word64 Reference
_) -> Map Reference Word64
fwd) (Maps -> Map Reference Word64)
-> IO Maps -> IO (Map Reference Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef Maps -> IO Maps
forall a. IORef a -> IO a
readIORef (Registry -> IORef Maps
maps Registry
registry)
seedTypeNumbers :: EnumMap Word64 Reference -> IO ()
seedTypeNumbers :: EnumMap Word64 Reference -> IO ()
seedTypeNumbers EnumMap Word64 Reference
m = do
[(Word64, Reference)] -> ((Word64, Reference) -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (EnumMap Word64 Reference -> [(Word64, Reference)]
forall k a. EnumKey k => EnumMap k a -> [(k, a)]
EC.mapToList EnumMap Word64 Reference
m) \(Word64
n, Reference
_) ->
IORef Word64 -> (Word64 -> (Word64, ())) -> IO ()
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (Registry -> IORef Word64
next Registry
registry) \Word64
i -> (Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max Word64
i (Word64
n Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1), ())
[(Word64, Reference)] -> ((Word64, Reference) -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (EnumMap Word64 Reference -> [(Word64, Reference)]
forall k a. EnumKey k => EnumMap k a -> [(k, a)]
EC.mapToList EnumMap Word64 Reference
m) \(Word64
n, Reference
r) -> do
Word64
n' <- Registry -> Word64 -> Reference -> IO Word64
assign Registry
registry Word64
n Reference
r
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word64
n' Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
n) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
[Word] -> [Char] -> IO ()
forall a. HasCallStack => [Word] -> [Char] -> a
internalBug [] ([Char]
"TypeNumbers: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Reference -> [Char]
forall a. Show a => a -> [Char]
show Reference
r [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" is number " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n' [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" here, cannot seed it as " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Word64 -> [Char]
forall a. Show a => a -> [Char]
show Word64
n)