Generate code for if statements
authorLuke Lau <luke_lau@icloud.com>
Wed, 5 Jun 2019 21:31:04 +0000 (22:31 +0100)
committerLuke Lau <luke_lau@icloud.com>
Fri, 8 Nov 2019 15:07:39 +0000 (15:07 +0000)
Hopefully you found this addition straightforward and enjoyable, now
that we have all the infrastructure in place. Let's walk through this
step by step.

We first start off our loop by creating a basic block called "if". A
basic block is a chunk of code that has a single entry and exit point,
and in this block we're putting in the condition expression.
Since everything in Kaleidoscope is a double (yuck), we need to compare
it to zero to get a boolean that we can actually branch with.

After branching we create two other basic blocks labelled "then" and
"else", containing the then and else expressions of the branch
respectively. They both branch back to the final basic block, "ifcont".

Now since LLVM IR is in SSA (single static assignment) form, the then
and else branches both write to separate variables: We can't write to
the same variable twice. In order to actually differentiate between the
then expression and the else expression, we use a phi node in the merge
block. Depending on what basic block it arrived from, it will use either
one of two operands that we've passed to it.

In this case, we use it to return the then expression if the then branch
was taken, or the else expression if the else branch was taken. Phi
nodes are very common in LLVM IR, and open up lots of optimisations by
allowing us to stay in SSA form.

You might have also noticed that unlike the C++ tutorial, we didn't need
to rewind our builder back and generate the "mergeB" block first. In
fact, we actually end up referencing mergeB before it is even declared!
How can this be possible? Through recursive do! The MonadFix instance on
IRBuilder allow us to exploit laziness and use mdo to refer to variables
that haven't yet been evaluated.

Main.hs

diff --git a/Main.hs b/Main.hs
index 468573d865ab4fccb350bc78bf446d4e9d400563..749f77188172479f341a8018650af7549c2d0c02 100644 (file)
--- a/Main.hs
+++ b/Main.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecursiveDo #-}
 
 import AST as K -- K for Kaleidoscope
 import Utils
@@ -141,3 +142,24 @@ buildExpr (Call callee params) = do
       ptrTyp = Type.PointerType typ (AddrSpace 0)
       ref = GlobalReference ptrTyp nam
   call (ConstantOperand ref) (zip paramOps (repeat []))
+
+buildExpr (If cond thenE elseE) = mdo
+  _ifB <- block `named` "if"
+
+  -- since everything is a double, false == 0
+  let zero = ConstantOperand (Float (Double 0))
+  condV <- buildExpr cond
+  cmp <- fcmp ONE zero condV `named` "cmp"
+
+  condBr cmp thenB elseB
+
+  thenB <- block `named` "then"
+  thenOp <- buildExpr thenE
+  br mergeB
+
+  elseB <- block `named` "else"
+  elseOp <- buildExpr elseE
+  br mergeB
+
+  mergeB <- block `named` "ifcont"
+  phi [(thenOp, thenB), (elseOp, elseB)]