Remove InlinePlease and add inline function and RULE
authorsimonpj@microsoft.com <unknown>
Mon, 5 Jun 2006 11:49:00 +0000 (11:49 +0000)
committersimonpj@microsoft.com <unknown>
Mon, 5 Jun 2006 11:49:00 +0000 (11:49 +0000)
For a long time GHC has had some internal mechanism designed to support
a call-site inline directive, thus
inline f xs
makes f be inlined at the call site even if f is big.

However, the surface syntax seems to have gone, and in any case it
can be done more neatly using a RULE.

This commit:
  * Removes the InlineCall constructor for Note
    and InlinePlease for SimplCont

  * Adds a new known-key Id called 'inline', whose definition in
    GHC.Base is just the identity function

  * Adds a built-in RULE in PrelRules that rewrites (inline f) to
    the body of f, if possible

  * Adds documentation

NOTE: I have not tested this (aeroplane work).  Give it a try!

17 files changed:
compiler/coreSyn/CorePrep.lhs
compiler/coreSyn/CoreSyn.lhs
compiler/coreSyn/CoreUnfold.lhs
compiler/coreSyn/CoreUtils.lhs
compiler/coreSyn/MkExternalCore.lhs
compiler/coreSyn/PprCore.lhs
compiler/deSugar/DsCCall.lhs
compiler/iface/BinIface.hs
compiler/iface/IfaceSyn.lhs
compiler/iface/TcIface.lhs
compiler/prelude/PrelNames.lhs
compiler/prelude/PrelRules.lhs
compiler/simplCore/FloatIn.lhs
compiler/simplCore/SimplUtils.lhs
compiler/simplCore/Simplify.lhs
compiler/specialise/Rules.lhs
docs/users_guide/glasgow_exts.xml

index e5165f0..105d248 100644 (file)
@@ -498,7 +498,6 @@ corePrepExprFloat env expr@(App _ _)
          ty = exprType fun
 
     ignore_note        (CoreNote _) = True 
-    ignore_note        InlineCall   = True
     ignore_note        InlineMe     = True
     ignore_note        _other       = False
        -- We don't ignore SCCs, since they require some code generation
index 201d866..331a890 100644 (file)
@@ -123,9 +123,6 @@ data Note
        Type            -- The to-type:   type of whole coerce expression
        Type            -- The from-type: type of enclosed expression
 
-  | InlineCall         -- Instructs simplifier to inline
-                       -- the enclosed call
-
   | InlineMe           -- Instructs simplifer to treat the enclosed expression
                        -- as very small, and inline it at its call sites
 
index e50f304..169c4ec 100644 (file)
@@ -500,7 +500,6 @@ StrictAnal.addStrictnessInfoToTopId
 \begin{code}
 callSiteInline :: DynFlags
               -> Bool                  -- True <=> the Id can be inlined
-              -> Bool                  -- 'inline' note at call site
               -> OccInfo
               -> Id                    -- The Id
               -> [Bool]                -- One for each value arg; True if it is interesting
@@ -508,7 +507,7 @@ callSiteInline :: DynFlags
               -> Maybe CoreExpr        -- Unfolding, if any
 
 
