{-|
Module : LLVM
Description: An interface to use LLVM in all capacities.
Copyright: (c) Moritz Kiefer 2018
               Stephen Diehl 2018
               Benjamin Scarlett 2016
Maintainer: moritz.kiefer@purelyfunctional.org
-}
module LLVM (
  module LLVM.Module
  -- * Overview of the @llvm-hs@ library ecosystem
  -- $ecosystem

  -- * Constructing the C++ representation of an LLVM module
  -- $moduleconstruction

  -- * #objectcode# Generating object code
  -- $objectcode

  -- * #jitcompilation# JIT compilation
  -- $jitcompilation
  ) where

import LLVM.Module

import LLVM.AST ()

{- $ecosystem

The main two libraries in the @llvm-hs@ ecosystem are @llvm-hs-pure@
and @llvm-hs@.

* @llvm-hs-pure@ defines a pure Haskell representation of the LLVM
AST. It has no dependency on the LLVM C/C++ libraries so even if you
have trouble installing those or want to avoid that dependency, you
should be able to use it. The documentation
in [LLVM.AST](https://hackage.haskell.org/package/llvm-hs-pure/docs/LLVM-AST.html)
describes the different options for constructing the AST.

* @llvm-hs@ then builds upon @llvm-hs-pure@ and provides the actual FFI
bindings to LLVM’s C++ libraries. Most importantly this includes
bidirectional conversions from the Haskell representation of an LLVM
module to the C++ representation and the other way around.

Once you have constructed the C++ representation, there are two main options:

1. Generate object code as described in "LLVM#objectcode"
2. or JIT compile the module as described in "LLVM#jitcompilation".

In addition to @llvm-hs@ and @llvm-hs-pure@, there are a couple of
other libraries that you be interested in:

* [llvm-hs-pretty](https://hackage.haskell.org/package/llvm-hs-pretty)
  a pure Haskell prettyprinter for the AST in @llvm-hs-pure@. This is
  useful if you just want to render your AST to LLVM’s textual IR
  format either for debugging purposes (@llc@ will usually give pretty
  good error messages for invalid IR) or because you prefer to call
  the LLVM CLI tools over the linking against the LLVM libraries.

* [llvm-hs-typed](https://github.com/llvm-hs/llvm-hs-typed) contains a
  strongly-typed wrapper for the AST in @llvm-hs-pure@ which makes it
  harder to accidentally construct invalid LLVM IR.

* [llvm-hs-quote](https://github.com/llvm-hs/llvm-hs-quote) contains a
  Haskell quasiquoter that can be used for splicing larger chunks of
  existing LLVM IR into your Haskell code.

Finally, there is a [translation](https://github.com/llvm-hs/llvm-hs-kaleidoscope) of
LLVM’s official Kaleidoscope tutorial to @llvm-hs@ and you can find
small, self-contained examples covering various parts of the API in
the [llvm-hs-examples](https://github.com/llvm-hs/llvm-hs-examples)
repository.
-}

{- $moduleconstruction

Interacting with the LLVM libraries requires that you first construct
the C++ representation of an LLVM `Module`.

The most common way of doing that is to first construct the Haskell
representation of an LLVM module using @llvm-hs-pure@. You can then
use `withModuleFromAST` to convert the Haskell AST to the C++
representation.

Alternatively, you can also construct a module from LLVM’s textual IR
or the binary bitcode format using `withModuleFromLLVMAssembly` and
`withModuleFromBitcode`.

-}

{- $objectcode

Once you have constructed the C++ representation of an LLVM `Module`,
you can generate an object file using `moduleObject` which will give
you a `Data.ByteString.ByteString` or write it to a file using
`writeObjectToFile`. To construct the `TargetMachine` for these
functions you can use `LLVM.Target.withHostTargetMachine` if you want
to generate object code for the machine you are currently running on
or use `LLVM.Target.withTargetMachine` and customize the target
machine based on your needs.
-}

{- $jitcompilation

In addition to generating object code, you can also JIT-compile LLVM
modules and call functions in the resulting `Module` from Haskell.

LLVM has several JIT compilers but ORC JIT is the one that is actively
being developed and the one best supported by @llvm-hs@.

To use ORC JIT you first have to create a
`LLVM.OrcJIT.CompileLayer`. You can then use `LLVM.OrcJIT.withModule`
to add an LLVM module to the compile layer and finally use
`LLVM.Internal.OrcJIT.CompileLayer.findSymbol` to get the address of a
symbol in the module.  In most cases, you want to lookup the address
of a function so you have to first convert the `Foreign.Ptr.WordPtr`
to a `Foreign.Ptr.FunPtr` using `Foreign.Ptr.wordPtrToPtr` and
`Foreign.Ptr.castPtrToFunPtr`. Then you can use a foreign dynamic
import to construct a Haskell function which will call the function
located at the `Foreign.Ptr.FunPtr`.
-}