Documentation ¶
Overview ¶
Package ir2 contains an Intermediate Representation (IR) in Static Single Assignment (SSA) form.
- Each Program contains a list of Packages.
- Packages are a list of Globals, TypeDefs and Funcs.
- Each Func is a list of Blocks.
- Blocks have a list of Instrs.
- Instrs Def (define) Values, and have Values as Args.
- Values can be constants, types, temps, registers, memory locations, etc.
Note: Unlike other SSA representations, this representation separates the concept of instructions from the concept of values. This allows an instruction to define multiple values. This is handy to avoid needing tuples and unpacking tuples to handle instructions (like function calls) that return multiple values.
This IR is structured with ideas from Data Oriented Programming and Entity Component Systems type thinking to try to structure data to be close together in cache and avoid cache misses if possible.
As such there is an ID system that acts like a index into an array. These IDs are local to the Func in which they live. The different kinds of ID-able things are allocated in slabs of a fixed size in order to avoid invalidating pointers. Memory currently is not freed (but could be with a generation counter in the ID.)
The IR is designed to be serialized into a human readable text file and parsed back into IR to aid in creating tests.
Index ¶
- func BoolValue(c Const) (bool, bool)
- func Int64Value(c Const) (int64, bool)
- func IntValue(c Const) (int, bool)
- func StringValue(c Const) (string, bool)
- type Block
- func (blk *Block) AddPred(pred *Block)
- func (blk *Block) AddSucc(succ *Block)
- func (blk *Block) Control() *Instr
- func (blk *Block) Emit(out io.Writer, dec Decorator)
- func (blk *Block) Func() *Func
- func (blk *Block) InsertInstr(i int, instr *Instr)
- func (blk *Block) Instr(i int) *Instr
- func (blk *Block) InstrIter() *BlockIter
- func (blk *Block) NumInstrs() int
- func (blk *Block) NumPreds() int
- func (blk *Block) NumSuccs() int
- func (blk *Block) Pred(i int) *Block
- func (blk *Block) RemoveInstr(inst *Instr)
- func (blk *Block) String() string
- func (blk *Block) Succ(i int) *Block
- func (blk *Block) SwapInstr(a *Instr, b *Instr)
- func (blk *Block) SwapSuccs()
- func (blk *Block) Unlink()
- type BlockIter
- func (it *BlockIter) Block() *Block
- func (it *BlockIter) BlockIndex() int
- func (it *BlockIter) Changed()
- func (it *BlockIter) HasChanged() bool
- func (it *BlockIter) HasNext() bool
- func (it *BlockIter) HasPrev() bool
- func (it *BlockIter) Insert(op Op, typ typ.Type, args ...interface{}) *Instr
- func (it *BlockIter) InsertAfter(op Op, typ typ.Type, args ...interface{}) *Instr
- func (it *BlockIter) Instr() *Instr
- func (it *BlockIter) InstrIndex() int
- func (it *BlockIter) Last() bool
- func (it *BlockIter) Next() bool
- func (it *BlockIter) Prev() bool
- func (it *BlockIter) Remove() *Instr
- func (it *BlockIter) RemoveInstr(instr *Instr)
- func (it *BlockIter) ReplaceWith(v *Value) *Value
- func (it *BlockIter) Update(op Op, typ typ.Type, args ...interface{}) *Instr
- type Const
- type ConstKind
- type CrossBlockIter
- type Decorator
- type Func
- func (fn *Func) AllocSpillStorage(size int) int
- func (fn *Func) Block(i int) *Block
- func (fn *Func) BlockForID(b ID) *Block
- func (fn *Func) BlockIndex(blk *Block) int
- func (fn *Func) EliminateDeadCode()
- func (fn *Func) Emit(out io.Writer, dec Decorator)
- func (fn *Func) HasPlaceholders() bool
- func (fn *Func) InsertBlock(i int, blk *Block)
- func (fn *Func) InstrForID(i ID) *Instr
- func (fn *Func) InstrIter() *CrossBlockIter
- func (fn *Func) LongString() string
- func (fn *Func) NewBlock() *Block
- func (fn *Func) NewInstr(op Op, typ typ.Type, args ...interface{}) *Instr
- func (fn *Func) NewValue(typ typ.Type) *Value
- func (fn *Func) NumArgSlots() int
- func (fn *Func) NumBlocks() int
- func (fn *Func) NumValues() int
- func (fn *Func) Package() *Package
- func (fn *Func) PlaceholderFor(label string) *Value
- func (fn *Func) PlaceholderLabels() []string
- func (fn *Func) RemoveBlock(blk *Block)
- func (fn *Func) ResolvePlaceholder(label string, value *Value)
- func (fn *Func) SpillAreaSize() int
- func (fn *Func) Types() *typ.Types
- func (fn *Func) ValueFor(t typ.Type, v interface{}) *Value
- func (fn *Func) ValueForID(v ID) *Value
- type Global
- type ID
- type IDKind
- type Instr
- func (in *Instr) Emit(out io.Writer, dec Decorator)
- func (in *Instr) Index() int
- func (in *Instr) LineNo() int
- func (in *Instr) LongString() string
- func (in *Instr) MoveAfter(other *Instr)
- func (in *Instr) MoveBefore(other *Instr)
- func (in *Instr) Type() typ.Type
- func (in *Instr) Update(op Op, typ typ.Type, args ...interface{})
- type Iter
- type Location
- type Op
- type Package
- func (pkg *Package) Emit(out io.Writer, dec Decorator)
- func (pkg *Package) Func(name string) *Func
- func (pkg *Package) Funcs() []*Func
- func (pkg *Package) Global(name string) *Global
- func (pkg *Package) Globals() []*Global
- func (pkg *Package) NewFunc(name string, sig *typ.Function) *Func
- func (pkg *Package) NewGlobal(name string, typ typ.Type) *Global
- func (pkg *Package) NewStringLiteral(funcname, str string) *Global
- func (pkg *Package) NewTypeDef(name string, typ typ.Type) *TypeDef
- func (pkg *Package) Program() *Program
- func (pkg *Package) TypeDef(name string) *TypeDef
- func (pkg *Package) TypeDefs() []*TypeDef
- func (pkg *Package) Types() *typ.Types
- type Program
- func (prog *Program) AddPackage(pkg *Package)
- func (prog *Program) Emit(out io.Writer, dec Decorator)
- func (prog *Program) Func(name string) *Func
- func (prog *Program) Global(name string) *Global
- func (prog *Program) Package(name string) *Package
- func (prog *Program) Packages() []*Package
- func (prog *Program) StringLiteral(str string, fullname string) *Global
- func (prog *Program) Types() *typ.Types
- type SSAString
- func (ss SSAString) Begin(out io.Writer, what interface{})
- func (ss SSAString) BeginLabel(out io.Writer, what interface{})
- func (ss SSAString) End(out io.Writer, what interface{})
- func (ss SSAString) EndLabel(out io.Writer, what interface{})
- func (ss SSAString) SSAForm() bool
- func (ss SSAString) WrapLabel(str string, what interface{}) string
- func (ss SSAString) WrapOp(str string, what Op) string
- func (ss SSAString) WrapRef(str string, what interface{}) string
- func (ss SSAString) WrapType(str string) string
- type SlotID
- type SlotKind
- type StackFrame
- type TypeDef
- type User
- func (use *User) AddDef(val *Value) *Value
- func (use *User) Arg(i int) *Value
- func (use *User) ArgIndex(arg *Value) int
- func (use *User) Args() []*Value
- func (use *User) Block() *Block
- func (use *User) Def(i int) *Value
- func (use *User) Defs() []*Value
- func (use *User) Destroy()
- func (use *User) Func() *Func
- func (use *User) InsertArg(i int, arg *Value)
- func (use *User) Instr() *Instr
- func (use *User) NumArgs() int
- func (use *User) NumDefs() int
- func (use *User) RemoveArg(arg *Value)
- func (use *User) RemoveDef(def *Value)
- func (use *User) RemoveDefAt(index int)
- func (use *User) ReplaceArg(i int, arg *Value)
- func (use *User) SetCallRegisters(args bool, kind SlotKind)
- type Value
- func (val *Value) Const() Const
- func (val *Value) Def() *User
- func (val *Value) ForceAlive()
- func (val *Value) Func() *Func
- func (val *Value) HasConstValue(v interface{}) bool
- func (val *Value) InReg() bool
- func (val *Value) InTemp() bool
- func (val *Value) IsConst() bool
- func (val *Value) IsDefinedByOp(op Op) bool
- func (val *Value) MoveToStack(kind SlotKind)
- func (val *Value) NeedsReg() bool
- func (val *Value) NumUses() int
- func (val *Value) OnStack() bool
- func (val *Value) Op() Op
- func (val *Value) Reg() reg.Reg
- func (val *Value) ReplaceUsesWith(other *Value)
- func (val *Value) SetConst(con Const)
- func (val *Value) SetReg(reg reg.Reg)
- func (val *Value) SetSlotIndex(kind SlotKind, index int)
- func (val *Value) SetStackSlot(slot SlotID)
- func (val *Value) SetTemp()
- func (val *Value) StackSlotID() SlotID
- func (val *Value) String() string
- func (val *Value) Temp() ID
- func (val *Value) Use(i int) *User
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Int64Value ¶
Int64Value returns an int64 for an IntConst
func StringValue ¶
StringValue return a string for a StringConst
Types ¶
type Block ¶
type Block struct { User // contains filtered or unexported fields }
Block is a collection of Instrs which is a basic block in a control flow graph. The last Instr of a block must be a control flow Instr. Blocks can have Preds and Succs for the blocks that come before or after in the control flow graph respectively.
Blocks are also `User`s in that they are like instructions and can Def (define) values for use inside the block that act like block parameters, similar to a function call. They also have Args which are used as parameters to any successor blocks.
This system of blocks having parameters is instead of having Phi instructions. This is similar to Cranelift, and Cranelift has some good docs on how this works. But this is _not_ an extended basic block (EBB), there is only one exit from the block (excluding function calls).
func (*Block) Control ¶
Control returns the last instruction, which should be a control flow instruction
func (*Block) InsertInstr ¶
InsertInstr inserts the instruction at the ith position. -1 means append it.
func (*Block) InstrIter ¶
InstrIter will return an Iter which iterates over every instruction in this block.
func (*Block) RemoveInstr ¶
RemoveInstr removes the Instr from the list
type BlockIter ¶
type BlockIter struct {
// contains filtered or unexported fields
}
BlockIter is an iterator that iterates over instructions in a Block
func (*BlockIter) BlockIndex ¶
BlockIndex returns the index of the Block within the Func
func (*BlockIter) Changed ¶
func (it *BlockIter) Changed()
Changed forces `HasChanged()` to return true
func (*BlockIter) HasChanged ¶
HasChanged returns true if `Changed()` was called, or one of the mutation methods
func (*BlockIter) Insert ¶
Insert inserts an instruction at the cursor position and increments the position
func (*BlockIter) InsertAfter ¶
InsertAfter inserts after an instruction at the cursor position
func (*BlockIter) InstrIndex ¶
InstrIndex returns the index of the current instruction in the Block
func (*BlockIter) Remove ¶
Remove will remove the instruction at the current position and decrement the position, returning the removed instruction.
func (*BlockIter) RemoveInstr ¶
RemoveInstr removes an instruction from the middle of the block somewhere, making sure to adjust the iterator position appropriately
func (*BlockIter) ReplaceWith ¶
type Const ¶
type Const interface { Location() Location Kind() ConstKind String() string // contains filtered or unexported methods }
Const is a constant value of some sort
func MakeStructConst ¶
func StructValue ¶
type CrossBlockIter ¶
type CrossBlockIter struct { BlockIter // contains filtered or unexported fields }
func (*CrossBlockIter) HasNext ¶
func (it *CrossBlockIter) HasNext() bool
HasNext returns whether Next() will succeed
func (*CrossBlockIter) HasPrev ¶
func (it *CrossBlockIter) HasPrev() bool
HasPrev returns whether Prev() will succeed
func (*CrossBlockIter) Last ¶
func (it *CrossBlockIter) Last() bool
Last fast forwards to the end of the func
func (*CrossBlockIter) Next ¶
func (it *CrossBlockIter) Next() bool
Next increments the position and returns whether that was successful
func (*CrossBlockIter) Prev ¶
func (it *CrossBlockIter) Prev() bool
Prev decrements the position and returns whether that was successful
type Decorator ¶
type Decorator interface { Begin(out io.Writer, what interface{}) End(out io.Writer, what interface{}) BeginLabel(out io.Writer, what interface{}) EndLabel(out io.Writer, what interface{}) WrapLabel(str string, what interface{}) string WrapRef(str string, what interface{}) string WrapType(str string) string WrapOp(str string, what Op) string SSAForm() bool }
type Func ¶
type Func struct { Name string FullName string Sig *typ.Function Referenced bool NumCalls int Frame StackFrame // contains filtered or unexported fields }
Func is a collection of Blocks, which comprise a function or method in a Program.
func (*Func) AllocSpillStorage ¶
AllocSpillStorage will allocate the number of addressable units as spill area and return the current offset for the spill area in addressible units.
func (*Func) BlockIndex ¶
BlockIndex returns the index of the Block in the list
func (*Func) EliminateDeadCode ¶
func (fn *Func) EliminateDeadCode()
EliminateDeadCode eliminates dead code until the code stops changing.
func (*Func) HasPlaceholders ¶
HasPlaceholders returns whether there are unresolved placeholders or not
func (*Func) InsertBlock ¶
InsertBlock inserts the block at the specific location in the list
func (*Func) InstrForID ¶
InstrForID returns the Instr for the ID
func (*Func) InstrIter ¶
func (fn *Func) InstrIter() *CrossBlockIter
InstrIter returns an iterator that will iterate over every block and instruction in the func.
func (*Func) LongString ¶
func (*Func) NumArgSlots ¶
func (*Func) PlaceholderFor ¶
PlaceholderFor creates a special placeholder value that can be later resolved with a different value. This is useful for marking and resolving forward references.
func (*Func) PlaceholderLabels ¶
PlaceholderLabels returns a sorted list of placeholder labels
func (*Func) RemoveBlock ¶
RemoveBlock removes the Block from the list but does not remove it from succ/pred lists. See blk.Unlink()
func (*Func) ResolvePlaceholder ¶
ResolvePlaceholder removes the placeholder from the list, replacing its uses with the specified value
func (*Func) SpillAreaSize ¶
SpillAreaSize indicates the size of the spill area of the stack
func (*Func) ValueForID ¶
ValueForID returns the Value for the ID
type Global ¶
type Global struct { Name string FullName string Type typ.Type Referenced bool // initial value Value Const // contains filtered or unexported fields }
Global is a global variable or literal stored in memory
func GlobalValue ¶
GlobalValue returns a *Func for a GlobalConst
type ID ¶
type ID uint32
ID is an identifier that's unique within a Func
var Placeholder ID = idFor(PlaceholderID, -1)
Placeholder is an invalid ID meant to signal a place that needs to be filled
type Instr ¶
Instr is an instruction that may define one or more Values, and take as args (operands) one or more Values.
func (*Instr) LineNo ¶
LineNo returns the line number in the original Go source code, or 0 if that's not known
func (*Instr) LongString ¶
func (*Instr) MoveBefore ¶
MoveBefore moves this instruction before other
type Iter ¶
type Iter interface { // Instr returns the current instruction Instr() *Instr // InstrIndex returns the index of the current instruction in the Block InstrIndex() int // Block returns the current block Block() *Block // BlockIndex returns the index of the Block within the Func BlockIndex() int // HasNext returns whether Next() will succeed HasNext() bool // Next increments the position and returns whether that was successful Next() bool // HasPrev returns whether Prev() will succeed HasPrev() bool // Prev decrements the position and returns whether that was successful Prev() bool // Last fast forwards to the end Last() bool // Insert inserts an instruction at the cursor position and increments the position Insert(op Op, typ typ.Type, args ...interface{}) *Instr // InsertAfter inserts after an instruction at the cursor position InsertAfter(op Op, typ typ.Type, args ...interface{}) *Instr // Remove will remove the instruction at the current position and decrement the position, // returning the removed instruction. // NOTE: this only removes the instruction from the Block, it does not Unlink() it from // any uses. Remove() *Instr // RemoveInstr removes an instruction from anywhere and will adjust the iterator position // appropriately RemoveInstr(instr *Instr) // Update updates the instruction at the cursor position Update(op Op, typ typ.Type, args ...interface{}) *Instr // Replace the uses of the instruction at the cursor postion // with the specified value, making sure to instead insert a // copy if necessary ReplaceWith(v *Value) *Value // HasChanged returns true if `Changed()` was called, or one of the mutation methods HasChanged() bool // Changed forces `HasChanged()` to return true Changed() }
Iter is a iterator over instructions
type Op ¶
type Op interface { String() string IsCall() bool IsCompare() bool IsCopy() bool IsCommutative() bool IsSink() bool ClobbersArg() bool IsBranch() bool IsReturn() bool }
Op describes an operation (instruction) type Note: Implementations of Op should attempt to be uint8 type, since this is optimized by Go.
type Package ¶
Package is a collection of Funcs and Globals which comprise a part of a program.
func (*Package) NewStringLiteral ¶
NewStringLiteral creates a global with a string literal value
func (*Package) NewTypeDef ¶
NewTypeDef adds a typedef to the list
type Program ¶
Program is a collection of packages, which comprise a whole program.
func NewProgram ¶
func NewProgram() *Program
func (*Program) AddPackage ¶
AddPackage adds a package to the list
func (*Program) Package ¶
Package finds a package first by full name, then if there is no match, by short name.
func (*Program) StringLiteral ¶
type SSAString ¶
type SSAString struct{}
SSAString emits a plain string in SSA form
func (SSAString) BeginLabel ¶
type SlotID ¶
type SlotID uint32
SlotID represents a variable width "slot" where a value is stored on the stack frame.
type SlotKind ¶
type SlotKind uint8
const ( // InvalidSlot is an invalid unassigned slot InvalidSlot SlotKind = iota // Param slots are where a function's parameters live, which // are Args passed in from a calling function ParamSlot // SavedSlot is where callee saved registers live on the stack SavedSlot // AllocaSlot is where stack allocated data lives on the stack AllocaSlot // SpillSlot is where the register allocator stores spilled variables // These slots are reused when their previous values are no longer required SpillSlot // ArgSlot is where a caller stores a callee's parameters before a function // call. In other words they are "Args" before a function is called, and // become "Params" once the called function starts executing ArgSlot NumStackAreas )
type StackFrame ¶
type StackFrame struct {
// contains filtered or unexported fields
}
StackFrame represents the stack frame layout of the current function. "Slot" IDs are handed out for offsets to values which are stored on the stack, and space is allocated according to the largest value that will be assigned to that stack slot.
A stack frame looks like this:
low mem addresses +------------------------+ | | <-- SP +------------------------+ \ | callee stack arg 0 | | +------------------------+ | | callee stack arg 1 | | +------------------------+ | | spill 0 | | +------------------------+ | | spill 1 | | +------------------------+ | | alloca 0 | | +------------------------+ > stack frame | alloca 1 | | +------------------------+ | | saved reg 0 | | +------------------------+ | | saved reg 1 | | +------------------------+ | | saved RA | | +------------------------+ | | saved SP | | <-- previous SP +------------------------+ / | stack param 0 | \ +------------------------+ | | stack param 1 | > caller's stack frame +------------------------+ | high mem addresses
The SP is saved instead of a frame pointer to save a register. A frame pointer may be required to support dynamic stack allocations, but other than that, it is not needed. SP is saved to aide in unwinding the stack for debugging purposes.
The stack parameters of a called function reside in the caller's stack frame.
The order of items on the stack frame is an attempt to limit the size of offsets on load/store instructions that may appear frequently, such as variables spilled to the stack during register allocation.
func (*StackFrame) FrameSize ¶
func (frame *StackFrame) FrameSize() int
FrameSize is the total stack frame size minus
func (*StackFrame) Func ¶
func (frame *StackFrame) Func() *Func
func (*StackFrame) NewSlotID ¶
func (frame *StackFrame) NewSlotID(kind SlotKind) SlotID
NewSlotID returns the next unused SlotID of the given kind
func (*StackFrame) ReplaceOffsets ¶
func (frame *StackFrame) ReplaceOffsets()
ReplaceOffsets replaces all the stack offset variables with the actual calculated stack offsets. `Scan` must be called first.
func (*StackFrame) Scan ¶
func (frame *StackFrame) Scan()
Scan the function for stack variables and calculate the SP offset for them.
type TypeDef ¶
type TypeDef struct { Name string Referenced bool Type typ.Type // contains filtered or unexported fields }
TypeDef is a type definition
type User ¶
type User struct { ID // contains filtered or unexported fields }
User uses and defines Values. Blocks and Instrs are Users.
func (*User) Destroy ¶
func (use *User) Destroy()
Destroy removes all args and defs, thus removing references to other instructions
func (*User) InsertArg ¶
InsertArg inserts the Value in the argument list at position i, or appending if i is -1
func (*User) Instr ¶
Instr returns either the Instr or an empty Instr to cut down on having to check IsInstr() everywhere.
func (*User) RemoveDefAt ¶
RemoveDefAt removes the value from the defs list at the index
func (*User) ReplaceArg ¶
ReplaceArg replaces the ith argument with the value specified. Will call InsertArg instead if i == NumArgs().
func (*User) SetCallRegisters ¶
type Value ¶
type Value struct { ID // Type is the type of the Value Type typ.Type // contains filtered or unexported fields }
Value is a single value that may be stored in a single place. This may be a constant or variable, stored in a temp, register or on the stack.
func (*Value) ForceAlive ¶
func (val *Value) ForceAlive()
ForceAlive ensures dead code elimination sees this value as alive
func (*Value) HasConstValue ¶
HasConstValue returns if the value is constant and equals the provided constant. If provided a register it also checks that the register matches
func (*Value) IsDefinedByOp ¶
func (*Value) MoveToStack ¶
MoveToStack moves the value onto the stack in the next slot available
func (*Value) Reg ¶
Reg returns which register the Value is in, otherwise reg.None if its not in a register.
func (*Value) ReplaceUsesWith ¶
ReplaceUsesWith will go through each use of val and replace it with other. Does not modify any definitions.
func (*Value) SetSlotIndex ¶
SetSlotIndex sets the stack slot to a specific index
func (*Value) SetStackSlot ¶
SetStackSlot puts the Value on the stack at the specified slot.
func (*Value) StackSlotID ¶
StackSlotID returns which spill slot the Value is in, or -1 if not in a spill slot.