This expression defines a new variable ( in this case) which iterates from a starting value, while the condition (i < n in this case) is true, incrementing by an optional step value (1.0 in this case). While the loop is true, it executes its body expression. Because we don’t have anything better to return, we’ll just define the loop as always returning 0.0. In the future when we have mutable variables, it will get more useful.

    To get started, we again extend our lexer with new reserved names “for” and “in”.

    1. lexer :: Tok.TokenParser ()
    2. lexer = Tok.makeTokenParser style
    3. where
    4. ops = ["+","*","-","/",";",",","<"]
    5. names = ["def","extern","if","then","else","in","for"]
    6. style = emptyDef {
    7. Tok.commentLine = "#"
    8. , Tok.reservedOpNames = ops
    9. , Tok.reservedNames = names
    10. }

    As before, let’s talk about the changes that we need to Kaleidoscope to support this. The AST node is just as simple. It basically boils down to capturing the variable name and the constituent expressions in the node.

    1. data Expr
    2. ...
    3. | For Name Expr Expr Expr Expr
    4. deriving (Eq, Ord, Show)

    The parser code captures a named value for the iterator variable and the four expressions objects for the parameters of the loop parameters.

    1. for :: Parser Expr
    2. for = do
    3. reserved "for"
    4. var <- identifier
    5. reservedOp "="
    6. start <- expr
    7. reservedOp ","
    8. cond <- expr
    9. reservedOp ","
    10. step <- expr
    11. reserved "in"
    12. body <- expr
    13. return $ For var start cond step body

    The code to generate this is only slightly more complicated than the above “if” statement.

    1. cgen (S.For ivar start cond step body) = do
    2. forexit <- addBlock "for.exit"
    3. -- %entry
    4. ------------------
    5. i <- alloca double
    6. istart <- cgen start -- Generate loop variable initial value
    7. stepval <- cgen step -- Generate loop variable step
    8. store i istart -- Store the loop variable initial value
    9. assign ivar i -- Assign loop variable to the variable name
    10. br forloop -- Branch to the loop body block
    11. -- for.loop
    12. ------------------
    13. setBlock forloop
    14. cgen body -- Generate the loop body
    15. ival <- load i -- Load the current loop iteration
    16. inext <- fadd ival stepval -- Increment loop variable
    17. store i inext
    18. cond <- cgen cond -- Generate the loop condition
    19. test <- fcmp FP.ONE false cond -- Test if the loop condition is True ( 1.0 )
    20. cbr test forloop forexit -- Generate the loop condition

    The first step is to set up the LLVM basic block for the start of the loop body. In the case above, the whole loop body is one block, but remember that the generating code for the body of the loop could consist of multiple blocks (e.g. if it contains an if/then/else or a for/in expression).

    1. forloop <- addBlock "for.loop"
    2. forexit <- addBlock "for.exit"

    Next we allocate the iteration variable and generate the code for the constant initial value and step.

    1. i <- alloca double
    2. istart <- cgen start -- Generate loop variable initial value
    3. stepval <- cgen step -- Generate loop variable step

    Now that the “preheader” for the loop is set up, we switch to emitting code for the loop body.

    1. setBlock forloop
    2. cgen body -- Generate the loop body

    The body will contain the iteration variable scoped with its code generation. After loading its current state we increment it by the step value and store the value.

    1. ival <- load i -- Load the current loop iteration

    Finally, we evaluate the exit test of the loop, and conditionally either branch back to the same block or exit the loop.

    1. cond <- cgen cond -- Generate the loop condition
    2. test <- fcmp FP.ONE false cond -- Test if the loop condition is True ( 1.0 )
    3. cbr test forloop forexit -- Generate the loop condition

    Finally, code generation of the for loop always returns 0.0. Also note that the loop variable remains in scope even after the function exits.

    1. printstar: # @printstar
    2. .cfi_startproc
    3. # BB#0: # %entry
    4. subq $24, %rsp
    5. .Ltmp1:
    6. .cfi_def_cfa_offset 32
    7. vmovsd %xmm0, 8(%rsp) # 8-byte Spill
    8. vmovsd .LCPI0_0(%rip), %xmm0
    9. vmovapd %xmm0, %xmm1
    10. .align 16, 0x90
    11. .LBB0_1: # %loop
    12. # =>This Inner Loop Header: Depth=1
    13. vmovsd %xmm1, 16(%rsp) # 8-byte Spill
    14. vmovsd .LCPI0_1(%rip), %xmm0
    15. callq putchard
    16. vmovsd 16(%rsp), %xmm1 # 8-byte Reload
    17. vucomisd 8(%rsp), %xmm1 # 8-byte Folded Reload
    18. sbbl %eax, %eax
    19. andl $1, %eax
    20. vcvtsi2sd %eax, %xmm0, %xmm0
    21. vaddsd .LCPI0_0(%rip), %xmm1, %xmm1
    22. vucomisd .LCPI0_2, %xmm0
    23. jne .LBB0_1
    24. # BB#2: # %afterloop
    25. vxorpd %xmm0, %xmm0, %xmm0
    26. addq $24, %rsp