Code Generation Setup

We start with a new Haskell module Codegen.hs which will hold the pure code generation logic that we’ll use to drive building the llvm-hs AST. For simplicity’s sake we’ll insist that all variables be of a single type, the double type.

  1. double :: Type
  2. double = FloatingPointType 64 IEEE

To start we create a new record type to hold the internal state of our code generator as we walk the AST. We’ll use two records, one for the toplevel module code generation and one for basic blocks inside of function definitions.

  1. type SymbolTable = [(String, Operand)]
  2. data CodegenState
  3. = CodegenState {
  4. currentBlock :: Name -- Name of the active block to append to
  5. , blocks :: Map.Map Name BlockState -- Blocks for function
  6. , symtab :: SymbolTable -- Function scope symbol table
  7. , blockCount :: Int -- Count of basic blocks
  8. , count :: Word -- Count of unnamed instructions
  9. , names :: Names -- Name Supply
  10. } deriving Show
  11. data BlockState
  12. = BlockState {
  13. idx :: Int -- Block index
  14. , stack :: [Named Instruction] -- Stack of instructions
  15. , term :: Maybe (Named Terminator) -- Block terminator
  16. } deriving Show

We’ll hold the state of the code generator inside of Codegen State monad, the Codegen monad contains a map of block names to their BlockState representation.

  1. newtype Codegen a = Codegen { runCodegen :: State CodegenState a }
  2. deriving (Functor, Applicative, Monad, MonadState CodegenState )

At the top level we’ll create a LLVM State monad which will hold all code a for the LLVM module and upon evaluation will emit an llvm-hs Module containing the AST. We’ll append to the list of definitions in the AST.Module field moduleDefinitions.

  1. newtype LLVM a = LLVM (State AST.Module a)
  2. deriving (Functor, Applicative, Monad, MonadState AST.Module )
  3. runLLVM :: AST.Module -> LLVM a -> AST.Module
  4. runLLVM mod (LLVM m) = execState m mod
  5. emptyModule :: String -> AST.Module
  6. emptyModule label = defaultModule { moduleName = label }
  7. addDefn :: Definition -> LLVM ()
  8. addDefn d = do
  9. defs <- gets moduleDefinitions
  10. modify $ \s -> s { moduleDefinitions = defs ++ [d] }

Inside of our module we’ll need to insert our toplevel definitions. For our purposes this will consist entirely of local functions and external function declarations.

  1. define :: Type -> String -> [(Type, Name)] -> [BasicBlock] -> LLVM ()
  2. define retty label argtys body = addDefn $
  3. GlobalDefinition $ functionDefaults {
  4. name = Name label
  5. , parameters = ([Parameter ty nm [] | (ty, nm) <- argtys], False)
  6. , returnType = retty
  7. , basicBlocks = body
  8. }
  9. external :: Type -> String -> [(Type, Name)] -> LLVM ()
  10. external retty label argtys = addDefn $
  11. GlobalDefinition $ functionDefaults {
  12. name = Name label
  13. , linkage = L.External
  14. , parameters = ([Parameter ty nm [] | (ty, nm) <- argtys], False)
  15. , returnType = retty
  16. , basicBlocks = []
  17. }