Implement the while loop

This commit is contained in:
Eugen Wissner 2024-11-24 13:05:11 +01:00
parent 0c9799b887
commit f7b36cb81d
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
15 changed files with 89 additions and 53 deletions

View File

@ -1 +1 @@
3.3.5 3.3.6

2
TODO
View File

@ -14,8 +14,6 @@
- Each temporary variable gets a tn register where n is the variable index. If - Each temporary variable gets a tn register where n is the variable index. If
there more variables the allocation will fail with out of bounds runtime there more variables the allocation will fail with out of bounds runtime
error. Implement spill over. error. Implement spill over.
- The allocator puts temporary and local variables into the same registers,
causing conflicts.
# Language # Language

View File

@ -5,6 +5,7 @@ module Language.Elna.Backend.Allocator
) where ) where
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.Int (Int32)
import Data.Word (Word32) import Data.Word (Word32)
import Data.Vector (Vector) import Data.Vector (Vector)
import Language.Elna.Backend.Intermediate import Language.Elna.Backend.Intermediate
@ -17,11 +18,20 @@ import Language.Elna.Location (Identifier(..))
import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT) import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT)
import Control.Monad.Trans.State (State, runState, modify') import Control.Monad.Trans.State (State, runState, modify')
import GHC.Records (HasField(..)) import GHC.Records (HasField(..))
import Control.Monad.Trans.Class (MonadTrans(lift)) import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad.Trans.Except (ExceptT(..), runExceptT, throwE)
import Data.List ((!?))
data Store r data Store r
= RegisterStore r = RegisterStore r
| StackStore Word32 r | StackStore Int32 r
data AllocationError = AllocationError
deriving Eq
instance Show AllocationError
where
show = const "Ran out of registers during register allocation"
newtype MachineConfiguration r = MachineConfiguration newtype MachineConfiguration r = MachineConfiguration
{ temporaryRegisters :: [r] { temporaryRegisters :: [r]
@ -32,7 +42,7 @@ newtype MachineState = MachineState
} deriving (Eq, Show) } deriving (Eq, Show)
newtype Allocator r a = Allocator newtype Allocator r a = Allocator
{ runAllocator :: ReaderT (MachineConfiguration r) (State MachineState) a { runAllocator :: ExceptT AllocationError (ReaderT (MachineConfiguration r) (State MachineState)) a
} }
instance forall r. Functor (Allocator r) instance forall r. Functor (Allocator r)
@ -52,20 +62,22 @@ allocate
:: forall r :: forall r
. MachineConfiguration r . MachineConfiguration r
-> HashMap Identifier (Vector (Quadruple Variable)) -> HashMap Identifier (Vector (Quadruple Variable))
-> HashMap Identifier (ProcedureQuadruples (Store r)) -> Either AllocationError (HashMap Identifier (ProcedureQuadruples (Store r)))
allocate machineConfiguration = fmap function allocate machineConfiguration = traverse function
where where
function :: Vector (Quadruple Variable) -> ProcedureQuadruples (Store r) run = flip runState (MachineState{ stackSize = 0 })
. flip runReaderT machineConfiguration
. runExceptT
. runAllocator
. mapM quadruple
function :: Vector (Quadruple Variable) -> Either AllocationError (ProcedureQuadruples (Store r))
function quadruples' = function quadruples' =
let (result, lastState) let (result, lastState) = run quadruples'
= flip runState (MachineState{ stackSize = 0 }) in makeResult lastState <$> result
$ flip runReaderT machineConfiguration makeResult MachineState{ stackSize } result = ProcedureQuadruples
$ runAllocator { quadruples = result
$ mapM quadruple quadruples' , stackSize = stackSize
in ProcedureQuadruples }
{ quadruples = result
, stackSize = getField @"stackSize" lastState
}
quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r)) quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r))
quadruple = \case quadruple = \case
@ -131,11 +143,15 @@ operand (VariableOperand variableOperand) =
storeVariable :: Variable -> Allocator r (Store r) storeVariable :: Variable -> Allocator r (Store r)
storeVariable (TempVariable index) = do storeVariable (TempVariable index) = do
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters" temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
pure $ RegisterStore maybe (Allocator $ throwE AllocationError) (pure . RegisterStore)
$ temporaryRegisters' !! fromIntegral index $ temporaryRegisters' !? fromIntegral index
storeVariable (LocalVariable index) = do storeVariable (LocalVariable index) = do
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters" temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
Allocator $ lift $ modify' $ MachineState . (+ 4) . getField @"stackSize" Allocator $ lift $ lift $ modify' $ MachineState . (+ 4) . getField @"stackSize"
pure $ StackStore (index * 4) maybe (Allocator $ throwE AllocationError) (pure . StackStore (fromIntegral (succ index) * (-4)))
$ temporaryRegisters' !! pred (length temporaryRegisters' - fromIntegral index) $ temporaryRegisters' !? pred (length temporaryRegisters' - fromIntegral index)
storeVariable (ParameterVariable index) = do
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
maybe (Allocator $ throwE AllocationError) (pure . StackStore (fromIntegral index * 4))
$ temporaryRegisters' !? fromIntegral index

