This commit is contained in:
parent
f7be86a3d9
commit
31c5ed614c
27 changed files with 548 additions and 61 deletions
|
@ -2,11 +2,11 @@ package ssa
|
|||
|
||||
// Block is a list of instructions that can be targeted in branches.
|
||||
type Block struct {
|
||||
Instructions []Instruction
|
||||
Instructions []Value
|
||||
}
|
||||
|
||||
// Append adds a new instruction to the block.
|
||||
func (b *Block) Append(instr Instruction) *Instruction {
|
||||
func (b *Block) Append(instr Value) *Value {
|
||||
b.Instructions = append(b.Instructions, instr)
|
||||
return &b.Instructions[len(b.Instructions)-1]
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
// Function is a list of basic blocks.
|
||||
type Function struct {
|
||||
Blocks []*Block
|
||||
|
@ -10,4 +14,48 @@ func (f *Function) AddBlock() *Block {
|
|||
block := &Block{}
|
||||
f.Blocks = append(f.Blocks, block)
|
||||
return block
|
||||
}
|
||||
|
||||
// Append adds a new value to the last block.
|
||||
func (f *Function) Append(instr Value) *Value {
|
||||
if len(f.Blocks) == 0 {
|
||||
f.Blocks = append(f.Blocks, &Block{})
|
||||
}
|
||||
|
||||
if instr.IsConst() {
|
||||
for _, b := range f.Blocks {
|
||||
for _, existing := range b.Instructions {
|
||||
if instr.Equals(existing) {
|
||||
return &existing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f.Blocks[len(f.Blocks)-1].Append(instr)
|
||||
}
|
||||
|
||||
// AppendInt adds a new integer value to the last block.
|
||||
func (f *Function) AppendInt(x int) *Value {
|
||||
return f.Append(Value{Type: Int, Int: x})
|
||||
}
|
||||
|
||||
// AppendRegister adds a new register value to the last block.
|
||||
func (f *Function) AppendRegister(reg cpu.Register) *Value {
|
||||
return f.Append(Value{Type: Register, Register: reg})
|
||||
}
|
||||
|
||||
// AppendFunction adds a new function value to the last block.
|
||||
func (f *Function) AppendFunction(name string) *Value {
|
||||
return f.Append(Value{Type: Func, Text: name})
|
||||
}
|
||||
|
||||
// AppendBytes adds a new byte slice value to the last block.
|
||||
func (f *Function) AppendBytes(s []byte) *Value {
|
||||
return f.Append(Value{Type: String, Text: string(s)})
|
||||
}
|
||||
|
||||
// AppendString adds a new string value to the last block.
|
||||
func (f *Function) AppendString(s string) *Value {
|
||||
return f.Append(Value{Type: String, Text: s})
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Instruction is a single instruction in a basic block.
|
||||
// It is implemented as a "fat struct" for performance reasons.
|
||||
// It contains all the fields necessary to represent all instruction types.
|
||||
type Instruction struct {
|
||||
Args []*Instruction
|
||||
Int int64
|
||||
Type Type
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of the instruction.
|
||||
func (i *Instruction) String() string {
|
||||
switch i.Type {
|
||||
case Int:
|
||||
return fmt.Sprintf("%d", i.Int)
|
||||
case Add:
|
||||
return fmt.Sprintf("%s + %s", i.Args[0], i.Args[1])
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ const (
|
|||
// Values
|
||||
Int
|
||||
Float
|
||||
Func
|
||||
Register
|
||||
String
|
||||
|
||||
// Binary
|
||||
|
@ -25,11 +27,13 @@ const (
|
|||
Shl
|
||||
Shr
|
||||
|
||||
// Branch
|
||||
// Control flow
|
||||
If
|
||||
Jump
|
||||
Call
|
||||
Return
|
||||
Syscall
|
||||
|
||||
// Special
|
||||
Call
|
||||
Phi
|
||||
)
|
83
src/ssa/Value.go
Normal file
83
src/ssa/Value.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
// Value is a single instruction in a basic block.
|
||||
// It is implemented as a "fat struct" for performance reasons.
|
||||
// It contains all the fields necessary to represent all instruction types.
|
||||
type Value struct {
|
||||
Args []*Value
|
||||
Int int
|
||||
Text string
|
||||
Register cpu.Register
|
||||
Type Type
|
||||
}
|
||||
|
||||
// Equals returns true if the values are equal.
|
||||
func (a Value) Equals(b Value) bool {
|
||||
if a.Type != b.Type {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Int != b.Int {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Text != b.Text {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.Register != b.Register {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(a.Args) != len(b.Args) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range a.Args {
|
||||
if !a.Args[i].Equals(*b.Args[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsConst returns true if the value is constant.
|
||||
func (i *Value) IsConst() bool {
|
||||
switch i.Type {
|
||||
case Func, Int, Register, String:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of the instruction.
|
||||
func (i *Value) String() string {
|
||||
switch i.Type {
|
||||
case Func:
|
||||
return i.Text
|
||||
case Int:
|
||||
return fmt.Sprintf("%d", i.Int)
|
||||
case Register:
|
||||
return i.Register.String()
|
||||
case String:
|
||||
return fmt.Sprintf("\"%s\"", i.Text)
|
||||
case Add:
|
||||
return fmt.Sprintf("%s + %s", i.Args[0], i.Args[1])
|
||||
case Return:
|
||||
return fmt.Sprintf("return %s", i.Args[0])
|
||||
case Call:
|
||||
return fmt.Sprintf("call%v", i.Args)
|
||||
case Syscall:
|
||||
return fmt.Sprintf("syscall%v", i.Args)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -7,16 +7,20 @@ import (
|
|||
"git.urbach.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestBlock(t *testing.T) {
|
||||
f := ssa.Function{}
|
||||
block := f.AddBlock()
|
||||
a := block.Append(ssa.Instruction{Type: ssa.Int, Int: 1})
|
||||
b := block.Append(ssa.Instruction{Type: ssa.Int, Int: 2})
|
||||
c := block.Append(ssa.Instruction{Type: ssa.Add, Args: []*ssa.Instruction{a, b}})
|
||||
func TestFunction(t *testing.T) {
|
||||
fn := ssa.Function{}
|
||||
a := fn.AppendInt(1)
|
||||
b := fn.AppendInt(2)
|
||||
c := fn.Append(ssa.Value{Type: ssa.Add, Args: []*ssa.Value{a, b}})
|
||||
fn.AddBlock()
|
||||
d := fn.AppendInt(3)
|
||||
e := fn.AppendInt(4)
|
||||
f := fn.Append(ssa.Value{Type: ssa.Add, Args: []*ssa.Value{d, e}})
|
||||
assert.Equal(t, c.String(), "1 + 2")
|
||||
assert.Equal(t, f.String(), "3 + 4")
|
||||
}
|
||||
|
||||
func TestInvalidInstruction(t *testing.T) {
|
||||
instr := ssa.Instruction{}
|
||||
instr := ssa.Value{}
|
||||
assert.Equal(t, instr.String(), "")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue