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
there more variables the allocation will fail with out of bounds runtime
error. Implement spill over.
- The allocator puts temporary and local variables into the same registers,
causing conflicts.
# Language

View File

@ -5,6 +5,7 @@ module Language.Elna.Backend.Allocator
) where
import Data.HashMap.Strict (HashMap)
import Data.Int (Int32)
import Data.Word (Word32)
import Data.Vector (Vector)
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.State (State, runState, modify')
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
= 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
{ temporaryRegisters :: [r]
@ -32,7 +42,7 @@ newtype MachineState = MachineState
} deriving (Eq, Show)
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)
@ -52,20 +62,22 @@ allocate
:: forall r
. MachineConfiguration r
-> HashMap Identifier (Vector (Quadruple Variable))
-> HashMap Identifier (ProcedureQuadruples (Store r))
allocate machineConfiguration = fmap function
-> Either AllocationError (HashMap Identifier (ProcedureQuadruples (Store r)))
allocate machineConfiguration = traverse function
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' =
let (result, lastState)
= flip runState (MachineState{ stackSize = 0 })
$ flip runReaderT machineConfiguration
$ runAllocator
$ mapM quadruple quadruples'
in ProcedureQuadruples
{ quadruples = result
, stackSize = getField @"stackSize" lastState
}
let (result, lastState) = run quadruples'
in makeResult lastState <$> result
makeResult MachineState{ stackSize } result = ProcedureQuadruples
{ quadruples = result
, stackSize = stackSize
}
quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r))
quadruple = \case
@ -131,11 +143,15 @@ operand (VariableOperand variableOperand) =
storeVariable :: Variable -> Allocator r (Store r)
storeVariable (TempVariable index) = do
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters"
pure $ RegisterStore
$ temporaryRegisters' !! fromIntegral index
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
maybe (Allocator $ throwE AllocationError) (pure . RegisterStore)
$ temporaryRegisters' !? fromIntegral index
storeVariable (LocalVariable index) = do
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters"
Allocator $ lift $ modify' $ MachineState . (+ 4) . getField @"stackSize"
pure $ StackStore (index * 4)
$ temporaryRegisters' !! pred (length temporaryRegisters' - fromIntegral index)
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
Allocator $ lift $ lift $ modify' $ MachineState . (+ 4) . getField @"stackSize"
maybe (Allocator $ throwE AllocationError) (pure . StackStore (fromIntegral (succ index) * (-4)))
$ 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
show (Label label) = '.' : Text.unpack label
data Variable = TempVariable Word32 | LocalVariable Word32
data Variable = TempVariable Word32 | LocalVariable Word32 | ParameterVariable Word32
deriving Eq
instance Show Variable
where
show (LocalVariable variable) = '@' : show variable
show (TempVariable variable) = '$' : show variable
show (ParameterVariable variable) = '%' : show variable
data Operand v
= IntOperand Int32

View File

@ -71,7 +71,7 @@ data Statement
= EmptyStatement
| IfStatement Condition Statement (Maybe Statement)
| AssignmentStatement VariableAccess Expression
-- | WhileStatement Condition Statement
| WhileStatement Condition Statement
| CompoundStatement [Statement]
| CallStatement Identifier [Expression]
deriving Eq
@ -86,8 +86,8 @@ instance Show Statement
]
show (AssignmentStatement lhs rhs) =
concat [show lhs, " := ", show rhs, ";"]
{-show (WhileStatement expression statement) =
concat ["while (", show expression, ") ", show statement, ";"]-}
show (WhileStatement expression statement) =
concat ["while (", show expression, ") ", show statement, ";"]
show (CompoundStatement statements) =
concat ["{\n", unlines (show <$> statements), " }"]
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)
= variableAccess globalTable lvalue
>> expression globalTable rvalue
--statement globalTable (AST.WhileStatement whileCondition loop)
-- = condition globalTable whileCondition
-- >> statement globalTable loop
statement globalTable (AST.WhileStatement whileCondition loop)
= condition globalTable whileCondition
>> statement globalTable loop
condition :: SymbolTable -> AST.Condition -> NameAnalysis ()
condition globalTable (AST.EqualCondition lhs rhs)

View File

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

View File

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

View File

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

View File

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

View File

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