packages feed

linearscan-hoopl 0.4.0.1 → 0.5.0.0

raw patch · 7 files changed

+556/−363 lines, 7 filesdep +deepseqdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: deepseq

Dependency ranges changed: base

API changes (from Hackage documentation)

- LinearScan.Hoopl: SpillStack :: Int -> Int -> Map (Maybe Int) Int -> SpillStack
- LinearScan.Hoopl: data SpillStack
- LinearScan.Hoopl: getStackSlot :: Maybe VarId -> State SpillStack Int
- LinearScan.Hoopl: instance Eq SpillStack
- LinearScan.Hoopl: instance Show SpillStack
- LinearScan.Hoopl: newSpillStack :: Int -> Int -> SpillStack
- LinearScan.Hoopl: stackPtr :: SpillStack -> Int
- LinearScan.Hoopl: stackSlotSize :: SpillStack -> Int
- LinearScan.Hoopl: stackSlots :: SpillStack -> Map (Maybe Int) Int
+ LinearScan.Hoopl: fromReg :: NodeAlloc n v r => r -> PhysReg
+ LinearScan.Hoopl: fromVar :: NodeAlloc n v r => v -> Either PhysReg VarId
+ LinearScan.Hoopl: mkJumpOp :: NodeAlloc n v r => Label -> n v O C
+ LinearScan.Hoopl: mkLabelOp :: NodeAlloc n v r => Label -> n v C O
+ LinearScan.Hoopl: retargetBranch :: NodeAlloc n v r => n v O C -> Label -> Label -> n v O C
+ LinearScan.Hoopl.DSL: EnvState :: Labels -> BlockIds -> SpillStack -> Map PhysReg VarId -> EnvState
+ LinearScan.Hoopl.DSL: SpillStack :: Int -> Int -> Map (Maybe Int) Int -> SpillStack
+ LinearScan.Hoopl.DSL: data EnvState
+ LinearScan.Hoopl.DSL: data SpillStack
+ LinearScan.Hoopl.DSL: envAssignments :: EnvState -> Map PhysReg VarId
+ LinearScan.Hoopl.DSL: envBlockIds :: EnvState -> BlockIds
+ LinearScan.Hoopl.DSL: envLabels :: EnvState -> Labels
+ LinearScan.Hoopl.DSL: envSpillStack :: EnvState -> SpillStack
+ LinearScan.Hoopl.DSL: getAssignment :: PhysReg -> Env VarId
+ LinearScan.Hoopl.DSL: getStackSlot :: Maybe VarId -> Env Int
+ LinearScan.Hoopl.DSL: instance Eq SpillStack
+ LinearScan.Hoopl.DSL: instance Show SpillStack
+ LinearScan.Hoopl.DSL: newEnvState :: EnvState
+ LinearScan.Hoopl.DSL: newSpillStack :: Int -> Int -> SpillStack
+ LinearScan.Hoopl.DSL: setAssignment :: PhysReg -> VarId -> Env ()
+ LinearScan.Hoopl.DSL: stackPtr :: SpillStack -> Int
+ LinearScan.Hoopl.DSL: stackSlotSize :: SpillStack -> Int
+ LinearScan.Hoopl.DSL: stackSlots :: SpillStack -> Map (Maybe Int) Int
+ LinearScan.Hoopl.DSL: type BlockIds = Map Label Int
+ LinearScan.Hoopl.DSL: type Env = StateT EnvState SimpleUniqueMonad
- LinearScan.Hoopl: blockInfo :: NonLocal n1 => (Label -> Int) -> BlockInfo (Block n1 C C) (Block n2 C C) (NodeV n1) (NodeV n2)
+ LinearScan.Hoopl: blockInfo :: (NodeAlloc n v r, NonLocal (n v)) => (Label -> Env Int) -> BlockInfo Env (Block (n v) C C) (Block (n r) C C) (NodeV (n v)) (NodeV (n r))
- LinearScan.Hoopl: class HooplNode n1 => NodeAlloc s n1 n2 | n1 -> n2, n2 -> n1, n1 -> s, n2 -> s
+ LinearScan.Hoopl: class HooplNode (n v) => NodeAlloc n v r | n -> v, n -> r
- LinearScan.Hoopl: getReferences :: NodeAlloc s n1 n2 => n1 e x -> [VarInfo]
+ LinearScan.Hoopl: getReferences :: NodeAlloc n v r => n v e x -> [VarInfo]
- LinearScan.Hoopl: isBranch :: NodeAlloc s n1 n2 => n1 O C -> Bool
+ LinearScan.Hoopl: isBranch :: NodeAlloc n v r => n v O C -> Bool
- LinearScan.Hoopl: isCall :: NodeAlloc s n1 n2 => n1 O O -> Bool
+ LinearScan.Hoopl: isCall :: NodeAlloc n v r => n v O O -> Bool
- LinearScan.Hoopl: mkMoveOps :: NodeAlloc s n1 n2 => PhysReg -> PhysReg -> State s [n2 O O]
+ LinearScan.Hoopl: mkMoveOps :: NodeAlloc n v r => PhysReg -> PhysReg -> Env [n r O O]
- LinearScan.Hoopl: mkRestoreOps :: NodeAlloc s n1 n2 => Maybe VarId -> PhysReg -> State s [n2 O O]
+ LinearScan.Hoopl: mkRestoreOps :: NodeAlloc n v r => Maybe VarId -> PhysReg -> Env [n r O O]
- LinearScan.Hoopl: mkSaveOps :: NodeAlloc s n1 n2 => PhysReg -> Maybe VarId -> State s [n2 O O]
+ LinearScan.Hoopl: mkSaveOps :: NodeAlloc n v r => PhysReg -> Maybe VarId -> Env [n r O O]
- LinearScan.Hoopl: mkSwapOps :: NodeAlloc s n1 n2 => PhysReg -> PhysReg -> State s [n2 O O]
+ LinearScan.Hoopl: mkSwapOps :: NodeAlloc n v r => PhysReg -> PhysReg -> Env [n r O O]
- LinearScan.Hoopl: op1ToString :: NodeAlloc s n1 n2 => n1 e x -> String
+ LinearScan.Hoopl: op1ToString :: NodeAlloc n v r => n v e x -> String
- LinearScan.Hoopl: opInfo :: NodeAlloc s n1 n2 => OpInfo s (NodeV n1) (NodeV n2)
+ LinearScan.Hoopl: opInfo :: NodeAlloc n v r => OpInfo Env (NodeV (n v)) (NodeV (n r))
- LinearScan.Hoopl: setRegisters :: NodeAlloc s n1 n2 => [(Int, PhysReg)] -> n1 e x -> n2 e x
+ LinearScan.Hoopl: setRegisters :: NodeAlloc n v r => [(Int, PhysReg)] -> n v e x -> Env (n r e x)
- LinearScan.Hoopl.DSL: compile :: (NonLocal n, HooplNode n) => String -> Program n -> (Graph n C C, Label)
+ LinearScan.Hoopl.DSL: compile :: (NonLocal n, HooplNode n) => String -> Program n -> SimpleUniqueMonad (Graph n C C, Label)

