module RegAlloc.Types where

-- | Each "virtual variable" has details associated with it that affect the
-- allocation procedure.
data VarInfo = VarInfo
    { varId       :: Either PhysReg VarId
      -- ^ Identify the variable, or if it is an explicit register reference.

    , varKind     :: VarKind
      -- ^ The kind of a variable determines the scope of its lifetime, and
      -- when it is spilled or loaded to or from stack. For example, output
      -- variables are not needed in a basic block until the first point of
      -- use, while the lifetime of input variables extends until their final
      -- use.

    , regRequired :: Bool
      -- ^ If true, the variable's value must be loaded into a register at
      -- this use position.
  deriving (Eq, Show)

type VarId = Int
data VarKind = Input | InputOutput | Temp | Output deriving (Eq, Show)

-- | Every operation may reference multiple variables and/or specific physical
--   registers.  If a physical register is referenced, then that register is
--   considered unavailable for allocation over its range of use.
--   Certain operations have special significance as to how basic blocks are
--   organized and lifetime of allocations. Thus, if an operation begins or
--   ends a loop, or represents a method call, this should be indicated using
--   the 'OpKind' field. Indication of calls is necessary for saving and
--   restoring all registers around a call, while indication of loops is
--   optional, as it merely avoids reloading spilled variables inside loop
--   bodies.
data OpInfo m op1 op2 = OpInfo
    { opKind      :: op1 -> OpKind
      -- ^ Return the kind of operator prior to allocation.

    , opRefs      :: op1 -> [VarInfo]
      -- ^ Return all variable references for the operation.

    , moveOp      :: PhysReg -> VarId -> PhysReg -> m [op2]
      -- ^ Create move instruction(s) from one register to another, relating
      -- to the given variable.

    , saveOp      :: PhysReg -> VarId -> m [op2]
      -- ^ Create a spill instruction from the given restriction, to a stack
      -- slot for the given variable.

    , restoreOp   :: VarId -> PhysReg -> m [op2]
      -- ^ Create a load instruction from the stack slot for the given
      -- variable, to the given register.

    , applyAllocs :: op1 -> [((VarId, VarKind), PhysReg)] -> m [op2]
      -- ^ Given an operation, and a set of register allocations for each
      -- variable used by the operation (differentiated by type of use), apply
      -- the allocations to create one or more post-allocation operations.

    , showOp1     :: op1 -> String
      -- ^ Render the given pre-allocation operation as a string.
  deriving (Functor)

data OpKind = IsNormal | IsCall | IsBranch deriving (Eq, Show)

-- | From the point of view of this library, a basic block is nothing more
--   than an ordered sequence of operations.
data BlockInfo m blk1 blk2 op1 op2 = BlockInfo
    { blockId           :: blk1 -> Int
      -- ^ Identify the block with a unique number. The nature and ordering of
      -- the number is not significant, only its uniqueness.

    , blockSuccessors   :: blk1 -> [Int]
      -- ^ The immediate successors of a block.

    , splitCriticalEdge :: blk1 -> blk1 -> m (blk1, blk1)
      -- ^ Given two blocks, insert a new block between them to break up a
      -- "critical edge" (where the first block has multiple destinations due
      -- to a conditional branch, for example, while the second block has
      -- multiple originations due to branches from other blocks). The result
      -- is the new pair of blocks at that boundary. Typically, only one of
      -- the two will be a newly created block.

    , blockOps          :: blk1 -> ([op1], [op1], [op1])
      -- ^ Return the entry, body, and exit operation of a block. Typically,
      -- the entry operation is a "label", and the exit operation is a branch,
      -- jump or return.

    , setBlockOps       :: blk1 -> [op2] -> [op2] -> [op2] -> blk2
      -- ^ Replace the set of operations for a block with a set of allocated
      -- operations.

type PhysReg = Int

data UseVerifier = VerifyDisabled | VerifyEnabled | VerifyEnabledStrict
  deriving (Eq, Show)