ir-builder-0.1.0.0: Monadic DSL for constructing LLVM IR
Safe HaskellNone
LanguageHaskell2010

LLVM.IRBuilder

Description

This module provides a high-level monadic DSL for LLVM IR modules, functions, blocks, and instructions. The IRBuilder monad encapsulates the state of IR construction and provides error handling for common failure modes.

Example usage:

module <- compileModule "my_module" $ do
  define i32 "main" [] LExternal [] $ do
    beginBlock "entry"
    result <- add i32 (OConstant (CInt 32 1)) (OConstant (CInt 32 2))
    ret result
Synopsis

Core types and compilation

newtype IRBuilderT (m :: Type -> Type) a Source #

The IRBuilderT monad transformer for constructing LLVM IR.

This transformer allows IR construction operations to be embedded in any monad. It combines:

  • StateT for maintaining the builder environment (current function, block, etc.)
  • ExceptT for error handling with typed IRBuilderError exceptions
  • A parameterized base monad m

Use runIRBuilderT to extract the inner transformer stack for execution. The monad supports MonadFix when the base monad does, enabling forward references with mdo notation in recursive block structures like loops with phi nodes.

Instances

Instances details
MonadTrans IRBuilderT Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

lift :: Monad m => m a -> IRBuilderT m a #

Monad m => MonadError IRBuilderError (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Monad m => MonadState IRBuilderEnv (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

MonadFix m => MonadFix (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

mfix :: (a -> IRBuilderT m a) -> IRBuilderT m a #

MonadIO m => MonadIO (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

liftIO :: IO a -> IRBuilderT m a #

Monad m => Applicative (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

pure :: a -> IRBuilderT m a #

(<*>) :: IRBuilderT m (a -> b) -> IRBuilderT m a -> IRBuilderT m b #

liftA2 :: (a -> b -> c) -> IRBuilderT m a -> IRBuilderT m b -> IRBuilderT m c #

(*>) :: IRBuilderT m a -> IRBuilderT m b -> IRBuilderT m b #

(<*) :: IRBuilderT m a -> IRBuilderT m b -> IRBuilderT m a #

Functor m => Functor (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

fmap :: (a -> b) -> IRBuilderT m a -> IRBuilderT m b #

(<$) :: a -> IRBuilderT m b -> IRBuilderT m a #

Monad m => Monad (IRBuilderT m) Source # 
Instance details

Defined in LLVM.IRBuilder

Methods

(>>=) :: IRBuilderT m a -> (a -> IRBuilderT m b) -> IRBuilderT m b #

(>>) :: IRBuilderT m a -> IRBuilderT m b -> IRBuilderT m b #

return :: a -> IRBuilderT m a #

Monad m => MonadIRBuilder (IRBuilderT m) Source #

IRBuilderT instance for MonadIRBuilder

Instance details

Defined in LLVM.IRBuilder

type IRBuilder = IRBuilderT Identity Source #

Specialized IRBuilderT using Identity as the base monad.

This is the original non-transformer version, maintained for backward compatibility. Most existing code uses this type.

class Monad m => MonadIRBuilder (m :: Type -> Type) where Source #

MTL-style typeclass for monads that support IR building operations.

This class provides primitives for accessing and modifying the builder environment and throwing builder-specific errors. It is designed to be composable with other monad transformers via lift-through instances.

Minimal complete definition: getIRBuilderEnv, putIRBuilderEnv, throwIRBuilderError

Methods

getIRBuilderEnv :: m IRBuilderEnv Source #

Retrieve the current builder environment.

putIRBuilderEnv :: IRBuilderEnv -> m () Source #

Replace the current builder environment.

throwIRBuilderError :: IRBuilderError -> m a Source #

Throw a builder error, short-circuiting the computation.

modifyIRBuilderEnv :: (IRBuilderEnv -> IRBuilderEnv) -> m () Source #

Modify the builder environment using a function.

Default implementation in terms of getIRBuilderEnv and putIRBuilderEnv.

getsIRBuilderEnv :: (IRBuilderEnv -> a) -> m a Source #

Retrieve a projection of the builder environment.

Default implementation in terms of getIRBuilderEnv.

Instances

Instances details
Monad m => MonadIRBuilder (IRBuilderT m) Source #

IRBuilderT instance for MonadIRBuilder

Instance details

Defined in LLVM.IRBuilder

MonadIRBuilder m => MonadIRBuilder (ExceptT e m) Source #

Lift through ExceptT.

Instance details

Defined in LLVM.IRBuilder.Class

MonadIRBuilder m => MonadIRBuilder (ReaderT r m) Source #

Lift through ReaderT.

Instance details

Defined in LLVM.IRBuilder.Class

MonadIRBuilder m => MonadIRBuilder (StateT s m) Source #

Lift through StateT.

Instance details

Defined in LLVM.IRBuilder.Class

(MonadIRBuilder m, Monoid w) => MonadIRBuilder (WriterT w m) Source #

Lift through WriterT.

Instance details

Defined in LLVM.IRBuilder.Class

runIRBuilder :: IRBuilder a -> StateT IRBuilderEnv (ExceptT IRBuilderError Identity) a Source #

Extract the transformer stack from an IRBuilder computation.

For backward compatibility with code that uses runIRBuilder directly.

lift :: (MonadTrans t, Monad m) => m a -> t m a #

Lift a computation from the argument monad to the constructed monad.

compileModule :: IRName -> IRBuilder a -> Text Source #

Compile an LLVM IR module to text, terminating on error.

This is the primary entry point for IR generation. It executes the builder monad, finalizes all pending blocks and functions, and renders the complete module to LLVM assembly text format.

On error, this function will call error with a descriptive message. For explicit error handling, use compileModuleWith.

Example:

let code = compileModule "my_module" $ do
  define i32 "main" [] LExternal [] $ do
    beginBlock "entry"
    x <- add i32 (OConstant (CInt 32 1)) (OConstant (CInt 32 2))
    ret x
putStrLn code

Args:

  • First argument: module name
  • Second argument: builder computation

Returns: LLVM assembly as Text

Throws (via error): Any IRBuilderError encountered during building

compileModuleWith :: IRName -> IRBuilder a -> Either IRBuilderError Text Source #

Compile an LLVM IR module to text with explicit error handling.

This is the result-returning variant of compileModule. It executes the builder, finalizes all blocks and functions, and renders the module to LLVM assembly text.

For automatic error handling (terminates on error), use compileModule.

Args:

  • First argument: module name
  • Second argument: builder computation

Returns: Either IRBuilderError Text where:

  • Left: construction or rendering error
  • Right: LLVM assembly text

Errors: Returns Left on any IRBuilderError during building

buildModuleWith :: IRName -> IRBuilder a -> Either IRBuilderError (IRModule, a) Source #

Build an LLVM IR module with explicit error handling.

This is the result-returning variant of buildModule. It executes the builder computation and returns the result as an Either, allowing callers to handle errors explicitly rather than via error.

Args:

  • First argument: module name
  • Second argument: builder computation

Returns: Either IRBuilderError (IRModule, a) where:

  • Left: construction error
  • Right: tuple of (module, builder result)

Errors: Returns Left on any IRBuilderError during construction

buildModule :: IRName -> IRBuilder a -> IRModule Source #

Build an LLVM IR module, terminating on error.

This is the primary entry point for IR module construction. It executes the builder monad and returns the complete IR module. If any error occurs during construction, the program terminates with error.

For explicit error handling, use buildModuleWith.

Args:

  • First argument: module name
  • Second argument: builder computation

Returns: The constructed IRModule

Throws (via error): Any IRBuilderError encountered during construction

compileModuleM :: MonadIRBuilder m => IRName -> m a -> m Text Source #

Compile an LLVM IR module to text in any MonadIRBuilder context.

This is the generalized version of compileModule that works with any monad implementing MonadIRBuilder. It's a convenience wrapper around compileModuleWithM that discards the computation result.

Args:

  • First argument: module name
  • Second argument: builder computation in any MonadIRBuilder

Returns: LLVM assembly as Text

Throws: Propagates any IRBuilderError via throwIRBuilderError

Example:

text <- compileModuleM "my_module" $ do
  define i32 "main" [] LExternal [] $ do
    beginBlock "entry"
    customMonadOperation
    ret (OConstant (CInt 32 0))

compileModuleWithM :: MonadIRBuilder m => IRName -> m a -> m (Text, a) Source #

Compile an LLVM IR module to text in any MonadIRBuilder context, with result.

This is the generalized version of compileModuleWith that works with any monad implementing MonadIRBuilder. It builds the module and renders it to LLVM assembly, returning both the text and the computation result.

Args:

  • First argument: module name
  • Second argument: builder computation in any MonadIRBuilder

Returns: Tuple of (LLVM assembly Text, computation result)

Throws: Propagates any IRBuilderError via throwIRBuilderError

buildModuleWithM :: MonadIRBuilder m => IRName -> m a -> m (IRModule, a) Source #

Build an LLVM IR module in any MonadIRBuilder context, with result.

This is the generalized version of buildModuleWith that works with any monad implementing MonadIRBuilder, such as custom monad stacks built on top of IRBuilderT. It isolates the module construction in a fresh environment and returns both the module and the computation result.

The function:

  1. Saves the current builder environment
  2. Resets to an empty environment
  3. Executes the builder computation
  4. Extracts the final environment to construct the module
  5. Restores the original environment
  6. Returns the module and computation result

Args:

  • First argument: module name
  • Second argument: builder computation in any MonadIRBuilder

Returns: Tuple of (IRModule, computation result)

Throws: Propagates any IRBuilderError via throwIRBuilderError

Example:

moduleAndResult <- buildModuleWithM "my_module" $ do
 define i32 "main" [] LExternal [] $ do
   beginBlock "entry"
   customMonadOperation  -- works with custom MonadIRBuilder instances
   ret (OConstant (CInt 32 0))

buildModuleM :: MonadIRBuilder m => IRName -> m a -> m IRModule Source #

Build an LLVM IR module in any MonadIRBuilder context.

This is the generalized version of buildModule that works with any monad implementing MonadIRBuilder. It's a convenience wrapper around buildModuleWithM that discards the computation result.

Args:

  • First argument: module name
  • Second argument: builder computation in any MonadIRBuilder

Returns: The constructed IRModule

Throws: Propagates any IRBuilderError via throwIRBuilderError

Example:

module_ <- buildModuleM "my_module" $ do
 define i32 "main" [] LExternal [] $ do
   beginBlock "entry"
   customMonadOperation
   ret (OConstant (CInt 32 0))

Function definition

define Source #

Arguments

:: MonadIRBuilder m 
=> IRType

Return type of the function

-> IRName

Function name

-> [(IRType, IRName)]

Parameter list as (type, name) pairs

-> IRLinkage

Linkage visibility (e.g. LExternal, LInternal)

-> [IRAttribute]

Function attributes (e.g. [NoInline, NoReturn])

-> m a

Body computation that builds the function

-> m a 

Define a function with the given signature and body.

This is the primary high-level interface for function definition. It combines beginFunction and endFunction around a computation.

Args:

  • retType: the return type
  • name: function name
  • args: list of (type, name) parameter pairs
  • linkage: function linkage (e.g., LExternal, LInternal)
  • attributes: function attributes (e.g., [APure])
  • body: monadic computation that builds the function body

Returns: the result of the body computation

Example:

define i32 "add" [(i32, "a"), (i32, "b")] LExternal [] $ do
  beginBlock "entry"
  result <- add i32 (OLocal i32 "a") (OLocal i32 "b")
  ret result

Throws: Any error from body computation or finalization

beginFunction :: MonadIRBuilder m => FunctionBuilder -> m () Source #

Begin a new function definition in the current module.

This is a lower-level operation. For a higher-level interface, prefer define.

This function:

  1. Finalizes the current block (if any)
  2. Verifies no function is already active
  3. Activates the given function

Throws: CurrentFunctionActive if a function is already being defined

endFunction :: MonadIRBuilder m => m () Source #

Finalize the current function definition.

This is a lower-level operation. For a higher-level interface, prefer define.

This function:

  1. Finalizes the current block (if any)
  2. Constructs the function from current state
  3. Adds it to the module's function list
  4. Clears the current function context

Throws: NoCurrentFunction if no function is currently being defined

Block management

beginBlock :: MonadIRBuilder m => IRName -> m () Source #

Begin a new basic block within the current function.

Each block has a label and contains instructions ending with a terminator.

This function:

  1. Finalizes the previous block (if any)
  2. Creates a new empty block with the given label
  3. Makes it the current block

Blocks are accumulated within the function and rendered in order.

Args: block label (e.g., "entry", "loop", "exit")

Throws: BlockMissingTerminator if the previous block lacks a terminator

Example:

beginBlock "entry"
beginBlock "loop"
beginBlock "exit"

block :: MonadIRBuilder m => IRName -> m IRName Source #

Begin a fresh basic block with a suffixed label.

This is the high-level alternative to beginBlock for use in mdo blocks where the generated label must be captured and referenced by other instructions (e.g., br, condbr, phi).

The hint is suffixed with a fresh integer to guarantee uniqueness across nested or repeated uses of the same logical name:

-- "loop" becomes e.g. "loop.1", "body" becomes "body.2"
define i64 "fact" [(i64, "n")] LExternal [] $ mdo
  beginBlock "entry"
  br loopLabel

  loopLabel <- block "loop"
  ...
  condbr cond bodyLabel exitLabel

  bodyLabel <- block "body"
  ...
  br loopLabel

  exitLabel <- block "exit"
  ret result

Returns: the generated block label (e.g., "loop.1")

Throws: BlockMissingTerminator if the previous block lacks a terminator

finalizeCurrentBlock :: MonadIRBuilder m => m () Source #

Finalize the current block and add it to the function's block list.

This is normally called automatically by beginBlock, beginFunction, and endFunction. It's exported for advanced use cases.

This function:

  1. Validates the current block has a terminator
  2. Constructs an IRBlock from current state
  3. Adds it to the current function
  4. Clears the current block context

Throws: BlockMissingTerminator if the block lacks a terminator

Instruction emission

emitInstruction :: MonadIRBuilder m => IRInstruction (Maybe Text) -> m () Source #

Emit an instruction into the current block.

Instructions are appended to the current block’s instruction list. Each instruction may have an optional inline comment attached via ‘##’.

If no block is currently active, an implicit block labelled "entry" is created automatically.

This is the low-level primitive that all instruction smart constructors (e.g. ‘add’, ‘mul’, ‘load’) call internally. Prefer those over calling this function directly.

Example (direct use):

emitInstruction IRInstruction
  { instrResult   = Just ("x", i32)
  , instrOp       = IAdd i32 (OLocal i32 "a") (OLocal i32 "b")
  , instrMetadata = Nothing
  }

emitAnnotation :: MonadIRBuilder m => IRAnnotation -> m () Source #

Emit a comment annotation into the current block.

Annotations are block-level comments useful for documenting logic sections. Unlike inline comments (via <##>), annotations stand alone as IRBlockItems.

If no block is currently active, an implicit block labelled "entry" is created automatically.

Example:

emitAnnotation (commentBlock ["Section: input validation", "Check bounds..."])

emitTerminator :: MonadIRBuilder m => IRTerminator -> m () Source #

Emit a terminator instruction for the current block.

Every basic block must end with exactly one terminator (e.g., ret, br, condbr).

If no block is currently active, an implicit block labelled "entry" is created automatically, mirroring LLVM IR semantics.

This function validates that the block doesn't already have a terminator.

Throws: BlockAlreadyTerminated if the block already has a terminator

emitGlobal :: MonadIRBuilder m => IRGlobal -> m () Source #

Emit a global value (declaration or constant) into the module.

Global values include:

  • External declarations (functions, global variables)
  • Constant globals
  • String constants

Globals are collected in the module and rendered at the top level.

Example:

emitGlobal (declare i32 "printf" [TPtr] ...)
emitGlobal (IRGlobalConstant "myString" (IRConstantString "hello") ...)

emitTypeDecl :: MonadIRBuilder m => IRName -> IRType -> m () Source #

Emit a named type declaration into the module.

Renders at the top of the IR output as:

%IRName = type <type>

Duplicate declarations (same name) are silently ignored, so it is safe to call this function multiple times for the same type.

Example:

emitTypeDecl "Node" (TStruct [TInt 32, TPtr, TPtr])
-- renders: %Node = type { i32, ptr, ptr }

declare :: MonadIRBuilder m => IRName -> IRType -> [IRType] -> m () Source #

Emit an external function declaration into the module.

Renders as:

declare <retType> @<name>(<argTypes>)

Duplicate declarations (same function name) are silently ignored, so it is safe to call this freely without tracking what has already been declared.

Example:

declare "printf" TVoid [TPtr]
declare "malloc" TPtr [TInt 64]

declareVarArg :: MonadIRBuilder m => IRName -> IRType -> [IRType] -> m () Source #

Emit a variadic external function declaration into the module.

Like declare, but appends ... to the argument list:

declare i32 @printf(ptr, ...)

Duplicate declarations (same function name) are silently ignored.

Example:

declareVarArg "printf" i32 [TPtr]
declareVarArg "scanf"  i32 [TPtr]

setTerminator :: MonadIRBuilder m => IRTerminator -> m () Source #

Set the terminator instruction for the current block.

Prefer emitTerminator for proper error handling. This is a lower-level variant used internally.

Utilities

(<##>) :: MonadIRBuilder m => m a -> Text -> m a Source #

Attach an inline comment to the previously emitted instruction.

This operator must immediately follow an instruction-emitting expression. The comment is attached to the instruction's metadata and renders as a line comment in LLVM assembly.

Usage:

result <- add i32 a b <##> "sum of a and b"

This renders as:

%1 = add i32 %a, %b  ; sum of a and b

Throws:

Error handling

getCurrentBlockM :: MonadIRBuilder m => m BlockBuilder Source #

Retrieve the current active block, throwing NoCurrentBlock if none exists.

This is useful for explicit error handling patterns where you need the current block or want to handle the error case directly instead of relying on other operations to fail.

Throws: NoCurrentBlock if no block is currently active.

getCurrentFunctionM :: MonadIRBuilder m => m FunctionBuilder Source #

Get the current function, throwing NoCurrentFunction if none exists

liftEither :: MonadIRBuilder m => Either IRBuilderError a -> m a Source #

Lift an Either computation into the IRBuilder monad.

Useful for integrating external computations that return 'Either IRBuilderError a' into the builder pipeline:

result <- liftEither (someExternalComputation ...)

Atomic types