linearscan-1.0.0: Linear scan register allocator, formally verified in Coq

Safe HaskellNone
LanguageHaskell2010

LinearScan

Contents

Synopsis

Main entry point

allocate Source #

Arguments

:: (Functor m, Applicative m, Monad m) 
=> Int

Maximum number of registers to use

-> BlockInfo m blk1 blk2 op1 op2

Characterization of blocks

-> OpInfo m op1 op2

Characterization of operations

-> UseVerifier

Whether to use the runtime verifier

-> [blk1]

A topologically sorted list of blocks

-> m (String, Either [String] [blk2])

Status dump and allocated blocks, or error w/ context

Transform a list of basic blocks, containing variable references, into an equivalent list where each reference has been associated with a register allocation. Artificial save and restore instructions may be inserted into those blocks to indicate spilling and reloading of variables.

In order to call this function, the caller must provide records that functionally characterize these blocks and their operations according to an abstract API. This is done so that any program graph conforming to the API may be used, and no particular representation is required.

A tuple is return where the first value is a textual dump that describes the complete allocation state. If this value is never forced, that data is not collected.

The second value is the resulting list of allocated blocks. If allocation is found to be impossible -- for example if there are not enough registers -- a Left value is returned, with a list of strings describing the error and its possible context. This is usually when the textual dump should be provided, if one's user has selected verbose error output, for example.

Blocks

data BlockInfo m blk1 blk2 op1 op2 Source #

From the point of view of this library, a basic block is nothing more than an ordered sequence of operations.

Constructors

BlockInfo 

Fields

  • 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.

Operations

data OpInfo m op1 op2 Source #

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.

Constructors

OpInfo 

Fields

  • 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.

data OpKind Source #

Constructors

IsNormal 
IsCall 
IsBranch 

Variables

data VarInfo Source #

Each "virtual variable" has details associated with it that affect the allocation procedure.

Constructors

VarInfo 

Fields

  • 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.

data VarKind Source #

Constructors

Input 
InputOutput 
Temp 
Output 

type VarId = Int Source #

Orphan instances