View File

@ -19,13 +19,14 @@ instance Show Label
where where
show (Label label) = '.' : Text.unpack label show (Label label) = '.' : Text.unpack label
data Variable = TempVariable Word32 | LocalVariable Word32 data Variable = TempVariable Word32 | LocalVariable Word32 | ParameterVariable Word32
deriving Eq deriving Eq
instance Show Variable instance Show Variable
where where
show (LocalVariable variable) = '@' : show variable show (LocalVariable variable) = '@' : show variable
show (TempVariable variable) = '$' : show variable show (TempVariable variable) = '$' : show variable
show (ParameterVariable variable) = '%' : show variable
data Operand v data Operand v
= IntOperand Int32 = IntOperand Int32

View File

@ -71,7 +71,7 @@ data Statement
= EmptyStatement = EmptyStatement
| IfStatement Condition Statement (Maybe Statement) | IfStatement Condition Statement (Maybe Statement)
| AssignmentStatement VariableAccess Expression | AssignmentStatement VariableAccess Expression
-- | WhileStatement Condition Statement | WhileStatement Condition Statement
| CompoundStatement [Statement] | CompoundStatement [Statement]
| CallStatement Identifier [Expression] | CallStatement Identifier [Expression]
deriving Eq deriving Eq
@ -86,8 +86,8 @@ instance Show Statement
] ]
show (AssignmentStatement lhs rhs) = show (AssignmentStatement lhs rhs) =
concat [show lhs, " := ", show rhs, ";"] concat [show lhs, " := ", show rhs, ";"]
{-show (WhileStatement expression statement) = show (WhileStatement expression statement) =
concat ["while (", show expression, ") ", show statement, ";"]-} concat ["while (", show expression, ") ", show statement, ";"]
show (CompoundStatement statements) = show (CompoundStatement statements) =
concat ["{\n", unlines (show <$> statements), " }"] concat ["{\n", unlines (show <$> statements), " }"]
show (CallStatement name parameters) = show name <> "(" show (CallStatement name parameters) = show name <> "("

View File

@ -175,9 +175,9 @@ statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
statement globalTable (AST.AssignmentStatement lvalue rvalue) statement globalTable (AST.AssignmentStatement lvalue rvalue)
= variableAccess globalTable lvalue = variableAccess globalTable lvalue
>> expression globalTable rvalue >> expression globalTable rvalue
--statement globalTable (AST.WhileStatement whileCondition loop) statement globalTable (AST.WhileStatement whileCondition loop)
-- = condition globalTable whileCondition = condition globalTable whileCondition
-- >> statement globalTable loop >> statement globalTable loop
condition :: SymbolTable -> AST.Condition -> NameAnalysis () condition :: SymbolTable -> AST.Condition -> NameAnalysis ()
condition globalTable (AST.EqualCondition lhs rhs) condition globalTable (AST.EqualCondition lhs rhs)

View File

@ -188,7 +188,7 @@ statementP
<|> ifElseP <|> ifElseP
<|> CompoundStatement <$> blockP (many statementP) <|> CompoundStatement <$> blockP (many statementP)
<|> try assignmentP <|> try assignmentP
-- <|> try whileP <|> try whileP
<|> callP <|> callP
<?> "statement" <?> "statement"
where where
@ -200,9 +200,9 @@ statementP
<$> (symbol "if" *> parensP conditionP) <$> (symbol "if" *> parensP conditionP)
<*> statementP <*> statementP
<*> optional (symbol "else" *> statementP) <*> optional (symbol "else" *> statementP)
{-whileP = WhileStatement whileP = WhileStatement
<$> (symbol "while" *> parensP conditionP) <$> (symbol "while" *> parensP conditionP)
<*> statementP -} <*> statementP
assignmentP = AssignmentStatement assignmentP = AssignmentStatement
<$> variableAccessP <$> variableAccessP
<* symbol ":=" <* symbol ":="