Files

LinearScan/Hoopl.hs view
@@ -9,38 +9,63 @@  import           Compiler.Hoopl as Hoopl hiding ((<*>)) import           Control.Applicative-import           Control.Monad.Trans.State (State, get, put)+import           Control.Arrow+import           Control.Monad.Trans.Class+import           Control.Monad.Trans.State (State, get, put, modify) import qualified Data.Map as M import           Data.Monoid+import           Debug.Trace import           LinearScan+import           LinearScan.Hoopl.DSL -class HooplNode n1-      => NodeAlloc s n1 n2 | n1 -> n2, n2 -> n1, n1 -> s, n2 -> s where-    isCall   :: n1 O O -> Bool-    isBranch :: n1 O C -> Bool+class HooplNode (n v) => NodeAlloc n v r | n -> v, n -> r where+    fromVar :: v -> Either PhysReg VarId+    fromReg :: r -> PhysReg -    getReferences :: n1 e x -> [VarInfo]-    setRegisters  :: [(Int, PhysReg)] -> n1 e x -> n2 e x+    isCall   :: n v O O -> Bool+    isBranch :: n v O C -> Bool -    mkMoveOps    :: PhysReg     -> PhysReg     -> State s [n2 O O]-    mkSwapOps    :: PhysReg     -> PhysReg     -> State s [n2 O O]-    mkSaveOps    :: PhysReg     -> Maybe VarId -> State s [n2 O O]-    mkRestoreOps :: Maybe VarId -> PhysReg     -> State s [n2 O O]+    retargetBranch :: n v O C -> Label -> Label -> n v O C -    op1ToString  :: n1 e x -> String+    mkLabelOp :: Label -> n v C O+    mkJumpOp  :: Label -> n v O C +    getReferences :: n v e x -> [VarInfo]+    setRegisters  :: [(Int, PhysReg)] -> n v e x -> Env (n r e x)++    mkMoveOps    :: PhysReg -> PhysReg -> Env [n r O O]+    mkSwapOps    :: PhysReg -> PhysReg -> Env [n r O O]+    mkSaveOps    :: PhysReg -> Maybe VarId -> Env [n r O O]+    mkRestoreOps :: Maybe VarId -> PhysReg -> Env [n r O O]++    op1ToString  :: n v e x -> String+ data NodeV n = NodeCO { getNodeCO :: n C O }              | NodeOO { getNodeOO :: n O O }              | NodeOC { getNodeOC :: n O C } -blockInfo :: NonLocal n1-          => (Label -> Int)-          -> BlockInfo (Block n1 C C) (Block n2 C C) (NodeV n1) (NodeV n2)+blockInfo :: (NodeAlloc n v r, NonLocal (n v))+          => (Label -> Env Int)+          -> BlockInfo Env (Block (n v) C C) (Block (n r) C C)+                           (NodeV (n v)) (NodeV (n r)) blockInfo getBlockId = BlockInfo     { blockId = getBlockId . entryLabel -    , blockSuccessors = Prelude.map getBlockId . successors+    , blockSuccessors = Prelude.mapM getBlockId . successors +    , splitCriticalEdge = \(BlockCC b m e)+                           (BlockCC next _ _) -> do+        let lab = entryLabel next+        lab' <- lift freshLabel+        modify $ \st ->+            st { envLabels   = M.insert (show lab ++ "'") lab' (envLabels st)+               , envBlockIds = let m = envBlockIds st in+                               M.insert lab' (M.size m + 1) m+               }+        let e' = retargetBranch e lab lab'+        return (BlockCC b m e',+                BlockCC (mkLabelOp lab') BNil (mkJumpOp lab))+     , blockOps = \(BlockCC a b z) ->         ([NodeCO a], Prelude.map NodeOO (blockToList b), [NodeOC z]) @@ -51,7 +76,7 @@             (getNodeOC z)     } -opInfo :: forall s n1 n2. NodeAlloc s n1 n2 => OpInfo s (NodeV n1) (NodeV n2)+opInfo :: NodeAlloc n v r => OpInfo Env (NodeV (n v)) (NodeV (n r)) opInfo = OpInfo     { opKind = \node -> case node of            NodeOO n | isCall n  -> IsCall@@ -70,41 +95,14 @@     , saveOp    = \x y -> fmap NodeOO <$> mkSaveOps x y     , restoreOp = \x y -> fmap NodeOO <$> mkRestoreOps x y -    , applyAllocs = \node m -> case node of-           NodeCO n -> [NodeCO (setRegisters m n)]-           NodeOO n -> [NodeOO (setRegisters m n)]-           NodeOC n -> [NodeOC (setRegisters m n)]+    , applyAllocs = \node m ->+        case node of+           NodeCO n -> setRegisters m n >>= \alloc -> return [NodeCO alloc]+           NodeOO n -> setRegisters m n >>= \alloc -> return [NodeOO alloc]+           NodeOC n -> setRegisters m n >>= \alloc -> return [NodeOC alloc]      , showOp1 = \node -> case node of            NodeCO n -> op1ToString n            NodeOO n -> op1ToString n            NodeOC n -> op1ToString n     }--data SpillStack = SpillStack-    { stackPtr      :: Int-    , stackSlotSize :: Int-    , stackSlots    :: M.Map (Maybe Int) Int-    }-    deriving (Eq, Show)--newSpillStack :: Int -> Int -> SpillStack-newSpillStack offset slotSize = SpillStack-    { stackPtr      = offset-    , stackSlotSize = slotSize-    , stackSlots    = mempty-    }--getStackSlot :: Maybe VarId -> State SpillStack Int-getStackSlot vid = do-    stack <- get-    case M.lookup vid (stackSlots stack) of-        Just off -> return off-        Nothing -> do-            let off = stackPtr stack-            put stack-                 { stackPtr   = off + stackSlotSize stack-                 , stackSlots =-                     M.insert vid off (stackSlots stack)-                 }-            return off
LinearScan/Hoopl/DSL.hs view
@@ -2,16 +2,66 @@  import           Compiler.Hoopl as Hoopl hiding ((<*>)) import           Control.Applicative+import           Control.Arrow (first) import           Control.Monad.Free import           Control.Monad.Trans.Class import qualified Control.Monad.Trans.Free as TF import           Control.Monad.Trans.Free hiding (FreeF(..), Free)-import           Control.Monad.Trans.State (StateT, evalStateT, gets, modify)+import           Control.Monad.Trans.State (StateT, evalStateT,+                                            gets, modify, get, put) import qualified Data.Map as M import           Data.Monoid+import           LinearScan  type Labels = M.Map String Label+type BlockIds = M.Map Label Int +data SpillStack = SpillStack+    { stackPtr      :: Int+    , stackSlotSize :: Int+    , stackSlots    :: M.Map (Maybe Int) Int+    }+    deriving (Eq, Show)++data EnvState = EnvState+    { envLabels      :: Labels+    , envBlockIds    :: BlockIds+    , envSpillStack  :: SpillStack+    , envAssignments :: M.Map PhysReg VarId+    }++type Env = StateT EnvState SimpleUniqueMonad++newSpillStack :: Int -> Int -> SpillStack+newSpillStack offset slotSize = SpillStack+    { stackPtr      = offset+    , stackSlotSize = slotSize+    , stackSlots    = mempty+    }++newEnvState :: EnvState+newEnvState = EnvState+    { envLabels      = mempty+    , envBlockIds    = mempty+    , envSpillStack  = newSpillStack 0 8+    , envAssignments = mempty+    }++getStackSlot :: Maybe VarId -> Env Int+getStackSlot vid = do+    st <- get+    let stack = envSpillStack st+    case M.lookup vid (stackSlots stack) of+        Just off -> return off+        Nothing -> do+            let off = stackPtr stack+            put st { envSpillStack = stack+                 { stackPtr   = off + stackSlotSize stack+                 , stackSlots =+                     M.insert vid off (stackSlots stack)+                 }}+            return off+ -- | The 'Asm' monad lets us create labels by name and refer to them later. type Asm = StateT Labels SimpleUniqueMonad @@ -25,6 +75,18 @@             modify (M.insert str lbl)             return lbl +setAssignment :: PhysReg -> VarId -> Env ()+setAssignment reg vid =+    modify $ \env ->+        env { envAssignments = M.insert reg vid (envAssignments env) }++getAssignment :: PhysReg -> Env VarId+getAssignment reg = do+    l <- gets (M.lookup reg . envAssignments)+    case l of+        Just vid -> return vid+        Nothing  -> error $ "No assignment for register: " ++ show reg+ -- | A series of 'Nodes' is a set of assembly instructions that ends with some --   kind of closing operation, such as a jump, branch or return. type Nodes n a = Free ((,) (n O O)) a@@ -63,10 +125,9 @@ -- | When we compile a program, the result is a closed Hoopl Graph and the --   label corresponding to the requested entry label name. compile :: (NonLocal n, HooplNode n)-        => String -> Program n -> (Graph n C C, Label)+        => String -> Program n -> SimpleUniqueMonad (Graph n C C, Label) compile name prog-    = runSimpleUniqueMonad-    $ flip evalStateT (mempty :: Labels)+    = flip evalStateT (mempty :: Labels)     $ do body  <- go prog          entry <- gets (M.lookup name)          case entry of
linearscan-hoopl.cabal view
@@ -1,5 +1,5 @@ name:          linearscan-hoopl-version:       0.4.0.1+version:       0.5.0.0 synopsis:      Makes it easy to use the linearscan register allocator with Hoopl homepage:      http://github.com/jwiegley/linearscan-hoopl license:       BSD3@@ -24,7 +24,7 @@   exposed-modules:     LinearScan.Hoopl     LinearScan.Hoopl.DSL-  build-depends:    base >=4.7 && <4.8+  build-depends:    base >=4.7 && <5                   , hoopl >= 3.10.0.1                   , linearscan >= 0.3.0.1                   , containers@@ -34,11 +34,12 @@ test-suite test   default-language: Haskell2010   type:             exitcode-stdio-1.0-  ghc-options:      -Wall -Werror -fno-warn-deprecated-flags+  ghc-options:      -Wall -fno-warn-deprecated-flags   hs-source-dirs:   test   main-is:          Main.hs   other-modules:    AsmTest                     Assembly+                    Normal   build-depends:          base >=3       , linearscan@@ -50,3 +51,4 @@       , containers         >= 0.5.5       , transformers       >= 0.3.0.0       , lens-family-core+      , deepseq
test/AsmTest.hs view
@@ -1,44 +1,66 @@ {-# LANGUAGE GADTs #-}-{-# LANGUAGE ViewPatterns #-}  module AsmTest where  import           Assembly import           Compiler.Hoopl as Hoopl hiding ((<*>))+import           Control.DeepSeq import           Control.Exception-import           Control.Monad.Trans.State (evalState)+import           Control.Monad.Trans.State (evalStateT, gets) import           Data.Foldable import qualified Data.Map as M import           Data.Maybe (fromMaybe) import           LinearScan import           LinearScan.Hoopl import           LinearScan.Hoopl.DSL+import           Normal () import           Test.Hspec -asmTest :: Int -> Program (Node IRVar) -> Program (Node PhysReg) -> Expectation-asmTest regs (compile "entry" -> (prog, entry))-             (compile "entry" -> (result, _)) =-    go $ M.fromList $ zip (Prelude.map entryLabel blocks) [(0 :: Int)..]-  where-    GMany NothingO body NothingO = prog-    blocks = postorder_dfs_from body entry--    alloc blockIds = allocate regs (blockInfo getBlockId) opInfo blocks-      where-        getBlockId :: Hoopl.Label -> Int-        getBlockId lbl = fromMaybe (error "The impossible happened")-                                   (M.lookup lbl blockIds)--    go blockIds = case evalState (alloc blockIds) (newSpillStack 0 8) of+asmTestLiteral :: Int -> Program (Node IRVar) -> String -> Expectation+asmTestLiteral regs program expected = do+    let eres = runSimpleUniqueMonad $ do+            x <- compile "entry" program+            uncurry runTest x+    case eres of         Left err -> error $ "Allocation failed: " ++ err         Right blks -> do-            let graph' = newGraph blks-            catch (showGraph show graph' `shouldBe`-                   showGraph show result) $ \e -> do-                putStrLn $ "---- Expecting ----\n" ++ showGraph show result-                putStrLn $ "---- Compiled  ----\n" ++ showGraph show graph'+            let graph' = newGraph $!! blks+            let g = showGraph show graph'+            catch (g `shouldBe` expected) $ \e -> do+                let input = runSimpleUniqueMonad $ compile "entry" program+                putStrLn $ "---- Parsed ----\n" ++ showGraph show (fst input)+                putStrLn $ "---- Expecting ----\n" ++ expected+                putStrLn $ "---- Compiled  ----\n" ++ g                 putStrLn "-------------------"                 throwIO (e :: SomeException)+  where+    runTest prog entry =+        go $ M.fromList $ zip (Prelude.map entryLabel blocks) [(0 :: Int)..]       where-        newBody = Data.Foldable.foldl' (flip addBlock) emptyBody-        newGraph xs = GMany NothingO (newBody xs) NothingO+        GMany NothingO body NothingO = prog+        blocks = postorder_dfs_from body entry++        alloc = allocate regs (blockInfo getBlockId) opInfo $!! blocks+          where+            getBlockId :: Hoopl.Label -> Env Int+            getBlockId lbl = do+                bids <- gets envBlockIds+                return $ fromMaybe+                    (error $ "Unable to find block at label " ++ show lbl)+                    (M.lookup lbl bids)++        go blockIds = evalStateT alloc (newEnvState { envBlockIds = blockIds })++    newBody = Data.Foldable.foldl' (flip addBlock) emptyBody+    newGraph xs = GMany NothingO (newBody xs) NothingO++asmTest :: Int+        -> Program (Node IRVar)+        -> Program (Node (Assign VarId PhysReg))+        -> Expectation+asmTest regs program expected+    = asmTestLiteral regs program+    $ showGraph show+    $ fst+    $ runSimpleUniqueMonad+    $ compile "entry" expected
test/Assembly.hs view
@@ -25,10 +25,15 @@ default (Int)  -- | The basic instructions that have nothing to do with control flow.-data Instruction reg = Add reg reg reg-                     | Nop-    deriving (Eq, Show, Functor, Foldable, Traversable)+data Instruction reg+    = Add reg reg reg+    | Nop+    deriving (Eq, Functor, Foldable, Traversable) +instance Show r => Show (Instruction r) where+    show (Add x1 x2 x3) = "add " ++ show x1 ++ " " ++ show x2 ++ " " ++ show x3+    show Nop = "nop"+ -- | Tests used for branching (correspond to branching instructions) data Test = Zero                -- ^ beq           | NonZero             -- ^ bne@@ -79,8 +84,8 @@     show (LoadConst _c v)  = "\t@lc " ++ show v -- ++ " " ++ show c     show (Move x1 x2)      = "\t@mvrr " ++ show x1 ++ " " ++ show x2     show (Copy x1 x2)      = "\t@cprr " ++ show x1 ++ " " ++ show x2-    show (Save src dst)    = "\t@save " ++ " " ++ show src ++ " " ++ show dst-    show (Restore src dst) = "\t@restore " ++ " " ++ show src ++ " " ++ show dst+    show (Save src dst)    = "\t@save " ++ show src ++ " " ++ show dst+    show (Restore src dst) = "\t@restore " ++ show src ++ " " ++ show dst     show (Trace str)       = "\tTRACE " ++ " " ++ show str     show (Jump l)          = "\t@jmp " ++ show l     show (Branch c v t f)  = "\t@b" ++ show c ++ " " ++ show v@@ -131,10 +136,10 @@ lc :: v -> BodyNode (Node v) lc x0 = bodyNode $ LoadConst 0 x0 -save :: PhysReg -> Dst PhysReg -> BodyNode (Node PhysReg)+save :: v -> Dst PhysReg -> BodyNode (Node v) save r dst = bodyNode $ Save r dst -restore :: Src PhysReg -> PhysReg -> BodyNode (Node PhysReg)+restore :: Src PhysReg -> v -> BodyNode (Node v) restore src r = bodyNode $ Restore src r  trace :: String -> BodyNode (Node v)@@ -147,13 +152,25 @@ return_ :: EndNode (Node v) return_ = endNode $ return $ ReturnInstr [] Nop +data Assign a b = Assign a b++instance Show a => Show (Assign a PhysReg) where+    show (Assign v (-1)) = "<<v" ++ show v ++ ">>"+    -- show (Assign v r)    = "r" ++ show r ++ "|v" ++ show v+    show (Assign v r)    = show r+ data IRVar = PhysicalIV PhysReg | VirtualIV Int deriving Eq  instance Show IRVar where     show (PhysicalIV r) = "r" ++ show r     show (VirtualIV n)  = "v" ++ show n -instance NodeAlloc SpillStack (Node IRVar) (Node PhysReg) where+instance NodeAlloc Node IRVar (Assign VarId PhysReg) where+    fromVar (PhysicalIV r) = Left r+    fromVar (VirtualIV n)  = Right n++    fromReg (Assign _ r) = r+     isCall (Call {}) = True     isCall _ = False @@ -161,6 +178,15 @@     isBranch (Branch {}) = True     isBranch _ = False +    retargetBranch (Jump _) _ lab = Jump lab+    retargetBranch (Branch b v x y) old lab+        | x == old  = Branch b v lab y+        | otherwise = Branch b v x lab+    retargetBranch x _ _ = error $ "Cannot retarget " ++ show x++    mkLabelOp = Label+    mkJumpOp  = Jump+     getReferences = go       where         go :: Node IRVar e x -> [VarInfo]@@ -190,23 +216,29 @@             , regRequired = True             } -    setRegisters = over variables . go+    setRegisters m g = do+        for_ m $ \(v, r) -> setAssignment r v+        return $ over variables go g       where-        go :: [(Int, PhysReg)] -> IRVar -> PhysReg-        go _ (PhysicalIV r) = r-        go m (VirtualIV n)  =-            fromMaybe-                (-1) -- (error $ "Allocation failed for variable " ++ show n)-                (Data.List.lookup n m)+        go :: IRVar -> Assign VarId PhysReg+        go (PhysicalIV r) = Assign (-1) r+        go (VirtualIV n)  = Assign n (fromMaybe (-1) (Data.List.lookup n m)) -    mkMoveOps src dst = return [Move src dst]-    mkSwapOps src dst = liftA2 (++) (mkRestoreOps Nothing dst)-                                    (mkSaveOps src Nothing)+    mkMoveOps src dst = do+        vid <- getAssignment src+        return [Move (Assign vid src) (Assign vid dst)]+    mkSwapOps src dst =+        liftA2 (++) (mkRestoreOps Nothing dst)+                    (mkSaveOps src Nothing) -    mkSaveOps    src dst = do off <- getStackSlot dst-                              return [Save src off]-    mkRestoreOps src dst = do off <- getStackSlot src-                              return [Restore off dst]+    mkSaveOps src dst = do+        off <- getStackSlot dst+        vid <- getAssignment src+        return [Save (Assign vid src) off]+    mkRestoreOps src dst = do+        off <- getStackSlot src+        vid <- getAssignment dst+        return [Restore off (Assign vid dst)]      op1ToString = show @@ -255,8 +287,9 @@ v39 = var 39 v40 = var 40 -reg :: PhysReg -> PhysReg-reg r = r+reg :: PhysReg -> IRVar -> Assign VarId PhysReg+reg _ (PhysicalIV _) = error "Don't use reg to reference a literal register"+reg r (VirtualIV v) = Assign v r  r0  = reg 0 r1  = reg 1
test/Main.hs view
@@ -12,6 +12,7 @@ main :: IO () main = hspec $ do   describe "Sanity tests" sanityTests+  describe "Spill tests" spillTests   describe "Block tests" blockTests   describe "Call tests" callTests   describe "Loop tests" loopTests@@ -24,7 +25,7 @@         return_) $      label "entry" $ do-        add 0 1 0+        add (r0 v0) (r1 v1) (r0 v2)         return_    it "Single, repeated instruction" $ asmTest 32@@ -35,9 +36,9 @@         return_) $      label "entry" $ do-        add 0 1 2-        add 0 1 2-        add 0 1 2+        add (r0 v0) (r1 v0) (r2 v0)+        add (r0 v0) (r1 v0) (r2 v0)+        add (r0 v0) (r1 v0) (r2 v0)         return_    it "Multiple instructions" $ asmTest 32@@ -48,9 +49,9 @@         return_) $      label "entry" $ do-        add 0 1 2-        add 0 1 3-        add 0 1 2+        add (r0 v0) (r1 v0) (r2 v0)+        add (r0 v0) (r1 v0) (r3 v0)+        add (r0 v0) (r1 v0) (r2 v0)         return_    it "More variables used than registers" $ asmTest 6@@ -73,31 +74,31 @@         return_) $      label "entry" $ do-	lc 0-	lc 1-	lc 2-	lc 3-	lc 4-	lc 5-	save  4 0-	lc 4-	save  4 8-	lc 4-	save  4 16-	lc 4-	save  4 24-	lc 4-	add 0 1 0-	add 2 3 1-	restore  0 3-	add 3 5 2-	save  0 32-	restore  8 5-	restore  16 0-	add 5 0 3-	restore  24 5-	add 5 4 0-	return_+        lc (r0 v0)+        lc (r1 v0)+        lc (r2 v0)+        lc (r3 v0)+        lc (r4 v0)+        lc (r5 v0)+        save (r4 v0) 0+        lc (r4 v0)+        save (r4 v0) 8+        lc (r4 v0)+        save (r4 v0) 16+        lc (r4 v0)+        save (r4 v0) 24+        lc (r4 v0)+        add (r0 v0) (r1 v0) (r0 v0)+        add (r2 v0) (r3 v0) (r1 v0)+        restore 0 (r3 v0)+        add (r3 v0) (r5 v0) (r2 v0)+        save (r0 v0) 32+        restore 8 (r5 v0)+        restore 16 (r0 v0)+        add (r5 v0) (r0 v0) (r3 v0)+        restore 24 (r5 v0)+        add (r5 v0) (r4 v0) (r0 v0)+        return_    it "Single long-lived variable" $ asmTest 32     (label "entry" $ do@@ -108,10 +109,10 @@         return_) $      label "entry" $ do-        add 0 1 1-        add 0 2 2-        add 0 3 3-        add 0 4 0+        add (r0 v0) (r1 v0) (r1 v0)+        add (r0 v0) (r2 v0) (r2 v0)+        add (r0 v0) (r3 v0) (r3 v0)+        add (r0 v0) (r4 v0) (r0 v0)         return_    it "Two long-lived variables" $ asmTest 32@@ -123,10 +124,10 @@         return_) $      label "entry" $ do-        add 0 1 1-        add 0 2 3-        add 0 2 4-        add 0 2 0+        add (r0 v0) (r1 v0) (r1 v0)+        add (r0 v0) (r2 v0) (r3 v0)+        add (r0 v0) (r2 v0) (r4 v0)+        add (r0 v0) (r2 v0) (r0 v0)         return_    it "One variable with a long interval" $ asmTest 32@@ -146,18 +147,18 @@         return_) $      label "entry" $ do-        add 0 1 1-        add 2 3 2-        add 4 5 3-        add 6 7 4-        add 8 9 5-        add 10 11 6-        add 12 13 7-        add 14 15 8-        add 16 17 9-        add 18 19 10-        add 20 21 11-        add 0 22 0+        add (r0 v0) (r1 v0) (r1 v0)+        add (r2 v0) (r3 v0) (r2 v0)+        add (r4 v0) (r5 v0) (r3 v0)+        add (r6 v0) (r7 v0) (r4 v0)+        add (r8 v0) (r9 v0) (r5 v0)+        add (r10 v0) (r11 v0) (r6 v0)+        add (r12 v0) (r13 v0) (r7 v0)+        add (r14 v0) (r15 v0) (r8 v0)+        add (r16 v0) (r17 v0) (r9 v0)+        add (r18 v0) (r19 v0) (r10 v0)+        add (r20 v0) (r21 v0) (r11 v0)+        add (r0 v0) (r22 v0) (r0 v0)         return_    it "Many variables with long intervals" $ asmTest 32@@ -185,28 +186,30 @@         return_) $      label "entry" $ do-        add 0 1 20-        add 2 3 21-        add 4 5 22-        add 6 7 23-        add 8 9 24-        add 10 11 25-        add 12 13 26-        add 14 15 27-        add 16 17 28-        add 18 19 29-        add 0 1 20-        add 2 3 21-        add 4 5 22-        add 6 7 23-        add 8 9 24-        add 10 11 25-        add 12 13 26-        add 14 15 27-        add 16 17 28-        add 18 19 29+        add (r0 v0) (r1 v0) (r20 v0)+        add (r2 v0) (r3 v0) (r21 v0)+        add (r4 v0) (r5 v0) (r22 v0)+        add (r6 v0) (r7 v0) (r23 v0)+        add (r8 v0) (r9 v0) (r24 v0)+        add (r10 v0) (r11 v0) (r25 v0)+        add (r12 v0) (r13 v0) (r26 v0)+        add (r14 v0) (r15 v0) (r27 v0)+        add (r16 v0) (r17 v0) (r28 v0)+        add (r18 v0) (r19 v0) (r29 v0)+        add (r0 v0) (r1 v0) (r20 v0)+        add (r2 v0) (r3 v0) (r21 v0)+        add (r4 v0) (r5 v0) (r22 v0)+        add (r6 v0) (r7 v0) (r23 v0)+        add (r8 v0) (r9 v0) (r24 v0)+        add (r10 v0) (r11 v0) (r25 v0)+        add (r12 v0) (r13 v0) (r26 v0)+        add (r14 v0) (r15 v0) (r27 v0)+        add (r16 v0) (r17 v0) (r28 v0)+        add (r18 v0) (r19 v0) (r29 v0)         return_ +spillTests :: SpecWith ()+spillTests = do   it "Spilling one variable" $ asmTest 32     (label "entry" $ do         add v0   v1  v2@@ -234,40 +237,40 @@         return_) $      label "entry" $ do-        add 0 1 22-        add 2 3 23-        add 4 5 24-        add 6 7 25-        add 8 9 26-        add 10 11 27-        add 12 13 28-        add 14 15 29-        add 16 17 30-        add 18 19 31+        add (r0 v0) (r1 v0) (r22 v0)+        add (r2 v0) (r3 v0) (r23 v0)+        add (r4 v0) (r5 v0) (r24 v0)+        add (r6 v0) (r7 v0) (r25 v0)+        add (r8 v0) (r9 v0) (r26 v0)+        add (r10 v0) (r11 v0) (r27 v0)+        add (r12 v0) (r13 v0) (r28 v0)+        add (r14 v0) (r15 v0) (r29 v0)+        add (r16 v0) (r17 v0) (r30 v0)+        add (r18 v0) (r19 v0) (r31 v0)          -- When we reach the 32nd variable considered (which happens to be         -- v30), we must spill a register because there are not 32 registers.         -- So we pick the first register, counting from 0, whose next use         -- position is the furthest from this position.  That happens to be         -- 18, which is next used at position 41.-        save 18 0-        add 20 21 18-        add 0 1 22-        add 2 3 23-        add 4 5 24-        add 6 7 25-        add 8 9 26-        add 10 11 27-        add 12 13 28-        add 14 15 29-        add 16 17 30+        save (r18 v0) 0+        add (r20 v0) (r21 v0) (r18 v0)+        add (r0 v0) (r1 v0) (r22 v0)+        add (r2 v0) (r3 v0) (r23 v0)+        add (r4 v0) (r5 v0) (r24 v0)+        add (r6 v0) (r7 v0) (r25 v0)+        add (r8 v0) (r9 v0) (r26 v0)+        add (r10 v0) (r11 v0) (r27 v0)+        add (r12 v0) (r13 v0) (r28 v0)+        add (r14 v0) (r15 v0) (r29 v0)+        add (r16 v0) (r17 v0) (r30 v0)          -- When it comes time to reload v29 (which had been allocated to 18),         -- we pick the first available register which happens to be 0 in this         -- case.-        restore 0 0-        add 0 19 31-        add 20 21 18+        restore 0 (r0 v0)+        add (r0 v0) (r19 v0) (r31 v0)+        add (r20 v0) (r21 v0) (r18 v0)         return_    it "Higher register pressure" $ asmTest 3@@ -285,25 +288,25 @@         return_) $      label "entry" $ do-	lc 0-	lc 1-	add 0 1 2-	add 2 1 0-	save  1 0-	add 0 2 1-	save  2 8-	add 1 0 2-	save  1 16-	restore  8 1-	add 1 0 0-	save  0 24-	restore  16 0-	add 0 2 1-	restore  24 2-	add 2 1 0-	restore  0 1-	add 0 1 0-	return_+        lc (r0 v0)+        lc (r1 v0)+        add (r0 v0) (r1 v0) (r2 v0)+        add (r2 v0) (r1 v0) (r0 v0)+        save (r1 v0) 0+        add (r0 v0) (r2 v0) (r1 v0)+        save (r2 v0) 8+        add (r1 v0) (r0 v0) (r2 v0)+        save (r1 v0) 16+        restore 8 (r1 v0)+        add (r1 v0) (r0 v0) (r0 v0)+        save (r0 v0) 24+        restore 16 (r0 v0)+        add (r0 v0) (r2 v0) (r1 v0)+        restore 24 (r2 v0)+        add (r2 v0) (r1 v0) (r0 v0)+        restore 0 (r1 v0)+        add (r0 v0) (r1 v0) (r0 v0)+        return_    it "Inserts necessary saves and restores" $ asmTest 4     (label "entry" $ do@@ -320,18 +323,18 @@         return_) $      label "entry" $ do-        lc 0-        lc 1-        add 0 1 2-        add 2 1 3-        add 3 2 0-        save 1 0-        add 0 3 1-        add 2 3 2-        add 0 1 0-        add 2 0 0-        restore 0 1-        add 0 1 0+        lc (r0 v0)+        lc (r1 v0)+        add (r0 v0) (r1 v0) (r2 v0)+        add (r2 v0) (r1 v0) (r3 v0)+        add (r3 v0) (r2 v0) (r0 v0)+        save (r1 v0) 0+        add (r0 v0) (r3 v0) (r1 v0)+        add (r2 v0) (r3 v0) (r2 v0)+        add (r0 v0) (r1 v0) (r0 v0)+        add (r2 v0) (r0 v0) (r0 v0)+        restore 0 (r1 v0)+        add (r0 v0) (r1 v0) (r0 v0)         return_  blockTests :: SpecWith ()@@ -353,18 +356,18 @@             return_) $      do label "entry" $ do-           add 0 1 0+           add (r0 v0) (r1 v0) (r0 v0)            jump "L2"         label "L2" $ do-           add 0 2 1-           add 0 1 1+           add (r0 v0) (r2 v0) (r1 v0)+           add (r0 v0) (r1 v0) (r1 v0)            jump "L3"         label "L3" $ do-           add 0 1 1-           add 0 1 1-           add 0 1 0+           add (r0 v0) (r1 v0) (r1 v0)+           add (r0 v0) (r1 v0) (r1 v0)+           add (r0 v0) (r1 v0) (r0 v0)            return_    it "Inserts resolving moves" $ asmTest 3@@ -391,30 +394,30 @@             return_) $      do label "entry" $ do-           lc 0-           lc 1-           add 0 1 2-           branch Zero 2 "B2" "B3"+           lc (r0 v0)+           lc (r1 v0)+           add (r0 v0) (r1 v0) (r2 v0)+           branch Zero (r2 v0) "B2" "B3"         label "B2" $ do-           add 1 2 0-           move 0 1+           move (r1 v0) (r0 v0)+           add (r0 v0) (r2 v0) (r1 v0)            jump "B4"         label "B3" $ do-           save 0 0-           add 1 2 0-           restore 0 2-           add 2 0 1-           save 0 8-           add 2 1 0-           add 1 0 0-           restore 8 1-           add 0 1 0+           save (r0 v0) 0+           add (r1 v0) (r2 v0) (r0 v0)+           restore 0 (r2 v0)+           add (r2 v0) (r0 v0) (r1 v0)+           save (r0 v0) 8+           add (r2 v0) (r1 v0) (r0 v0)+           add (r1 v0) (r0 v0) (r0 v0)+           restore 8 (r1 v0)+           add (r0 v0) (r1 v0) (r0 v0)            jump "B4"         label "B4" $ do-           add 1 1 2+           add (r1 v0) (r1 v0) (r0 v0)            return_    it "When resolving moves are not needed" $ asmTest 4@@ -439,82 +442,104 @@             return_) $      do label "entry" $ do-           add 0 1 2-           branch Zero 2 "B2" "B3"+           add (r0 v0) (r1 v0) (r2 v0)+           branch Zero (r2 v0) "B2" "B3"         label "B2" $ do-           add 1 2 3-           add 0 0 1-           add 0 0 2-           add 0 1 1-           add 0 2 1+           add (r1 v0) (r2 v0) (r3 v0)+           add (r0 v0) (r0 v0) (r1 v0)+           add (r0 v0) (r0 v0) (r2 v0)+           add (r0 v0) (r1 v0) (r1 v0)+           add (r0 v0) (r2 v0) (r1 v0)            jump "B4"         label "B3" $ do-           add 1 2 3+           add (r1 v0) (r2 v0) (r3 v0)            jump "B4"         label "B4" $ do-           add 3 3 0+           add (r3 v0) (r3 v0) (r0 v0)            return_ -  -- it "Another resolution case" $ asmTest 4-  --   (do label "entry" $ do-  --           lc v3-  --           lc v4-  --           lc v15-  --           lc v20-  --           jump "L3"+  it "Another resolution case" $ asmTest 4+    (do label "entry" $ do+            lc v3+            lc v4+            lc v15+            lc v20+            jump "L3" -  --       label "L3" $ do-  --           move v3 v9-  --           move v9 v11-  --           move v11 v10-  --           move v10 v12-  --           move v12 v13-  --           lc v14-  --           move v15 v5-  --           jump "L6"+        label "L3" $ do+            move v3 v9+            move v9 v11+            move v11 v10+            move v10 v12+            move v12 v13+            lc v14+            move v15 v5+            jump "L6" -  --       label "L6" $-  --           branch Zero v4 "L3" "L2"+        label "L6" $+            branch Zero v4 "L3" "L2" -  --       label "L2" $ do-  --           lc v21-  --           move v21 v18-  --           move v5 v4-  --           lc v19-  --           move v20 v17-  --           jump "L6") $+        label "L2" $ do+            lc v21+            move v21 v18+            move v5 v4+            lc v19+            move v20 v17+            jump "L6") $ -  --   do label "entry" $ do-  --          lc 0-  --          lc 1-  --          lc 2-  --          lc 3-  --          jump "L3"+    do label "entry" $ do+           lc (r0 v0)+           lc (r1 v0)+           lc (r2 v0)+           lc (r3 v0)+           save (r3 v0) 0+           jump "L3" -  --      label "L3" $ do-  --          move 0 3-  --          move 3 0-  --          move 0 3-  --          move 3 0-  --          move 0 3-  --          lc 0-  --          move 2 0-  --          jump "L6"+       label "L3" $ do+           move (r0 v0) (r3 v0)+           move (r3 v0) (r3 v0)+           move (r3 v0) (r3 v0)+           move (r3 v0) (r3 v0)+           move (r3 v0) (r3 v0)+           save (r0 v0) 8+           lc (r0 v0)+           save (r0 v0) 16+           move (r2 v0) (r0 v0)+           jump "L6" -  --      label "L6" $-  --          branch Zero 1 "L3" "L2"+       label "L2" $ do+           lc (r3 v0)+           save (r2 v0) 24+           move (r3 v0) (r2 v0)+           move (r0 v0) (r1 v0)+           save (r0 v0) 32+           lc (r0 v0)+           save (r1 v0) 48+           save (r0 v0) 40+           restore 0 (r1 v0)+           move (r1 v0) (r0 v0)+           save (r1 v0) 0+           restore 24 (r2 v0)+           restore 32 (r0 v0)+           restore 48 (r1 v0)+           jump "L6" -  --      label "L2" $ do-  --          lc 3-  --          move 3 2-  --          move 0 1-  --          lc 3-  --          move 1 0-  --          jump "L6"+       label "L6" $+           branch Zero (r1 v0) "L5" "L2" +       label "L5" $ do+           save (r1 v0) 48+           restore 0 (r1 v0)+           save (r2 v0) 24+           save (r1 v0) 0+           restore 24 (r2 v0)+           restore 48 (r1 v0)+           restore 8 (r0 v0)+           jump "L3"+ callTests :: SpecWith () callTests = do   it "Spills all registers at call" $ asmTest 4@@ -533,32 +558,32 @@         return_) $      label "entry" $ do-        lc 0-        lc 1-        add 0 1 2-        add 2 1 3-        add 3 2 0-        save 1 0-        add 0 3 1-        save 2 32-        save 3 24-        save 0 16-        save 1 8+        lc (r0 v0)+        lc (r1 v1)+        add (r0 v0) (r1 v1) (r2 v2)+        add (r2 v2) (r1 v1) (r3 v3)+        add (r3 v3) (r2 v2) (r0 v4)+        save (r1 v1) 0+        add (r0 v4) (r3 v3) (r1 v5)+        save (r2 v2) 32+        save (r3 v3) 24+        save (r0 v4) 16+        save (r1 v5) 8         call 1000-        restore 32 1-        restore 24 2-        add 1 2 0-        restore 16 2-        restore 8 3-        add 2 3 1-        add 0 1 0-        restore 0 1-        add 0 1 0+        restore 32 (r1 v2)+        restore 24 (r2 v3)+        add (r1 v2) (r2 v3) (r0 v6)+        restore 16 (r2 v4)+        restore 8 (r3 v5)+        add (r2 v4) (r3 v5) (r1 v7)+        add (r0 v6) (r1 v7) (r0 v8)+        restore 0 (r1 v1)+        add (r0 v8) (r1 v1) (r0 v0)         return_  loopTests :: SpecWith () loopTests = do-  it "Correctly orders loop blocks" $ asmTest 4+  it "Correctly orders loop blocks" $ asmTestLiteral 4     (do label "entry" $ do             trace "B0"             jump "B1"@@ -591,34 +616,29 @@             trace "B7"             jump "B1") $ -    do label "entry" $ do-           trace "B0"-           jump "B1"--       label "B1" $ do-           trace "B1"-           branch Zero 0 "B3" "B2"--       label "B2" $ do-           trace "B2"-           branch Zero 0 "B5" "B4"--       label "B3" $ do-           trace "B3"-           jump "B1"--       label "B4" $ do-           trace "B4"-           branch Zero 0 "B7" "B6"--       label "B5" $ do-           trace "B5"-           return_--       label "B6" $ do-           trace "B6"-           jump "B4"--       label "B7" $ do-           trace "B7"-           jump "B1"+    "L1:\n\+\\tTRACE  \"B0\"\n\+\\t@jmp L2\n\+\L2:\n\+\\tTRACE  \"B1\"\n\+\\t@bZero 0 L3; @jmp L4\n\+\L3:\n\+\\tTRACE  \"B3\"\n\+\\t@jmp L2\n\+\L4:\n\+\\tTRACE  \"B2\"\n\+\\t@bZero 0 L5; @jmp L9\n\+\L5:\n\+\\tTRACE  \"B5\"\n\+\\t@return [] nop\n\+\L6:\n\+\\tTRACE  \"B4\"\n\+\\t@bZero 0 L7; @jmp L8\n\+\L7:\n\+\\tTRACE  \"B7\"\n\+\\t@jmp L2\n\+\L8:\n\+\\tTRACE  \"B6\"\n\+\\t@jmp L6\n\+\L9:\n\+\\t@jmp L6\n"
+ test/Normal.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ViewPatterns #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Normal where++import Assembly+import Compiler.Hoopl as Hoopl hiding ((<*>))+import Control.DeepSeq++instance (NFData a, NFData b) => NFData (Assign a b) where+    rnf (Assign a b) = a `deepseq` b `deepseq` ()++instance NFData IRVar where+    rnf (PhysicalIV r) = r `deepseq` ()+    rnf (VirtualIV r) = r `deepseq` ()++instance NFData v => NFData (Instruction v) where+    rnf (Add x y z) = x `deepseq` y `deepseq` z `deepseq` ()+    rnf Nop = ()++instance NFData CConv where+    rnf InlineC = ()+    rnf (CConvC args results _) = args `deepseq` results `deepseq` ()++instance NFData Label where rnf _ = ()++instance NFData Test where rnf _ = ()++instance NFData v => NFData (Node v e x) where+    rnf (Alloc msrc dst)           = msrc `deepseq` dst `deepseq` ()+    rnf (Reclaim src)              = src `deepseq` ()+    rnf (Instr i)                  = i `deepseq` ()+    rnf (LoadConst c dst)          = c `deepseq` dst `deepseq` ()+    rnf (Move src dst)             = src `deepseq` dst `deepseq` ()+    rnf (Copy src dst)             = src `deepseq` dst `deepseq` ()+    rnf (Save src x)               = src `deepseq` x `deepseq` ()+    rnf (Restore x src)            = x `deepseq` src `deepseq` ()+    rnf (Trace str)                = str `deepseq` ()+    rnf (Branch x1 cond x2 x3)     = x1 `deepseq` cond `deepseq`+                                     x2 `deepseq` x3 `deepseq` ()+    rnf (Call cc i)                = cc `deepseq` i `deepseq` ()+    rnf (ReturnInstr liveInRegs i) = liveInRegs `deepseq` i `deepseq` ()+    rnf (Label x)                  = x `deepseq` ()+    rnf (Jump x)                   = x `deepseq` ()++instance NFData v => NFData (Block (Node v) e x) where+    rnf (BlockCO i b)   = i `deepseq` b `deepseq` ()+    rnf (BlockCC i b o) = i `deepseq` b `deepseq` o `deepseq` ()+    rnf (BlockOC b o)   = b `deepseq` o `deepseq` ()+    rnf BNil            = ()+    rnf (BMiddle n)     = n `deepseq` ()+    rnf (BCat n1 n2)    = n1 `deepseq` n2 `deepseq` ()+    rnf (BSnoc b n)     = b `deepseq` n `deepseq` ()+    rnf (BCons n b)     = n `deepseq` b `deepseq` ()