| ... |
... |
@@ -30,34 +30,6 @@ import Control.Arrow (first, second) |
|
30
|
30
|
import Data.List.NonEmpty (NonEmpty (..))
|
|
31
|
31
|
import qualified Data.List.NonEmpty as NE
|
|
32
|
32
|
|
|
33
|
|
--- -----------------------------------------------------------------------------
|
|
34
|
|
--- Eliminate common blocks
|
|
35
|
|
-
|
|
36
|
|
--- If two blocks are identical except for the label on the first node,
|
|
37
|
|
--- then we can eliminate one of the blocks. To ensure that the semantics
|
|
38
|
|
--- of the program are preserved, we have to rewrite each predecessor of the
|
|
39
|
|
--- eliminated block to proceed with the block we keep.
|
|
40
|
|
-
|
|
41
|
|
--- The algorithm iterates over the blocks in the graph,
|
|
42
|
|
--- checking whether it has seen another block that is equal modulo labels.
|
|
43
|
|
--- If so, then it adds an entry in a map indicating that the new block
|
|
44
|
|
--- is made redundant by the old block.
|
|
45
|
|
--- Otherwise, it is added to the useful blocks.
|
|
46
|
|
-
|
|
47
|
|
--- To avoid comparing every block with every other block repeatedly, we group
|
|
48
|
|
--- them by
|
|
49
|
|
--- * a hash of the block, ignoring labels (explained below)
|
|
50
|
|
--- * the list of outgoing labels
|
|
51
|
|
--- The hash is invariant under relabeling, so we only ever compare within
|
|
52
|
|
--- the same group of blocks.
|
|
53
|
|
---
|
|
54
|
|
--- The list of outgoing labels is updated as we merge blocks (that is why they
|
|
55
|
|
--- are not included in the hash, which we want to calculate only once).
|
|
56
|
|
---
|
|
57
|
|
--- All in all, two blocks should never be compared if they have different
|
|
58
|
|
--- hashes, and at most once otherwise. Previously, we were slower, and people
|
|
59
|
|
--- rightfully complained: #10397
|
|
60
|
|
-
|
|
61
|
33
|
{- Note [Resolving the CBE substitution]
|
|
62
|
34
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
63
|
35
|
The substitution built by `iterate` can contain chains: the winner of a
|
| ... |
... |
@@ -79,10 +51,13 @@ produced no stack map for it, and setInfoTableStackMap panicked. |
|
79
|
51
|
|
|
80
|
52
|
With the substitution resolved, every edge -- including those in the
|
|
81
|
53
|
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
|
|
|
54
|
+block the resolved substitution maps to is reachable: merging diverts
|
|
|
55
|
+each loser's predecessors to its winner, so a winner inherits its
|
|
|
56
|
+losers' reachability. (The input graph is not entirely free of
|
|
|
57
|
+unreachable blocks -- the control-flow optimiser that runs first can
|
|
|
58
|
+leave unreachable goto-only blocks behind -- but such blocks contain
|
|
|
59
|
+no calls, and if one wins a merge it thereby becomes reachable.) In
|
|
|
60
|
+particular the continuation of a call in a losing copy is also the
|
|
86
|
61
|
continuation of its reachable winner, so stack layout has a stack map
|
|
87
|
62
|
for it.
|
|
88
|
63
|
|
| ... |
... |
@@ -90,17 +65,46 @@ Resolving also improves copyTicks: each loser's ticks are copied into |
|
90
|
65
|
its final representative instead of into a dead intermediate block.
|
|
91
|
66
|
-}
|
|
92
|
67
|
|
|
93
|
|
--- TODO: Use optimization fuel
|
|
|
68
|
+-- | Merge identical blocks
|
|
|
69
|
+--
|
|
|
70
|
+-- If two blocks are identical except for the label on the first node,
|
|
|
71
|
+-- then we can eliminate one of the blocks. To ensure that the semantics
|
|
|
72
|
+-- of the program are preserved, we have to rewrite each predecessor of the
|
|
|
73
|
+-- eliminated block to proceed with the block we keep.
|
|
|
74
|
+--
|
|
|
75
|
+-- Unreachable blocks may occur both in the input and in the output:
|
|
|
76
|
+-- we process whatever the block map contains, and a merge leaves the
|
|
|
77
|
+-- losing copy behind, unreachable. See Note [unreachable blocks] in
|
|
|
78
|
+-- GHC.Cmm.Pipeline.
|
|
|
79
|
+--
|
|
|
80
|
+-- The algorithm iterates over the blocks in the graph,
|
|
|
81
|
+-- checking whether it has seen another block that is equal modulo labels.
|
|
|
82
|
+-- If so, then it adds an entry in a map indicating that the new block
|
|
|
83
|
+-- is made redundant by the old block.
|
|
|
84
|
+-- Otherwise, it is added to the useful blocks.
|
|
|
85
|
+--
|
|
|
86
|
+-- To avoid comparing every block with every other block repeatedly, we group
|
|
|
87
|
+-- them by
|
|
|
88
|
+-- * a hash of the block, ignoring labels (explained below)
|
|
|
89
|
+-- * the list of outgoing labels
|
|
|
90
|
+-- The hash is invariant under relabeling, so we only ever compare within
|
|
|
91
|
+-- the same group of blocks.
|
|
|
92
|
+--
|
|
|
93
|
+-- The list of outgoing labels is updated as we merge blocks (that is why they
|
|
|
94
|
+-- are not included in the hash, which we want to calculate only once).
|
|
|
95
|
+--
|
|
|
96
|
+-- All in all, two blocks should never be compared if they have different
|
|
|
97
|
+-- hashes, and at most once otherwise. Previously, we were slower, and people
|
|
|
98
|
+-- rightfully complained: #10397
|
|
94
|
99
|
elimCommonBlocks :: CmmGraph -> CmmGraph
|
|
|
100
|
+-- TODO: Use optimization fuel
|
|
95
|
101
|
elimCommonBlocks g = replaceLabels env' $ copyTicks env' g
|
|
96
|
102
|
where
|
|
97
|
103
|
-- See Note [Resolving the CBE substitution]
|
|
98
|
104
|
env' = mapMap (lookupBid env) env
|
|
99
|
105
|
env = iterate mapEmpty blocks_with_key
|
|
100
|
|
- -- The order of blocks doesn't matter here. While we could use
|
|
101
|
|
- -- revPostorder which drops unreachable blocks this is done in
|
|
102
|
|
- -- ContFlowOpt already which runs before this pass. So we use
|
|
103
|
|
- -- toBlockList since it is faster.
|
|
|
106
|
+ -- Block order is irrelevant here, so we use toBlockList, which
|
|
|
107
|
+ -- is cheaper than revPostorder.
|
|
104
|
108
|
groups = groupByInt hash_block (toBlockList g) :: [[CmmBlock]]
|
|
105
|
109
|
blocks_with_key = [ [ (successors b, [b]) | b <- bs] | bs <- groups]
|
|
106
|
110
|
|