-- | -- Module : Cryptol.Backend.WordValue -- Copyright : (c) 2013-2021 Galois, Inc. -- License : BSD3 -- Maintainer : cryptol@galois.com -- Stability : provisional -- Portability : portable {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DoAndIfThenElse #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} module Cryptol.Backend.WordValue ( -- * WordValue WordValue , wordVal , bitmapWordVal , asWordList , asWordVal , asBitsMap , joinWordVal , takeWordVal , dropWordVal , extractWordVal , wordValLogicOp , wordValUnaryOp , assertWordValueInBounds , enumerateWordValue , enumerateWordValueRev , enumerateIndexSegments , wordValueSize , indexWordValue , updateWordValue , delayWordValue , joinWords , shiftSeqByWord , shiftWordByInteger , shiftWordByWord , wordValAsLit , reverseWordVal , forceWordValue , wordValueEqualsInteger , updateWordByWord , mergeWord , mergeWord' ) where import Control.Monad (unless) import Data.Bits import GHC.Generics (Generic) import Cryptol.Backend import Cryptol.Backend.Concrete (Concrete(..)) import Cryptol.Backend.Monad (EvalError(..)) import Cryptol.Backend.SeqMap import Cryptol.TypeCheck.Solver.InfNat(widthInteger, Nat'(..)) -- | Force the evaluation of a word value forceWordValue :: Backend sym => WordValue sym -> SEval sym () forceWordValue (WordVal w) = seq w (return ()) forceWordValue (ThunkWordVal _ m) = forceWordValue =<< m forceWordValue (BitmapVal _n packed _) = do w <- packed; seq w (return ()) -- | An arbitrarily-chosen number of elements where we switch from a dense -- sequence representation of bit-level words to 'SeqMap' representation. largeBitSize :: Integer largeBitSize = bit 32 -- | For efficiency reasons, we handle finite sequences of bits as special cases -- in the evaluator. In cases where we know it is safe to do so, we prefer to -- used a "packed word" representation of bit sequences. This allows us to rely -- directly on Integer types (in the concrete evaluator) and SBV's Word types (in -- the symbolic simulator). -- -- However, if we cannot be sure all the bits of the sequence -- will eventually be forced, we must instead rely on an explicit sequence of bits -- representation. data WordValue sym = ThunkWordVal Integer !(SEval sym (WordValue sym)) | WordVal !(SWord sym) -- ^ Packed word representation for bit sequences. | BitmapVal !Integer -- ^ Length of the word !(SEval sym (SWord sym)) -- ^ Thunk for packing the word !(SeqMap sym (SBit sym)) -- ^ deriving (Generic) wordVal :: SWord sym -> WordValue sym wordVal = WordVal packBitmap :: Backend sym => sym -> Integer -> SeqMap sym (SBit sym) -> SEval sym (SWord sym) packBitmap sym sz bs = packWord sym =<< sequence (enumerateSeqMap sz bs) unpackBitmap :: Backend sym => sym -> SWord sym -> SeqMap sym (SBit sym) unpackBitmap sym w = indexSeqMap $ \i -> wordBit sym w i bitmapWordVal :: Backend sym => sym -> Integer -> SeqMap sym (SBit sym) -> SEval sym (WordValue sym) bitmapWordVal sym sz bs = do packed <- sDelay sym (packBitmap sym sz bs) pure (BitmapVal sz packed bs) {-# INLINE joinWordVal #-} joinWordVal :: Backend sym => sym -> WordValue sym -> WordValue sym -> SEval sym (WordValue sym) joinWordVal sym wv1 wv2 = let fallback = fallbackWordJoin sym wv1 wv2 in case (wv1, wv2) of (WordVal w1, WordVal w2) -> WordVal <$> joinWord sym w1 w2 (ThunkWordVal _ m1, _) -> isReady sym m1 >>= \case Just x -> joinWordVal sym x wv2 Nothing -> fallback (_,ThunkWordVal _ m2) -> isReady sym m2 >>= \case Just x -> joinWordVal sym wv1 x Nothing -> fallback (WordVal w1, BitmapVal _ packed2 _) -> isReady sym packed2 >>= \case Just w2 -> WordVal <$> joinWord sym w1 w2 Nothing -> fallback (BitmapVal _ packed1 _, WordVal w2) -> isReady sym packed1 >>= \case Just w1 -> WordVal <$> joinWord sym w1 w2 Nothing -> fallback (BitmapVal _ packed1 _, BitmapVal _ packed2 _) -> do r1 <- isReady sym packed1 r2 <- isReady sym packed2 case (r1,r2) of (Just w1, Just w2) -> WordVal <$> joinWord sym w1 w2 _ -> fallback {-# INLINE fallbackWordJoin #-} fallbackWordJoin :: Backend sym => sym -> WordValue sym -> WordValue sym -> SEval sym (WordValue sym) fallbackWordJoin sym w1 w2 = do let n1 = wordValueSize sym w1 let n2 = wordValueSize sym w2 let len = n1 + n2 packed <- sDelay sym (do a <- asWordVal sym w1 b <- asWordVal sym w2 joinWord sym a b) let bs = concatSeqMap n1 (asBitsMap sym w1) (asBitsMap sym w2) pure (BitmapVal len packed bs) {-# INLINE takeWordVal #-} takeWordVal :: Backend sym => sym -> Integer -> Integer -> WordValue sym -> SEval sym (WordValue sym) takeWordVal sym leftWidth rigthWidth (WordVal w) = do (lw, _rw) <- splitWord sym leftWidth rigthWidth w pure (WordVal lw) takeWordVal sym leftWidth rightWidth (ThunkWordVal _ m) = isReady sym m >>= \case Just w -> takeWordVal sym leftWidth rightWidth w Nothing -> do m' <- sDelay sym (takeWordVal sym leftWidth rightWidth =<< m) return (ThunkWordVal leftWidth m') takeWordVal sym leftWidth rightWidth (BitmapVal _n packed xs) = isReady sym packed >>= \case Just w -> do (lw, _rw) <- splitWord sym leftWidth rightWidth w pure (WordVal lw) Nothing -> bitmapWordVal sym leftWidth xs {-# INLINE dropWordVal #-} dropWordVal :: Backend sym => sym -> Integer -> Integer -> WordValue sym -> SEval sym (WordValue sym) dropWordVal sym leftWidth rigthWidth (WordVal w) = do (_lw, rw) <- splitWord sym leftWidth rigthWidth w pure (WordVal rw) dropWordVal sym leftWidth rightWidth (ThunkWordVal _ m) = isReady sym m >>= \case Just w -> dropWordVal sym leftWidth rightWidth w Nothing -> do m' <- sDelay sym (dropWordVal sym leftWidth rightWidth =<< m) return (ThunkWordVal rightWidth m') dropWordVal sym leftWidth rightWidth (BitmapVal _n packed xs) = isReady sym packed >>= \case Just w -> do (_lw, rw) <- splitWord sym leftWidth rightWidth w pure (WordVal rw) Nothing -> do let rxs = dropSeqMap leftWidth xs bitmapWordVal sym rightWidth rxs {-# INLINE extractWordVal #-} -- | Extract a subsequence of bits from a @WordValue@. -- The first integer argument is the number of bits in the -- resulting word. The second integer argument is the -- number of less-significant digits to discard. Stated another -- way, the operation `extractWordVal n i w` is equivalent to -- first shifting `w` right by `i` bits, and then truncating to -- `n` bits. extractWordVal :: Backend sym => sym -> Integer -> Integer -> WordValue sym -> SEval sym (WordValue sym) extractWordVal sym len start (WordVal w) = WordVal <$> extractWord sym len start w extractWordVal sym len start (ThunkWordVal _n m) = isReady sym m >>= \case Just w -> extractWordVal sym len start w Nothing -> do m' <- sDelay sym (extractWordVal sym len start =<< m) pure (ThunkWordVal len m') extractWordVal sym len start (BitmapVal n packed xs) = isReady sym packed >>= \case Just w -> WordVal <$> extractWord sym len start w Nothing -> do let xs' = dropSeqMap (n - start - len) xs bitmapWordVal sym len xs' {-# INLINE wordValLogicOp #-} wordValLogicOp :: Backend sym => sym -> (SBit sym -> SBit sym -> SEval sym (SBit sym)) -> (SWord sym -> SWord sym -> SEval sym (SWord sym)) -> WordValue sym -> WordValue sym -> SEval sym (WordValue sym) wordValLogicOp _sym _ wop (WordVal w1) (WordVal w2) = WordVal <$> wop w1 w2 wordValLogicOp sym bop wop (WordVal w1) (BitmapVal n2 packed2 bs2) = isReady sym packed2 >>= \case Just w2 -> WordVal <$> wop w1 w2 Nothing -> bitmapWordVal sym n2 =<< zipSeqMap sym bop (Nat n2) (unpackBitmap sym w1) bs2 wordValLogicOp sym bop wop (BitmapVal n1 packed1 bs1) (WordVal w2) = isReady sym packed1 >>= \case Just w1 -> WordVal <$> wop w1 w2 Nothing -> bitmapWordVal sym n1 =<< zipSeqMap sym bop (Nat n1) bs1 (unpackBitmap sym w2) wordValLogicOp sym bop wop (BitmapVal n1 packed1 bs1) (BitmapVal _n2 packed2 bs2) = do r1 <- isReady sym packed1 r2 <- isReady sym packed2 case (r1,r2) of (Just w1, Just w2) -> WordVal <$> wop w1 w2 _ -> bitmapWordVal sym n1 =<< zipSeqMap sym bop (Nat n1) bs1 bs2 wordValLogicOp sym bop wop (ThunkWordVal _ m1) w2 = do w1 <- m1 wordValLogicOp sym bop wop w1 w2 wordValLogicOp sym bop wop w1 (ThunkWordVal _ m2) = do w2 <- m2 wordValLogicOp sym bop wop w1 w2 {-# INLINE wordValUnaryOp #-} wordValUnaryOp :: Backend sym => sym -> (SBit sym -> SEval sym (SBit sym)) -> (SWord sym -> SEval sym (SWord sym)) -> WordValue sym -> SEval sym (WordValue sym) wordValUnaryOp _ _ wop (WordVal w) = WordVal <$> wop w wordValUnaryOp sym bop wop (ThunkWordVal _ m) = wordValUnaryOp sym bop wop =<< m wordValUnaryOp sym bop wop (BitmapVal n packed xs) = isReady sym packed >>= \case Just w -> WordVal <$> wop w Nothing -> bitmapWordVal sym n =<< mapSeqMap sym bop (Nat n) xs {-# SPECIALIZE joinWords :: Concrete -> Integer -> Integer -> SeqMap Concrete (WordValue Concrete)-> SEval Concrete (WordValue Concrete) #-} joinWords :: forall sym. Backend sym => sym -> Integer -> Integer -> SeqMap sym (WordValue sym) -> SEval sym (WordValue sym) -- small enough to pack joinWords sym nParts nEach xs | nParts * nEach < largeBitSize = do z <- wordLit sym 0 0 loop (wordVal z) (enumerateSeqMap nParts xs) where loop :: WordValue sym -> [SEval sym (WordValue sym)] -> SEval sym (WordValue sym) loop !wv [] = pure wv loop !wv (w : ws) = do w' <- delayWordValue sym nEach w wv' <- joinWordVal sym wv w' loop wv' ws -- too large to pack joinWords sym nParts nEach xs = bitmapWordVal sym (nParts * nEach) zs where zs = indexSeqMap $ \i -> do let (q,r) = divMod i nEach ys <- lookupSeqMap xs q indexWordValue sym ys r reverseWordVal :: Backend sym => sym -> WordValue sym -> SEval sym (WordValue sym) reverseWordVal sym w = let m = wordValueSize sym w in bitmapWordVal sym m <$> reverseSeqMap m $ asBitsMap sym w wordValAsLit :: Backend sym => sym -> WordValue sym -> SEval sym (Maybe Integer) wordValAsLit sym (WordVal w) = pure (snd <$> wordAsLit sym w) wordValAsLit sym (ThunkWordVal _ m) = isReady sym m >>= \case Just v -> wordValAsLit sym v Nothing -> pure Nothing wordValAsLit sym (BitmapVal _ packed _) = isReady sym packed >>= \case Just w -> pure (snd <$> wordAsLit sym w) Nothing -> pure Nothing -- | Force a word value into packed word form asWordVal :: Backend sym => sym -> WordValue sym -> SEval sym (SWord sym) asWordVal _ (WordVal w) = return w asWordVal sym (ThunkWordVal _ m) = asWordVal sym =<< m asWordVal _ (BitmapVal _ packed _) = packed wordValueEqualsInteger :: forall sym. Backend sym => sym -> WordValue sym -> Integer -> SEval sym (SBit sym) wordValueEqualsInteger sym wv i | wordValueSize sym wv < widthInteger i = return (bitLit sym False) | otherwise = loop wv where loop (ThunkWordVal _ m) = loop =<< m loop (WordVal w) = wordEq sym w =<< wordLit sym (wordLen sym w) i loop (BitmapVal n packed bs) = isReady sym packed >>= \case Just w -> loop (WordVal w) Nothing -> bitsAre i =<< sequence (enumerateSeqMap n (reverseSeqMap n bs)) -- NB little-endian sequence of bits bitsAre :: Integer -> [SBit sym] -> SEval sym (SBit sym) bitsAre !n [] = return (bitLit sym (n == 0)) bitsAre !n (b:bs) = do pb <- bitIs (testBit n 0) b pbs <- bitsAre (n `shiftR` 1) bs bitAnd sym pb pbs bitIs :: Bool -> SBit sym -> SEval sym (SBit sym) bitIs b x = if b then pure x else bitComplement sym x asWordList :: forall sym. Backend sym => sym -> [WordValue sym] -> SEval sym (Maybe [SWord sym]) asWordList sym = loop id where loop :: ([SWord sym] -> [SWord sym]) -> [WordValue sym] -> SEval sym (Maybe [SWord sym]) loop f [] = pure (Just (f [])) loop f (WordVal x : vs) = loop (f . (x:)) vs loop f (ThunkWordVal _ m : vs) = isReady sym m >>= \case Just m' -> loop f (m' : vs) Nothing -> pure Nothing loop f (BitmapVal _ packed _ : vs) = isReady sym packed >>= \case Just x -> loop (f . (x:)) vs Nothing -> pure Nothing -- | Force a word value into a sequence of bits asBitsMap :: Backend sym => sym -> WordValue sym -> SeqMap sym (SBit sym) asBitsMap _ (BitmapVal _ _ xs) = xs asBitsMap sym (WordVal w) = indexSeqMap $ \i -> wordBit sym w i asBitsMap sym (ThunkWordVal _ m) = indexSeqMap $ \i -> do mp <- asBitsMap sym <$> (unwindThunks m) lookupSeqMap mp i -- | Turn a word value into a sequence of bits, forcing each bit. -- The sequence is returned in big-endian order. enumerateWordValue :: Backend sym => sym -> WordValue sym -> SEval sym [SBit sym] enumerateWordValue sym (WordVal w) = unpackWord sym w enumerateWordValue sym (ThunkWordVal _ m) = enumerateWordValue sym =<< m -- TODO? used the packed value if it is ready? enumerateWordValue _ (BitmapVal n _ xs) = sequence (enumerateSeqMap n xs) -- | Turn a word value into a sequence of bits, forcing each bit. -- The sequence is returned in reverse of the usual order, which is little-endian order. enumerateWordValueRev :: Backend sym => sym -> WordValue sym -> SEval sym [SBit sym] enumerateWordValueRev sym (WordVal w) = reverse <$> unpackWord sym w enumerateWordValueRev sym (ThunkWordVal _ m) = enumerateWordValueRev sym =<< m -- TODO? used the packed value if it is ready? enumerateWordValueRev _ (BitmapVal n _ xs) = sequence (enumerateSeqMap n (reverseSeqMap n xs)) enumerateIndexSegments :: Backend sym => sym -> WordValue sym -> SEval sym [IndexSegment sym] enumerateIndexSegments _sym (WordVal w) = pure [WordIndexSegment w] enumerateIndexSegments sym (ThunkWordVal _ m) = enumerateIndexSegments sym =<< m enumerateIndexSegments sym (BitmapVal n packed xs) = isReady sym packed >>= \case Just w -> pure [WordIndexSegment w] Nothing -> traverse (BitIndexSegment <$>) (enumerateSeqMap n xs) {-# SPECIALIZE bitsValueLessThan :: Concrete -> Integer -> [SBit Concrete] -> Integer -> SEval Concrete (SBit Concrete) #-} bitsValueLessThan :: Backend sym => sym -> Integer {- ^ bit-width -} -> [SBit sym] {- ^ big-endian list of index bits -} -> Integer {- ^ Upper bound to test against -} -> SEval sym (SBit sym) bitsValueLessThan sym _w [] _n = pure $ bitLit sym False bitsValueLessThan sym w (b:bs) n | nbit = do notb <- bitComplement sym b bitOr sym notb =<< bitsValueLessThan sym (w-1) bs n | otherwise = do notb <- bitComplement sym b bitAnd sym notb =<< bitsValueLessThan sym (w-1) bs n where nbit = testBit n (fromInteger (w-1)) assertWordValueInBounds :: Backend sym => sym -> Integer -> WordValue sym -> SEval sym () -- Can't index out of bounds for a sequence that is -- longer than the expressible index values assertWordValueInBounds sym n idx | n >= 2^(wordValueSize sym idx) = return () assertWordValueInBounds sym n (WordVal idx) | Just (_w,i) <- wordAsLit sym idx = unless (i < n) (raiseError sym (InvalidIndex (Just i))) -- If the index is a packed word, test that it -- is less than the concrete value of n, which -- fits into w bits because of the above test. assertWordValueInBounds sym n (WordVal idx) = do n' <- wordLit sym (wordLen sym idx) n p <- wordLessThan sym idx n' assertSideCondition sym p (InvalidIndex Nothing) -- Force thunks assertWordValueInBounds sym n (ThunkWordVal _ m) = assertWordValueInBounds sym n =<< m -- If the index is an unpacked word, force all the bits -- and compute the unsigned less-than test directly. assertWordValueInBounds sym n (BitmapVal sz packed bits) = isReady sym packed >>= \case Just w -> assertWordValueInBounds sym n (WordVal w) Nothing -> do bitsList <- sequence (enumerateSeqMap sz bits) p <- bitsValueLessThan sym sz bitsList n assertSideCondition sym p (InvalidIndex Nothing) delayWordValue :: Backend sym => sym -> Integer -> SEval sym (WordValue sym) -> SEval sym (WordValue sym) delayWordValue sym sz m = isReady sym m >>= \case Just w -> pure w Nothing -> ThunkWordVal sz <$> sDelay sym (unwindThunks m) -- If we are calling this, we know the spine of the word value has been -- demanded, so we unwind any chains of `ThunkWordValue` that may have built up. unwindThunks :: Backend sym => SEval sym (WordValue sym) -> SEval sym (WordValue sym) unwindThunks m = m >>= \case ThunkWordVal _ m' -> unwindThunks m' x -> pure x {-# INLINE shiftWordByInteger #-} shiftWordByInteger :: Backend sym => sym -> (SWord sym -> SWord sym -> SEval sym (SWord sym)) {- ^ operation on word values -} -> (Integer -> Integer -> Maybe Integer) {- ^ reindexing operation -} -> WordValue sym {- ^ word value to shift -} -> SInteger sym {- ^ shift amount, assumed to be in range [0,len] -} -> SEval sym (WordValue sym) shiftWordByInteger sym wop reindex x idx = case x of ThunkWordVal w wm -> isReady sym wm >>= \case Just x' -> shiftWordByInteger sym wop reindex x' idx Nothing -> do m' <- sDelay sym (do x' <- wm shiftWordByInteger sym wop reindex x' idx) return (ThunkWordVal w m') WordVal x' -> WordVal <$> (wop x' =<< wordFromInt sym (wordLen sym x') idx) BitmapVal n packed bs0 -> isReady sym packed >>= \case Just w -> shiftWordByInteger sym wop reindex (WordVal w) idx Nothing -> bitmapWordVal sym n =<< shiftSeqByInteger sym (iteBit sym) reindex (pure (bitLit sym False)) (Nat n) bs0 idx {-# INLINE shiftWordByWord #-} shiftWordByWord :: Backend sym => sym -> (SWord sym -> SWord sym -> SEval sym (SWord sym)) {- ^ operation on word values -} -> (Integer -> Integer -> Maybe Integer) {- ^ reindexing operation -} -> WordValue sym {- ^ value to shift -} -> WordValue sym {- ^ amount to shift -} -> SEval sym (WordValue sym) shiftWordByWord sym wop reindex x idx = case x of ThunkWordVal w wm -> isReady sym wm >>= \case Just wm' -> shiftWordByWord sym wop reindex wm' idx Nothing -> do m' <- sDelay sym (do wm' <- wm shiftWordByWord sym wop reindex wm' idx) return (ThunkWordVal w m') WordVal x' -> WordVal <$> (wop x' =<< asWordVal sym idx) BitmapVal n packed bs0 -> isReady sym packed >>= \case Just w -> shiftWordByWord sym wop reindex (WordVal w) idx Nothing -> bitmapWordVal sym n =<< shiftSeqByWord sym (iteBit sym) reindex (pure (bitLit sym False)) (Nat n) bs0 idx {-# INLINE updateWordByWord #-} updateWordByWord :: Backend sym => sym -> IndexDirection -> WordValue sym {- ^ value to update -} -> WordValue sym {- ^ index to update at -} -> SEval sym (SBit sym) {- ^ fresh bit -} -> SEval sym (WordValue sym) updateWordByWord sym dir w0 idx bitval = wordValAsLit sym idx >>= \case Just j -> let sz = wordValueSize sym w0 in case dir of IndexForward -> updateWordValue sym w0 j bitval IndexBackward -> updateWordValue sym w0 (sz - j - 1) bitval Nothing -> loop w0 where loop (ThunkWordVal sz m) = isReady sym m >>= \case Just w' -> loop w' Nothing -> delayWordValue sym sz (loop =<< m) loop (BitmapVal sz packed bs) = isReady sym packed >>= \case Just w -> loop (WordVal w) Nothing -> case dir of IndexForward -> bitmapWordVal sym sz $ indexSeqMap $ \i -> do b <- wordValueEqualsInteger sym idx i mergeEval sym (iteBit sym) b bitval (lookupSeqMap bs i) IndexBackward -> bitmapWordVal sym sz $ indexSeqMap $ \i -> do b <- wordValueEqualsInteger sym idx (sz - i - 1) mergeEval sym (iteBit sym) b bitval (lookupSeqMap bs i) loop (WordVal wv) = WordVal <$> -- TODO, this is too strict in bit do let sz = wordLen sym wv b <- bitval msk <- case dir of IndexForward -> do highbit <- wordLit sym sz (bit (fromInteger (sz-1))) wordShiftRight sym highbit =<< asWordVal sym idx IndexBackward -> do lowbit <- wordLit sym sz 1 wordShiftLeft sym lowbit =<< asWordVal sym idx case bitAsLit sym b of Just True -> wordOr sym wv msk Just False -> wordAnd sym wv =<< wordComplement sym msk Nothing -> do zro <- wordLit sym sz 0 one <- wordComplement sym zro q <- iteWord sym b one zro w' <- wordAnd sym wv =<< wordComplement sym msk wordXor sym w' =<< wordAnd sym msk q {-# INLINE shiftSeqByWord #-} shiftSeqByWord :: Backend sym => sym -> (SBit sym -> a -> a -> SEval sym a) {- ^ if/then/else operation of values -} -> (Integer -> Integer -> Maybe Integer) {- ^ reindexing operation -} -> SEval sym a {- ^ zero value -} -> Nat' {- ^ size of the sequence -} -> SeqMap sym a {- ^ sequence to shift -} -> WordValue sym {- ^ shift amount -} -> SEval sym (SeqMap sym a) shiftSeqByWord sym merge reindex zro sz xs idx = wordValAsLit sym idx >>= \case Just j -> shiftOp xs j Nothing -> do idx_segs <- enumerateIndexSegments sym idx barrelShifter sym merge shiftOp sz xs idx_bits idx_segs where idx_bits = wordValueSize sym idx shiftOp vs shft = pure $ indexSeqMap $ \i -> case reindex i shft of Nothing -> zro Just i' -> lookupSeqMap vs i' -- | Compute the size of a word value -- TODO, can we get rid of this? If feels like it should be -- unnecessary. wordValueSize :: Backend sym => sym -> WordValue sym -> Integer wordValueSize sym (WordVal w) = wordLen sym w wordValueSize _ (ThunkWordVal n _) = n wordValueSize _ (BitmapVal n _ _) = n -- | Select an individual bit from a word value indexWordValue :: Backend sym => sym -> WordValue sym -> Integer -> SEval sym (SBit sym) indexWordValue sym (ThunkWordVal _ m) idx = do m' <- m ; indexWordValue sym m' idx indexWordValue sym (WordVal w) idx | 0 <= idx && idx < wordLen sym w = wordBit sym w idx | otherwise = invalidIndex sym idx indexWordValue sym (BitmapVal n _packed xs) idx | 0 <= idx && idx < n = lookupSeqMap xs idx | otherwise = invalidIndex sym idx -- | Produce a new 'WordValue' from the one given by updating the @i@th bit with the -- given bit value. updateWordValue :: Backend sym => sym -> WordValue sym -> Integer -> SEval sym (SBit sym) -> SEval sym (WordValue sym) updateWordValue sym wv0 idx b = loop wv0 where loop (ThunkWordVal sz m) = isReady sym m >>= \case Just w -> loop w Nothing -> delayWordValue sym sz (loop =<< m) loop (WordVal w) | idx < 0 || idx >= wordLen sym w = invalidIndex sym idx | otherwise = isReady sym b >>= \case Just b' -> WordVal <$> wordUpdate sym w idx b' Nothing -> do let bs = unpackBitmap sym w bitmapWordVal sym (wordLen sym w) $ updateSeqMap bs idx b loop (BitmapVal sz packed bs) | 0 <= idx && idx < sz = isReady sym packed >>= \case Just w -> loop (WordVal w) Nothing -> bitmapWordVal sym sz $ updateSeqMap bs idx b | otherwise = invalidIndex sym idx {-# INLINE mergeWord #-} mergeWord :: Backend sym => sym -> SBit sym -> WordValue sym -> WordValue sym -> SEval sym (WordValue sym) mergeWord sym c (ThunkWordVal _ m1) (ThunkWordVal _ m2) = mergeWord' sym c (unwindThunks m1) (unwindThunks m2) mergeWord sym c (ThunkWordVal _ m1) w2 = mergeWord' sym c (unwindThunks m1) (pure w2) mergeWord sym c w1 (ThunkWordVal _ m2) = mergeWord' sym c (pure w1) (unwindThunks m2) mergeWord sym c (WordVal w1) (WordVal w2) = WordVal <$> iteWord sym c w1 w2 mergeWord sym c (BitmapVal n1 packed1 bs1) (WordVal w2) = isReady sym packed1 >>= \case Just w1 -> WordVal <$> iteWord sym c w1 w2 Nothing -> mergeBitmaps sym c n1 bs1 (unpackBitmap sym w2) mergeWord sym c (WordVal w1) (BitmapVal n2 packed2 bs2) = isReady sym packed2 >>= \case Just w2 -> WordVal <$> iteWord sym c w1 w2 Nothing -> mergeBitmaps sym c n2 (unpackBitmap sym w1) bs2 mergeWord sym c (BitmapVal n1 packed1 bs1) (BitmapVal _n2 packed2 bs2) = do r1 <- isReady sym packed1 r2 <- isReady sym packed2 case (r1,r2) of (Just w1, Just w2) -> WordVal <$> iteWord sym c w1 w2 _ -> mergeBitmaps sym c n1 bs1 bs2 mergeBitmaps :: Backend sym => sym -> SBit sym -> Integer -> SeqMap sym (SBit sym) -> SeqMap sym (SBit sym) -> SEval sym (WordValue sym) mergeBitmaps sym c sz bs1 bs2 = do bs <- memoMap sym (Nat sz) (mergeSeqMap sym (iteBit sym) c bs1 bs2) bitmapWordVal sym sz bs {-# INLINE mergeWord' #-} mergeWord' :: Backend sym => sym -> SBit sym -> SEval sym (WordValue sym) -> SEval sym (WordValue sym) -> SEval sym (WordValue sym) mergeWord' sym c x y | Just b <- bitAsLit sym c = if b then x else y | otherwise = mergeEval sym (mergeWord sym) c x y