linearscan-hoopl 0.11.1 → 1.0.0
raw patch · 3 files changed
+125/−28 lines, 3 filesdep ~linearscanPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: linearscan
API changes (from Hackage documentation)
- LinearScan.Hoopl: NodeCO :: n C O -> NodeV n
- LinearScan.Hoopl: NodeOC :: n O C -> NodeV n
- LinearScan.Hoopl: NodeOO :: n O O -> NodeV n
- LinearScan.Hoopl: [getNodeCO] :: NodeV n -> n C O
- LinearScan.Hoopl: [getNodeOC] :: NodeV n -> n O C
- LinearScan.Hoopl: [getNodeOO] :: NodeV n -> n O O
- LinearScan.Hoopl: blockInfo :: (NodeAlloc nv nr, NonLocal nv, NonLocal nr) => BlockInfo Env (Block nv C C) (Block nr C C) (NodeV nv) (NodeV nr)
- LinearScan.Hoopl: data NodeV n
- LinearScan.Hoopl: opInfo :: NodeAlloc nv nr => OpInfo Env (NodeV nv) (NodeV nr)
- LinearScan.Hoopl.DSL: SpillStack :: Int -> Int -> Map (Maybe Int) Int -> SpillStack
+ LinearScan.Hoopl.DSL: SpillStack :: Int -> Int -> Map (Maybe VarId) Int -> SpillStack
- LinearScan.Hoopl.DSL: [stackSlots] :: SpillStack -> Map (Maybe Int) Int
+ LinearScan.Hoopl.DSL: [stackSlots] :: SpillStack -> Map (Maybe VarId) Int
Files
- LinearScan/Hoopl.hs +38/−9
- LinearScan/Hoopl/DSL.hs +70/−10
- linearscan-hoopl.cabal +17/−9
LinearScan/Hoopl.hs view
@@ -5,7 +5,10 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} -module LinearScan.Hoopl where+module LinearScan.Hoopl+ ( NodeAlloc(..)+ , allocateHoopl+ ) where import Compiler.Hoopl as Hoopl hiding ((<*>)) import Control.Applicative@@ -24,21 +27,45 @@ class HooplNode nv => NodeAlloc nv nr | nv -> nr, nr -> nv where isCall :: nv O O -> Bool+ -- ^ Return @True@ if the operation node represents a call to another+ -- procedure.+ isBranch :: nv O C -> Bool+ -- ^ Return @True@ if the operation node is a branch at the end of a basic+ -- block. Often, the only other possibility is a return instruction. retargetBranch :: nv O C -> Label -> Label -> nv O C+ -- ^ Given a branching node and a destination label, retarget the branch+ -- so it goes to the second label in place of the first. mkLabelOp :: Label -> nv C O+ -- ^ Construct a label operation.+ mkJumpOp :: Label -> nv O C+ -- ^ Construct a jump operation to the given label. getReferences :: nv e x -> [VarInfo]+ -- ^ Given a node, return its list of 'LinearScan.VarInfo' references.+ setRegisters :: [((VarId, VarKind), PhysReg)] -> nv e x -> Env (nr e x)+ -- ^ Given a set of register allocations and an operation node, apply+ -- those allocations within the provided 'Env' environment and produce a+ -- result node with the allocations applied. mkMoveOps :: PhysReg -> VarId -> PhysReg -> Env [nr O O]+ -- ^ Construct operation(s) to move a variable's value from one register+ -- to another.+ mkSaveOps :: PhysReg -> VarId -> Env [nr O O]+ -- ^ Construct operation(s) that spill a variable's value from a register+ -- to the spill stack.+ mkRestoreOps :: VarId -> PhysReg -> Env [nr O O]+ -- ^ Construct operation(s) that load a variable's value into a register+ -- from the spill stack. op1ToString :: nv e x -> String+ -- ^ Render the given operation node as a 'String'. data NodeV n = NodeCO { getNodeCO :: n C O } | NodeOO { getNodeOO :: n O O }@@ -107,14 +134,16 @@ NodeOC n -> op1ToString n } -allocateHoopl :: (NodeAlloc nv nr, NonLocal nv, NonLocal nr)- => Int -- ^ Number of machine registers- -> Int -- ^ Offset of the spill stack- -> Int -- ^ Size of spilled register in bytes- -> UseVerifier -- ^ Whether to use allocation verifier- -> Label -- ^ Label of graph entry block- -> Graph nv C C -- ^ Program graph- -> (String, Either [String] (Graph nr C C))+allocateHoopl+ :: (NodeAlloc nv nr, NonLocal nv, NonLocal nr)+ => Int -- ^ Number of machine registers available+ -> Int -- ^ Offset of the spill stack in bytes+ -> Int -- ^ Size of a spilled register in bytes+ -> UseVerifier -- ^ Whether to use the runtime allocation verifier+ -> Label -- ^ Entry label of the program graph+ -> Graph nv C C -- ^ Hoopl program graph+ -> (String, Either [String] (Graph nr C C))+ -- ^ Status dump and allocated blocks, or error w/ context allocateHoopl regs offset slotSize useVerifier entry graph = fmap newGraph <$> runIdentity (go (1 + IM.size (unsafeCoerce body))) where
LinearScan/Hoopl/DSL.hs view
@@ -3,7 +3,31 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -module LinearScan.Hoopl.DSL where+module LinearScan.Hoopl.DSL+ ( -- * Compiling Assembly programs+ compile+ -- * Programs+ , ProgramF(..)+ , Program+ -- * Labels+ , Labels+ , getLabel+ -- * Assembly nodes+ , Asm+ , Nodes+ , nodesToList+ , BodyNode+ , bodyNode+ , EndNode+ , endNode+ , LinearScan.Hoopl.DSL.label+ , jump+ -- * Spill stack+ , SpillStack(..)+ , newSpillStack+ , getStackSlot+ , Env+ ) where import Compiler.Hoopl as Hoopl hiding ((<*>)) import Control.Applicative@@ -24,13 +48,24 @@ data SpillStack = SpillStack { stackPtr :: Int+ -- ^ Offset to the beginning of the spill stack. This can have whatever+ -- meaning the user of this library desires; it is not used directly by+ -- the allocation code.+ , stackSlotSize :: Int- , stackSlots :: M.Map (Maybe Int) Int+ -- ^ The size of a stack slot in bytes. This should be the same or+ -- larger than the size of a register.++ , stackSlots :: M.Map (Maybe VarId) Int+ -- ^ A mapping of variables to their stack slot offsets. The special+ -- variable 'Nothing' is used for temporary storage, for example when+ -- swapping registers through the stack. } deriving (Eq, Show) type Env = State ([Int], SpillStack) +-- | Create a new 'SpillStack', given an offset and slot size. newSpillStack :: Int -> Int -> SpillStack newSpillStack offset slotSize = SpillStack { stackPtr = offset@@ -38,6 +73,8 @@ , stackSlots = mempty } +-- | Given a variable identifier, determine its spill stack offset. The value+-- 'Nothing' refers to the temporary stack slot. getStackSlot :: Maybe VarId -> Env Int getStackSlot vid = do (supply, stack) <- get@@ -51,8 +88,11 @@ }) return off --- | The 'Asm' monad lets us create labels by name and refer to them later.+-- | Labels is the type of a mapping from label names to Hoopl labels. type Labels = M.Map String Label++-- | The 'Asm' static monad allows for the creation labels by name, and+-- referencing them later. type Asm = StateT Labels SimpleUniqueMonad getLabel :: String -> Asm Label@@ -66,44 +106,64 @@ return lbl -- | 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.+-- kind of closing operation, such as a jump, branch or return. The Free monad+-- is used as a convenient way to describe a list that must result in a+-- closing operation at the end. type Nodes n a = Free ((,) (n O O)) a --- | The 'Nodes' free monad is really just a convenient way to describe a list--- that must result in a closing operation at the end.+-- | 'nodesToList' renders a set of nodes as a list of operations followed by+-- a final value 'a'. nodesToList :: Nodes n a -> (a, [n O O]) nodesToList (Pure a) = (a, []) nodesToList (Free (n, xs)) = (n :) <$> nodesToList xs +-- | A 'BodyNode' represents an instruction within a program. type BodyNode n = Nodes n () +-- | Construct a 'BodyNode' from a Hoopl graph node. bodyNode :: n O O -> BodyNode n bodyNode n = Free (n, Pure ()) +-- | An 'EndNode' represents a program with a final instruction. This+-- instruction is generated from an 'Asm' environment, so that it may refer to+-- and create labels for other blocks. type EndNode n = Nodes n (Asm (n O C)) +-- | Construct an 'EndNode' from a Hoopl final node generated from an 'Asm'+-- environment. endNode :: Asm (n O C) -> EndNode n endNode = return --- | A program is a series of 'Nodes', each associated with a label.+-- | A 'ProgramF' abstracts a generic basic block: a series of 'Nodes',+-- associated with a label, that ends in a final node. data ProgramF n = FreeBlock { labelEntry :: Label , labelBody :: EndNode n }++-- | A 'Program' abstracts a sequence of basic blocks generated from an 'Asm'+-- environment. type Program n = FreeT ((,) (ProgramF n)) Asm () +-- | Create and associate a label with an series of instructions, creating a+-- 'Program' for that block. label :: String -> EndNode n -> Program n label str body = do lbl <- lift $ getLabel str liftF (FreeBlock lbl body, ()) +-- | Create a final jump instruction to the given label. jump :: HooplNode n => String -> EndNode n jump dest = endNode $ mkBranchNode <$> getLabel dest --- | When we compile a program, the result is a closed Hoopl Graph and the--- label corresponding to the requested entry label name.+-- | When a program is compiled, the result is a closed Hoopl Graph, and the+-- label corresponding to the requested entry label name. This is done within+-- a 'SimpleUniqueMonad' so that unique labels may be created. compile :: (NonLocal n, HooplNode n)- => String -> Program n -> SimpleUniqueMonad (Graph n C C, Label)+ => String -- ^ Entry label name+ -> Program n -- ^ The assembly language program+ -> SimpleUniqueMonad (Graph n C C, Label)+ -- ^ Returns the Hoopl 'Graph' and its entry 'Label' compile name prog = flip evalStateT (mempty :: Labels) $ do body <- go prog
linearscan-hoopl.cabal view
@@ -1,5 +1,5 @@ name: linearscan-hoopl-version: 0.11.1+version: 1.0.0 synopsis: Makes it easy to use the linearscan register allocator with Hoopl homepage: http://github.com/jwiegley/linearscan-hoopl license: BSD3@@ -11,13 +11,21 @@ cabal-version: >=1.10 description:- This module provides a convenience wrapper and a type class, 'NodeAlloc',- which makes it much easier to use the @linearscan@ library to allocate- registers for Hoople intermediate representations.+ This module provides two convenience features for Hoopl users that wish to+ use @linearscan@ for register allocation in their compilers. .- Additionally, it provides a DSL for construction of assembly language DSLs- that compile into Hoople program graphs. See the tests for a concrete- example.+ First, it defines a type class called 'NodeAlloc'. After defining an+ instance of this class for your particular graph node type, simply call+ 'LinearScan.Hoopl.allocateHoopl'. This is a simpler interface than using+ @linearscan@ directly, which requires two records of functions that are more+ general in nature than the methods of 'NodeAlloc'.+ .+ Second, it provides a DSL for constructing assembly language DSLs that+ compile into Hoople program graphs. See the tests for a concrete example.+ This is mainly useful for constructing tests of intermediate representations.+ .+ Please see the tests for an example of the simple assembly language that is+ used to test the @linearscan@ allocator. Source-repository head type: git@@ -31,7 +39,7 @@ build-depends: base >=4.7 && <5 , hoopl >= 3.10.0.1- , linearscan >= 0.11 && < 0.12+ , linearscan >= 1.0 && < 1.1 , containers , transformers , free@@ -56,7 +64,7 @@ , hspec >= 1.4.4 , hspec-expectations >= 0.3 , hoopl >= 3.10.0.1 && < 3.11- , linearscan >= 0.11 && < 0.12+ , linearscan >= 1.0 && < 1.1 , linearscan-hoopl , containers >= 0.5.5 , transformers >= 0.3.0.0