-callSiteInline dflags active_inline inline_call occ id arg_infos interesting_cont
+callSiteInline dflags active_inline occ id arg_infos interesting_cont
   = case idUnfolding id of {
        NoUnfolding -> Nothing ;
        OtherCon cs -> Nothing ;
@@ -547,9 +546,6 @@ callSiteInline dflags active_inline inline_call occ id arg_infos interesting_con
                -- consider_safe decides whether it's a good idea to
                -- inline something, given that there's no
                -- work-duplication issue (the caller checks that).
-         | inline_call  = True
-
-         | otherwise
          = case guidance of
              UnfoldNever  -> False
              UnfoldIfGoodArgs n_vals_wanted arg_discounts size res_discount
index f82435b..00cce7e 100644 (file)
@@ -165,12 +165,6 @@ mkNote (SCC cc)    expr               = mkSCC cc expr
 mkNote InlineMe expr              = mkInlineMe expr
 mkNote note     expr              = Note note expr
 #endif
-
--- Slide InlineCall in around the function
---     No longer necessary I think (SLPJ Apr 99)
--- mkNote InlineCall (App f a) = App (mkNote InlineCall f) a
--- mkNote InlineCall (Var v)   = Note InlineCall (Var v)
--- mkNote InlineCall expr      = expr
 \end{code}
 
 Drop trivial InlineMe's.  This is somewhat important, because if we have an unfolding
@@ -1106,7 +1100,6 @@ eq_alt env (c1,vs1,r1) (c2,vs2,r2) = c1==c2 && tcEqExprX (rnBndrs2 env vs1  vs2)
 
 eq_note env (SCC cc1)      (SCC cc2)      = cc1 == cc2
 eq_note env (Coerce t1 f1) (Coerce t2 f2) = tcEqTypeX env t1 t2 && tcEqTypeX env f1 f2
-eq_note env InlineCall     InlineCall     = True
 eq_note env (CoreNote s1)  (CoreNote s2)  = s1 == s2
 eq_note env other1            other2     = False
 \end{code}
@@ -1136,7 +1129,6 @@ exprSize (Type t)        = seqType t `seq` 1
 
 noteSize (SCC cc)       = cc `seq` 1
 noteSize (Coerce t1 t2) = seqType t1 `seq` seqType t2 `seq` 1
-noteSize InlineCall     = 1
 noteSize InlineMe       = 1
 noteSize (CoreNote s)   = s `seq` 1  -- hdaume: core annotations
 
index b4970cc..ce09288 100644 (file)
@@ -117,7 +117,6 @@ make_exp (Let b e) = C.Let (make_vdef b) (make_exp e)
 make_exp (Case e v ty alts) = C.Case (make_exp e) (make_vbind v) (make_ty ty) (map make_alt alts)
 make_exp (Note (SCC cc) e) = C.Note "SCC"  (make_exp e) -- temporary
 make_exp (Note (Coerce t_to t_from) e) = C.Coerce (make_ty t_to) (make_exp e)
-make_exp (Note InlineCall e) = C.Note "InlineCall" (make_exp e)
 make_exp (Note (CoreNote s) e) = C.Note s (make_exp e)  -- hdaume: core annotations
 make_exp (Note InlineMe e) = C.Note "InlineMe" (make_exp e)
 make_exp _ = error "MkExternalCore died: make_exp"
index c834abb..0e3b82d 100644 (file)
@@ -232,9 +232,6 @@ ppr_expr add_par (Note (Coerce to_ty from_ty) expr)
         pprParendExpr expr]
 #endif
 
-ppr_expr add_par (Note InlineCall expr)
-  = add_par (ptext SLIT("__inline_call") <+> pprParendExpr expr)
-
 ppr_expr add_par (Note InlineMe expr)
   = add_par $ ptext SLIT("__inline_me") <+> pprParendExpr expr
 
index 8467539..2ee9d08 100644 (file)
@@ -32,7 +32,7 @@ import Type           ( Type, isUnLiftedType, mkFunTys, mkFunTy,
                          tyVarsOfType, mkForAllTys, mkTyConApp, 
                          isPrimitiveType, splitTyConApp_maybe, 
                          splitRecNewType_maybe, splitForAllTy_maybe,
-                         isUnboxedTupleType, coreView
+                         isUnboxedTupleType
                        )
 
 import PrimOp          ( PrimOp(..) )
@@ -51,7 +51,7 @@ import TysWiredIn     ( unitDataConId,
                        )
 import BasicTypes       ( Boxity(..) )
 import Literal         ( mkMachInt )
