Add new debugging flag -dinline-check
authorBen Gamari <ben@smart-cactus.org>
Mon, 26 Mar 2018 02:17:24 +0000 (22:17 -0400)
committerBen Gamari <ben@smart-cactus.org>
Mon, 26 Mar 2018 02:18:07 +0000 (22:18 -0400)
This flag reports a summary of the inlining decision for identifiers
prefixed by the flag's argument.

For example, `-dinline-check foo` will report why definitions whose
prefix is `foo` are inlined or not.

Previously the only way to get this information was to pass a
combination of `-dverbose-core2core` and `-ddump-inlinings`.

This combination led to a log of 12 million lines in a module of about
200 lines I recently had to apply it to. This flag provides a much more
direct way to find the occurence you care about.

Reviewers: osa1, dfeuer, bgamari

Reviewed By: bgamari

Subscribers: rwbarton, thomie, carter

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

compiler/coreSyn/CoreUnfold.hs
compiler/main/DynFlags.hs
docs/users_guide/debugging.rst
testsuite/tests/driver/all.T
testsuite/tests/driver/inline-check.hs [new file with mode: 0644]
testsuite/tests/driver/inline-check.stderr [new file with mode: 0644]

index 2e2b7a3..c1f7892 100644 (file)
@@ -65,8 +65,10 @@ import Bag
 import Util
 import Outputable
 import ForeignCall
+import Name
 
 import qualified Data.ByteString as BS
