Documentation
¶
Index ¶
- Constants
- func IsBranchOp(op string) bool
- type Addr
- type Blk
- func (blk Blk) EvalNextUseInfo()
- func (blk Blk) FindNextUse(line int, name string) int
- func (blk Blk) GetReg(stmt *Stmt, ts *TextSec, typeInfo map[string]types.RegType)
- func (blk *Blk) InitHeap()
- func (blk *Blk) InitRegDS()
- func (blk *Blk) IsDirty(reg int) bool
- func (blk *Blk) IsLoaded(reg int, varName string) bool
- func (blk *Blk) MarkDirty(reg int)
- func (blk *Blk) MarkLoaded(reg int)
- func (blk *Blk) NewBlock(tac *Tac) (*Blk, int)
- func (blk *Blk) UnmarkDirty(reg int)
- func (blk *Blk) UnmarkLoaded(reg int)
- type Child
- type DataFlowInfo
- type DataSec
- type I32
- type Label
- type PriorityQueue
- type RegDesc
- type SrcVar
- type Stmt
- type Str
- type Tac
- func (tac Tac) ControlFlow() Tac
- func (tac Tac) EvalDataFlowSets()
- func (tac Tac) EvalFlowGraph(labelMap labelinfotype)
- func (tac Tac) EvalTransferFuncs()
- func (tac Tac) FixLineNumbers()
- func (tac Tac) JumpsOverJumps() Tac
- func (tac Tac) LabelInfo() labelinfotype
- func (tac Tac) PeepHole() Tac
- func (tac Tac) PrintTAC()
- type TextSec
- type UseInfo
Constants ¶
const ( // Variables which are dead have their next-use set to MaxInt. MaxInt = int(^uint(0) >> 1) MinInt = -MaxInt - 1 )
const ( // arithmetic operators ADD = "+" SUB = "-" MUL = "*" DIV = "/" REM = "%" EQ = "=" // branch operators BGT = "bgt" BGE = "bge" BLT = "blt" BLE = "ble" BEQ = "beq" BNE = "bne" JMP = "jmp" // array operators FROM = "from" INTO = "into" // binary operators OR = "or" AND = "and" NOR = "nor" XOR = "xor" NOT = "not" // shift operators RST = ">>" LST = "<<" // function operators FUNC = "func" LABEL = "label" RET = "ret" CALL = "call" STORE = "store" CMT = "#" // comments // declaration operators DECL = "decl" DECLInt = "declInt" DECLSTR = "declStr" // I/O operators SCANINT = "scanInt" PRINTINT = "printInt" PRINTSTR = "printStr" )
const ( NIL = iota FALSE // cannot be dropped MAYBE // might be dropped TRUE // will be dropped )
The following constant declarations determine the confidence in dropping a basic block from the three-address code data structure.
const RegLimit = 32
RegLimit determines the upper bound on the number of free registers at any given instant supported by the concerned architecture (MIPS in this case).
Variables ¶
This section is empty.
Functions ¶
func IsBranchOp ¶
IsBranchOp verifies whether the given operator is jump/branch.
Types ¶
type Blk ¶
type Blk struct {
Stmts []Stmt
// Address descriptor
// * Keeps track of location where current value of the
// name can be found at compile time.
// * The location can be either one or a set of -
// - register
// - memory address
// - stack (TODO)
Adesc map[string]Addr
// Register descriptor
// * Keeps track of what is currently in each register.
// * Initially all registers are empty.
Rdesc map[int]RegDesc
NextUseTab [][]UseInfo
Pq PriorityQueue
// contains filtered or unexported fields
}
Blk represents the structure of a basic block.
func (Blk) EvalNextUseInfo ¶
func (blk Blk) EvalNextUseInfo()
Next-use allocation heuristic ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Traverse the statements in a basic block from bottom-up, while updating the next-use symbol table using the following algorithm (x = y op z) -
Step 1: attach to i'th line in NextUseTab information currently in symbol table (nuSymTab) about variables x, y and z. Step 2: mark x as dead and no next-use in nuSymTab. Step 3: mark y and z to be live and set their next-use to i in nuSymTab.
func (Blk) FindNextUse ¶
FindNextUse returns the next-use information corresponding to a variable "name" available in line number "line" of the table.
func (Blk) GetReg ¶
Register allocation ~~~~~~~~~~~~~~~~~~~ Arguments:
- stmt: The allocator ensures that all the variables available in Stmt object have been allocated a register.
- ts: If a register had to be spilled when GetReg() was called, the text segment should be updated with an equivalent "sw" instruction.
GetReg handles all the side-effects induced due to register allocation -
- Updating lookup tables.
- Generating additional instructions resulting due to register spilling.
A note on register spilling ~~~~~~~~~~~~~~~~~~~~~~~~~~~ A variable which doesn't have a next-use in the current basic block is spilled right away even if there are free registers available, resulting in one "sw" instruction. In case spilling was avoided and one of the free registers was used instead, that too would have resulted in one "sw" instruction at the end of the basic block.
func (*Blk) InitHeap ¶
func (blk *Blk) InitHeap()
InitHeap initializes the heap used during register allocation.
func (*Blk) InitRegDS ¶
func (blk *Blk) InitRegDS()
InitRegDS initializes the basic block data structures relevant to registers.
func (*Blk) IsLoaded ¶
IsLoaded determines whether the given register stores the value of the given variable and the value has been loaded from memory.
func (*Blk) MarkDirty ¶
MarkDirty marks the given register as dirty. A register whose contents have been modified after being loaded from memory is marked dirty.
func (*Blk) MarkLoaded ¶
MarkLoaded is invoked after loading a variable from memory, and it marks the corresponding register's entry as loaded.
func (*Blk) UnmarkDirty ¶
UnmarkDirty marks the given register as free.
func (*Blk) UnmarkLoaded ¶
UnmarkLoaded unmarks the register entry as loaded.
type Child ¶
type Child struct {
// contains filtered or unexported fields
}
Child determines the index of the left and right child of a basic block in a flow graph.
type DataFlowInfo ¶
type DataFlowInfo struct {
Gen set
Kill set
In set
Out set
}
DataFlowInfo represents the data structures used in the data-flow analysis.
type DataSec ¶
type DataSec struct {
// Stmts is a slice of statements which will be flushed into the data
// section of the generated assembly file.
Stmts bytes.Buffer
// Lookup keeps track of all the variables currently available in the
// the data section.
Lookup map[string]bool
}
Data section
type PriorityQueue ¶
type PriorityQueue []*UseInfo
func (PriorityQueue) Len ¶
func (pq PriorityQueue) Len() int
func (PriorityQueue) Less ¶
func (pq PriorityQueue) Less(i, j int) bool
func (*PriorityQueue) Pop ¶
func (pq *PriorityQueue) Pop() interface{}
func (*PriorityQueue) Push ¶
func (pq *PriorityQueue) Push(x interface{})
func (PriorityQueue) Swap ¶
func (pq PriorityQueue) Swap(i, j int)
type RegDesc ¶
type RegDesc struct {
// Name determines the variable name whose value the register stores.
Name string
// Dirty determines whether the value of the variable stored by the
// register has changed after loading from memory.
Dirty bool
// Loaded determines whether the variable whose value the register is
// supposed to store has been loaded from memory.
Loaded bool
}
type Stmt ¶
type Stmt struct {
Line int // line number where the statement is available
Op string // operator
Dst string // destination variable
Src []SrcVar // source variables
}
Stmt defines the structure of a single statement in three-address code form.
type Tac ¶
type Tac []Blk
Tac represents the three-address code for the entire source program.
func GenTAC ¶
GenTAC generates the three-address code (in-memory) data structure from the input file. The format of each statement in the input file is a tuple of the form -
<line-number, operation, destination-variable, source-variable(s)>
The three-address code is a collection of basic block data structures, which are identified while reading the IR file as per following rules -
A basic block starts: * at label instruction * after jump instruction and ends: * before label instruction * at jump instruction
func (Tac) ControlFlow ¶
--- [ Flow-of-Control Optimizations ] --------------------------------------- Consider the following code sequence -
goto L1 ... L1: goto L2
The unnecessary jump to L1 can be eliminated as follows -
goto L2 ... L1: goto L2
If there are no more jumps to L1, the entire label can be dropped provided that it is immediately preceded by an unconditional jump. Similar logic can be extended to branch statements.
A notation which is used in the following segment is a "fallthrough label". Fallthrough labels are the blocks which are not preceded by an unconditional jump. Consider the following code -
a = 0 L1: a = 1
In the above code, L1 is a fallthrough label.
ControlFlow performs flow of control optimizations.
func (Tac) EvalDataFlowSets ¶
func (tac Tac) EvalDataFlowSets()
EvalDataFlowSets computes the data-flow sets GEN and KILL.
func (Tac) EvalFlowGraph ¶
func (tac Tac) EvalFlowGraph(labelMap labelinfotype)
EvalFlowGraph generates a flow graph from a three-address code DS. When a jump/branch is encountered, the target and the next basic block is marked as the right and left child respectively. The immediate predecessors of all the basic blocks are also updated.
func (Tac) EvalTransferFuncs ¶
func (tac Tac) EvalTransferFuncs()
Transfer functions evaluation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following equations are used for evaluating the transfer functions -
- IN[n] = ∪ p∈pred(n) OUT[p]
- OUT[n] = GEN[n] ∪ (IN[n] − KILL[n])
These transfer functions are evaluated using a fixpoint computation.
func (Tac) FixLineNumbers ¶
func (tac Tac) FixLineNumbers()
FixLineNumbers fixes the line numbers in each basic block in case they get disturbed after the optimization passes finish. Correctness of line numbers is essential to ensure proper calculation of next-use information.
func (Tac) JumpsOverJumps ¶
--- [ Eliminating jumps over jumps ] ---------------------------------------- Consider the following code sequence -
if debug == 1 goto L1 goto L2 L1: print debugging information L2:
After eliminating jumps over jumps, the transformed code looks as -
if debug != 1 goto L2 print debugging information L2:
JumpsOverJumps performs jump-over-jump peephole optimization.
func (Tac) LabelInfo ¶
func (tac Tac) LabelInfo() labelinfotype
LabelInfo traverses across the tac DS and collects details about label statements.
type UseInfo ¶
type UseInfo struct {
// The Name field is used in two different contexts -
// - when dealing with priority queue of registers, it is the name
// of a register
// - when dealing with lookup tables, it is the name of a variable
Name string
// Nextuse determines the next usage (line number) of a varible. If the
// variable is dead, its Nextuse is set to MaxInt.
Nextuse int
}