2024-12-11 21:44:32 +01:00
|
|
|
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
|
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
|
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
|
|
|
|
2024-10-02 22:56:15 +02:00
|
|
|
module Language.Elna.Backend.Allocator
|
2024-10-01 00:02:19 +02:00
|
|
|
( MachineConfiguration(..)
|
|
|
|
, Store(..)
|
|
|
|
, allocate
|
2024-09-27 00:22:44 +02:00
|
|
|
) where
|
2024-10-01 00:02:19 +02:00
|
|
|
|
|
|
|
import Data.HashMap.Strict (HashMap)
|
2024-12-04 16:11:06 +01:00
|
|
|
import qualified Data.HashMap.Strict as HashMap
|
2024-11-24 13:05:11 +01:00
|
|
|
import Data.Int (Int32)
|
2024-10-01 00:02:19 +02:00
|
|
|
import Data.Vector (Vector)
|
2024-11-14 19:55:30 +01:00
|
|
|
import Language.Elna.Backend.Intermediate
|
|
|
|
( ProcedureQuadruples(..)
|
|
|
|
, Operand(..)
|
|
|
|
, Quadruple(..)
|
|
|
|
, Variable(..)
|
|
|
|
)
|
2024-10-01 00:02:19 +02:00
|
|
|
import Language.Elna.Location (Identifier(..))
|
2024-11-16 09:23:40 +01:00
|
|
|
import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT)
|
2024-12-04 16:11:06 +01:00
|
|
|
import Control.Monad.Trans.State (State, runState)
|
2024-11-16 09:23:40 +01:00
|
|
|
import GHC.Records (HasField(..))
|
2024-11-24 13:05:11 +01:00
|
|
|
import Control.Monad.Trans.Class (MonadTrans(..))
|
|
|
|
import Control.Monad.Trans.Except (ExceptT(..), runExceptT, throwE)
|
|
|
|
import Data.List ((!?))
|
2024-12-04 16:11:06 +01:00
|
|
|
import Language.Elna.Frontend.SymbolTable (Info(..), SymbolTable)
|
|
|
|
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
|
2024-10-01 00:02:19 +02:00
|
|
|
|
2024-11-14 19:55:30 +01:00
|
|
|
data Store r
|
|
|
|
= RegisterStore r
|
2024-11-24 13:05:11 +01:00
|
|
|
| StackStore Int32 r
|
|
|
|
|
2024-12-11 21:44:32 +01:00
|
|
|
data AllocationError
|
|
|
|
= OutOfRegistersError
|
|
|
|
| UnexpectedProcedureInfoError Info
|
|
|
|
| UndefinedSymbolError Identifier
|
2024-11-24 13:05:11 +01:00
|
|
|
deriving Eq
|
|
|
|
|
|
|
|
instance Show AllocationError
|
|
|
|
where
|
2024-12-11 21:44:32 +01:00
|
|
|
show OutOfRegistersError = "Ran out of registers during register allocation"
|
|
|
|
show (UnexpectedProcedureInfoError info) =
|
|
|
|
"Expected to encounter a procedure, got: " <> show info
|
|
|
|
show (UndefinedSymbolError identifier) =
|
|
|
|
concat ["Symbol \"", show identifier, "\" is not defined"]
|
2024-10-01 00:02:19 +02:00
|
|
|
|
|
|
|
newtype MachineConfiguration r = MachineConfiguration
|
2024-10-06 18:07:57 +02:00
|
|
|
{ temporaryRegisters :: [r]
|
2024-10-01 00:02:19 +02:00
|
|
|
}
|
|
|
|
|
2024-11-20 17:38:03 +01:00
|
|
|
newtype MachineState = MachineState
|
2024-12-04 16:11:06 +01:00
|
|
|
{ symbolTable :: SymbolTable
|
2024-11-20 17:38:03 +01:00
|
|
|
} deriving (Eq, Show)
|
|
|
|
|
2024-11-16 09:23:40 +01:00
|
|
|
newtype Allocator r a = Allocator
|
2024-11-24 13:05:11 +01:00
|
|
|
{ runAllocator :: ExceptT AllocationError (ReaderT (MachineConfiguration r) (State MachineState)) a
|
2024-11-16 09:23:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
instance forall r. Functor (Allocator r)
|
|
|
|
where
|
|
|
|
fmap f = Allocator . fmap f . runAllocator
|
|
|
|
|
|
|
|
instance forall r. Applicative (Allocator r)
|
|
|
|
where
|
|
|
|
pure = Allocator . pure
|
|
|
|
(Allocator x) <*> (Allocator y) = Allocator $ x <*> y
|
|
|
|
|
|
|
|
instance forall r. Monad (Allocator r)
|
|
|
|
where
|
|
|
|
(Allocator allocator) >>= f = Allocator $ allocator >>= (runAllocator . f)
|
|
|
|
|
2024-10-01 00:02:19 +02:00
|
|
|
allocate
|
|
|
|
:: forall r
|
|
|
|
. MachineConfiguration r
|
2024-12-04 16:11:06 +01:00
|
|
|
-> SymbolTable
|
2024-10-01 00:02:19 +02:00
|
|
|
-> HashMap Identifier (Vector (Quadruple Variable))
|
2024-11-24 13:05:11 +01:00
|
|
|
-> Either AllocationError (HashMap Identifier (ProcedureQuadruples (Store r)))
|
2024-12-04 16:11:06 +01:00
|
|
|
allocate machineConfiguration globalTable = HashMap.traverseWithKey function
|
2024-10-01 00:02:19 +02:00
|
|
|
where
|
2024-12-04 16:11:06 +01:00
|
|
|
run localTable = flip runState (MachineState{ symbolTable = localTable })
|
2024-11-24 13:05:11 +01:00
|
|
|
. flip runReaderT machineConfiguration
|
|
|
|
. runExceptT
|
|
|
|
. runAllocator
|
|
|
|
. mapM quadruple
|
2024-12-04 16:11:06 +01:00
|
|
|
function :: Identifier -> Vector (Quadruple Variable) -> Either AllocationError (ProcedureQuadruples (Store r))
|
2024-12-11 21:44:32 +01:00
|
|
|
function procedureName quadruples' =
|
|
|
|
case SymbolTable.lookup procedureName globalTable of
|
|
|
|
Just (ProcedureInfo localTable _) ->
|
|
|
|
let (result, lastState) = run localTable quadruples'
|
|
|
|
in makeResult lastState <$> result
|
|
|
|
Just anotherInfo -> Left $ UnexpectedProcedureInfoError anotherInfo
|
|
|
|
Nothing -> Left $ UndefinedSymbolError procedureName
|
|
|
|
|
2024-12-04 16:11:06 +01:00
|
|
|
makeResult MachineState{ symbolTable } result = ProcedureQuadruples
|
2024-11-24 13:05:11 +01:00
|
|
|
{ quadruples = result
|
2024-12-04 16:11:06 +01:00
|
|
|
, stackSize = fromIntegral $ SymbolTable.size symbolTable * 4
|
2024-11-24 13:05:11 +01:00
|
|
|
}
|
2024-11-16 09:23:40 +01:00
|
|
|
|
|
|
|
quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r))
|
|
|
|
quadruple = \case
|
|
|
|
StartQuadruple -> pure StartQuadruple
|
|
|
|
StopQuadruple -> pure StopQuadruple
|
2024-12-04 16:11:06 +01:00
|
|
|
ParameterQuadruple operand1 -> ParameterQuadruple
|
|
|
|
<$> operand operand1
|
2024-11-16 09:23:40 +01:00
|
|
|
CallQuadruple name count -> pure $ CallQuadruple name count
|
2024-12-04 16:11:06 +01:00
|
|
|
AddQuadruple operand1 operand2 variable -> AddQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> storeVariable variable
|
|
|
|
SubtractionQuadruple operand1 operand2 variable -> SubtractionQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> storeVariable variable
|
|
|
|
NegationQuadruple operand1 variable -> NegationQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> storeVariable variable
|
|
|
|
ProductQuadruple operand1 operand2 variable -> ProductQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> storeVariable variable
|
|
|
|
DivisionQuadruple operand1 operand2 variable -> DivisionQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> storeVariable variable
|
2024-11-16 09:23:40 +01:00
|
|
|
LabelQuadruple label -> pure $ LabelQuadruple label
|
|
|
|
GoToQuadruple label -> pure $ GoToQuadruple label
|
2024-12-04 16:11:06 +01:00
|
|
|
EqualQuadruple operand1 operand2 goToLabel -> EqualQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> pure goToLabel
|
|
|
|
NonEqualQuadruple operand1 operand2 goToLabel -> NonEqualQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> pure goToLabel
|
|
|
|
LessQuadruple operand1 operand2 goToLabel -> LessQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> pure goToLabel
|
2024-11-16 09:23:40 +01:00
|
|
|
GreaterQuadruple operand1 operand2 goToLabel -> do
|
|
|
|
operand1' <- operand operand1
|
|
|
|
operand2' <- operand operand2
|
|
|
|
pure $ GreaterQuadruple operand1' operand2' goToLabel
|
2024-12-04 16:11:06 +01:00
|
|
|
LessOrEqualQuadruple operand1 operand2 goToLabel -> LessOrEqualQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> pure goToLabel
|
|
|
|
GreaterOrEqualQuadruple operand1 operand2 goToLabel -> GreaterOrEqualQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> pure goToLabel
|
|
|
|
AssignQuadruple operand1 variable -> AssignQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> storeVariable variable
|
|
|
|
ArrayAssignQuadruple operand1 operand2 variable -> ArrayAssignQuadruple
|
|
|
|
<$> operand operand1
|
|
|
|
<*> operand operand2
|
|
|
|
<*> storeVariable variable
|
|
|
|
ArrayQuadruple variable1 operand1 variable2 -> ArrayQuadruple
|
|
|
|
<$> storeVariable variable1
|
|
|
|
<*> operand operand1
|
|
|
|
<*> storeVariable variable2
|
2024-11-16 09:23:40 +01:00
|
|
|
|
|
|
|
operand :: Operand Variable -> Allocator r (Operand (Store r))
|
2024-12-04 16:11:06 +01:00
|
|
|
operand (IntOperand literalOperand) = pure $ IntOperand literalOperand
|
2024-11-16 09:23:40 +01:00
|
|
|
operand (VariableOperand variableOperand) =
|
|
|
|
VariableOperand <$> storeVariable variableOperand
|
|
|
|
|
|
|
|
storeVariable :: Variable -> Allocator r (Store r)
|
|
|
|
storeVariable (TempVariable index) = do
|
2024-11-24 13:05:11 +01:00
|
|
|
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
2024-12-11 21:44:32 +01:00
|
|
|
maybe (Allocator $ throwE OutOfRegistersError) (pure . RegisterStore)
|
2024-11-24 13:05:11 +01:00
|
|
|
$ temporaryRegisters' !? fromIntegral index
|
2024-11-16 09:23:40 +01:00
|
|
|
storeVariable (LocalVariable index) = do
|
2024-11-24 13:05:11 +01:00
|
|
|
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
2024-12-11 21:44:32 +01:00
|
|
|
maybe (Allocator $ throwE OutOfRegistersError) (pure . StackStore (fromIntegral (succ index) * (-4)))
|
2024-11-24 13:05:11 +01:00
|
|
|
$ temporaryRegisters' !? pred (length temporaryRegisters' - fromIntegral index)
|
|
|
|
storeVariable (ParameterVariable index) = do
|
|
|
|
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
2024-12-11 21:44:32 +01:00
|
|
|
maybe (Allocator $ throwE OutOfRegistersError) (pure . StackStore (fromIntegral index * 4))
|
2024-11-24 13:05:11 +01:00
|
|
|
$ temporaryRegisters' !? fromIntegral index
|