packages feed

rail-compiler-editor-0.3.0.0: src/RailCompiler/IntermediateCode.hs

{- |
Module      :  IntermediateCode.hs
Description :  Intermediate code generation
Maintainer  :  Philipp Borgers, Tilman Blumenbach, Lyudmila Vaseva, Sascha Zinke,
               Maximilian Claus, Michal Ajchman, Nicolas Lehmann, Tudor Soroceanu
License     :  MIT
Stability   :  unstable

IntemediateCode.hs takes the output from the SemanticalAnalysis module
(which is a list of paths) and generates LLVM IR code.
It turns every path of the form (PathID, [Lexeme], PathID) into a basic block.

-}

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module IntermediateCode(process) where

-- imports --
import ErrorHandling as EH
import InterfaceDT as IDT
import FunctionDeclarations
import TypeDefinitions

import Control.Applicative
import Control.Monad.State
import Data.Char
import Data.List
import Data.Map hiding (filter, map)
import Data.Word
import LLVM.General.AST
import LLVM.General.AST.AddrSpace
import LLVM.General.AST.CallingConvention
import LLVM.General.AST.Constant as Constant
import LLVM.General.AST.Float
import LLVM.General.AST.Instruction as Instruction
import LLVM.General.AST.IntegerPredicate
import LLVM.General.AST.Linkage
import LLVM.General.AST.Operand
import qualified LLVM.General.AST.Global as Global

data CodegenState = CodegenState {
  blocks :: [BasicBlock],
  count :: Word, --Count of unnamed Instructions
  localDict :: Map String (Int, Integer)
}

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

data GlobalCodegenState = GlobalCodegenState {
  dict :: Map String (Int, Integer)
}

newtype GlobalCodegen a = GlobalCodegen { runGlobalCodegen :: State GlobalCodegenState a }
  deriving (Functor, Applicative, Monad, MonadState GlobalCodegenState)

execGlobalCodegen :: Map String (Int, Integer) -> GlobalCodegen a -> a
execGlobalCodegen d m = evalState (runGlobalCodegen m) $ GlobalCodegenState d

execCodegen :: Map String (Int, Integer) -> Codegen a -> a
execCodegen d m = evalState (runCodegen m) $ CodegenState [] 0 d

-- |Generate module from list of definitions.
generateModule :: [Definition] -> Module
generateModule definitions = defaultModule {
  moduleName = "rail-heaven",
  moduleDefinitions = definitions
}

-- |Generate a ret statement, returning a 32-bit Integer to the caller.
-- While we use 64-bit integers everywhere else, our "main" function
-- needs to return an "int" which usually is 32-bits even on 64-bit systems.
terminator :: Integer -- ^The 32-bit Integer to return.
    -> Named Terminator -- ^The return statement.
terminator ret = Do Ret {
  returnOperand = Just $ ConstantOperand $ Int 32 ret,
  metadata' = []
}

-- |Generate global byte array (from a constant string).
createGlobalString :: Lexeme -> Global
createGlobalString (Constant s) = globalVariableDefaults {
  Global.type' = ArrayType {
    nArrayElements = fromInteger l,
    elementType = IntegerType {typeBits = 8}
  },
  Global.initializer = Just Array {
    memberType = IntegerType {typeBits = 8},
    memberValues = map trans s ++ [Int { integerBits = 8, integerValue = 0 }]
  }
}
  where
    l = toInteger $ 1 + length s
    trans c = Int { integerBits = 8, integerValue = toInteger $ ord c }

-- |Create constant strings/byte arrays for a module.
-- TODO: Maybe rename these subfunctions?
generateConstants :: [AST] -> [Global]
generateConstants = map createGlobalString . getAllCons

-- |Get all 'Constant's in a module.
getAllCons :: [AST] -> [Lexeme]
getAllCons = concatMap generateCons
  where
    generateCons :: AST -> [Lexeme]
    generateCons (name, paths) = concatMap generateC paths

    generateC :: (Int, [Lexeme], Int) -> [Lexeme]
    generateC (pathID, lex, nextPath) = filter checkCons lex

    checkCons :: Lexeme -> Bool
    checkCons (Constant c) = True
    checkCons _ = False

