Now the fun begins.
You should start by installing the llvm-hs packages. This tutorial will
be keeping things "vanilla" by just installing the packages globally
rather than using a .cabal file.
$ cabal new-install --lib llvm-hs llvm-hs-pure llvm-hs-pretty --write-ghc-environment-files=always
The above command should be enough to install them, assuming you already
have LLVM installed correctly. (At the time of writing llvm-hs-pretty
needs to be installed from source for llvm-8.0:
https://github.com/llvm-hs/llvm-hs-pretty)
Like our parsing, our code generation is also monadic (this is a Haskell
tutorial). There are two monads that we can use: IRBuilder and
ModuleBuilder. The former is for LLVM IR instructions and the latter for
function definitions, constants and the like.
The original tutorial puts everything into one module, so we are going
to follow suit here. This makes things a bit hairy since now the repl
needs to take place inside of a ModuleBuilderT IO. In an effort to keep
our code as pure as possible, we've added a hoist function so that we
can keep our codegen code inside ModuleBuilder.
There's also a helper function to grab the most recent definition so
that we can print out similar to in the tutorial. This and hoist have
been tucked away into Util.hs.
So far we only generate code for numbers: But this now paves the way for
the rest of the code generation.
import Text.Read
import Text.ParserCombinators.ReadP hiding ((+++), choice)
import Text.Read
import Text.ParserCombinators.ReadP hiding ((+++), choice)
| Var String
| BinOp BinOp Expr Expr
| Call String [Expr]
| Var String
| BinOp BinOp Expr Expr
| Call String [Expr]
+{-# LANGUAGE OverloadedStrings #-}
+
+import Utils
+import Control.Monad.IO.Class
+import qualified Data.Text.Lazy.IO as Text
+import LLVM.AST.Constant
+import LLVM.AST.Float
+import LLVM.AST.Operand
+import LLVM.AST.Type as Type
+import LLVM.IRBuilder
+import LLVM.Pretty
-import Text.Read
-main = do
- hPutStr stderr "ready> "
- ast <- (readMaybe <$> getLine) :: IO (Maybe AST)
+import Text.Read (readMaybe)
+
+main = buildModuleT "main" repl
+
+repl :: ModuleBuilderT IO ()
+repl = do
+ liftIO $ hPutStr stderr "ready> "
+ ast <- liftIO $ readMaybe <$> getLine
- Just x -> hPrint stderr x
- Nothing -> hPutStrLn stderr "Couldn't parse"
- main
+ Nothing -> liftIO $ hPutStrLn stderr "Couldn't parse"
+ Just x -> do
+ hoist $ buildAST x
+ mostRecentDef >>= liftIO . Text.hPutStrLn stderr . ppll
+ repl
+ where
+
+buildAST :: AST -> ModuleBuilder Operand
+buildAST (TopLevelExpr x) = function "__anon_expr" [] Type.double $
+ const $ buildExpr x >>= ret
+
+buildExpr :: Expr -> IRBuilderT ModuleBuilder Operand
+buildExpr (Num x) = pure $ ConstantOperand (Float (Double x))
--- /dev/null
+{-|
+Shoving away gross stuff into this one module.
+-}
+module Utils where
+
+import Control.Monad.Trans.State
+import Data.Functor.Identity
+import LLVM.AST
+import LLVM.IRBuilder.Module
+import LLVM.IRBuilder.Internal.SnocList
+
+mostRecentDef :: Monad m => ModuleBuilderT m Definition
+mostRecentDef = last . getSnocList . builderDefs <$> liftModuleState get
+
+hoist :: Monad m => ModuleBuilder a -> ModuleBuilderT m a
+hoist m = ModuleBuilderT $ StateT $
+ return . runIdentity . runStateT (unModuleBuilderT m)