Simon Jakobi pushed to branch wip/sjakobi/T27448 at Glasgow Haskell Compiler / GHC
Commits:
-
6813f002
by Simon Jakobi at 2026-06-26T04:51:47-04:00
-
3b2a9409
by Zubin Duggal at 2026-06-26T04:52:39-04:00
-
980b9f36
by Simon Jakobi at 2026-06-27T11:53:13+02:00
11 changed files:
- compiler/GHC/CmmToAsm/CFG.hs
- compiler/GHC/CmmToAsm/Reg/Linear.hs
- compiler/GHC/CmmToAsm/Reg/Linear/FreeRegs.hs
- compiler/GHC/CmmToAsm/Reg/Linear/JoinToTargets.hs
- compiler/GHC/CmmToAsm/Reg/Linear/X86.hs
- compiler/GHC/CmmToAsm/Reg/Linear/X86_64.hs
- compiler/GHC/CmmToAsm/X86/RegInfo.hs
- compiler/GHC/CmmToAsm/X86/Regs.hs
- testsuite/driver/junit.py
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/perf/compiler/genManyBasicBlocks
Changes:
| ... | ... | @@ -885,17 +885,15 @@ loopInfo cfg root = LoopInfo { liBackEdges = backEdges |
| 885 | 885 | in map (\n -> (n, loopCount n)) $ nodes :: [(BlockId, Int)]
|
| 886 | 886 | |
| 887 | 887 | mkDomMap :: Tree BlockId -> LabelMap LabelSet
|
| 888 | - mkDomMap root = mapFromList $ go setEmpty root
|
|
| 888 | + -- The entry/root dominates every block and is deliberately excluded, so its
|
|
| 889 | + -- children start from the empty dominator set.
|
|
| 890 | + mkDomMap (Node _root children) = foldl' (go setEmpty) mapEmpty children
|
|
| 889 | 891 | where
|
| 890 | - go :: LabelSet -> Tree BlockId -> [(Label,LabelSet)]
|
|
| 891 | - go parents (Node lbl [])
|
|
| 892 | - = [(lbl, parents)]
|
|
| 893 | - go parents (Node _ leaves)
|
|
| 894 | - = let nodes = map rootLabel leaves
|
|
| 895 | - entries = map (\x -> (x,parents)) nodes
|
|
| 896 | - in entries ++ concatMap
|
|
| 897 | - (\n -> go (setInsert (rootLabel n) parents) n)
|
|
| 898 | - leaves
|
|
| 892 | + -- `doms`: the dominators of `lbl`, i.e. its ancestors in the dominator
|
|
| 893 | + -- tree excluding the tree root.
|
|
| 894 | + go :: LabelSet -> LabelMap LabelSet -> Tree BlockId -> LabelMap LabelSet
|
|
| 895 | + go doms acc (Node lbl kids)
|
|
| 896 | + = foldl' (go (setInsert lbl doms)) (mapInsert lbl doms acc) kids
|
|
| 899 | 897 | |
| 900 | 898 | -- We make the CFG a Hoopl Graph, so we can reuse revPostOrder.
|
| 901 | 899 | newtype BlockNode (e :: Extensibility) (x :: Extensibility) = BN (BlockId,[BlockId])
|
| ... | ... | @@ -367,7 +367,7 @@ initBlock id block_live |
| 367 | 367 | Nothing ->
|
| 368 | 368 | setFreeRegsR (frInitFreeRegs platform)
|
| 369 | 369 | Just live ->
|
| 370 | - setFreeRegsR $ foldl' (flip $ frAllocateReg platform) (frInitFreeRegs platform)
|
|
| 370 | + setFreeRegsR $ foldl' (flip frAllocateReg) (frInitFreeRegs platform)
|
|
| 371 | 371 | (nonDetEltsUniqSet $ takeRealRegs $ getRegs live)
|
| 372 | 372 | -- See Note [Unique Determinism and code generation]
|
| 373 | 373 | setAssigR emptyRegMap
|
| ... | ... | @@ -638,20 +638,19 @@ genRaInsn block_live new_instrs block_id instr r_dying w_dying = do |
| 638 | 638 | |
| 639 | 639 | releaseRegs :: FR freeRegs => [Reg] -> RegM freeRegs ()
|
| 640 | 640 | releaseRegs regs = do
|
| 641 | - platform <- getPlatform
|
|
| 642 | 641 | assig <- getAssigR
|
| 643 | 642 | free <- getFreeRegsR
|
| 644 | 643 | |
| 645 | 644 | let loop assig !free [] = do setAssigR assig; setFreeRegsR free; return ()
|
| 646 | - loop assig !free (RegReal rr : rs) = loop assig (frReleaseReg platform rr free) rs
|
|
| 645 | + loop assig !free (RegReal rr : rs) = loop assig (frReleaseReg rr free) rs
|
|
| 647 | 646 | loop assig !free (r:rs) =
|
| 648 | 647 | case lookupUFM assig r of
|
| 649 | 648 | Just (Loc (InBoth real _) _) ->
|
| 650 | 649 | loop (delFromUFM assig r)
|
| 651 | - (frReleaseReg platform real free) rs
|
|
| 650 | + (frReleaseReg real free) rs
|
|
| 652 | 651 | Just (Loc (InReg real) _) ->
|
| 653 | 652 | loop (delFromUFM assig r)
|
| 654 | - (frReleaseReg platform real free) rs
|
|
| 653 | + (frReleaseReg real free) rs
|
|
| 655 | 654 | _ ->
|
| 656 | 655 | loop (delFromUFM assig r) free rs
|
| 657 | 656 | loop assig free regs
|
| ... | ... | @@ -716,7 +715,7 @@ saveClobberedTemps clobbered dying |
| 716 | 715 | |
| 717 | 716 | freeRegs <- getFreeRegsR
|
| 718 | 717 | let regclass = targetClassOfRealReg platform reg
|
| 719 | - freeRegs_thisClass = frGetFreeRegs platform regclass freeRegs
|
|
| 718 | + freeRegs_thisClass = frGetFreeRegs regclass freeRegs
|
|
| 720 | 719 | |
| 721 | 720 | case filter (`notElem` clobbered) freeRegs_thisClass of
|
| 722 | 721 | |
| ... | ... | @@ -724,7 +723,7 @@ saveClobberedTemps clobbered dying |
| 724 | 723 | -- clobbered by this instruction; use it to save the
|
| 725 | 724 | -- clobbered value.
|
| 726 | 725 | (my_reg : _) -> do
|
| 727 | - setFreeRegsR (frAllocateReg platform my_reg freeRegs)
|
|
| 726 | + setFreeRegsR (frAllocateReg my_reg freeRegs)
|
|
| 728 | 727 | |
| 729 | 728 | let new_assign = addToUFM_Directly assig temp (Loc (InReg my_reg) fmt)
|
| 730 | 729 | let instr = mkRegRegMoveInstr config fmt
|
| ... | ... | @@ -763,13 +762,13 @@ clobberRegs clobbered |
| 763 | 762 | Unified -> Unified.allRegClasses
|
| 764 | 763 | Separate -> Separate.allRegClasses
|
| 765 | 764 | NoVectors -> NoVectors.allRegClasses
|
| 766 | - allFreeRegs = foldMap (\ rc -> frGetFreeRegs platform rc freeregs) allRegClasses
|
|
| 765 | + allFreeRegs = foldMap (\ rc -> frGetFreeRegs rc freeregs) allRegClasses
|
|
| 767 | 766 | |
| 768 | 767 | let extra_clobbered = [ r | r <- clobbered, r `elem` allFreeRegs ]
|
| 769 | 768 | |
| 770 | - setFreeRegsR $! foldl' (flip $ frAllocateReg platform) freeregs extra_clobbered
|
|
| 769 | + setFreeRegsR $! foldl' (flip frAllocateReg) freeregs extra_clobbered
|
|
| 771 | 770 | |
| 772 | - -- setFreeRegsR $! foldl' (flip $ frAllocateReg platform) freeregs clobbered
|
|
| 771 | + -- setFreeRegsR $! foldl' (flip frAllocateReg) freeregs clobbered
|
|
| 773 | 772 | |
| 774 | 773 | assig <- getAssigR
|
| 775 | 774 | setAssigR $! clobber assig (nonDetUFMToList assig)
|
| ... | ... | @@ -896,7 +895,7 @@ allocRegsAndSpill_spill reading keep spills alloc r@(VirtualRegWithFormat vr vrF |
| 896 | 895 | = do platform <- getPlatform
|
| 897 | 896 | freeRegs <- getFreeRegsR
|
| 898 | 897 | let regclass = classOfVirtualReg (platformArch platform) vr
|
| 899 | - freeRegs_thisClass = frGetFreeRegs platform regclass freeRegs :: [RealReg]
|
|
| 898 | + freeRegs_thisClass = frGetFreeRegs regclass freeRegs :: [RealReg]
|
|
| 900 | 899 | |
| 901 | 900 | -- Can we put the variable into a register it already was?
|
| 902 | 901 | pref_reg <- findPrefRealReg vr
|
| ... | ... | @@ -915,7 +914,7 @@ allocRegsAndSpill_spill reading keep spills alloc r@(VirtualRegWithFormat vr vrF |
| 915 | 914 | |
| 916 | 915 | setAssigR $ toRegMap
|
| 917 | 916 | $ (addToUFM assig vr $! newLocation spill_loc $ RealRegUsage final_reg vrFmt)
|
| 918 | - setFreeRegsR $ frAllocateReg platform final_reg freeRegs
|
|
| 917 | + setFreeRegsR $ frAllocateReg final_reg freeRegs
|
|
| 919 | 918 | |
| 920 | 919 | allocateRegsAndSpill reading keep spills' (final_reg : alloc) rs
|
| 921 | 920 |
| ... | ... | @@ -43,49 +43,51 @@ import qualified GHC.CmmToAsm.RV64.Instr as RV64.Instr |
| 43 | 43 | import qualified GHC.CmmToAsm.LA64.Instr as LA64.Instr
|
| 44 | 44 | |
| 45 | 45 | class Show freeRegs => FR freeRegs where
|
| 46 | - frAllocateReg :: Platform -> RealReg -> freeRegs -> freeRegs
|
|
| 47 | - frGetFreeRegs :: Platform -> RegClass -> freeRegs -> [RealReg]
|
|
| 46 | + frAllocateReg :: RealReg -> freeRegs -> freeRegs
|
|
| 47 | + frGetFreeRegs :: RegClass -> freeRegs -> [RealReg]
|
|
| 48 | + -- | The initial allocatable set is platform-dependent. See Note
|
|
| 49 | + -- [Aarch64 Register x18 at Darwin and Windows].
|
|
| 48 | 50 | frInitFreeRegs :: Platform -> freeRegs
|
| 49 | - frReleaseReg :: Platform -> RealReg -> freeRegs -> freeRegs
|
|
| 51 | + frReleaseReg :: RealReg -> freeRegs -> freeRegs
|
|
| 50 | 52 | |
| 51 | 53 | instance FR X86.FreeRegs where
|
| 52 | - frAllocateReg = \_ -> X86.allocateReg
|
|
| 54 | + frAllocateReg = X86.allocateReg
|
|
| 53 | 55 | frGetFreeRegs = X86.getFreeRegs
|
| 54 | 56 | frInitFreeRegs = X86.initFreeRegs
|
| 55 | - frReleaseReg = \_ -> X86.releaseReg
|
|
| 57 | + frReleaseReg = X86.releaseReg
|
|
| 56 | 58 | |
| 57 | 59 | instance FR X86_64.FreeRegs where
|
| 58 | - frAllocateReg = \_ -> X86_64.allocateReg
|
|
| 60 | + frAllocateReg = X86_64.allocateReg
|
|
| 59 | 61 | frGetFreeRegs = X86_64.getFreeRegs
|
| 60 | 62 | frInitFreeRegs = X86_64.initFreeRegs
|
| 61 | - frReleaseReg = \_ -> X86_64.releaseReg
|
|
| 63 | + frReleaseReg = X86_64.releaseReg
|
|
| 62 | 64 | |
| 63 | 65 | instance FR PPC.FreeRegs where
|
| 64 | - frAllocateReg = \_ -> PPC.allocateReg
|
|
| 65 | - frGetFreeRegs = \_ -> PPC.getFreeRegs
|
|
| 66 | + frAllocateReg = PPC.allocateReg
|
|
| 67 | + frGetFreeRegs = PPC.getFreeRegs
|
|
| 66 | 68 | frInitFreeRegs = PPC.initFreeRegs
|
| 67 | - frReleaseReg = \_ -> PPC.releaseReg
|
|
| 69 | + frReleaseReg = PPC.releaseReg
|
|
| 68 | 70 | |
| 69 | 71 | instance FR AArch64.FreeRegs where
|
| 70 | - frAllocateReg = \_ -> AArch64.allocateReg
|
|
| 71 | - frGetFreeRegs = \_ -> AArch64.getFreeRegs
|
|
| 72 | + frAllocateReg = AArch64.allocateReg
|
|
| 73 | + frGetFreeRegs = AArch64.getFreeRegs
|
|
| 72 | 74 | frInitFreeRegs = AArch64.initFreeRegs
|
| 73 | - frReleaseReg = \_ -> AArch64.releaseReg
|
|
| 75 | + frReleaseReg = AArch64.releaseReg
|
|
| 74 | 76 | |
| 75 | 77 | instance FR RV64.FreeRegs where
|
| 76 | - frAllocateReg = const RV64.allocateReg
|
|
| 77 | - frGetFreeRegs = const RV64.getFreeRegs
|
|
| 78 | + frAllocateReg = RV64.allocateReg
|
|
| 79 | + frGetFreeRegs = RV64.getFreeRegs
|
|
| 78 | 80 | frInitFreeRegs = RV64.initFreeRegs
|
| 79 | - frReleaseReg = const RV64.releaseReg
|
|
| 81 | + frReleaseReg = RV64.releaseReg
|
|
| 80 | 82 | |
| 81 | 83 | instance FR LA64.FreeRegs where
|
| 82 | - frAllocateReg = \_ -> LA64.allocateReg
|
|
| 83 | - frGetFreeRegs = \_ -> LA64.getFreeRegs
|
|
| 84 | + frAllocateReg = LA64.allocateReg
|
|
| 85 | + frGetFreeRegs = LA64.getFreeRegs
|
|
| 84 | 86 | frInitFreeRegs = LA64.initFreeRegs
|
| 85 | - frReleaseReg = \_ -> LA64.releaseReg
|
|
| 87 | + frReleaseReg = LA64.releaseReg
|
|
| 86 | 88 | |
| 87 | 89 | allFreeRegs :: FR freeRegs => Platform -> freeRegs -> [RealReg]
|
| 88 | -allFreeRegs plat fr = foldMap (\rcls -> frGetFreeRegs plat rcls fr) allRegClasses
|
|
| 90 | +allFreeRegs plat fr = foldMap (\rcls -> frGetFreeRegs rcls fr) allRegClasses
|
|
| 89 | 91 | where
|
| 90 | 92 | allRegClasses =
|
| 91 | 93 | case registerArch (platformArch plat) of
|
| ... | ... | @@ -15,7 +15,6 @@ import GHC.CmmToAsm.Reg.Linear.Base |
| 15 | 15 | import GHC.CmmToAsm.Reg.Linear.FreeRegs
|
| 16 | 16 | import GHC.CmmToAsm.Reg.Liveness
|
| 17 | 17 | import GHC.CmmToAsm.Instr
|
| 18 | -import GHC.CmmToAsm.Config
|
|
| 19 | 18 | import GHC.CmmToAsm.Types
|
| 20 | 19 | |
| 21 | 20 | import GHC.Platform.Reg
|
| ... | ... | @@ -132,12 +131,9 @@ joinToTargets_first block_live new_blocks block_id instr dest dests |
| 132 | 131 | block_assig src_assig
|
| 133 | 132 | to_free
|
| 134 | 133 | |
| 135 | - = do config <- getConfig
|
|
| 136 | - let platform = ncgPlatform config
|
|
| 137 | - |
|
| 138 | - -- free up the regs that are not live on entry to this block.
|
|
| 134 | + = do -- free up the regs that are not live on entry to this block.
|
|
| 139 | 135 | freeregs <- getFreeRegsR
|
| 140 | - let freeregs' = foldl' (flip $ frReleaseReg platform) freeregs to_free
|
|
| 136 | + let freeregs' = foldl' (flip frReleaseReg) freeregs to_free
|
|
| 141 | 137 | |
| 142 | 138 | -- remember the current assignment on entry to this block.
|
| 143 | 139 | setBlockAssigR (updateBlockAssignment dest (freeregs', src_assig) block_assig)
|
| ... | ... | @@ -25,17 +25,17 @@ initFreeRegs :: Platform -> FreeRegs |
| 25 | 25 | initFreeRegs platform
|
| 26 | 26 | = foldl' (flip releaseReg) noFreeRegs (allocatableRegs platform)
|
| 27 | 27 | |
| 28 | -getFreeRegs :: Platform -> RegClass -> FreeRegs -> [RealReg] -- lazily
|
|
| 29 | -getFreeRegs platform cls (FreeRegs f) =
|
|
| 28 | +getFreeRegs :: RegClass -> FreeRegs -> [RealReg] -- lazily
|
|
| 29 | +getFreeRegs cls (FreeRegs f) =
|
|
| 30 | 30 | case cls of
|
| 31 | 31 | RcInteger ->
|
| 32 | 32 | [ RealRegSingle i
|
| 33 | - | i <- intregnos platform
|
|
| 33 | + | i <- intregnos PW4
|
|
| 34 | 34 | , testBit f i
|
| 35 | 35 | ]
|
| 36 | 36 | RcFloatOrVector ->
|
| 37 | 37 | [ RealRegSingle i
|
| 38 | - | i <- xmmregnos platform
|
|
| 38 | + | i <- xmmregnos PW4
|
|
| 39 | 39 | , testBit f i
|
| 40 | 40 | ]
|
| 41 | 41 |
| ... | ... | @@ -25,17 +25,17 @@ initFreeRegs :: Platform -> FreeRegs |
| 25 | 25 | initFreeRegs platform
|
| 26 | 26 | = foldl' (flip releaseReg) noFreeRegs (allocatableRegs platform)
|
| 27 | 27 | |
| 28 | -getFreeRegs :: Platform -> RegClass -> FreeRegs -> [RealReg] -- lazily
|
|
| 29 | -getFreeRegs platform cls (FreeRegs f) =
|
|
| 28 | +getFreeRegs :: RegClass -> FreeRegs -> [RealReg] -- lazily
|
|
| 29 | +getFreeRegs cls (FreeRegs f) =
|
|
| 30 | 30 | case cls of
|
| 31 | 31 | RcInteger ->
|
| 32 | 32 | [ RealRegSingle i
|
| 33 | - | i <- intregnos platform
|
|
| 33 | + | i <- intregnos PW8
|
|
| 34 | 34 | , testBit f i
|
| 35 | 35 | ]
|
| 36 | 36 | RcFloatOrVector ->
|
| 37 | 37 | [ RealRegSingle i
|
| 38 | - | i <- xmmregnos platform
|
|
| 38 | + | i <- xmmregnos PW8
|
|
| 39 | 39 | , testBit f i
|
| 40 | 40 | ]
|
| 41 | 41 |
| ... | ... | @@ -41,9 +41,11 @@ regColors platform = listToUFM (normalRegColors platform) |
| 41 | 41 | |
| 42 | 42 | normalRegColors :: Platform -> [(RealReg,String)]
|
| 43 | 43 | normalRegColors platform =
|
| 44 | - zip (map realRegSingle [0..lastint platform]) colors
|
|
| 45 | - ++ zip (map realRegSingle [firstxmm..lastxmm platform]) greys
|
|
| 44 | + zip (map realRegSingle [0..lastint wordSize]) colors
|
|
| 45 | + ++ zip (map realRegSingle [firstxmm..lastxmm wordSize]) greys
|
|
| 46 | 46 | where
|
| 47 | + wordSize = platformWordSize platform
|
|
| 48 | + |
|
| 47 | 49 | -- 16 colors - enough for amd64 gp regs
|
| 48 | 50 | colors = ["#800000","#ff0000","#808000","#ffff00","#008000"
|
| 49 | 51 | ,"#00ff00","#008080","#00ffff","#000080","#0000ff"
|
| ... | ... | @@ -194,27 +194,23 @@ spRel platform n |
| 194 | 194 | firstxmm :: RegNo
|
| 195 | 195 | firstxmm = 16
|
| 196 | 196 | |
| 197 | --- on 32bit platformOSs, only the first 8 XMM/YMM/ZMM registers are available
|
|
| 198 | -lastxmm :: Platform -> RegNo
|
|
| 199 | -lastxmm platform
|
|
| 200 | - | target32Bit platform = firstxmm + 7 -- xmm0 - xmmm7
|
|
| 201 | - | otherwise = firstxmm + 15 -- xmm0 -xmm15
|
|
| 197 | +-- on 32bit platforms, only the first 8 XMM/YMM/ZMM registers are available
|
|
| 198 | +lastxmm :: PlatformWordSize -> RegNo
|
|
| 199 | +lastxmm PW4 = firstxmm + 7 -- xmm0 - xmm7
|
|
| 200 | +lastxmm PW8 = firstxmm + 15 -- xmm0 - xmm15
|
|
| 202 | 201 | |
| 203 | -lastint :: Platform -> RegNo
|
|
| 204 | -lastint platform
|
|
| 205 | - | target32Bit platform = 7 -- not %r8..%r15
|
|
| 206 | - | otherwise = 15
|
|
| 202 | +lastint :: PlatformWordSize -> RegNo
|
|
| 203 | +lastint PW4 = 7 -- not %r8..%r15
|
|
| 204 | +lastint PW8 = 15
|
|
| 207 | 205 | |
| 208 | -intregnos :: Platform -> [RegNo]
|
|
| 209 | -intregnos platform = [0 .. lastint platform]
|
|
| 206 | +intregnos :: PlatformWordSize -> [RegNo]
|
|
| 207 | +intregnos wordSize = [0 .. lastint wordSize]
|
|
| 210 | 208 | |
| 211 | - |
|
| 212 | - |
|
| 213 | -xmmregnos :: Platform -> [RegNo]
|
|
| 214 | -xmmregnos platform = [firstxmm .. lastxmm platform]
|
|
| 209 | +xmmregnos :: PlatformWordSize -> [RegNo]
|
|
| 210 | +xmmregnos wordSize = [firstxmm .. lastxmm wordSize]
|
|
| 215 | 211 | |
| 216 | 212 | floatregnos :: Platform -> [RegNo]
|
| 217 | -floatregnos platform = xmmregnos platform
|
|
| 213 | +floatregnos platform = xmmregnos (platformWordSize platform)
|
|
| 218 | 214 | |
| 219 | 215 | -- argRegs is the set of regs which are read for an n-argument call to C.
|
| 220 | 216 | -- For archs which pass all args on the stack (x86), is empty.
|
| ... | ... | @@ -224,7 +220,7 @@ argRegs _ = panic "MachRegs.argRegs(x86): should not be used!" |
| 224 | 220 | |
| 225 | 221 | -- | The complete set of machine registers.
|
| 226 | 222 | allMachRegNos :: Platform -> [RegNo]
|
| 227 | -allMachRegNos platform = intregnos platform ++ floatregnos platform
|
|
| 223 | +allMachRegNos platform = intregnos (platformWordSize platform) ++ floatregnos platform
|
|
| 228 | 224 | |
| 229 | 225 | -- | Take the class of a register.
|
| 230 | 226 | {-# INLINE classOfRealReg #-}
|
| ... | ... | @@ -236,9 +232,11 @@ classOfRealReg :: Platform -> RealReg -> RegClass |
| 236 | 232 | classOfRealReg platform reg
|
| 237 | 233 | = case reg of
|
| 238 | 234 | RealRegSingle i
|
| 239 | - | i <= lastint platform -> RcInteger
|
|
| 240 | - | i <= lastxmm platform -> RcFloatOrVector
|
|
| 235 | + | i <= lastint wordSize -> RcInteger
|
|
| 236 | + | i <= lastxmm wordSize -> RcFloatOrVector
|
|
| 241 | 237 | | otherwise -> panic "X86.Reg.classOfRealReg registerSingle too high"
|
| 238 | + where
|
|
| 239 | + wordSize = platformWordSize platform
|
|
| 242 | 240 | |
| 243 | 241 | -- machine specific ------------------------------------------------------------
|
| 244 | 242 |
| ... | ... | @@ -14,12 +14,13 @@ def junit(t: TestRun) -> ET.ElementTree: |
| 14 | 14 | + len(t.unexpected_stat_failures)
|
| 15 | 15 | + len(t.unexpected_passes)),
|
| 16 | 16 | errors = str(len(t.framework_failures)),
|
| 17 | + skipped = str(len(t.fragile_failures)),
|
|
| 17 | 18 | timestamp = datetime.now().isoformat())
|
| 18 | 19 | |
| 19 | - for res_type, group in [('stat failure', t.unexpected_stat_failures),
|
|
| 20 | - ('unexpected failure', t.unexpected_failures),
|
|
| 21 | - ('unexpected pass', t.unexpected_passes),
|
|
| 22 | - ('fragile failure', t.fragile_failures)]:
|
|
| 20 | + for kind, res_type, group in [('failure', 'stat failure', t.unexpected_stat_failures),
|
|
| 21 | + ('failure', 'unexpected failure', t.unexpected_failures),
|
|
| 22 | + ('failure', 'unexpected pass', t.unexpected_passes),
|
|
| 23 | + ('skipped', 'fragile failure', t.fragile_failures)]:
|
|
| 23 | 24 | for tr in group:
|
| 24 | 25 | testcase = ET.SubElement(testsuite, 'testcase',
|
| 25 | 26 | classname = tr.way,
|
| ... | ... | @@ -30,7 +31,7 @@ def junit(t: TestRun) -> ET.ElementTree: |
| 30 | 31 | if tr.stderr:
|
| 31 | 32 | message += ['', 'stderr:', '==========', tr.stderr]
|
| 32 | 33 | |
| 33 | - result = ET.SubElement(testcase, 'failure',
|
|
| 34 | + result = ET.SubElement(testcase, kind,
|
|
| 34 | 35 | type = res_type,
|
| 35 | 36 | message = tr.reason)
|
| 36 | 37 | result.text = '\n'.join(message)
|
| ... | ... | @@ -857,3 +857,18 @@ test ('T26989', |
| 857 | 857 | multimod_compile,
|
| 858 | 858 | ['T26989', '-v0 -O'])
|
| 859 | 859 | |
| 860 | +# A single Cmm procedure with thousands of basic blocks (diamonds plus periodic
|
|
| 861 | +# backedges), stressing the native code generator's CFG construction, edge-weight
|
|
| 862 | +# estimation, register allocation and block layout. Works with all NCG backends
|
|
| 863 | +# (X86, AArch64, WASM, etc.). See #27448.
|
|
| 864 | +test('ManyBasicBlocks',
|
|
| 865 | + [ only_ways(['optasm']),
|
|
| 866 | + unless(have_ncg(), skip),
|
|
| 867 | + cmm_src,
|
|
| 868 | + collect_compiler_stats('bytes allocated', 2),
|
|
| 869 | + pre_cmd('./genManyBasicBlocks'),
|
|
| 870 | + extra_files(['genManyBasicBlocks']),
|
|
| 871 | + ],
|
|
| 872 | + compile,
|
|
| 873 | + ['-O -v0'])
|
|
| 874 | + |
| 1 | +#!/usr/bin/env bash
|
|
| 2 | +set -euo pipefail
|
|
| 3 | + |
|
| 4 | +# Generate a Cmm source file that is intentionally heavy on control flow.
|
|
| 5 | +#
|
|
| 6 | +# The benchmark shape is one large procedure containing:
|
|
| 7 | +# - many split/left/right/join diamonds
|
|
| 8 | +# - periodic backedges that make each chunk a natural loop
|
|
| 9 | +#
|
|
| 10 | +# It produces thousands of actual basic blocks in a single Cmm graph (rather
|
|
| 11 | +# than merely a very large source file), stressing the native code generator's
|
|
| 12 | +# CFG construction, edge-weight estimation, register allocation and block
|
|
| 13 | +# layout.
|
|
| 14 | +#
|
|
| 15 | +# Based on GitLab snippet #6044
|
|
| 16 | +#
|
|
| 17 | +# Tuning knobs:
|
|
| 18 | +# STAGES Number of diamond stages to generate. Default: 2048
|
|
| 19 | +# LOOP_PERIOD Number of stages per loop chunk. Default: 16
|
|
| 20 | +# LOOP_TRIPS Runtime trip count for each chunk. Default: 2
|
|
| 21 | +# OUT Output file. Default: ManyBasicBlocks.cmm
|
|
| 22 | + |
|
| 23 | +: "${STAGES:=2048}"
|
|
| 24 | +: "${LOOP_PERIOD:=16}"
|
|
| 25 | +: "${LOOP_TRIPS:=2}"
|
|
| 26 | +: "${OUT:=ManyBasicBlocks.cmm}"
|
|
| 27 | + |
|
| 28 | +die() {
|
|
| 29 | + printf '%s\n' "$*" >&2
|
|
| 30 | + exit 1
|
|
| 31 | +}
|
|
| 32 | + |
|
| 33 | +check_nat() {
|
|
| 34 | + local value=$1
|
|
| 35 | + local name=$2
|
|
| 36 | + |
|
| 37 | + [[ $value =~ ^[0-9]+$ ]] || die "$name must be a non-negative integer"
|
|
| 38 | +}
|
|
| 39 | + |
|
| 40 | +check_nat "$STAGES" STAGES
|
|
| 41 | +check_nat "$LOOP_PERIOD" LOOP_PERIOD
|
|
| 42 | +check_nat "$LOOP_TRIPS" LOOP_TRIPS
|
|
| 43 | + |
|
| 44 | +(( STAGES > 0 )) || die "STAGES must be greater than zero"
|
|
| 45 | +(( LOOP_PERIOD > 0 )) || die "LOOP_PERIOD must be greater than zero"
|
|
| 46 | +(( LOOP_TRIPS > 0 )) || die "LOOP_TRIPS must be greater than zero"
|
|
| 47 | + |
|
| 48 | +chunk_count=$(( (STAGES + LOOP_PERIOD - 1) / LOOP_PERIOD ))
|
|
| 49 | +block_count=$(( 1 + 4 * STAGES + chunk_count ))
|
|
| 50 | + |
|
| 51 | +emit() {
|
|
| 52 | + printf '%s\n' "$*"
|
|
| 53 | +}
|
|
| 54 | + |
|
| 55 | +{
|
|
| 56 | + emit "// Generated by genManyBasicBlocks"
|
|
| 57 | + emit "//"
|
|
| 58 | + emit "// Parameters:"
|
|
| 59 | + emit "// STAGES=${STAGES}"
|
|
| 60 | + emit "// LOOP_PERIOD=${LOOP_PERIOD}"
|
|
| 61 | + emit "// LOOP_TRIPS=${LOOP_TRIPS}"
|
|
| 62 | + emit "//"
|
|
| 63 | + emit "// Approximate block count: ${block_count}"
|
|
| 64 | + emit "#include \"Cmm.h\""
|
|
| 65 | + emit
|
|
| 66 | + emit "many_basic_blocks (W_ seed) {"
|
|
| 67 | + emit " W_ acc;"
|
|
| 68 | + emit " W_ mix;"
|
|
| 69 | + for ((chunk = 0; chunk < chunk_count; chunk++)); do
|
|
| 70 | + emit " W_ trip${chunk};"
|
|
| 71 | + done
|
|
| 72 | + emit
|
|
| 73 | + emit "entry:"
|
|
| 74 | + emit " acc = seed;"
|
|
| 75 | + emit " mix = seed + (17 :: W_);"
|
|
| 76 | + for ((chunk = 0; chunk < chunk_count; chunk++)); do
|
|
| 77 | + emit " trip${chunk} = 0;"
|
|
| 78 | + done
|
|
| 79 | + emit " goto split0;"
|
|
| 80 | + emit
|
|
| 81 | + |
|
| 82 | + for ((stage = 0; stage < STAGES; stage++)); do
|
|
| 83 | + next_stage=$(( stage + 1 ))
|
|
| 84 | + chunk=$(( stage / LOOP_PERIOD ))
|
|
| 85 | + stage1=$(( 4 * stage + 1 ))
|
|
| 86 | + stage2=$(( 4 * stage + 2 ))
|
|
| 87 | + stage3=$(( 4 * stage + 3 ))
|
|
| 88 | + stage4=$(( 4 * stage + 4 ))
|
|
| 89 | + |
|
| 90 | + emit "split${stage}:"
|
|
| 91 | + emit " mix = mix + (${stage1} :: W_);"
|
|
| 92 | + emit " if (((mix + (${stage2} :: W_)) % (7 :: W_)) < (3 :: W_)) {"
|
|
| 93 | + emit " goto left${stage};"
|
|
| 94 | + emit " }"
|
|
| 95 | + emit " goto right${stage};"
|
|
| 96 | + emit
|
|
| 97 | + |
|
| 98 | + emit "left${stage}:"
|
|
| 99 | + emit " acc = acc + (${stage3} :: W_);"
|
|
| 100 | + emit " mix = mix + (${stage4} :: W_);"
|
|
| 101 | + emit " goto join${stage};"
|
|
| 102 | + emit
|
|
| 103 | + |
|
| 104 | + emit "right${stage}:"
|
|
| 105 | + emit " acc = acc + (${stage4} :: W_);"
|
|
| 106 | + emit " mix = mix + (${stage3} :: W_);"
|
|
| 107 | + emit " goto join${stage};"
|
|
| 108 | + emit
|
|
| 109 | + |
|
| 110 | + emit "join${stage}:"
|
|
| 111 | + emit " acc = acc + mix + (${stage1} :: W_);"
|
|
| 112 | + if (( next_stage == STAGES || next_stage % LOOP_PERIOD == 0 )); then
|
|
| 113 | + emit " goto latch${chunk};"
|
|
| 114 | + else
|
|
| 115 | + emit " goto split${next_stage};"
|
|
| 116 | + fi
|
|
| 117 | + emit
|
|
| 118 | + done
|
|
| 119 | + |
|
| 120 | + for ((chunk = 0; chunk < chunk_count; chunk++)); do
|
|
| 121 | + loop_start=$(( chunk * LOOP_PERIOD ))
|
|
| 122 | + next_stage=$(( (chunk + 1) * LOOP_PERIOD ))
|
|
| 123 | + latch_mix=$(( 1000000 + chunk ))
|
|
| 124 | + latch_acc=$(( 2000000 + chunk ))
|
|
| 125 | + |
|
| 126 | + emit "latch${chunk}:"
|
|
| 127 | + emit " trip${chunk} = trip${chunk} + (1 :: W_);"
|
|
| 128 | + emit " mix = mix + (${latch_mix} :: W_);"
|
|
| 129 | + emit " acc = acc + (${latch_acc} :: W_);"
|
|
| 130 | + emit " if (trip${chunk} < (${LOOP_TRIPS} :: W_)) {"
|
|
| 131 | + emit " goto split${loop_start};"
|
|
| 132 | + emit " }"
|
|
| 133 | + if (( next_stage < STAGES )); then
|
|
| 134 | + emit " goto split${next_stage};"
|
|
| 135 | + else
|
|
| 136 | + emit " return (acc);"
|
|
| 137 | + fi
|
|
| 138 | + emit
|
|
| 139 | + done
|
|
| 140 | + |
|
| 141 | + emit "}"
|
|
| 142 | +} > "$OUT" |