--------------------------------------------------------------------------------
-- |Generate global variables for push and pop from and into variables.
createGlobalVariable :: Lexeme -> Global
createGlobalVariable (Pop v) = globalVariableDefaults {
  Global.name = Name v,
  Global.type' = ArrayType {
    nArrayElements = fromInteger l,
    elementType = IntegerType {typeBits = 8}
  },
  Global.initializer = Just Array {
    memberType = IntegerType {typeBits = 8},
    memberValues = map trans v ++ [Int { integerBits = 8, integerValue = 0 }]
  }
}
  where
    l = toInteger $ 1 + length v
    trans c = Int { integerBits = 8, integerValue = toInteger $ ord c }

-- |Generate all global variable definitions for a module.
generateVariables :: [AST] -> [Global]
generateVariables = map createGlobalVariable . getAllVars
  where
    getAllVars :: [AST] -> [Lexeme]
    getAllVars = concatMap generateVars

    generateVars :: AST -> [Lexeme]
    generateVars (name, paths) = nub $ concatMap generateV paths --delete duplicates

    generateV :: (Int, [Lexeme], Int) -> [Lexeme]
    generateV (pathID, lex, nextPath) = filter checkVars lex

    checkVars :: Lexeme -> Bool
    checkVars (Pop v) = True
    checkVars _ = False

--------------------------------------------------------------------------------

-- |Generate an instruction for the 'u'nderflow check command.
generateInstruction :: (Lexeme, String) -> Codegen [Named Instruction]
generateInstruction (Underflow, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "underflow_check",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instructions for junctions.
generateInstruction (Junction label, funcName) = do
  index <- fresh
  index2 <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "pop_bool",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }, UnName index2 := LLVM.General.AST.ICmp {
    LLVM.General.AST.iPredicate = LLVM.General.AST.IntegerPredicate.EQ,
    LLVM.General.AST.operand0 = LocalReference (UnName index),
    LLVM.General.AST.operand1 = ConstantOperand $ Int 64 0,
    metadata = []
  }]

-- |Generate instruction for pop into a variable
generateInstruction (Pop name, funcName) = do
  index <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "pop_into",
    arguments = [ (LocalReference $ Name "table", []),
    (ConstantOperand Constant.GetElementPtr {
      Constant.inBounds = True,
      Constant.address = Constant.GlobalReference (Name name),
      Constant.indices = [
       Int { integerBits = 8, integerValue = 0 },
       Int { integerBits = 8, integerValue = 0 }
      ]}, [])],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for push from a variable
generateInstruction (Push name, funcName) = do
  index <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "push_from",
    arguments = [(LocalReference $ Name "table", []),
      (ConstantOperand Constant.GetElementPtr {
      Constant.inBounds = True,
      Constant.address = Constant.GlobalReference (Name name),
      Constant.indices = [
       Int { integerBits = 8, integerValue = 0 },
       Int { integerBits = 8, integerValue = 0 }
      ]}, [])],
    functionAttributes = [],
    metadata = []
  }]


-- |Generate instruction for push of a constant.
-- access to our push function definied in stack.ll??
-- http://llvm.org/docs/LangRef.html#call-instruction
generateInstruction (Constant value, funcName) = do
  index <- fresh
  dict <- gets localDict
  return [UnName index := LLVM.General.AST.Call {
    -- The optional tail and musttail markers indicate that the optimizers
    --should perform tail call optimization.
    isTailCall = False,
    -- The optional "cconv" marker indicates which calling convention the call
    -- should use. If none is specified, the call defaults to using C calling
    -- conventions.
    callingConvention = C,
    -- The optional Parameter Attributes list for return values. Only 'zeroext',
    -- 'signext', and 'inreg' attributes are valid here
    returnAttributes = [],
    -- actual function to call
    function = Right $ ConstantOperand $ GlobalReference $ Name "push_string_cpy",
    -- argument list whose types match the function signature argument types
    -- and parameter attributes. All arguments must be of first class type. If
    -- the function signature indicates the function accepts a variable number of
    -- arguments, the extra arguments can be specified.
    arguments = [
          -- The 'getelementptr' instruction is used to get the address of a
          -- subelement of an aggregate data structure. It performs address
          -- calculation only and does not access memory.
          -- http://llvm.org/docs/LangRef.html#getelementptr-instruction
          (ConstantOperand Constant.GetElementPtr {
            Constant.inBounds = True,
            Constant.address = Constant.GlobalReference (UnName $ fromInteger $ snd $ dict ! value),
            Constant.indices = [
              Int { integerBits = 8, integerValue = 0 },
              Int { integerBits = 8, integerValue = 0 }
            ]
          }, [])
    ],
    -- optional function attributes list. Only 'noreturn', 'nounwind',
    -- 'readonly' and 'readnone' attributes are valid here.
    functionAttributes = [],
    metadata = []
  }]

