fortran-vars-0.1.0: src/Language/Fortran/Vars/Memory.hs
{-# LANGUAGE TupleSections #-}
module Language.Fortran.Vars.Memory
( allocateMemoryBlocks
, processCommon
)
where
import Language.Fortran.Extras
( allPUS )
import Data.Data ( Data )
import Data.List ( foldl' )
import Data.Maybe ( fromMaybe )
import qualified Data.Map as M
import Language.Fortran.Analysis ( Analysis
, srcName
)
import Language.Fortran.AST ( AList(..)
, aStrip
, CommonGroup(..)
, Expression(..)
, Name
, ProgramUnit
, Statement(..)
)
import Language.Fortran.Vars.MemoryLocation
( getStartLocation )
import Language.Fortran.Vars.Types
( SymbolTableEntry(..)
, MemoryBlock(..)
, ProgramUnitModel
, SymbolTable
, StorageClass(..)
, StorageTable
, Type(..)
, SemType(..)
)
import Language.Fortran.Vars.Kind
( getTypeKind )
import Language.Fortran.Vars.Union
( union )
-- | Given a 'SymbolTable' and an 'Expression', return the size of
-- the variable represented by the expression
getSize :: Data a => SymbolTable -> Expression (Analysis a) -> Int
getSize symTable expr =
let symbol = case expr of
ExpValue{} -> srcName expr
ExpFunctionCall _ _ e@ExpValue{} _ -> srcName e
ExpSubscript _ _ e@ExpValue{} _ -> srcName e
_ -> error "Unsupported expression"
Just entity = M.lookup symbol symTable
in case entity of
SVariable (TArray ty dims) _ ->
fromMaybe (error "Can't calculate size of dynamic array")
$ sizeOfStaticArray
<$> getTypeKind ty
<*> dims
SVariable ty _ ->
fromMaybe (error "Can't get size of dynamic variable")
$ getTypeKind ty
_ -> error (symbol ++ " is not a VariableEntry.")
-- | Given a static array's 'kind' and 'dimension', calculate its size
sizeOfStaticArray :: Int -> [(Int, Int)] -> Int
sizeOfStaticArray kind' dimension' =
let arraySize = foldl (\acc (l, h) -> acc * (h - l + 1)) 1 dimension'
in kind' * arraySize
-- | Given a 'SymbolTable', generate a 'StorageTable' for the 'SymbolTable' where
-- each symbol has been assinged to a 'MemoryBlock' within the 'StorageTable' so
-- long as it is not constant
allocateMemoryBlocks :: SymbolTable -> StorageTable
allocateMemoryBlocks = M.foldlWithKey f M.empty
where
f :: StorageTable -> Name -> SymbolTableEntry -> StorageTable
f storageTable symbol entry = case entry of
SVariable ty _ ->
let size = case ty of
TArray ty' dims -> sizeOfStaticArray <$> getTypeKind ty' <*> dims
_ -> getTypeKind ty
block = MemoryBlock
{ blockSize = size
, storageClass = case size of
Nothing -> Automatic
_ -> Unspecified
, variables = [symbol]
}
in M.insert symbol block storageTable
_ -> storageTable
-- | Given a 'ProgramUnit' and a 'ProgramUnitModel', resolve any commonly defined global
-- variables in the 'ProgramUnit's to be the same in both memory and within the 'SymbolTable'
processCommon
:: Data a => ProgramUnit (Analysis a) -> ProgramUnitModel -> ProgramUnitModel
processCommon pu puModel =
let commonGrps =
[ commGrps | (StCommon _ _ (AList _ _ commGrps)) <- allPUS pu ]
mergeCommonVariables mapping (CommonGroup _ _ commName varList) =
let commonName = case commName of
Just e -> "/" ++ srcName e ++ "/"
Nothing -> "*blank_common*"
vars = aStrip varList
precedingVars = fromMaybe [] (M.lookup commonName mapping)
in M.insert commonName (precedingVars ++ vars) mapping
commons = foldl' mergeCommonVariables M.empty (concat commonGrps)
processComm commonName varExps (symTable, mbs) =
let varLocations = map (getStartLocation symTable) varExps
varSizes = map (getSize symTable) varExps
varAccumSizes = scanl1 (+) varSizes
commBlockLocations = map (commonName, ) (0 : varAccumSizes)
mbs' = case M.lookup commonName mbs of
Just _ -> mbs
Nothing ->
let newBlock = MemoryBlock { blockSize = Just $ sum varSizes
, storageClass = Common
, variables = []
}
in M.insert commonName newBlock mbs
f model (l1, l2) = let (model', _) = union model l1 l2 in model'
in foldl' f (symTable, mbs') (zip commBlockLocations varLocations)
in M.foldrWithKey processComm puModel commons