1-- SPDX-FileCopyrightText: 2023-2024 University of Bremen2-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>3--4-- SPDX-License-Identifier: MIT AND GPL-3.0-only56-- | This module provides an implementation of a simple byte-addressable memory7-- based on "Data.Array".8module Language.QBE.Simulator.Memory9 ( -- * Type Aliases10 Address,11 Size,12 showAddr,1314 -- * Value Representation15 Storable (toBytes, fromBytes),1617 -- * Memory Representation18 Memory,19 mkMemory,20 memSize,21 loadBytes,22 storeBytes,2324 -- * Memory Address25 toMemAddr,26 addrOverlap,27 alignAddr,28 )29where3031import Data.Array.IO32 ( MArray,33 getBounds,34 newArray,35 newArray_,36 readArray,37 writeArray,38 )39import Data.Bits (complement, (.&.))40import Data.Word (Word64)41import Language.QBE.Types qualified as QBE42import Numeric (showHex)4344-- | Type used to represent an address in memory.45type Address = Word644647-- | Type used to represent the memory's size.48type Size = Word644950-- | Represent an address as a hexadecimal string.51showAddr :: Address -> String52showAddr addr = "0x" ++ showHex addr ""5354------------------------------------------------------------------------5556-- | Type class for types that can be stored in memory. That is, types whose57-- values can be converted to the given byte representation and vice versa.58class Storable valTy byteTy where59 -- | Convert a value type to a list of byte types.60 toBytes :: valTy -> [byteTy]6162 -- | Convert a list of bytes to a value type of 'QBE.LoadType'. Returns63 -- 'Nothing' if the length of the list is incompatible with the given64 -- 'QBE.LoadType'.65 fromBytes :: QBE.LoadType -> [byteTy] -> Maybe valTy6667-- | Memory parameterized over the Array type (e.g. 'Data.Array.IO.IOUArray')68-- and a byte polymorphic representation (e.g. 'Data.Word.Word8').69data Memory a v = Memory70 { memStart :: Address,71 memBytes :: a Address v72 }7374-- | Create a new t'Memory' which starts at the given base address and75-- has a maximum capacity (i.e., can store up to the given amount of bytes).76-- The memory is not initialized, reading an uninitialized values results77-- in an error.78mkMemory :: (MArray t a IO) => Address -> Maybe a -> Size -> IO (Memory t a)79mkMemory startAddr zeroValueMay size = do80 let bounds = (0, size - 1)81 ary <-82 case zeroValueMay of83 Just zeroValue -> newArray bounds zeroValue84 _ -> newArray_ bounds8586 return $ Memory startAddr ary8788-- | Translate global address to a memory-local address. That is, performs89-- address translation relative to the base address of the t'Memory'.90toMemAddr :: Memory t a -> Address -> Address91toMemAddr mem addr = addr - memStart mem9293-- | Returns true if the given addresses, passed in the first and second94-- argument, overlap in the given range (i.e., the given amount of bytes).95addrOverlap :: Address -> Address -> Size -> Bool96addrOverlap addr1 addr2 range =97 addr1 < endAddr addr2 && endAddr addr1 > addr298 where99 endAddr :: Address -> Address100 endAddr a = a + range101102-- | Align an address upwards for the given alignment.103alignAddr :: Address -> Size -> Address104alignAddr addr align = (addr + (align - 1)) .&. complement (align - 1)105106-- | Returns the size of the memory in bytes.107memSize :: (MArray t a IO) => Memory t a -> IO Size108memSize = fmap ((+ 1) . snd) . getBounds . memBytes109110-- | Write the list of bytes to memory at the given address.111storeBytes :: (MArray t a IO) => Memory t a -> Address -> [a] -> IO ()112storeBytes mem addr bytes =113 mapM_ (\(off, val) -> storeByte mem (addr + off) val) $114 zip [0 ..] bytes115 where116 storeByte :: (MArray t a IO) => Memory t a -> Address -> a -> IO ()117 storeByte m a = writeArray (memBytes m) $ toMemAddr mem a118{-# INLINEABLE storeBytes #-}119120-- | Load the given amount of bytes at the given address.121loadBytes :: (MArray t a IO) => Memory t a -> Address -> Size -> IO [a]122loadBytes mem addr byteSize =123 mapM (\off -> loadByte mem (addr + off)) [0 .. byteSize - 1]124 where125 loadByte :: (MArray t a IO) => Memory t a -> Address -> IO a126 loadByte m = readArray (memBytes m) . toMemAddr m127{-# INLINEABLE loadBytes #-}