-import PrelNames       ( Unique, hasKey, ioTyConKey, boolTyConKey, unitTyConKey,
+import PrelNames       ( Unique, hasKey, boolTyConKey, unitTyConKey,
                          int8TyConKey, int16TyConKey, int32TyConKey,
                          word8TyConKey, word16TyConKey, word32TyConKey
                          -- dotnet interop
index a31988a..6b56119 100644 (file)
@@ -864,8 +864,6 @@ instance Binary IfaceNote where
     put_ bh (IfaceCoerce ab) = do
            putByte bh 1
            put_ bh ab
-    put_ bh IfaceInlineCall = do
-           putByte bh 2
     put_ bh IfaceInlineMe = do
            putByte bh 3
     put_ bh (IfaceCoreNote s) = do
@@ -878,7 +876,6 @@ instance Binary IfaceNote where
                      return (IfaceSCC aa)
              1 -> do ab <- get bh
                      return (IfaceCoerce ab)
-             2 -> do return IfaceInlineCall
              3 -> do return IfaceInlineMe
               _ -> do ac <- get bh
                       return (IfaceCoreNote ac)
index 5309367..608e62a 100644 (file)
@@ -215,7 +215,6 @@ data IfaceExpr
 
 data IfaceNote = IfaceSCC CostCentre
               | IfaceCoerce IfaceType
-              | IfaceInlineCall
               | IfaceInlineMe
                | IfaceCoreNote String
 
@@ -411,7 +410,6 @@ pprIfaceApp fun                    args = sep (pprIfaceExpr parens fun : args)
 instance Outputable IfaceNote where
     ppr (IfaceSCC cc)     = pprCostCentreCore cc
     ppr (IfaceCoerce ty)  = ptext SLIT("__coerce") <+> pprParendIfaceType ty
-    ppr IfaceInlineCall   = ptext SLIT("__inline_call")
     ppr IfaceInlineMe     = ptext SLIT("__inline_me")
     ppr (IfaceCoreNote s) = ptext SLIT("__core_note") <+> pprHsString (mkFastString s)
 
@@ -661,7 +659,6 @@ toIfaceExpr ext (Note n e)    = IfaceNote (toIfaceNote ext n) (toIfaceExpr ext e
 ---------------------
 toIfaceNote ext (SCC cc)      = IfaceSCC cc
 toIfaceNote ext (Coerce t1 _) = IfaceCoerce (toIfaceType ext t1)
-toIfaceNote ext InlineCall    = IfaceInlineCall
 toIfaceNote ext InlineMe      = IfaceInlineMe
 toIfaceNote ext (CoreNote s)  = IfaceCoreNote s
 
@@ -906,7 +903,6 @@ eq_ifaceConAlt _ _ = False
 eq_ifaceNote :: EqEnv -> IfaceNote -> IfaceNote -> IfaceEq
 eq_ifaceNote env (IfaceSCC c1)    (IfaceSCC c2)        = bool (c1==c2)
 eq_ifaceNote env (IfaceCoerce t1) (IfaceCoerce t2)     = eq_ifType env t1 t2
-eq_ifaceNote env IfaceInlineCall  IfaceInlineCall      = Equal
 eq_ifaceNote env IfaceInlineMe    IfaceInlineMe        = Equal
 eq_ifaceNote env (IfaceCoreNote s1) (IfaceCoreNote s2) = bool (s1==s2)
 eq_ifaceNote env _ _ = NotEqual
index caff95f..7c4c535 100644 (file)
@@ -639,7 +639,6 @@ tcIfaceExpr (IfaceNote note expr)
        IfaceCoerce to_ty -> tcIfaceType to_ty  `thenM` \ to_ty' ->
                             returnM (Note (Coerce to_ty'
                                                    (exprType expr')) expr')
-       IfaceInlineCall   -> returnM (Note InlineCall expr')
        IfaceInlineMe     -> returnM (Note InlineMe   expr')
        IfaceSCC cc       -> returnM (Note (SCC cc)   expr')
        IfaceCoreNote n   -> returnM (Note (CoreNote n) expr')
index 3d57033..9fd1419 100644 (file)
@@ -453,6 +453,9 @@ unpackCStringFoldrName  = varQual pREL_BASE FSLIT("unpackFoldrCString#") unpackC
 unpackCStringUtf8Name   = varQual pREL_BASE FSLIT("unpackCStringUtf8#") unpackCStringUtf8IdKey
 eqStringName           = varQual pREL_BASE FSLIT("eqString")  eqStringIdKey
 
+-- The 'inline' function
+inlineIdName           = varQual pREL_BASE FSLIT("inline") inlineIdKey
+
 -- Base classes (Eq, Ord, Functor)
 eqClassName      = clsQual pREL_BASE FSLIT("Eq")      eqClassKey
 eqName           = methName eqClassName FSLIT("==")   eqClassOpKey
@@ -911,6 +914,8 @@ breakpointCondIdKey           = mkPreludeMiscIdUnique 63
 breakpointJumpIdKey           = mkPreludeMiscIdUnique 64
 breakpointCondJumpIdKey       = mkPreludeMiscIdUnique 65
 
+inlineIdKey                  = mkPreludeMiscIdUnique 66
+
 -- Parallel array functions
 nullPIdKey                   = mkPreludeMiscIdUnique 80
 lengthPIdKey                 = mkPreludeMiscIdUnique 81
index 9cdddc9..ae26f84 100644 (file)
@@ -20,7 +20,7 @@ module PrelRules ( primOpRules, builtinRules ) where
 #include "HsVersions.h"
 
 import CoreSyn
-import Id              ( mkWildId, isPrimOpId_maybe )
+import Id              ( mkWildId, isPrimOpId_maybe, idUnfolding )
 import Literal         ( Literal(..), mkMachInt, mkMachWord
                        , literalType
                        , word2IntLit, int2WordLit
@@ -38,7 +38,7 @@ import CoreUtils      ( cheapEqExpr, exprIsConApp_maybe )
 import Type            ( tyConAppTyCon, coreEqType )
 import OccName         ( occNameFS )
 import PrelNames       ( unpackCStringFoldrName, unpackCStringFoldrIdKey, hasKey,
-                         eqStringName, unpackCStringIdKey )
+                         eqStringName, unpackCStringIdKey, inlineIdName )
 import Maybes          ( orElse )
 import Name            ( Name )
 import Outputable
@@ -409,10 +409,12 @@ builtinRules :: [CoreRule]
 -- Rules for non-primops that can't be expressed using a RULE pragma
 builtinRules
   = [ BuiltinRule FSLIT("AppendLitString") unpackCStringFoldrName match_append_lit,
-      BuiltinRule FSLIT("EqString") eqStringName match_eq_string
+      BuiltinRule FSLIT("EqString") eqStringName match_eq_string,
+      BuiltinRule FSLIT("Inline") inlineIdName match_inline
     ]
 
 
+---------------------------------------------------
 -- The rule is this:
 --     unpackFoldrCString# "foo" c (unpackFoldrCString# "baz" c n)  =  unpackFoldrCString# "foobaz" c n
 
@@ -434,6 +436,7 @@ match_append_lit [Type ty1,
 
 match_append_lit other = Nothing
 
+---------------------------------------------------
 -- The rule is this:
 --     eqString (unpackCString# (Lit s1)) (unpackCString# (Lit s2) = s1==s2
 
@@ -444,4 +447,16 @@ match_eq_string [Var unpk1 `App` Lit (MachStr s1),
   = Just (if s1 == s2 then trueVal else falseVal)
 
 match_eq_string other = Nothing
+
+
+---------------------------------------------------
+-- The rule is this:
+--     inline (f a b c) = <f's unfolding> a b c
+-- (if f has an unfolding)
+match_inline (e:args2)
+  | (Var f, args1) <- collectArgs e,
+    Just unf <- maybeUnfoldingTemplate (idUnfolding f)
+  = Just (mkApps (mkApps unf args1) args2)
+
+match_inline other = Nothing
 \end{code}             
index 0e8edb5..0d4e397 100644 (file)
@@ -208,11 +208,6 @@ fiExpr to_drop (_, AnnNote note@(SCC cc) expr)
   =    -- Wimp out for now
     mkCoLets' to_drop (Note note (fiExpr [] expr))
 
-fiExpr to_drop (_, AnnNote InlineCall expr)
-  =    -- Wimp out for InlineCall; keep it close
-       -- the the call it annotates
-    mkCoLets' to_drop (Note InlineCall (fiExpr [] expr))
-
 fiExpr to_drop (_, AnnNote InlineMe expr)
   =    -- Ditto... don't float anything into an INLINE expression
     mkCoLets' to_drop (Note InlineMe (fiExpr [] expr))
index 265ded6..1e51042 100644 (file)
@@ -78,9 +78,6 @@ data SimplCont                -- Strict contexts
   | CoerceIt OutType                   -- The To-type, simplified
             SimplCont
 
-  | InlinePlease                       -- This continuation makes a function very
-            SimplCont                  -- keen to inline itelf
-
   | ApplyTo  DupFlag 
             InExpr SimplEnv            -- The argument, as yet unsimplified, 
             SimplCont                  -- and its environment
@@ -116,7 +113,6 @@ instance Outputable SimplCont where
   ppr (Select dup bndr alts se cont) = (ptext SLIT("Select") <+> ppr dup <+> ppr bndr) $$ 
                                       (nest 4 (ppr alts)) $$ ppr cont
   ppr (CoerceIt ty cont)            = (ptext SLIT("CoerceIt") <+> ppr ty) $$ ppr cont
-  ppr (InlinePlease cont)           = ptext SLIT("InlinePlease") $$ ppr cont
 
 data DupFlag = OkToDup | NoDup
 
@@ -150,14 +146,12 @@ contIsDupable (Stop _ _ _)                 = True
 contIsDupable (ApplyTo  OkToDup _ _ _)   = True
 contIsDupable (Select   OkToDup _ _ _ _) = True
 contIsDupable (CoerceIt _ cont)          = contIsDupable cont
-contIsDupable (InlinePlease cont)        = contIsDupable cont
 contIsDupable other                     = False
 
 -------------------
 discardableCont :: SimplCont -> Bool
 discardableCont (Stop _ _ _)       = False
 discardableCont (CoerceIt _ cont)   = discardableCont cont
-discardableCont (InlinePlease cont) = discardableCont cont
 discardableCont other              = True
 
 discardCont :: SimplCont       -- A continuation, expecting
@@ -174,7 +168,6 @@ contResultType (Stop to_ty _ _)          = to_ty
 contResultType (ArgOf _ _ to_ty _)   = to_ty
 contResultType (ApplyTo _ _ _ cont)  = contResultType cont
 contResultType (CoerceIt _ cont)     = contResultType cont
-contResultType (InlinePlease cont)   = contResultType cont
 contResultType (Select _ _ _ _ cont) = contResultType cont
 
 -------------------
@@ -199,8 +192,7 @@ pushContArgs env (arg : args) cont = ApplyTo NoDup arg env (pushContArgs env arg
 getContArgs :: SwitchChecker
            -> OutId -> SimplCont 
            -> ([(InExpr, SimplEnv, Bool)],     -- Arguments; the Bool is true for strict args
-               SimplCont,                      -- Remaining continuation
-               Bool)                           -- Whether we came across an InlineCall
+               SimplCont)                      -- Remaining continuation
 -- getContArgs id k = (args, k', inl)
 --     args are the leading ApplyTo items in k
 --     (i.e. outermost comes first)
@@ -213,22 +205,18 @@ getContArgs chkr fun orig_cont
        stricts | switchIsOn chkr NoCaseOfCase = vanilla_stricts
                | otherwise                    = computed_stricts
     in
-    go [] stricts False orig_cont
+    go [] stricts orig_cont
   where
     ----------------------------
 
        -- Type argument
-    go acc ss inl (ApplyTo _ arg@(Type _) se cont)
-       = go ((arg,se,False) : acc) ss inl cont
+    go acc ss (ApplyTo _ arg@(Type _) se cont)
+       = go ((arg,se,False) : acc) ss cont
                -- NB: don't bother to instantiate the function type
 
        -- Value argument
-    go acc (s:ss) inl (ApplyTo _ arg se cont)
-       = go ((arg,se,s) : acc) ss inl cont
-
-       -- An Inline continuation
-    go acc ss inl (InlinePlease cont)
-       = go acc ss True cont
+    go acc (s:ss) (ApplyTo _ arg se cont)
+       = go ((arg,se,s) : acc) ss cont
 
        -- We're run out of arguments, or else we've run out of demands
        -- The latter only happens if the result is guaranteed bottom
@@ -240,9 +228,9 @@ getContArgs chkr fun orig_cont
        -- Then, especially in the first of these cases, we'd like to discard
        -- the continuation, leaving just the bottoming expression.  But the
        -- type might not be right, so we may have to add a coerce.
-    go acc ss inl cont 
-       | null ss && discardableCont cont = (reverse acc, discardCont cont, inl)
-       | otherwise                       = (reverse acc, cont,             inl)
+    go acc ss cont 
+       | null ss && discardableCont cont = (reverse acc, discardCont cont)
+       | otherwise                       = (reverse acc, cont)
 
     ----------------------------
     vanilla_stricts, computed_stricts :: [Bool]
@@ -386,7 +374,6 @@ interestingCallContext :: Bool              -- False <=> no args at all
 interestingCallContext some_args some_val_args cont
   = interesting cont
   where
-    interesting (InlinePlease _)         = True
     interesting (Select _ _ _ _ _)       = some_args
     interesting (ApplyTo _ _ _ _)        = True        -- Can happen if we have (coerce t (f x)) y
                                                -- Perhaps True is a bit over-keen, but I've
@@ -431,7 +418,6 @@ interestingArgContext :: Id -> SimplCont -> Bool
 interestingArgContext fn cont
   = idHasRules fn || go cont
   where
-    go (InlinePlease c)       = go c
     go (Select {})           = False
     go (ApplyTo {})          = False
     go (ArgOf {})            = True
index 329d326..d8f9506 100644 (file)
@@ -869,9 +869,6 @@ simplNote env (SCC cc) e cont
   = simplExpr (setEnclosingCC env currentCCS) e        `thenSmpl` \ e' ->
     rebuild env (mkSCC cc e') cont
 
-simplNote env InlineCall e cont
-  = simplExprF env e (InlinePlease cont)
-
 -- See notes with SimplMonad.inlineMode
 simplNote env InlineMe e cont
   | contIsRhsOrArg cont                -- Totally boring continuation; see notes above
@@ -919,9 +916,9 @@ completeCall env var occ_info cont
   =     -- Simplify the arguments
     getDOptsSmpl                                       `thenSmpl` \ dflags ->
     let
-       chkr                           = getSwitchChecker env
-       (args, call_cont, inline_call) = getContArgs chkr var cont
-       fn_ty                          = idType var
+       chkr              = getSwitchChecker env
+       (args, call_cont) = getContArgs chkr var cont
+       fn_ty             = idType var
     in
     simplifyArgs env fn_ty (interestingArgContext var call_cont) args 
                 (contResultType call_cont)     $ \ env args ->
@@ -981,7 +978,7 @@ completeCall env var occ_info cont
                                                  (notNull arg_infos)
                                                  call_cont
        active_inline = activeInline env var occ_info
-       maybe_inline  = callSiteInline dflags active_inline inline_call occ_info
+       maybe_inline  = callSiteInline dflags active_inline occ_info
                                       var arg_infos interesting_cont
     in
     case maybe_inline of {
@@ -1255,7 +1252,6 @@ rebuild :: SimplEnv -> OutExpr -> SimplCont -> SimplM FloatsWithExpr
 rebuild env expr (Stop _ _ _)                = rebuildDone env expr
 rebuild env expr (ArgOf _ _ _ cont_fn)       = cont_fn env expr
 rebuild env expr (CoerceIt to_ty cont)       = rebuild env (mkCoerce to_ty expr) cont
-rebuild env expr (InlinePlease cont)         = rebuild env (Note InlineCall expr) cont
 rebuild env expr (Select _ bndr alts se cont) = rebuildCase (setInScope se env) expr bndr alts cont
 rebuild env expr (ApplyTo _ arg se cont)      = rebuildApp  (setInScope se env) expr arg cont
 
@@ -1806,10 +1802,6 @@ mkDupableCont env (CoerceIt ty cont)
   = mkDupableCont env cont             `thenSmpl` \ (floats, (dup_cont, nondup_cont)) ->
     returnSmpl (floats, (CoerceIt ty dup_cont, nondup_cont))
 
-mkDupableCont env (InlinePlease cont)
-  = mkDupableCont env cont             `thenSmpl` \ (floats, (dup_cont, nondup_cont)) ->
-    returnSmpl (floats, (InlinePlease dup_cont, nondup_cont))
-
 mkDupableCont env cont@(ArgOf _ arg_ty _ _)
   =  returnSmpl (emptyFloats env, (mkBoringStop arg_ty, cont))
        -- Do *not* duplicate an ArgOf continuation
index b12147d..f70266e 100644 (file)
@@ -368,7 +368,7 @@ matchN in_scope tmpl_vars tmpl_es target_es
 --   from nested matches; see the Let case of match, below
 --
 type SubstEnv   = (TvSubstEnv, IdSubstEnv, OrdList CoreBind)
-type IdSubstEnv = IdEnv    CoreExpr            
+type IdSubstEnv = IdEnv CoreExpr               
 
 emptySubstEnv :: SubstEnv
 emptySubstEnv = (emptyVarEnv, emptyVarEnv, nilOL)
index f9cb3f7..8a21368 100644 (file)
@@ -5989,6 +5989,74 @@ r) ->
 
 </sect1>
 
+<sect1 id="special-ids">
+<title>Special built-in functions</title>
+<para>GHC has a few built-in funcions with special behaviour, 
+described in this section.  All are exported by
+<literal>GHC.Exts</literal>.</para>
+
+<sect2> <title>The <literal>inline</literal> function </title>
+<para>
+The <literal>inline</literal> function is somewhat experimental.
+<programlisting>
+  inline :: a -> a
+</programlisting>
+The call <literal>(inline f)</literal> arranges that <literal>f</literal> 
+is inlined, regardless of its size.  More precisely, the call
+<literal>(inline f)</literal> rewrites to the right-hand side of <literal>f</literal>'s 
+definition.  
+This allows the programmer to control inlining from 
+a particular <emphasis>call site</emphasis>
+rather than the <emphasis>definition site</emphasis> of the function 
+(c.f. <literal>INLINE</literal> pragmas <xref linkend="inline-noinline-pragma"/>).
+</para>
+<para>
+This inlining occurs regardless of the argument to the call
+or the size of <literal>f</literal>'s definition; it is unconditional.
+The main caveat is that <literal>f</literal>'s definition must be
+visible to the compiler.  That is, <literal>f</literal> must be
+let-bound in the current scope.
+If no inlining takes place, the <literal>inline</literal> function
+expands to the identity function in Phase zero; so its use imposes
+no overhead.</para>
+
+<para> If the function is defined in another
+module, GHC only exposes its inlining in the interface file if the
+function is sufficiently small that it <emphasis>might</emphasis> be
+inlined by the automatic mechanism.  There is currently no way to tell
+GHC to expose arbitrarily-large functions in the interface file.  (This
+shortcoming is something that could be fixed, with some kind of pragma.)
+</para>
+</sect2>
+
+<sect2> <title>The <literal>inline</literal> function </title>
+<para>
+The <literal>lazy</literal> function restrains strictness analysis a little:
+<programlisting>
+  lazy :: a -> a
+</programlisting>
+The call <literal>(lazy e)</literal> means the same as <literal>e</literal>, 
+but <literal>lazy</literal> has a magical property so far as strictness
+analysis is concerned: it is lazy in its first argument,
+even though its semantics is strict.  After strictness analysis has run,
+calls to <literal>lazy</literal> are inlined to be the identity function.
+</para>
+<para>
+This behaviour is occasionally useful when controlling evaluation order.
+Notably, <literal>lazy</literal> is used in the library definition of
+<literal>Control.Parallel.par</literal>:
+<programlisting>
+  par :: a -> b -> b
+  par x y = case (par# x) of { _ -> lazy y }
+</programlisting>
+If <literal>lazy</literal> were not lazy, <literal>par</literal> would
+look strict in <literal>y</literal> which would defeat the whole 
+purpose of <literal>par</literal>.
+</para>
+</sect2>
+</sect1>
+
+
 <sect1 id="generic-classes">
 <title>Generic classes</title>