-- depending on the Lexeme we see we need to create one or more Instructions
-- the generateInstruction function should return a list of instructions
-- after the mapping phase we should flatten the array with concat so we that we get
-- a list of Instructions that we can insert in the BasicBlock

-- |Generate instruction for printing strings to stdout.
generateInstruction (Output, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "print",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the Boom lexeme (crashes program).
generateInstruction (Boom, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "crash",
    arguments = [(ConstantOperand $ Int 1 1, [])],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the Input lexeme.
generateInstruction (Input, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "input",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the EOF lexeme.
generateInstruction (EOF, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "eof_check",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the add instruction.
generateInstruction (Add1, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "add",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the remainder instruction.
generateInstruction (Remainder, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "rem",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the type instruction.
generateInstruction (RType, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "type",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the sub instruction.
generateInstruction (Subtract, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "sub",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the mul instruction.
generateInstruction (Multiply, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "mult",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the div instruction.
generateInstruction (Divide, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "div",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the strlen instruction.
generateInstruction (Size, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "strlen",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the strapp instruction.
generateInstruction (Append, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "strapp",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the strcut instruction.
generateInstruction (Cut, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "strcut",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the equal instruction.
generateInstruction (Equal, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "equal",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for the greater instruction.
generateInstruction (Greater, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "greater",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for start instruction
generateInstruction (Start, funcName) = do
  index <- fresh
  index2 <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "start",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]  

-- |Generate instructions for lambda push
generateInstruction (Lambda label, funcName) = do
  index <- fresh
  index2 <- fresh
  index3 <- fresh
  index4 <- fresh
  index5 <- fresh
  index6 <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "malloc",
    arguments = [(ConstantOperand $ Int 64 24, [])],
    functionAttributes = [],
    metadata = []
  },
    UnName index2 := LLVM.General.AST.BitCast {
    Instruction.operand0 = LocalReference $ UnName index,
    Instruction.type' = PointerType (NamedTypeReference $ Name "struct.table") (AddrSpace 0),
    metadata = []
  },
    UnName index3 := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "initialise",
    arguments = [(LocalReference $ UnName index2, [])],
    functionAttributes = [],
    metadata = []
  },
    UnName index4 := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "copy_symbol_table",
    arguments = [(LocalReference $ Name "table", []), (LocalReference $ UnName index2, [])],
    functionAttributes = [],
    metadata = []
  },
    UnName index5 := LLVM.General.AST.Alloca {
    allocatedType = PointerType functionReturnLambda (AddrSpace 0),
    numElements = Nothing,
    alignment = 8,
    metadata = []
  },
    Do LLVM.General.AST.Store {
    volatile = False,
    Instruction.address = LocalReference $ UnName index5,
    value = ConstantOperand $ GlobalReference $ Name (funcName ++ "!" ++ show label),
    maybeAtomicity = Nothing,
    alignment = 8,
    metadata = []
  },
    UnName index6 := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "push_lambda",
    arguments = [(LocalReference $ UnName index5, []), (LocalReference $ UnName index2, [])],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for finish instruction
generateInstruction (Finish, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "finish",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]


-- |Generate instruction for function call
generateInstruction (IDT.Call "", funcName) = do
  index <- fresh
  index2 <- fresh
  index6 <- fresh
  return [ UnName index := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "pop_lambda",
    arguments = [],
    functionAttributes = [],
    metadata = []
  },
    UnName index2 := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "get_lambda_pointer",
    arguments = [(LocalReference $ UnName index, [])],
    functionAttributes = [],
    metadata = []
  },
    UnName index6 := LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "get_lambda_table",
    arguments = [(LocalReference $ UnName index, [])],
    functionAttributes = [],
    metadata = []
  },
    Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ LocalReference $ UnName index2,
    arguments = [(LocalReference $ UnName index6, [])],
    functionAttributes = [],
    metadata = []
  }]
generateInstruction (IDT.Call functionName, funcName) = 
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name functionName,
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for pushing nil.
generateInstruction (Nil, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "gen_list_push_nil",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for list cons.
generateInstruction (Cons, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "gen_list_cons",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]

-- |Generate instruction for list breakup.
generateInstruction (Breakup, funcName) =
  return [Do LLVM.General.AST.Call {
    isTailCall = False,
    callingConvention = C,
    returnAttributes = [],
    function = Right $ ConstantOperand $ GlobalReference $ Name "gen_list_breakup",
    arguments = [],
    functionAttributes = [],
    metadata = []
  }]


-- |Fallback for unhandled lexemes (generates no-op).
generateInstruction _ = return [ Do $ Instruction.FAdd (ConstantOperand $ Float $ Single 1.0) (ConstantOperand $ Float $ Single 1.0) [] ]

-- |Appends the function name to the lexemes/instructions.
--
-- The function name is only relevant for the Lambda instruction, because we use
-- "functionName"!"jumpLable" as name for the Lambda functions.
appendName :: [a] -> String -> [(a, String)]
appendName [] name = []
appendName (x:[]) name = [(x, name)]
appendName (x:xs) name = (x, name):rest
  where rest = appendName xs name

-- |Generate the instructions making up one basic block.
generateBasicBlock :: ((Int, [Lexeme], Int), String) -> Codegen BasicBlock
generateBasicBlock ((label, instructions, 0), name) = do
  tmp <- mapM generateInstruction (appendName instructions name)
  return $ BasicBlock (Name $ "l_" ++ show label) (concat tmp) $ terminator 0
generateBasicBlock ((label, instructions, jumpLabel), name) = do
  tmp <- mapM generateInstruction (appendName instructions name)
  i <- gets count
  case filter isJunction instructions of
    [Junction junctionLabel] -> return $ BasicBlock (Name $ "l_" ++ show label) (concat tmp) $ condbranch junctionLabel i
    [] -> return $ BasicBlock (Name $ "l_" ++ show label) (concat tmp) branch
  where
    isJunction (Junction a) = True
    isJunction _ = False
    condbranch junctionLabel i = Do CondBr {
      condition = LocalReference $ UnName i,
      trueDest = Name $ "l_" ++ show junctionLabel,
      falseDest = Name $ "l_" ++ show jumpLabel,
      metadata' = []
    }
    branch = Do Br {
      dest = Name $ "l_" ++ show jumpLabel,
      metadata' = []
    }

-- |Generate all basic blocks for a function.
generateBasicBlocks :: [(Int, [Lexeme], Int)] -> String -> Codegen [BasicBlock]
generateBasicBlocks lexemes name = mapM generateBasicBlock (appendName lexemes name)

-- |Generate a function definition from an AST.
-- 
-- At the beginning of each function, we create a block with lable "entry".
-- In this block we create the symbol table and jump then to "l_1", the first 
-- regular block in each function.
-- The first pattern-matching is for lambda functions
generateFunction :: AST -> GlobalCodegen Definition
generateFunction (name, lexemes) = do
  dict <- gets dict
  return $ GlobalDefinition
    (if "!" `isInfixOf` name then
    Global.functionDefaults {
        Global.name = Name name,
        Global.returnType = IntegerType 32,
        Global.parameters = ( [ Parameter (PointerType (NamedTypeReference $ Name  
          "struct.table") (AddrSpace 0)) (Name "t") [] ], False ),
        Global.basicBlocks = concat ( 
        return ( BasicBlock (Name "entry")  [ 
        Name "table_alloc" := LLVM.General.AST.Call {
        isTailCall = False,
        callingConvention = C,
        returnAttributes = [],
        function = Right $ ConstantOperand $ GlobalReference $ Name "malloc",
        arguments = [(ConstantOperand $ Int 64 24, [])],
        functionAttributes = [],
        metadata = []
      },
        Name "table" := LLVM.General.AST.BitCast {
        Instruction.operand0 = LocalReference $ Name "table_alloc",
        Instruction.type' = PointerType (NamedTypeReference $ Name "struct.table") (AddrSpace 0),
        metadata = []
      },
        Name "" := LLVM.General.AST.Call {
        isTailCall = False,
        callingConvention = C,
        returnAttributes = [],
        function = Right $ ConstantOperand $ GlobalReference $ Name "initialise",
        arguments = [(LocalReference $ Name "table", [])],
        functionAttributes = [],
        metadata = []
      },
      Name "" := LLVM.General.AST.Call {
        isTailCall = False,
        callingConvention = C,
        returnAttributes = [],
        function = Right $ ConstantOperand $ GlobalReference $ Name "copy_symbol_table",
        arguments = [(LocalReference $ Name "t", []), (LocalReference $ Name "table", [])],
        functionAttributes = [],
        metadata = []
      }]
        ( Do Br {
        dest = Name "l_1", 
        metadata' = []} ))
       : [execCodegen dict $ generateBasicBlocks lexemes name])
      }
    else 
    Global.functionDefaults {
        Global.name = Name name,
        Global.returnType = IntegerType 32,
        Global.basicBlocks = concat ( 
        return ( BasicBlock (Name "entry")  [ 
        Name "table_alloc" := LLVM.General.AST.Call {
        isTailCall = False,
        callingConvention = C,
        returnAttributes = [],
        function = Right $ ConstantOperand $ GlobalReference $ Name "malloc",
        arguments = [(ConstantOperand $ Int 64 24, [])],
        functionAttributes = [],
        metadata = []
      },
        Name "table" := LLVM.General.AST.BitCast {
        Instruction.operand0 = LocalReference $ Name "table_alloc",
        Instruction.type' = PointerType (NamedTypeReference $ Name "struct.table") (AddrSpace 0),
        metadata = []
      },
        Name "" := LLVM.General.AST.Call {
        isTailCall = False,
        callingConvention = C,
        returnAttributes = [],
        function = Right $ ConstantOperand $ GlobalReference $ Name "initialise",
        arguments = [(LocalReference $ Name "table", [])],
        functionAttributes = [],
        metadata = []
      }]   
        ( Do Br {
        dest = Name "l_1", 
        metadata' = []} ))
       : [execCodegen dict $ generateBasicBlocks lexemes name])
      })


-- |Create a new local variable (?).
fresh :: Codegen Word
fresh = do
  i <- gets count
  modify $ \s -> s { count = 1 + i }
  return $ i + 1

-- |Generate list of LLVM Definitions from list of ASTs
generateFunctions :: [AST] -> GlobalCodegen [Definition]
generateFunctions = mapM generateFunction

-- |Generate a global definition for a constant.
--
-- This is an unnamed, global constant, i. e. it has a numeric name
-- like '@0'.
generateGlobalDefinition :: Integer -> Global -> Definition
generateGlobalDefinition index def = GlobalDefinition def {
  Global.name = UnName $ fromInteger index,
  Global.isConstant = True,
  Global.linkage = Internal,
  Global.hasUnnamedAddr = True
}

-- |Generate a global definition for a variable name.
--
-- Such definitions are used to pass variable names to
-- LLVM backend functions like 'pop_into()'.
generateGlobalDefinitionVar :: Integer -> Global -> Definition
generateGlobalDefinitionVar i def = GlobalDefinition def {
  Global.isConstant = True,
  Global.linkage = Internal,
  Global.hasUnnamedAddr = True
}

-- |Entry point into module.
process :: IDT.SemAna2InterCode -> IDT.InterCode2Backend
process (IDT.ISI input) = IDT.IIB $ generateModule $ constants ++ variables ++ 
    [
      stackElementTypeDef,
      structTable,
      lambdaElement,
      underflowCheck,
      FunctionDeclarations.print,
      crash,
      start,
      finish,
      inputFunc,
      eofCheck,
      pushStringCpy,
      pop,
      peek,
      add,
      sub,
      rem1,
      mul,
      div1,
      streq,
      strlen,
      strapp,
      strcut,
      popInt,
      equal,
      greater,
      popInto,
      pushFrom,
      popBool,
      initialiseSymbolTable,
      malloc,
      type1,
      copySymbolTable,
      pushLambda,
      getLambda,
      popLambda,
      getTable,
      listPushNil,
      listCons,
      listBreakup
    ] ++ codegen input
  where
    constants = zipWith generateGlobalDefinition [0..] $ generateConstants input
    variables = zipWith generateGlobalDefinitionVar [0..] $ generateVariables input
    constantPool = fromList $ zipWith createConstantPoolEntry [0..] $ getAllCons input
    createConstantPoolEntry index (Constant s) = (s, (length s, index))
    codegen input = execGlobalCodegen constantPool $ generateFunctions input

-- vim:ts=2 sw=2 et