diff --git a/GhcDump/Ast.hs b/GhcDump/Ast.hs
new file mode 100644
--- /dev/null
+++ b/GhcDump/Ast.hs
@@ -0,0 +1,236 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DeriveGeneric #-}
+module GhcDump.Ast where
+
+import GHC.Generics
+
+import Data.Monoid
+import qualified Data.ByteString as BS
+import Codec.Serialise
+import qualified Data.Text as T
+
+import Unique (mkUnique)
+
+data Unique = Unique !Char !Int
+            deriving (Eq, Ord, Generic)
+instance Serialise Unique
+
+-- | This is dependent upon GHC
+instance Show Unique where
+    show (Unique c n) = show $ mkUnique c n
+
+data ExternalName = ExternalName { externalModuleName :: !ModuleName
+                                 , externalName :: !T.Text
+                                 , externalUnique :: !Unique
+                                 }
+                  | ForeignCall
+                  deriving (Eq, Ord, Generic, Show)
+instance Serialise ExternalName
+
+newtype BinderId = BinderId Unique
+                 deriving (Eq, Ord, Serialise, Show)
+
+newtype SBinder = SBndr { unSBndr :: Binder' SBinder BinderId }
+                deriving (Eq, Ord, Generic, Show)
+instance Serialise SBinder
+
+newtype Binder = Bndr { unBndr :: Binder' Binder Binder }
+               deriving (Eq, Ord, Generic, Show)
+instance Serialise Binder
+
+binderUniqueName :: Binder -> T.Text
+binderUniqueName (Bndr b) =
+    binderName b <> T.pack "_" <> T.pack (show u)
+  where BinderId u = binderId b
+
+data Binder' bndr var = Binder { binderName   :: !T.Text
+                               , binderId     :: !BinderId
+                               , binderIdInfo :: IdInfo bndr var
+                               , binderIdDetails :: IdDetails
+                               , binderType   :: Type' bndr var
+                               }
+                      | TyBinder { binderName :: !T.Text
+                                 , binderId   :: !BinderId
+                                 , binderKind :: Type' bndr var
+                                 }
+                      deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Binder' bndr var)
+
+data IdInfo bndr var
+    = IdInfo { idiArity         :: !Int
+             , idiIsOneShot     :: Bool
+             , idiUnfolding     :: Unfolding bndr var
+             , idiInlinePragma  :: !T.Text
+             , idiOccInfo       :: OccInfo
+             , idiStrictnessSig :: !T.Text
+             , idiDemandSig     :: !T.Text
+             , idiCallArity     :: !Int
+             }
+    deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (IdInfo bndr var)
+
+data Unfolding bndr var
+    = NoUnfolding
+    | BootUnfolding
+    | OtherCon [AltCon]
+    | DFunUnfolding
+    | CoreUnfolding { unfTemplate   :: Expr' bndr var
+                    , unfIsValue    :: Bool
+                    , unfIsConLike  :: Bool
+                    , unfIsWorkFree :: Bool
+                    , unfGuidance   :: T.Text
+                    }
+    deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Unfolding bndr var)
+
+data OccInfo = OccManyOccs -- | introduced in GHC 8.2
+             | OccDead
+             | OccOneOcc
+             | OccLoopBreaker { occStrongLoopBreaker :: Bool }
+    deriving (Eq, Ord, Generic, Show)
+instance Serialise OccInfo
+
+data IdDetails = VanillaId
+               | RecSelId
+               | DataConWorkId
+               | DataConWrapId
+               | ClassOpId
+               | PrimOpId
+               -- | FCallId  (these are treated as ExternalNames since they have no binding site)
+               | TickBoxOpId
+               | DFunId
+               | CoVarId -- | introduced in GHC 8.0
+               | JoinId { joinIdArity :: !Int }
+               deriving (Eq, Ord, Generic, Show)
+instance Serialise IdDetails
+
+data Lit = MachChar Char
+         | MachStr BS.ByteString
+         | MachNullAddr
+         | MachInt Integer
+         | MachInt64 Integer
+         | MachWord Integer
+         | MachWord64 Integer
+         | MachFloat Rational
+         | MachDouble Rational
+         | MachLabel T.Text
+         | LitInteger Integer
+         deriving (Eq, Ord, Generic, Show)
+instance Serialise Lit
+
+data TyCon = TyCon !T.Text !Unique
+           deriving (Eq, Ord, Generic, Show)
+instance Serialise TyCon
+
+type SType = Type' SBinder BinderId
+type Type = Type' Binder Binder
+
+data Type' bndr var
+    = VarTy var
+    | FunTy (Type' bndr var) (Type' bndr var)
+    | TyConApp TyCon [Type' bndr var]
+    | AppTy (Type' bndr var) (Type' bndr var)
+    | ForAllTy bndr (Type' bndr var)
+    | LitTy
+    | CoercionTy
+    deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Type' bndr var)
+
+newtype ModuleName = ModuleName {getModuleName :: T.Text}
+                   deriving (Eq, Ord, Serialise, Show)
+
+type Module = Module' Binder Binder
+type SModule = Module' SBinder BinderId
+
+data Module' bndr var
+    = Module { moduleName        :: ModuleName
+             , modulePhase       :: T.Text
+             , moduleTopBindings :: [TopBinding' bndr var]
+             }
+    deriving (Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Module' bndr var)
+
+moduleBindings :: Module' bndr var -> [(bndr, CoreStats, Expr' bndr var)]
+moduleBindings = concatMap topBindings . moduleTopBindings
+
+-- $binders
+--
+-- The binder story:
+--
+-- Things which might contain bound variables (e.g. expressions and types) have
+-- a type variable which is instantiated at 'BinderId' in the serialised form or
+-- 'Binder' after post-processing.
+--
+-- Note that bindings sites themselves are always 'Binder's.
+
+type SExpr = Expr' SBinder BinderId
+type Expr = Expr' Binder Binder
+
+data Expr' bndr var
+    = EVar var
+    | EVarGlobal ExternalName
+    | ELit Lit
+    | EApp (Expr' bndr var) (Expr' bndr var)
+    | ETyLam bndr (Expr' bndr var)
+    | ELam bndr (Expr' bndr var)
+    | ELet [(bndr, Expr' bndr var)] (Expr' bndr var)
+    | ECase (Expr' bndr var) bndr [Alt' bndr var]
+    | EType (Type' bndr var)
+    | ECoercion
+    deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Expr' bndr var)
+
+type SAlt = Alt' SBinder BinderId
+type Alt = Alt' Binder Binder
+
+data Alt' bndr var = Alt { altCon     :: !AltCon
+                         , altBinders :: [bndr]
+                         , altRHS     :: Expr' bndr var
+                         }
+                  deriving (Eq, Ord, Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (Alt' bndr var)
+
+data AltCon = AltDataCon !T.Text
+            | AltLit Lit
+            | AltDefault
+            deriving (Eq, Ord, Generic, Show)
+instance Serialise AltCon
+
+type STopBinding = TopBinding' SBinder BinderId
+type TopBinding = TopBinding' Binder Binder
+
+data TopBinding' bndr var
+    = NonRecTopBinding bndr CoreStats (Expr' bndr var)
+    | RecTopBinding [(bndr, CoreStats, Expr' bndr var)]
+    deriving (Generic, Show)
+instance (Serialise bndr, Serialise var) => Serialise (TopBinding' bndr var)
+
+topBindings :: TopBinding' bndr var -> [(bndr, CoreStats, Expr' bndr var)]
+topBindings (NonRecTopBinding a b c) = [(a,b,c)]
+topBindings (RecTopBinding bs) = bs
+
+data CoreStats
+    = CoreStats { csTerms       :: !Int
+                , csTypes       :: !Int
+                , csCoercions   :: !Int
+                , csValBinds   :: !Int
+                , csJoinBinds  :: !Int
+                }
+    deriving (Generic, Show)
+instance Serialise CoreStats
+
+instance Monoid CoreStats where
+    mempty = CoreStats 0 0 0 0 0
+    CoreStats a b c d e `mappend` CoreStats a' b' c' d' e' =
+        CoreStats (a+a') (b+b') (c+c') (d+d') (e+e')
+
+{-
+data Rule' bndr var
+    = Rule { ruleName :: T.Text
+           , ruleActivation :: Activation
+           , ruleFn :: Name
+           , ruleBinders :: [bndr]
+           , ruleRHS :: Expr' bndr var
+           , ruleAuto :: Bool
+           }
+-}
diff --git a/GhcDump/Convert.hs b/GhcDump/Convert.hs
new file mode 100644
--- /dev/null
+++ b/GhcDump/Convert.hs
@@ -0,0 +1,232 @@
+{-# LANGUAGE CPP #-}
+module GhcDump.Convert where
+
+import Data.Bifunctor
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as TE
+
+import Literal (Literal(..))
+import Var (Var)
+import qualified Var
+import Id (isFCallId)
+import Module (ModuleName, moduleNameFS, moduleName)
+import Unique (Unique, getUnique, unpkUnique)
+import Name (getOccName, occNameFS, OccName, getName, nameModule_maybe)
+import qualified IdInfo
+import qualified BasicTypes as OccInfo (OccInfo(..), isStrongLoopBreaker)
+#if MIN_VERSION_ghc(8,0,0)
+import qualified CoreStats
+#else
+import qualified CoreUtils as CoreStats
+#endif
+import qualified CoreSyn
+import CoreSyn (Expr(..), CoreExpr, Bind(..), CoreAlt, CoreBind, AltCon(..))
+import HscTypes (ModGuts(..))
+import FastString (FastString, fastStringToByteString)
+#if MIN_VERSION_ghc(8,0,0)
+import TyCoRep as Type (Type(..), TyBinder(..))
+#else
+import TypeRep as Type (Type(..))
+#endif
+import Type (splitFunTy_maybe)
+import TyCon (TyCon, tyConUnique)
+
+import Outputable (ppr, showSDoc, SDoc)
+import DynFlags (unsafeGlobalDynFlags)
+
+import GhcDump.Ast as Ast
+
+cvtSDoc :: SDoc -> T.Text
+cvtSDoc = T.pack . showSDoc unsafeGlobalDynFlags
+
+fastStringToText :: FastString -> T.Text
+fastStringToText = TE.decodeUtf8 . fastStringToByteString
+
+occNameToText :: OccName -> T.Text
+occNameToText = fastStringToText . occNameFS
+
+cvtUnique :: Unique.Unique -> Ast.Unique
+cvtUnique u =
+    let (a,b) = unpkUnique u
+    in Ast.Unique a b
+
+cvtVar :: Var -> BinderId
+cvtVar = BinderId . cvtUnique . Var.varUnique
+
+cvtBinder :: Var -> SBinder
+cvtBinder v
+  | Var.isId v =
+    SBndr $ Binder { binderName   = occNameToText $ getOccName v
+                   , binderId     = cvtVar v
+                   , binderIdInfo = cvtIdInfo $ Var.idInfo v
+                   , binderIdDetails = cvtIdDetails $ Var.idDetails v
+                   , binderType   = cvtType $ Var.varType v
+                   }
+  | otherwise =
+    SBndr $ TyBinder { binderName   = occNameToText $ getOccName v
+                     , binderId     = cvtVar v
+                     , binderKind   = cvtType $ Var.varType v
+                     }
+
+cvtIdInfo :: IdInfo.IdInfo -> Ast.IdInfo SBinder BinderId
+cvtIdInfo i =
+    IdInfo { idiArity         = IdInfo.arityInfo i
+           , idiIsOneShot     = IdInfo.oneShotInfo i == IdInfo.OneShotLam
+           , idiUnfolding     = cvtUnfolding $ IdInfo.unfoldingInfo i
+           , idiInlinePragma  = cvtSDoc $ ppr $ IdInfo.inlinePragInfo i
+           , idiOccInfo       = case IdInfo.occInfo i of
+#if MIN_VERSION_ghc(8,1,0)
+                                  OccInfo.ManyOccs{} -> OccManyOccs
+#else
+                                  OccInfo.NoOccInfo  -> OccManyOccs
+#endif
+                                  OccInfo.IAmDead    -> OccDead
+                                  OccInfo.OneOcc{}   -> OccOneOcc
+                                  oi@OccInfo.IAmALoopBreaker{} -> OccLoopBreaker (OccInfo.isStrongLoopBreaker oi)
+           , idiStrictnessSig = cvtSDoc $ ppr $ IdInfo.strictnessInfo i
+           , idiDemandSig     = cvtSDoc $ ppr $ IdInfo.demandInfo i
+           , idiCallArity     = IdInfo.callArityInfo i
+           }
+
+cvtUnfolding :: CoreSyn.Unfolding -> Ast.Unfolding SBinder BinderId
+cvtUnfolding CoreSyn.NoUnfolding = Ast.NoUnfolding
+#if MIN_VERSION_ghc(8,2,0)
+cvtUnfolding CoreSyn.BootUnfolding = Ast.BootUnfolding
+#endif
+cvtUnfolding (CoreSyn.OtherCon cons) = Ast.OtherCon (map cvtAltCon cons)
+cvtUnfolding (CoreSyn.DFunUnfolding{}) = Ast.DFunUnfolding
+cvtUnfolding u@(CoreSyn.CoreUnfolding{}) =
+    Ast.CoreUnfolding { unfTemplate   = cvtExpr $ CoreSyn.uf_tmpl u
+                      , unfIsValue    = CoreSyn.uf_is_value u
+                      , unfIsConLike  = CoreSyn.uf_is_conlike u
+                      , unfIsWorkFree = CoreSyn.uf_is_work_free u
+                      , unfGuidance   = cvtSDoc $ ppr $ CoreSyn.uf_guidance u
+                      }
+
+cvtIdDetails :: IdInfo.IdDetails -> Ast.IdDetails
+cvtIdDetails d =
+    case d of
+      IdInfo.VanillaId -> Ast.VanillaId
+      IdInfo.RecSelId{} -> Ast.RecSelId
+      IdInfo.DataConWorkId{} -> Ast.DataConWorkId
+      IdInfo.DataConWrapId{} -> Ast.DataConWrapId
+      IdInfo.ClassOpId{} -> Ast.ClassOpId
+      IdInfo.PrimOpId{} -> Ast.PrimOpId
+      IdInfo.FCallId{} -> error "This shouldn't happen"
+      IdInfo.TickBoxOpId{} -> Ast.TickBoxOpId
+      IdInfo.DFunId{} -> Ast.DFunId
+#if MIN_VERSION_ghc(8,0,0)
+      IdInfo.CoVarId{} -> Ast.CoVarId
+#endif
+#if MIN_VERSION_ghc(8,2,0)
+      IdInfo.JoinId n -> Ast.JoinId n
+#endif
+
+cvtCoreStats :: CoreStats.CoreStats -> Ast.CoreStats
+cvtCoreStats stats =
+    Ast.CoreStats
+      { csTerms     = CoreStats.cs_tm stats
+      , csTypes     = CoreStats.cs_ty stats
+      , csCoercions = CoreStats.cs_co stats
+#if MIN_VERSION_ghc(8,2,0)
+      , csValBinds  = CoreStats.cs_vb stats
+      , csJoinBinds = CoreStats.cs_jb stats
+#else
+      , csValBinds  = 0
+      , csJoinBinds = 0
+#endif
+      }
+
+exprStats :: CoreExpr -> CoreStats.CoreStats
+#if MIN_VERSION_ghc(8,0,0)
+exprStats = CoreStats.exprStats
+#else
+-- exprStats wasn't exported in 7.10
+exprStats _ = CoreStats.CS 0 0 0
+#endif
+
+cvtTopBind :: CoreBind -> STopBinding
+cvtTopBind (NonRec b e) =
+    NonRecTopBinding (cvtBinder b) (cvtCoreStats $ exprStats e) (cvtExpr e)
+cvtTopBind (Rec bs) =
+    RecTopBinding $ map to bs
+  where to (b, e) = (cvtBinder b, cvtCoreStats $ exprStats e, cvtExpr e)
+
+cvtExpr :: CoreExpr -> Ast.SExpr
+cvtExpr expr =
+  case expr of
+    Var x
+        -- foreign calls are local but have no binding site.
+        -- TODO: use hasNoBinding here.
+      | isFCallId x   -> EVarGlobal ForeignCall
+      | Just m <- nameModule_maybe $ getName x
+                      -> EVarGlobal $ ExternalName (cvtModuleName $ Module.moduleName m)
+                                                   (occNameToText $ getOccName x)
+                                                   (cvtUnique $ getUnique x)
+      | otherwise     -> EVar (cvtVar x)
+    Lit l             -> ELit (cvtLit l)
+    App x y           -> EApp (cvtExpr x) (cvtExpr y)
+    Lam x e
+      | Var.isTyVar x -> ETyLam (cvtBinder x) (cvtExpr e)
+      | otherwise     -> ELam (cvtBinder x) (cvtExpr e)
+    Let (NonRec b e) body -> ELet [(cvtBinder b, cvtExpr e)] (cvtExpr body)
+    Let (Rec bs) body -> ELet (map (bimap cvtBinder cvtExpr) bs) (cvtExpr body)
+    Case e x _ as     -> ECase (cvtExpr e) (cvtBinder x) (map cvtAlt as)
+    Cast x _          -> cvtExpr x
+    Tick _ e          -> cvtExpr e
+    Type t            -> EType $ cvtType t
+    Coercion _        -> ECoercion
+
+cvtAlt :: CoreAlt -> Ast.SAlt
+cvtAlt (con, bs, e) = Alt (cvtAltCon con) (map cvtBinder bs) (cvtExpr e)
+
+cvtAltCon :: CoreSyn.AltCon -> Ast.AltCon
+cvtAltCon (DataAlt altcon) = Ast.AltDataCon $ occNameToText $ getOccName altcon
+cvtAltCon (LitAlt l)       = Ast.AltLit $ cvtLit l
+cvtAltCon DEFAULT          = Ast.AltDefault
+
+cvtLit :: Literal -> Ast.Lit
+cvtLit l =
+    case l of
+      Literal.MachChar x -> Ast.MachChar x
+      Literal.MachStr x -> Ast.MachStr x
+      Literal.MachNullAddr -> Ast.MachNullAddr
+      Literal.MachInt x -> Ast.MachInt x
+      Literal.MachInt64 x -> Ast.MachInt64 x
+      Literal.MachWord x -> Ast.MachWord x
+      Literal.MachWord64 x -> Ast.MachWord64 x
+      Literal.MachFloat x -> Ast.MachFloat x
+      Literal.MachDouble x -> Ast.MachDouble x
+      Literal.MachLabel x _ _ -> Ast.MachLabel $ fastStringToText  x
+      Literal.LitInteger x _ -> Ast.LitInteger x
+
+cvtModule :: String -> ModGuts -> Ast.SModule
+cvtModule phase guts =
+    Ast.Module name (T.pack phase) (map cvtTopBind $ mg_binds guts)
+  where name = cvtModuleName $ Module.moduleName $ mg_module guts
+
+cvtModuleName :: Module.ModuleName -> Ast.ModuleName
+cvtModuleName = Ast.ModuleName . fastStringToText . moduleNameFS
+
+cvtType :: Type.Type -> Ast.SType
+cvtType t
+  | Just (a,b) <- splitFunTy_maybe t = Ast.FunTy (cvtType a) (cvtType b)
+cvtType (Type.TyVarTy v)       = Ast.VarTy (cvtVar v)
+cvtType (Type.AppTy a b)       = Ast.AppTy (cvtType a) (cvtType b)
+cvtType (Type.TyConApp tc tys) = Ast.TyConApp (cvtTyCon tc) (map cvtType tys)
+#if MIN_VERSION_ghc(8,2,0)
+cvtType (Type.ForAllTy (Var.TvBndr b _) t) = Ast.ForAllTy (cvtBinder b) (cvtType t)
+#elif MIN_VERSION_ghc(8,0,0)
+cvtType (Type.ForAllTy (Named b _) t) = Ast.ForAllTy (cvtBinder b) (cvtType t)
+cvtType (Type.ForAllTy (Anon _) t)    = cvtType t
+#else
+cvtType (Type.ForAllTy b t)    = Ast.ForAllTy (cvtBinder b) (cvtType t)
+#endif
+cvtType (Type.LitTy _)         = Ast.LitTy
+#if MIN_VERSION_ghc(8,0,0)
+cvtType (Type.CastTy t _)      = cvtType t
+cvtType (Type.CoercionTy _)    = Ast.CoercionTy
+#endif
+
+cvtTyCon :: TyCon.TyCon -> Ast.TyCon
+cvtTyCon tc = TyCon (occNameToText $ getOccName tc) (cvtUnique $ tyConUnique tc)
diff --git a/GhcDump/Plugin.hs b/GhcDump/Plugin.hs
new file mode 100644
--- /dev/null
+++ b/GhcDump/Plugin.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE CPP #-}
+
+module GhcDump.Plugin where
+
+import Data.Maybe
+import qualified Data.ByteString.Lazy as BSL
+import qualified Codec.Serialise as Ser
+import GhcPlugins hiding (TB)
+import CoreMonad (pprPassDetails)
+import ErrUtils (showPass)
+import Text.Printf
+import System.FilePath
+
+import GhcDump.Convert
+
+plugin :: Plugin
+plugin = defaultPlugin { installCoreToDos = install }
+
+install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
+install _opts todo = do
+    dflags <- getDynFlags
+    return (intersperseDumps dflags todo)
+
+intersperseDumps :: DynFlags -> [CoreToDo] -> [CoreToDo]
+intersperseDumps dflags = go 0 "desugar"
+  where
+    go n phase (todo : rest) = pass n phase : todo : go (n+1) phase' rest
+      where phase' = showSDocDump dflags (ppr todo <> text ":" <+> pprPassDetails todo)
+    go n phase [] = [pass n phase]
+
+    pass n phase = CoreDoPluginPass "DumpCore" (liftIO . dumpIn dflags n phase)
+
+dumpIn :: DynFlags -> Int -> String -> ModGuts -> IO ModGuts
+dumpIn dflags n phase guts = do
+    let prefix = fromMaybe "dump" $ dumpPrefix dflags
+        fname = printf "%spass-%04u.cbor" prefix n
+    showPass dflags $ "GhcDump: Dumping core to "++fname
+    let in_dump_dir = maybe id (</>) (dumpDir dflags)
+    BSL.writeFile (in_dump_dir fname) $ Ser.serialise (cvtModule phase guts)
+    return guts
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Ben Gamari
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Ben Gamari nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/ghc-dump-core.cabal b/ghc-dump-core.cabal
new file mode 100644
--- /dev/null
+++ b/ghc-dump-core.cabal
@@ -0,0 +1,43 @@
+name:                ghc-dump-core
+version:             0.1.0.0
+synopsis:            An AST and compiler plugin for dumping GHC's Core representation.
+description:
+  @ghc-dump@ is a library, GHC plugin, and set of tools for recording and
+  analysing GHC's Core representation. The plugin is compatible with GHC 7.10
+  through 8.3, exporting a consistent (albeit somewhat lossy) representation
+  across these versions. The AST is encoded as CBOR, which is small and easy to
+  deserialise.
+  .
+  This package provides the AST and compiler plugin. See the @ghc-dump-util@
+  package for a useful command-line tool for working with dumps produced by this
+  plugin.
+  .
+  = Usage
+  .
+  "GhcDump.Plugin" provides a Core-to-Core plugin which dumps a representation
+  of the Core AST to a file after every Core-to-Core pass. To use it, simply
+  install this package and add @-fplugin GhcDump.Plugin@ to your GHC
+  command-line. See the [README](https://github.com/bgamari/ghc-dump)
+  for further analysis tips.
+
+license:             BSD3
+license-file:        LICENSE
+author:              Ben Gamari
+maintainer:          ben@well-typed.com
+copyright:           (c) 2017 Ben Gamari
+category:            Development
+build-type:          Simple
+tested-with:         GHC==7.10.3, GHC==8.0.2, GHC==8.2.2
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     GhcDump.Convert, GhcDump.Ast, GhcDump.Plugin
+  ghc-options:         -Wall
+  other-extensions:    GeneralizedNewtypeDeriving
+  build-depends:       base >=4.8 && <4.11,
+                       bytestring >= 0.10,
+                       text >=1.2 && <1.3,
+                       filepath >= 1.4,
+                       serialise >= 0.2 && <0.3,
+                       ghc >= 7.10 && < 8.4
+  default-language:    Haskell2010
