Implemented compilation to SSA form
All checks were successful
/ test (push) Successful in 31s

This commit is contained in:
Eduard Urbach 2025-06-23 00:17:05 +02:00
parent f7be86a3d9
commit 31c5ed614c
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
27 changed files with 548 additions and 61 deletions

View file

@ -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]
}

View file

@ -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})
}

View file

@ -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 ""
}
}

View file

@ -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
View 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 ""
}
}

View file

@ -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(), "")
}