+import Data.List
 
 {-
 ************************************************************************
@@ -1155,14 +1157,18 @@ callSiteInline dflags id active_unfolding lone_variable arg_infos cont_info
           | active_unfolding -> tryUnfolding dflags id lone_variable
                                     arg_infos cont_info unf_template is_top
                                     is_wf is_exp guidance
-          | otherwise -> traceInline dflags "Inactive unfolding:" (ppr id) Nothing
+          | otherwise -> traceInline dflags id "Inactive unfolding:" (ppr id) Nothing
         NoUnfolding      -> Nothing
         BootUnfolding    -> Nothing
         OtherCon {}      -> Nothing
         DFunUnfolding {} -> Nothing     -- Never unfold a DFun
 
-traceInline :: DynFlags -> String -> SDoc -> a -> a
-traceInline dflags str doc result
+traceInline :: DynFlags -> Id -> String -> SDoc -> a -> a
+traceInline dflags inline_id str doc result
+ | Just prefix <- inlineCheck dflags
+ =  if prefix `isPrefixOf` occNameString (getOccName inline_id)
+      then pprTrace str doc result
+      else result
  | dopt Opt_D_dump_inlinings dflags && dopt Opt_D_verbose_core2core dflags
  = pprTrace str doc result
  | otherwise
@@ -1175,25 +1181,25 @@ tryUnfolding dflags id lone_variable
              arg_infos cont_info unf_template is_top
              is_wf is_exp guidance
  = case guidance of
-     UnfNever -> traceInline dflags str (text "UnfNever") Nothing
+     UnfNever -> traceInline dflags id str (text "UnfNever") Nothing
 
      UnfWhen { ug_arity = uf_arity, ug_unsat_ok = unsat_ok, ug_boring_ok = boring_ok }
         | enough_args && (boring_ok || some_benefit || ufVeryAggressive dflags)
                 -- See Note [INLINE for small functions (3)]
-        -> traceInline dflags str (mk_doc some_benefit empty True) (Just unf_template)
+        -> traceInline dflags id str (mk_doc some_benefit empty True) (Just unf_template)
         | otherwise
-        -> traceInline dflags str (mk_doc some_benefit empty False) Nothing
+        -> traceInline dflags id str (mk_doc some_benefit empty False) Nothing
         where
           some_benefit = calc_some_benefit uf_arity
           enough_args = (n_val_args >= uf_arity) || (unsat_ok && n_val_args > 0)
 
      UnfIfGoodArgs { ug_args = arg_discounts, ug_res = res_discount, ug_size = size }
         | ufVeryAggressive dflags
-        -> traceInline dflags str (mk_doc some_benefit extra_doc True) (Just unf_template)
+        -> traceInline dflags id str (mk_doc some_benefit extra_doc True) (Just unf_template)
         | is_wf && some_benefit && small_enough
-        -> traceInline dflags str (mk_doc some_benefit extra_doc True) (Just unf_template)
+        -> traceInline dflags id str (mk_doc some_benefit extra_doc True) (Just unf_template)
         | otherwise
-        -> traceInline dflags str (mk_doc some_benefit extra_doc False) Nothing
+        -> traceInline dflags id str (mk_doc some_benefit extra_doc False) Nothing
         where
           some_benefit = calc_some_benefit (length arg_discounts)
           extra_doc = text "discounted size =" <+> int discounted_size
index 0d018a7..ba4d281 100644 (file)
@@ -833,6 +833,7 @@ data DynFlags = DynFlags {
   maxSimplIterations    :: Int,         -- ^ Max simplifier iterations
   maxPmCheckIterations  :: Int,         -- ^ Max no iterations for pm checking
   ruleCheck             :: Maybe String,
+  inlineCheck           :: Maybe String, -- ^ A prefix to report inlining decisions about
   strictnessBefore      :: [Int],       -- ^ Additional demand analysis
 
   parMakeCount          :: Maybe Int,   -- ^ The number of modules to compile in parallel
@@ -1730,6 +1731,7 @@ defaultDynFlags mySettings myLlvmTargets =
         maxSimplIterations      = 4,
         maxPmCheckIterations    = 2000000,
         ruleCheck               = Nothing,
+        inlineCheck             = Nothing,
         maxRelevantBinds        = Just 6,
         maxValidSubstitutions   = Just 6,
         maxRefSubstitutions     = Just 6,
@@ -3403,6 +3405,8 @@ dynamic_flags_deps = [
       (noArg (\d -> d { liberateCaseThreshold = Nothing }))
   , make_ord_flag defFlag "drule-check"
       (sepArg (\s d -> d { ruleCheck = Just s }))
+  , make_ord_flag defFlag "dinline-check"
+      (sepArg (\s d -> d { inlineCheck = Just s }))
   , make_ord_flag defFlag "freduction-depth"
       (intSuffix (\n d -> d { reductionDepth = treatZeroAsInf n }))
   , make_ord_flag defFlag "fconstraint-solver-iterations"
index c6d90e6..ada0018 100644 (file)
@@ -288,6 +288,20 @@ subexpression elimination pass.
     ``-drule-check=SPEC`` will check whether there are any applications which
     might be subject to a rule created by specialisation.
 
+.. ghc-flag:: -dinline-check=⟨str⟩
+    :shortdesc: Dump information about inlining decisions
+    :type: dynamic
+
+    This flag is useful for debugging why a definition is not inlined.
+
+    When a string is passed to this flag we report information
+    about all functions whose name shares a prefix with the string.
+
+    For example, if you are inspecting the core of your program and you observe
+    that ``foo`` is not being inlined. You can pass ``-dinline-check foo`` and
+    you will see a report about why ``foo`` is not inlined.
+
+
 .. ghc-flag:: -ddump-vect
     :shortdesc: Dump vectoriser input and output
     :type: dynamic
index 976a316..c07badb 100644 (file)
@@ -273,3 +273,4 @@ test('json', normal, compile_fail, ['-ddump-json'])
 test('json2', normal, compile, ['-ddump-types -ddump-json'])
 test('T13604', [], run_command, ['$MAKE -s --no-print-directory T13604'])
 test('T13604a', [], run_command, ['$MAKE -s --no-print-directory T13604a'])
+test('inline-check', normal, compile, ['-dinline-check foo -O -ddebug-output'])
diff --git a/testsuite/tests/driver/inline-check.hs b/testsuite/tests/driver/inline-check.hs
new file mode 100644 (file)
index 0000000..83112e1
--- /dev/null
@@ -0,0 +1,12 @@
+module InlineCheck where
+
+foo = (+1)
+
+foo1 = (+1)
+{-# NOINLINE foo1 #-}
+
+qux = foo 3
+
+qux1 = foo1 3
+
+too = qux
diff --git a/testsuite/tests/driver/inline-check.stderr b/testsuite/tests/driver/inline-check.stderr
new file mode 100644 (file)
index 0000000..5bf9eda
--- /dev/null
@@ -0,0 +1,26 @@
+Considering inlining: foo
+  arg infos [ValueArg]
+  interesting continuation RhsCtxt
+  some_benefit True
+  is exp: True
+  is work-free: True
+  guidance IF_ARGS [0] 30 0
+  discounted size = 10
+  ANSWER = YES
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Inactive unfolding: foo1
+Considering inlining: foo
+  arg infos []
+  interesting continuation RhsCtxt
+  some_benefit False
+  is exp: True
+  is work-free: True
+  guidance ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
+  ANSWER = NO
+Inactive unfolding: foo1