Support for noinline magic function.
authorEdward Z. Yang <ezyang@cs.stanford.edu>
Fri, 13 May 2016 02:38:57 +0000 (19:38 -0700)
committerEdward Z. Yang <ezyang@cs.stanford.edu>
Sun, 21 Aug 2016 07:53:21 +0000 (00:53 -0700)
Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
Test Plan: validate

Reviewers: simonpj, austin, bgamari

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D2209

compiler/basicTypes/MkId.hs
compiler/coreSyn/CorePrep.hs
compiler/prelude/PrelNames.hs
docs/users_guide/8.2.1-notes.rst
libraries/ghc-prim/GHC/Magic.hs
testsuite/tests/simplCore/should_compile/all.T
testsuite/tests/simplCore/should_compile/noinline01.hs [new file with mode: 0644]
testsuite/tests/simplCore/should_compile/noinline01.stderr [new file with mode: 0644]

index 151e1cf..649100a 100644 (file)
@@ -31,7 +31,7 @@ module MkId (
         voidPrimId, voidArgId,
         nullAddrId, seqId, lazyId, lazyIdKey, runRWId,
         coercionTokenId, magicDictId, coerceId,
-        proxyHashId,
+        proxyHashId, noinlineIdName,
 
         -- Re-export error Ids
         module PrelRules
@@ -112,6 +112,9 @@ There are several reasons why an Id might appear in the wiredInIds:
 (4) lazyId is wired in because the wired-in version overrides the
     strictness of the version defined in GHC.Base
 
+(5) noinlineId is wired in because when we serialize to interfaces
+    we may insert noinline statements.
+
 In cases (2-4), the function has a definition in a library module, and
 can be called; but the wired-in version means that the details are
 never read from that module's interface file; instead, the full definition
@@ -120,7 +123,7 @@ is right here.
 
 wiredInIds :: [Id]
 wiredInIds
-  =  [lazyId, dollarId, oneShotId, runRWId]
+  =  [lazyId, dollarId, oneShotId, runRWId, noinlineId]
   ++ errorIds           -- Defined in MkCore
   ++ ghcPrimIds
 
@@ -1039,7 +1042,7 @@ another gun with which to shoot yourself in the foot.
 lazyIdName, unsafeCoerceName, nullAddrName, seqName,
    realWorldName, voidPrimIdName, coercionTokenName,
    magicDictName, coerceName, proxyName, dollarName, oneShotName,
-   runRWName :: Name
+   runRWName, noinlineIdName :: Name
 unsafeCoerceName  = mkWiredInIdName gHC_PRIM  (fsLit "unsafeCoerce#")  unsafeCoerceIdKey  unsafeCoerceId
 nullAddrName      = mkWiredInIdName gHC_PRIM  (fsLit "nullAddr#")      nullAddrIdKey      nullAddrId
 seqName           = mkWiredInIdName gHC_PRIM  (fsLit "seq")            seqIdKey           seqId
@@ -1053,6 +1056,7 @@ proxyName         = mkWiredInIdName gHC_PRIM  (fsLit "proxy#")         proxyHash
 dollarName        = mkWiredInIdName gHC_BASE  (fsLit "$")              dollarIdKey        dollarId
 oneShotName       = mkWiredInIdName gHC_MAGIC (fsLit "oneShot")        oneShotKey         oneShotId
 runRWName         = mkWiredInIdName gHC_MAGIC (fsLit "runRW#")         runRWKey           runRWId
+noinlineIdName    = mkWiredInIdName gHC_MAGIC (fsLit "noinline") noinlineIdKey noinlineId
 
 dollarId :: Id  -- Note [dollarId magic]
 dollarId = pcMiscPrelId dollarName ty
@@ -1159,6 +1163,12 @@ lazyId = pcMiscPrelId lazyIdName ty info
     info = noCafIdInfo
     ty  = mkSpecForAllTys [alphaTyVar] (mkFunTy alphaTy alphaTy)
 
+noinlineId :: Id -- See Note [noinlineId magic]
+noinlineId = pcMiscPrelId noinlineIdName ty info
+  where
+    info = noCafIdInfo
+    ty  = mkSpecForAllTys [alphaTyVar] (mkFunTy alphaTy alphaTy)
+
 oneShotId :: Id -- See Note [The oneShot function]
 oneShotId = pcMiscPrelId oneShotName ty info
   where
@@ -1362,6 +1372,22 @@ Implementing 'lazy' is a bit tricky:
 * lazyId is defined in GHC.Base, so we don't *have* to inline it.  If it
   appears un-applied, we'll end up just calling it.
 
+Note [noinlineId magic]
+~~~~~~~~~~~~~~~~~~~~~~~
+noinline :: forall a. a -> a
+
+'noinline' is used to make sure that a function f is never inlined,
+e.g., as in 'noinline f x'.  Ordinarily, the identity function with NOINLINE
+could be used to achieve this effect; however, this has the unfortunate
+result of leaving a (useless) call to noinline at runtime.  So we have
+a little bit of magic to optimize away 'noinline' after we are done
+running the simplifier.
+
+'noinline' needs to be wired-in because it gets inserted automatically
+when we serialize an expression to the interface format, and we DON'T
+want use its fingerprints.
+
+
 Note [runRW magic]
 ~~~~~~~~~~~~~~~~~~
 Some definitions, for instance @runST@, must have careful control over float out
index 320a989..8e9c01a 100644 (file)
@@ -110,6 +110,7 @@ The goal of this pass is to prepare for code generation.
     aren't inlined by some caller.
 
 9.  Replace (lazy e) by e.  See Note [lazyId magic] in MkId.hs
+    Also replace (noinline e) by e.
 
 10. Convert (LitInteger i t) into the core representation
     for the Integer i. Normally this uses mkInteger, but if
@@ -517,7 +518,8 @@ cpeRhsE _env expr@(Lit {}) = return (emptyFloats, expr)
 cpeRhsE env expr@(Var {})  = cpeApp env expr
 
 cpeRhsE env (Var f `App` _{-type-} `App` arg)
-  | f `hasKey` lazyIdKey          -- Replace (lazy a) by a
+  | f `hasKey` lazyIdKey          -- Replace (lazy a) with a, and
+ || f `hasKey` noinlineIdKey      -- Replace (noinline a) with a
   = cpeRhsE env arg               -- See Note [lazyId magic] in MkId
 
 cpeRhsE env (Var f `App` _runtimeRep `App` _type `App` arg)
index e5e458d..00e9ffe 100644 (file)
@@ -2034,8 +2034,9 @@ breakpointJumpIdKey           = mkPreludeMiscIdUnique 113
 breakpointCondJumpIdKey       = mkPreludeMiscIdUnique 114
 breakpointAutoJumpIdKey       = mkPreludeMiscIdUnique 115
 
-inlineIdKey :: Unique
+inlineIdKey, noinlineIdKey :: Unique
 inlineIdKey                   = mkPreludeMiscIdUnique 120
+-- see below
 
 mapIdKey, groupWithIdKey, dollarIdKey :: Unique
 mapIdKey              = mkPreludeMiscIdUnique 121
@@ -2045,6 +2046,8 @@ dollarIdKey           = mkPreludeMiscIdUnique 123
 coercionTokenIdKey :: Unique
 coercionTokenIdKey    = mkPreludeMiscIdUnique 124
 
+noinlineIdKey                 = mkPreludeMiscIdUnique 125
+
 rationalToFloatIdKey, rationalToDoubleIdKey :: Unique
 rationalToFloatIdKey   = mkPreludeMiscIdUnique 130
 rationalToDoubleIdKey  = mkPreludeMiscIdUnique 131
index 0126427..d9dd74c 100644 (file)
@@ -157,6 +157,9 @@ ghc-prim
 
 -  Added new ``isByteArrayPinned#`` and ``isMutableByteArrayPinned#`` operation.
 
+-  New function ``noinline`` in ``GHC.Magic`` lets you mark that a function
+   should not be inlined.  It is optimized away after the simplifier runs.
+
 haskell98
 ~~~~~~~~~
 
index 495705b..96f1742 100644 (file)
@@ -21,7 +21,7 @@
 --
 -----------------------------------------------------------------------------
 
-module GHC.Magic ( inline, lazy, oneShot, runRW# ) where
+module GHC.Magic ( inline, noinline, lazy, oneShot, runRW# ) where
 
 import GHC.Prim
 import GHC.CString ()
@@ -45,6 +45,13 @@ import GHC.CString ()
 inline :: a -> a
 inline x = x
 
+-- | The call @noinline f@ arranges that 'f' will not be inlined.
+-- It is removed during CorePrep so that its use imposes no overhead
+-- (besides the fact that it blocks inlining.)
+{-# NOINLINE noinline #-}
+noinline :: a -> a
+noinline x = x
+
 -- | The 'lazy' function restrains strictness analysis a little. The
 -- call @lazy e@ means the same as 'e', but 'lazy' has a magical
 -- property so far as strictness analysis is concerned: it is lazy in
index ecf990c..d59fa1c 100644 (file)
@@ -241,3 +241,5 @@ test('T12076', extra_clean(['T12076a.hi', 'T12076a.o']), multimod_compile, ['T12
 test('T12076lit', normal, compile, ['-O'])
 test('T12076sat', normal, compile, ['-O'])
 test('T12212', normal, compile, ['-O'])
+
+test('noinline01', only_ways(['optasm']), compile, ['-ddump-stg -dsuppress-uniques -O'])
diff --git a/testsuite/tests/simplCore/should_compile/noinline01.hs b/testsuite/tests/simplCore/should_compile/noinline01.hs
new file mode 100644 (file)
index 0000000..255d584
--- /dev/null
@@ -0,0 +1,7 @@
+module Noinline01 where
+import GHC.Magic
+
+{-# INLINE f #-}
+f x = True
+
+g = noinline f False
diff --git a/testsuite/tests/simplCore/should_compile/noinline01.stderr b/testsuite/tests/simplCore/should_compile/noinline01.stderr
new file mode 100644 (file)
index 0000000..5dc4887
--- /dev/null
@@ -0,0 +1,29 @@
+
+==================== STG syntax: ====================
+Noinline01.f [InlPrag=INLINE (sat-args=1)]
+  :: forall t. t -> GHC.Types.Bool
+[GblId, Arity=1, Caf=NoCafRefs, Str=<L,A>, Unf=OtherCon []] =
+    \r [eta] GHC.Types.True [];
+
+Noinline01.g1 :: GHC.Types.Bool -> GHC.Types.Bool
+[GblId, Unf=OtherCon []] =
+    \u [] Noinline01.f;
+
+Noinline01.g :: GHC.Types.Bool
+[GblId] =
+    \u [] Noinline01.g1 GHC.Types.False;
+
+Noinline01.$trModule2 :: GHC.Types.TrName
+[GblId, Caf=NoCafRefs, Str=m1, Unf=OtherCon []] =
+    NO_CCS GHC.Types.TrNameS! ["main"#];
+
+Noinline01.$trModule1 :: GHC.Types.TrName
+[GblId, Caf=NoCafRefs, Str=m1, Unf=OtherCon []] =
+    NO_CCS GHC.Types.TrNameS! ["Noinline01"#];
+
+Noinline01.$trModule :: GHC.Types.Module
+[GblId, Caf=NoCafRefs, Str=m, Unf=OtherCon []] =
+    NO_CCS GHC.Types.Module! [Noinline01.$trModule2
+                              Noinline01.$trModule1];
+
+