View File

@ -101,11 +101,11 @@ statement globalTable = \case
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError lhsType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError lhsType
unless (rhsType == intType) unless (rhsType == intType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError rhsType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError rhsType
{- AST.WhileStatement whileCondition whileStatement -> do AST.WhileStatement whileCondition whileStatement -> do
conditionType <- condition globalTable whileCondition conditionType <- condition globalTable whileCondition
unless (conditionType == booleanType) unless (conditionType == booleanType)
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType $ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
statement globalTable whileStatement -} statement globalTable whileStatement
AST.IfStatement ifCondition ifStatement elseStatement -> do AST.IfStatement ifCondition ifStatement elseStatement -> do
conditionType <- condition globalTable ifCondition conditionType <- condition globalTable ifCondition
unless (conditionType == booleanType) unless (conditionType == booleanType)

View File

@ -71,14 +71,16 @@ declaration
:: SymbolTable :: SymbolTable
-> AST.Declaration -> AST.Declaration
-> Glue (Maybe (AST.Identifier, Vector (Quadruple Variable))) -> Glue (Maybe (AST.Identifier, Vector (Quadruple Variable)))
declaration globalTable (AST.ProcedureDeclaration procedureName _ variableDeclarations statements) declaration globalTable (AST.ProcedureDeclaration procedureName parameters variableDeclarations statements)
= traverse_ registerVariable variableDeclarations = traverseWithIndex registerVariable variableDeclarations
>> traverseWithIndex registerParameter parameters
>> nameQuadruplesTuple <$> traverse (statement globalTable) statements >> nameQuadruplesTuple <$> traverse (statement globalTable) statements
where where
registerVariable (AST.VariableDeclaration identifier _) = do traverseWithIndex f = traverse_ (uncurry f) . zip [0..]
currentCounter <- fmap (fromIntegral . HashMap.size) registerParameter index (AST.Parameter identifier _ _) =
$ Glue $ gets $ getField @"localMap" Glue $ modify' $ modifier identifier $ ParameterVariable index
Glue $ modify' $ modifier identifier $ LocalVariable currentCounter registerVariable index (AST.VariableDeclaration identifier _) =
Glue $ modify' $ modifier identifier $ LocalVariable index
modifier identifier currentCounter generator = generator modifier identifier currentCounter generator = generator
{ localMap = HashMap.insert identifier currentCounter { localMap = HashMap.insert identifier currentCounter
$ getField @"localMap" generator $ getField @"localMap" generator
@ -131,7 +133,7 @@ statement localTable (AST.AssignmentStatement variableAccess' assignee) = do
. AssignQuadruple rhsOperand . AssignQuadruple rhsOperand
<$> lookupLocal identifier <$> lookupLocal identifier
pure $ rhsStatements <> lhsStatements pure $ rhsStatements <> lhsStatements
{- statement localTable (AST.WhileStatement whileCondition whileStatement) = do statement localTable (AST.WhileStatement whileCondition whileStatement) = do
(conditionStatements, jumpConstructor) <- condition localTable whileCondition (conditionStatements, jumpConstructor) <- condition localTable whileCondition
startLabel <- createLabel startLabel <- createLabel
endLabel <- createLabel endLabel <- createLabel
@ -141,7 +143,7 @@ statement localTable (AST.AssignmentStatement variableAccess' assignee) = do
<> conditionStatements <> conditionStatements
<> Vector.fromList [jumpConstructor startLabel, GoToQuadruple endLabel, LabelQuadruple startLabel] <> Vector.fromList [jumpConstructor startLabel, GoToQuadruple endLabel, LabelQuadruple startLabel]
<> whileStatements <> whileStatements
<> Vector.fromList [GoToQuadruple conditionLabel, LabelQuadruple endLabel] -} <> Vector.fromList [GoToQuadruple conditionLabel, LabelQuadruple endLabel]
createTemporary :: Glue Variable createTemporary :: Glue Variable
createTemporary = do createTemporary = do

View File

@ -98,7 +98,7 @@ generateRiscV = flip evalState 0
quadruple :: Word32 -> RiscVQuadruple -> RiscVGenerator (Vector Statement) quadruple :: Word32 -> RiscVQuadruple -> RiscVGenerator (Vector Statement)
quadruple stackSize StartQuadruple = quadruple stackSize StartQuadruple =
let totalStackSize = stackSize + 4 let totalStackSize = stackSize + 8
in pure $ Vector.fromList in pure $ Vector.fromList
[ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (negate totalStackSize)) [ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (negate totalStackSize))
, Instruction (RiscV.BaseInstruction RiscV.Store $ RiscV.S 0 RiscV.SW RiscV.SP RiscV.S0) , Instruction (RiscV.BaseInstruction RiscV.Store $ RiscV.S 0 RiscV.SW RiscV.SP RiscV.S0)
@ -106,7 +106,7 @@ quadruple stackSize StartQuadruple =
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.S0 RiscV.ADDI RiscV.SP totalStackSize) , Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.S0 RiscV.ADDI RiscV.SP totalStackSize)
] ]
quadruple stackSize StopQuadruple = quadruple stackSize StopQuadruple =
let totalStackSize = stackSize + 4 let totalStackSize = stackSize + 8
in pure $ Vector.fromList in pure $ Vector.fromList
[ Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.S0 RiscV.LW RiscV.SP 0) [ Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.S0 RiscV.LW RiscV.SP 0)
, Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.RA RiscV.LW RiscV.SP 4) , Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.RA RiscV.LW RiscV.SP 4)
@ -438,7 +438,7 @@ loadFromStore (RegisterStore register) = (register, mempty)
loadFromStore (StackStore offset register) = loadFromStore (StackStore offset register) =
let loadInstruction = Instruction let loadInstruction = Instruction
$ RiscV.BaseInstruction RiscV.Load $ RiscV.BaseInstruction RiscV.Load
$ RiscV.I register RiscV.LW RiscV.S0 offset $ RiscV.I register RiscV.LW RiscV.S0 (fromIntegral offset)
in (register, Vector.singleton loadInstruction) in (register, Vector.singleton loadInstruction)
storeToStore :: RiscVStore -> (RiscV.XRegister, Vector Statement) storeToStore :: RiscVStore -> (RiscV.XRegister, Vector Statement)
@ -446,5 +446,5 @@ storeToStore (RegisterStore register) = (register, mempty)
storeToStore (StackStore offset register) = storeToStore (StackStore offset register) =
let storeInstruction = Instruction let storeInstruction = Instruction
$ RiscV.BaseInstruction RiscV.Store $ RiscV.BaseInstruction RiscV.Store
$ RiscV.S offset RiscV.SW RiscV.S0 register $ RiscV.S (fromIntegral offset) RiscV.SW RiscV.S0 register
in (register, Vector.singleton storeInstruction) in (register, Vector.singleton storeInstruction)

