Simon Jakobi pushed to branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC

Commits:

4 changed files:

Changes:

  • changelog.d/27368
    1
    +section: compiler
    
    2
    +synopsis: Fix a ``setInfoTableStackMap`` panic caused by calls in unreachable Cmm blocks.
    
    3
    +issues: #27368
    
    4
    +mrs: !16543

  • compiler/GHC/Cmm/CommonBlockElim.hs
    ... ... @@ -58,10 +58,44 @@ import qualified Data.List.NonEmpty as NE
    58 58
     -- hashes, and at most once otherwise. Previously, we were slower, and people
    
    59 59
     -- rightfully complained: #10397
    
    60 60
     
    
    61
    +{- Note [Resolving the CBE substitution]
    
    62
    +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    63
    +The substitution built by `iterate` can contain chains: the winner of a
    
    64
    +merge may itself lose a later merge, giving k1 :-> k2 and k2 :-> k3.
    
    65
    +`lookupBid` resolves such chains transitively, but `replaceLabels`
    
    66
    +looks up each label only once.  So before rewriting the graph we
    
    67
    +resolve the substitution, mapping every eliminated label directly to
    
    68
    +its final representative.
    
    69
    +
    
    70
    +This matters because the losing blocks of the merges stay in the block
    
    71
    +map, unreachable (removing them here would take an extra reachability
    
    72
    +pass).  With an unresolved
    
    73
    +substitution, an edge in a losing block could be rewritten to k2 --
    
    74
    +itself an eliminated label with no live twin.  In #27368 such a stranded
    
    75
    +label was a call continuation: callProcPoints folds over the whole
    
    76
    +block map, so the label became a proc point, attachContInfoTables gave
    
    77
    +it an info table, but stack layout (which walks reachable blocks only)
    
    78
    +produced no stack map for it, and setInfoTableStackMap panicked.
    
    79
    +
    
    80
    +With the substitution resolved, every edge -- including those in the
    
    81
    +unreachable losing copies -- points at a surviving block, and every
    
    82
    +surviving block is reachable: the input graph contains no unreachable
    
    83
    +blocks (the control-flow optimiser runs first and drops them), and
    
    84
    +merging only diverts paths from losers to their body-equal winners.
    
    85
    +In particular the continuation of a call in a losing copy is also the
    
    86
    +continuation of its reachable winner, so stack layout has a stack map
    
    87
    +for it.
    
    88
    +
    
    89
    +Resolving also improves copyTicks: each loser's ticks are copied into
    
    90
    +its final representative instead of into a dead intermediate block.
    
    91
    +-}
    
    92
    +
    
    61 93
     -- TODO: Use optimization fuel
    
    62 94
     elimCommonBlocks :: CmmGraph -> CmmGraph
    
    63
    -elimCommonBlocks g = replaceLabels env $ copyTicks env g
    
    95
    +elimCommonBlocks g = replaceLabels env' $ copyTicks env' g
    
    64 96
       where
    
    97
    +     -- See Note [Resolving the CBE substitution]
    
    98
    +     env' = mapMap (lookupBid env) env
    
    65 99
          env = iterate mapEmpty blocks_with_key
    
    66 100
          -- The order of blocks doesn't matter here. While we could use
    
    67 101
          -- revPostorder which drops unreachable blocks this is done in
    

  • testsuite/tests/codeGen/should_compile/T27368.hs
    1
    +-- Regression test for #27368: setInfoTableStackMap panicked because a
    
    2
    +-- call in an unreachable block returned to an eliminated label. The two
    
    3
    +-- branches below have identical suffixes from the inner case onwards,
    
    4
    +-- so common block elimination merges the duplicated call blocks in
    
    5
    +-- several rounds, building a substitution chain. See Note [Resolving
    
    6
    +-- the CBE substitution] in GHC.Cmm.CommonBlockElim.
    
    7
    +module T27368 (f) where
    
    8
    +
    
    9
    +{-# NOINLINE put #-}
    
    10
    +put :: Int -> Int -> IO ()
    
    11
    +put h x = if h + x == 12345 then errorWithoutStackTrace "boom" else pure ()
    
    12
    +
    
    13
    +data T = N | J Int | K
    
    14
    +
    
    15
    +f :: Int -> Bool -> T -> IO ()
    
    16
    +f h a t = do
    
    17
    +  if a
    
    18
    +    then do put h 1; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0
    
    19
    +    else do put h 2; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0
    
    20
    +  put h 0

  • testsuite/tests/codeGen/should_compile/all.T
    ... ... @@ -150,3 +150,5 @@ test('T16351', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppres
    150 150
     test('T20298a', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
    
    151 151
     test('T20298b', normal, compile, ['-O2 -dno-bignum-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
    
    152 152
     test('T20298c', normal, compile, ['-O2 -dno-builtin-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
    
    153
    +
    
    154
    +test('T27368', normal, compile, ['-O2'])