Improved ssa compiler
All checks were successful
/ test (push) Successful in 15s

This commit is contained in:
Eduard Urbach 2025-07-02 16:55:24 +02:00
parent c9c6b94c18
commit 3301cf5542
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
49 changed files with 690 additions and 262 deletions

View file

@ -12,6 +12,7 @@ type BinaryOp struct {
Left Value
Right Value
Op token.Kind
Id
Liveness
Source
}
@ -42,6 +43,10 @@ func (v *BinaryOp) IsConst() bool {
return true
}
func (v *BinaryOp) Debug() string {
return fmt.Sprintf("%%%d %s %%%d", v.Left.ID(), expression.Operators[v.Op].Symbol, v.Right.ID())
}
func (v *BinaryOp) String() string {
return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right)
}

View file

@ -8,7 +8,7 @@ type Block struct {
// Append adds a new instruction to the block.
func (block *Block) Append(instr Value) Value {
for _, dep := range instr.Dependencies() {
dep.AddUse(instr)
dep.AddUser(instr)
}
block.Instructions = append(block.Instructions, instr)

View file

@ -8,6 +8,7 @@ import (
)
type Bytes struct {
Id
Bytes []byte
Liveness
Source
@ -31,10 +32,14 @@ func (v *Bytes) IsConst() bool {
return true
}
func (v *Bytes) Debug() string {
return v.String()
}
func (v *Bytes) String() string {
return strconv.Quote(string(v.Bytes))
}
func (v *Bytes) Type() types.Type {
return types.String
return types.CString
}

View file

@ -19,5 +19,5 @@ func TestBytes(t *testing.T) {
assert.False(t, hello.Equals(one))
assert.True(t, hello.Equals(helloDup))
assert.Equal(t, hello.String(), "\"Hello\"")
assert.True(t, types.Is(hello.Type(), types.String))
assert.True(t, types.Is(hello.Type(), types.CString))
}

View file

@ -1,13 +1,14 @@
package ssa
import (
"fmt"
"strconv"
"strings"
"git.urbach.dev/cli/q/src/types"
)
type Call struct {
Id
Arguments
Liveness
Source
@ -27,14 +28,42 @@ func (v *Call) IsConst() bool {
return false
}
func (v *Call) String() string {
args := make([]string, 0, len(v.Arguments)-1)
func (v *Call) Debug() string {
tmp := strings.Builder{}
tmp.WriteString("%")
tmp.WriteString(strconv.Itoa(v.Arguments[0].ID()))
tmp.WriteString("(")
args := v.Arguments[1:]
for _, arg := range v.Arguments[1:] {
args = append(args, arg.String())
for i, arg := range args {
tmp.WriteString("%")
tmp.WriteString(strconv.Itoa(arg.ID()))
if i != len(args)-1 {
tmp.WriteString(", ")
}
}
return fmt.Sprintf("%s(%s)", v.Arguments[0].String(), strings.Join(args, ", "))
tmp.WriteString(")")
return tmp.String()
}
func (v *Call) String() string {
tmp := strings.Builder{}
tmp.WriteString(v.Arguments[0].String())
tmp.WriteString("(")
args := v.Arguments[1:]
for i, arg := range args {
tmp.WriteString(arg.String())
if i != len(args)-1 {
tmp.WriteString(", ")
}
}
tmp.WriteString(")")
return tmp.String()
}
func (v *Call) Type() types.Type {

View file

@ -5,6 +5,8 @@ import "git.urbach.dev/cli/q/src/types"
type Function struct {
UniqueName string
Typ *types.Function
IsExtern bool
Id
Liveness
Source
}
@ -27,6 +29,10 @@ func (v *Function) IsConst() bool {
return true
}
func (v *Function) Debug() string {
return v.String()
}
func (v *Function) String() string {
return v.UniqueName
}

11
src/ssa/ID.go Normal file
View file

@ -0,0 +1,11 @@
package ssa
type Id int
func (id Id) ID() int {
return int(id)
}
func (id *Id) SetID(newId int) {
*id = Id(newId)
}

View file

@ -1,10 +1,13 @@
package ssa
import "git.urbach.dev/cli/q/src/types"
import (
"git.urbach.dev/cli/q/src/types"
)
// IR is a list of basic blocks.
type IR struct {
Blocks []*Block
nextId int
}
// AddBlock adds a new block to the function.
@ -31,35 +34,29 @@ func (f *IR) Append(instr Value) Value {
}
}
instr.SetID(f.nextId)
f.nextId++
return f.Blocks[len(f.Blocks)-1].Append(instr)
}
// AppendInt adds a new integer value to the last block.
func (f *IR) AppendInt(x int) *Int {
v := &Int{Int: x}
f.Append(v)
return v
func (f *IR) AppendInt(x int) Value {
return f.Append(&Int{Int: x})
}
// AppendFunction adds a new function value to the last block.
func (f *IR) AppendFunction(name string, typ *types.Function) *Function {
v := &Function{UniqueName: name, Typ: typ}
f.Append(v)
return v
func (f *IR) AppendFunction(name string, typ *types.Function, extern bool) Value {
return f.Append(&Function{UniqueName: name, Typ: typ, IsExtern: extern})
}
// AppendBytes adds a new byte slice value to the last block.
func (f *IR) AppendBytes(s []byte) *Bytes {
v := &Bytes{Bytes: s}
f.Append(v)
return v
func (f *IR) AppendBytes(s []byte) Value {
return f.Append(&Bytes{Bytes: s})
}
// AppendString adds a new string value to the last block.
func (f *IR) AppendString(s string) *Bytes {
v := &Bytes{Bytes: []byte(s)}
f.Append(v)
return v
func (f *IR) AppendString(s string) Value {
return f.Append(&Bytes{Bytes: []byte(s)})
}
// Values yields on each value.

View file

@ -8,6 +8,7 @@ import (
type Int struct {
Int int
Id
Liveness
Source
}
@ -30,6 +31,10 @@ func (v *Int) IsConst() bool {
return true
}
func (v *Int) Debug() string {
return v.String()
}
func (v *Int) String() string {
return fmt.Sprintf("%d", v.Int)
}

View file

@ -1,13 +1,13 @@
package ssa
type Liveness struct {
alive int
users []Value
}
func (v *Liveness) AddUse(user Value) {
v.alive++
func (v *Liveness) AddUser(user Value) {
v.users = append(v.users, user)
}
func (v *Liveness) Alive() int {
return v.alive
func (v *Liveness) CountUsers() int {
return len(v.users)
}

6
src/ssa/NoLiveness.go Normal file
View file

@ -0,0 +1,6 @@
package ssa
type NoLiveness struct{}
func (a *NoLiveness) AddUser(user Value) { panic("value does not have liveness") }
func (a *NoLiveness) CountUsers() int { return 0 }

View file

@ -10,6 +10,7 @@ type Parameter struct {
Index uint8
Name string
Typ types.Type
Id
Liveness
Source
}
@ -32,6 +33,10 @@ func (v *Parameter) IsConst() bool {
return true
}
func (v *Parameter) Debug() string {
return v.String()
}
func (v *Parameter) String() string {
return fmt.Sprintf("in[%d]", v.Index)
}

View file

@ -1,20 +1,19 @@
package ssa
import (
"fmt"
"strconv"
"strings"
"git.urbach.dev/cli/q/src/types"
)
type Return struct {
Id
Arguments
Source
NoLiveness
}
func (a *Return) AddUse(user Value) { panic("return is not a value") }
func (a *Return) Alive() int { return 0 }
func (a *Return) Equals(v Value) bool {
b, sameType := v.(*Return)
@ -39,18 +38,43 @@ func (v *Return) IsConst() bool {
return false
}
func (v *Return) Debug() string {
if len(v.Arguments) == 0 {
return "return"
}
tmp := strings.Builder{}
tmp.WriteString("return ")
for i, arg := range v.Arguments {
tmp.WriteString("%")
tmp.WriteString(strconv.Itoa(arg.ID()))
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
return tmp.String()
}
func (v *Return) String() string {
if len(v.Arguments) == 0 {
return "return"
}
args := make([]string, 0, len(v.Arguments))
tmp := strings.Builder{}
tmp.WriteString("return ")
for _, arg := range v.Arguments {
args = append(args, arg.String())
for i, arg := range v.Arguments {
tmp.WriteString(arg.String())
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
return fmt.Sprintf("return %s", strings.Join(args, ", "))
return tmp.String()
}
func (v *Return) Type() types.Type {

View file

@ -4,10 +4,14 @@ import "git.urbach.dev/cli/q/src/token"
type Source token.List
func (v Source) Start() token.Position {
return v[0].Position
}
func (v Source) End() token.Position {
return v[len(v)-1].End()
}
func (v *Source) SetSource(source token.List) {
*v = Source(source)
}
func (v Source) Start() token.Position {
return v[0].Position
}

69
src/ssa/Struct.go Normal file
View file

@ -0,0 +1,69 @@
package ssa
import (
"strconv"
"strings"
"git.urbach.dev/cli/q/src/types"
)
type Struct struct {
Typ *types.Struct
Id
Arguments
Liveness
Source
}
func (a *Struct) Equals(v Value) bool {
b, sameType := v.(*Struct)
if !sameType {
return false
}
return a.Arguments.Equals(b.Arguments)
}
func (v *Struct) IsConst() bool {
return true
}
func (v *Struct) Debug() string {
tmp := strings.Builder{}
tmp.WriteString(v.Typ.Name())
tmp.WriteString("{")
for i, arg := range v.Arguments {
tmp.WriteString("%")
tmp.WriteString(strconv.Itoa(arg.ID()))
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
tmp.WriteString("}")
return tmp.String()
}
func (v *Struct) String() string {
tmp := strings.Builder{}
tmp.WriteString(v.Typ.Name())
tmp.WriteString("{")
for i, arg := range v.Arguments {
tmp.WriteString(arg.String())
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
tmp.WriteString("}")
return tmp.String()
}
func (v *Struct) Type() types.Type {
return v.Typ
}

45
src/ssa/StructField.go Normal file
View file

@ -0,0 +1,45 @@
package ssa
import (
"fmt"
"git.urbach.dev/cli/q/src/types"
)
type StructField struct {
Struct Value
Field *types.Field
Id
Liveness
Source
}
func (v *StructField) Dependencies() []Value {
return []Value{v.Struct}
}
func (a *StructField) Equals(v Value) bool {
b, sameType := v.(*StructField)
if !sameType {
return false
}
return a.Field == b.Field
}
func (v *StructField) IsConst() bool {
return true
}
func (v *StructField) Debug() string {
return fmt.Sprintf("%%%d.%s", v.Struct.ID(), v.Field)
}
func (v *StructField) String() string {
return fmt.Sprintf("%s.%s", v.Struct, v.Field)
}
func (v *StructField) Type() types.Type {
return v.Field.Type
}

View file

@ -1,13 +1,14 @@
package ssa
import (
"fmt"
"strconv"
"strings"
"git.urbach.dev/cli/q/src/types"
)
type Syscall struct {
Id
Arguments
Liveness
Source
@ -27,14 +28,37 @@ func (v *Syscall) IsConst() bool {
return false
}
func (v *Syscall) String() string {
args := make([]string, 0, len(v.Arguments))
func (v *Syscall) Debug() string {
tmp := strings.Builder{}
tmp.WriteString("syscall(")
for _, arg := range v.Arguments {
args = append(args, arg.String())
for i, arg := range v.Arguments {
tmp.WriteString("%")
tmp.WriteString(strconv.Itoa(arg.ID()))
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
return fmt.Sprintf("syscall(%s)", strings.Join(args, ", "))
tmp.WriteString(")")
return tmp.String()
}
func (v *Syscall) String() string {
tmp := strings.Builder{}
tmp.WriteString("syscall(")
for i, arg := range v.Arguments {
tmp.WriteString(arg.String())
if i != len(v.Arguments)-1 {
tmp.WriteString(", ")
}
}
tmp.WriteString(")")
return tmp.String()
}
func (v *Syscall) Type() types.Type {

View file

@ -6,13 +6,24 @@ import (
)
type Value interface {
AddUse(Value)
Alive() int
Dependencies() []Value
End() token.Position
Equals(Value) bool
// Essentials
Debug() string
ID() int
IsConst() bool
SetID(int)
String() string
Start() token.Position
Type() types.Type
// Arguments
Dependencies() []Value
Equals(Value) bool
// Liveness
AddUser(Value)
CountUsers() int
// Source
SetSource(token.List)
Start() token.Position
End() token.Position
}