View File

@ -25,6 +25,7 @@ import Control.Exception (IOException, catch)
-- 3 - Parse error. -- 3 - Parse error.
-- 4 - Name analysis error. -- 4 - Name analysis error.
-- 5 - Type error. -- 5 - Type error.
-- 6 - Register allocation error.
main :: IO () main :: IO ()
main = execParser commandLine >>= withCommandLine main = execParser commandLine >>= withCommandLine
@ -45,9 +46,9 @@ main = execParser commandLine >>= withCommandLine
| Just typeError <- typeAnalysis symbolTable program = | Just typeError <- typeAnalysis symbolTable program =
printAndExit 5 typeError printAndExit 5 typeError
| otherwise = | otherwise =
let instructions = generateRiscV let makeObject = elfObject output . riscv32Elf . generateRiscV
$ allocate riscVConfiguration in either (printAndExit 6) makeObject
$ glue symbolTable program $ allocate riscVConfiguration
in elfObject output $ riscv32Elf instructions $ glue symbolTable program
printAndExit :: Show b => forall a. Int -> b -> IO a printAndExit :: Show b => forall a. Int -> b -> IO a
printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode) printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode)

View File

@ -0,0 +1 @@
2

View File

@ -0,0 +1 @@
14

View File

@ -0,0 +1,9 @@
proc main() {
var x: int;
x := 0;
while (x < 2) {
x := x + 1;
}
printi(x);
}

View File

@ -0,0 +1,7 @@
proc print2(a: int) {
printi(a);
}
proc main() {
print2(14);
}