This commit is contained in:
parent
c9c6b94c18
commit
3301cf5542
49 changed files with 690 additions and 262 deletions
|
@ -1,3 +1,3 @@
|
||||||
write(buffer []byte) -> (written int) {
|
write(buffer string) -> (written int) {
|
||||||
return syscall(64, 1, buffer, len(buffer))
|
return syscall(64, 1, buffer.ptr, buffer.len)
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
write(buffer []byte) -> (written int) {
|
write(buffer string) -> (written int) {
|
||||||
return syscall(1, 1, buffer, len(buffer))
|
return syscall(1, 1, buffer.ptr, buffer.len)
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
write(buffer []byte) -> (written int) {
|
write(buffer string) -> (written int) {
|
||||||
return syscall(0x2000004, 1, buffer, len(buffer))
|
return syscall(0x2000004, 1, buffer.ptr, buffer.len)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
write(_ []byte) -> (written int) {
|
write(_ string) -> (written int) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,4 +45,5 @@ var CPU = cpu.CPU{
|
||||||
Call: []cpu.Register{X0, X1, X2, X3, X4, X5, X6},
|
Call: []cpu.Register{X0, X1, X2, X3, X4, X5, X6},
|
||||||
Syscall: []cpu.Register{X8, X0, X1, X2, X3, X4, X5},
|
Syscall: []cpu.Register{X8, X0, X1, X2, X3, X4, X5},
|
||||||
ExternCall: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7},
|
ExternCall: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7},
|
||||||
|
Return: []cpu.Register{X0, X1, X2},
|
||||||
}
|
}
|
|
@ -7,4 +7,18 @@ const (
|
||||||
UnknownArch Arch = iota
|
UnknownArch Arch = iota
|
||||||
ARM
|
ARM
|
||||||
X86
|
X86
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetArch sets the architecture which also influences the default alignment.
|
||||||
|
func (build *Build) SetArch(arch Arch) {
|
||||||
|
build.Arch = arch
|
||||||
|
|
||||||
|
switch arch {
|
||||||
|
case ARM:
|
||||||
|
build.MemoryAlign = 0x4000
|
||||||
|
default:
|
||||||
|
build.MemoryAlign = 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
|
build.FileAlign = build.MemoryAlign
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
package build
|
|
||||||
|
|
||||||
// SetArch sets the architecture which also influences the default alignment.
|
|
||||||
func (build *Build) SetArch(arch Arch) {
|
|
||||||
build.Arch = arch
|
|
||||||
|
|
||||||
switch arch {
|
|
||||||
case ARM:
|
|
||||||
build.MemoryAlign = 0x4000
|
|
||||||
default:
|
|
||||||
build.MemoryAlign = 0x1000
|
|
||||||
}
|
|
||||||
|
|
||||||
build.FileAlign = build.MemoryAlign
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ Usage:
|
||||||
Commands:
|
Commands:
|
||||||
|
|
||||||
build [directory | file] build an executable
|
build [directory | file] build an executable
|
||||||
|
|
||||||
--verbose, -v show everything
|
--verbose, -v show everything
|
||||||
|
|
||||||
run [directory | file] build and run the executable
|
run [directory | file] build and run the executable
|
||||||
|
|
|
@ -2,6 +2,7 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/core"
|
"git.urbach.dev/cli/q/src/core"
|
||||||
"git.urbach.dev/go/color/ansi"
|
"git.urbach.dev/go/color/ansi"
|
||||||
|
@ -10,18 +11,27 @@ import (
|
||||||
// showSSA shows the SSA IR.
|
// showSSA shows the SSA IR.
|
||||||
func showSSA(root *core.Function) {
|
func showSSA(root *core.Function) {
|
||||||
root.EachDependency(make(map[*core.Function]bool), func(f *core.Function) {
|
root.EachDependency(make(map[*core.Function]bool), func(f *core.Function) {
|
||||||
ansi.Yellow.Printf("%s:\n", f.UniqueName)
|
fmt.Print("# ")
|
||||||
|
ansi.Green.Print(f.UniqueName)
|
||||||
|
fmt.Print("\n\n")
|
||||||
|
|
||||||
for i, block := range f.Blocks {
|
for _, block := range f.Blocks {
|
||||||
if i != 0 {
|
ansi.Dim.Printf("| %-3s | %-30s | %-30s | %-4s |\n", "ID", "Raw", "Type", "Uses")
|
||||||
fmt.Println("---")
|
ansi.Dim.Printf("| %s | %s | %s | %s |\n", strings.Repeat("-", 3), strings.Repeat("-", 30), strings.Repeat("-", 30), strings.Repeat("-", 4))
|
||||||
}
|
|
||||||
|
|
||||||
for i, instr := range block.Instructions {
|
for i, instr := range block.Instructions {
|
||||||
ansi.Dim.Printf("%-4d", i)
|
ansi.Dim.Printf("| %%%-2d | ", i)
|
||||||
fmt.Printf("%-40s", instr.String())
|
|
||||||
ansi.Cyan.Printf("%-30s", instr.Type().Name())
|
if instr.IsConst() {
|
||||||
ansi.Dim.Printf("%s\n", f.File.Bytes[instr.Start():instr.End()])
|
fmt.Printf("%-30s ", instr.Debug())
|
||||||
|
} else {
|
||||||
|
ansi.Yellow.Printf("%-30s ", instr.Debug())
|
||||||
|
}
|
||||||
|
|
||||||
|
ansi.Dim.Print("|")
|
||||||
|
ansi.Dim.Printf(" %-30s |", instr.Type().Name())
|
||||||
|
ansi.Dim.Printf(" %-4d |", instr.CountUsers())
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
// CheckDeadCode checks for dead values.
|
// CheckDeadCode checks for dead values.
|
||||||
func (f *Function) CheckDeadCode() error {
|
func (f *Function) CheckDeadCode() error {
|
||||||
for instr := range f.Values {
|
for instr := range f.Values {
|
||||||
if instr.IsConst() && instr.Alive() == 0 {
|
if instr.IsConst() && instr.CountUsers() == 0 {
|
||||||
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Start())
|
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Start())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,24 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import "git.urbach.dev/cli/q/src/types"
|
||||||
"git.urbach.dev/cli/q/src/ssa"
|
|
||||||
"git.urbach.dev/cli/q/src/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Compile turns a function into machine code.
|
// Compile turns a function into machine code.
|
||||||
func (f *Function) Compile() {
|
func (f *Function) Compile() {
|
||||||
extra := 0
|
offset := 0
|
||||||
|
|
||||||
for i, input := range f.Input {
|
for i, input := range f.Input {
|
||||||
if input.Name == "_" {
|
if input.Name == "_" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
array, isArray := input.Typ.(*types.Array)
|
input.Index = uint8(offset + i)
|
||||||
|
|
||||||
if isArray {
|
|
||||||
pointer := &ssa.Parameter{
|
|
||||||
Index: uint8(i + extra),
|
|
||||||
Name: input.Name,
|
|
||||||
Typ: &types.Pointer{To: array.Of},
|
|
||||||
Source: input.Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Append(pointer)
|
|
||||||
f.Identifiers[pointer.Name] = pointer
|
|
||||||
extra++
|
|
||||||
|
|
||||||
length := &ssa.Parameter{
|
|
||||||
Index: uint8(i + extra),
|
|
||||||
Name: input.Name + ".len",
|
|
||||||
Typ: types.AnyInt,
|
|
||||||
Source: input.Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Append(length)
|
|
||||||
f.Identifiers[length.Name] = length
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
input.Index = uint8(i + extra)
|
|
||||||
f.Append(input)
|
f.Append(input)
|
||||||
f.Identifiers[input.Name] = input
|
f.Identifiers[input.Name] = input
|
||||||
|
structure, isStruct := input.Typ.(*types.Struct)
|
||||||
|
|
||||||
|
if isStruct {
|
||||||
|
offset += len(structure.Fields) - 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for instr := range f.Body.Instructions {
|
for instr := range f.Body.Instructions {
|
||||||
|
@ -59,5 +35,5 @@ func (f *Function) Compile() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.ssaToAsm()
|
f.GenerateAssembly(f.IR, f.IsLeaf())
|
||||||
}
|
}
|
|
@ -27,8 +27,8 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Dependencies.Add(function)
|
f.Dependencies.Add(function)
|
||||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern())
|
||||||
v.Source = ssa.Source(expr.Source)
|
v.SetSource(expr.Source)
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,14 +42,25 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
v := f.AppendInt(number)
|
v := f.AppendInt(number)
|
||||||
v.Source = ssa.Source(expr.Source)
|
v.SetSource(expr.Source)
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
||||||
case token.String:
|
case token.String:
|
||||||
data := expr.Token.Bytes(f.File.Bytes)
|
data := expr.Token.Bytes(f.File.Bytes)
|
||||||
data = Unescape(data)
|
data = Unescape(data)
|
||||||
v := f.AppendBytes(data)
|
|
||||||
v.Source = ssa.Source(expr.Source)
|
length := f.AppendInt(len(data))
|
||||||
|
length.SetSource(expr.Source)
|
||||||
|
|
||||||
|
pointer := f.AppendBytes(data)
|
||||||
|
pointer.SetSource(expr.Source)
|
||||||
|
|
||||||
|
v := f.Append(&ssa.Struct{
|
||||||
|
Arguments: []ssa.Value{pointer, length},
|
||||||
|
Typ: types.String,
|
||||||
|
})
|
||||||
|
|
||||||
|
v.SetSource(expr.Source)
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +120,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
||||||
return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position)
|
return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, param := range parameters {
|
for i, param := range slices.Backward(parameters) {
|
||||||
if !types.Is(param.Type(), fn.Input[i].Typ) {
|
if !types.Is(param.Type(), fn.Input[i].Typ) {
|
||||||
return nil, errors.New(&TypeMismatch{
|
return nil, errors.New(&TypeMismatch{
|
||||||
Encountered: param.Type().Name(),
|
Encountered: param.Type().Name(),
|
||||||
|
@ -130,22 +141,39 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
||||||
case token.Dot:
|
case token.Dot:
|
||||||
left := expr.Children[0]
|
left := expr.Children[0]
|
||||||
right := expr.Children[1]
|
right := expr.Children[1]
|
||||||
label := fmt.Sprintf("%s.%s", left.String(f.File.Bytes), right.String(f.File.Bytes))
|
leftText := left.String(f.File.Bytes)
|
||||||
|
rightText := right.String(f.File.Bytes)
|
||||||
|
identifier, exists := f.Identifiers[leftText]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
structure := identifier.Type().(*types.Struct)
|
||||||
|
field := structure.FieldByName(rightText)
|
||||||
|
|
||||||
|
if field == nil {
|
||||||
|
return nil, errors.New(&UnknownStructField{StructName: structure.Name(), FieldName: rightText}, f.File, right.Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := f.Append(&ssa.StructField{Struct: identifier, Field: field})
|
||||||
|
v.SetSource(expr.Source)
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
label := fmt.Sprintf("%s.%s", leftText, rightText)
|
||||||
function, exists := f.All.Functions[label]
|
function, exists := f.All.Functions[label]
|
||||||
|
|
||||||
if !exists {
|
if exists {
|
||||||
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
|
if function.IsExtern() {
|
||||||
|
f.Assembler.Libraries = f.Assembler.Libraries.Append(function.Package, function.Name)
|
||||||
|
} else {
|
||||||
|
f.Dependencies.Add(function)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern())
|
||||||
|
v.SetSource(expr.Source)
|
||||||
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if function.IsExtern() {
|
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
|
||||||
f.Assembler.Libraries = f.Assembler.Libraries.Append(function.Package, function.Name)
|
|
||||||
} else {
|
|
||||||
f.Dependencies.Add(function)
|
|
||||||
}
|
|
||||||
|
|
||||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
|
||||||
v.Source = ssa.Source(expr.Source)
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/asm"
|
"git.urbach.dev/cli/q/src/asm"
|
||||||
"git.urbach.dev/cli/q/src/cpu"
|
|
||||||
"git.urbach.dev/cli/q/src/fs"
|
"git.urbach.dev/cli/q/src/fs"
|
||||||
"git.urbach.dev/cli/q/src/set"
|
"git.urbach.dev/cli/q/src/set"
|
||||||
"git.urbach.dev/cli/q/src/ssa"
|
"git.urbach.dev/cli/q/src/ssa"
|
||||||
|
"git.urbach.dev/cli/q/src/ssa2asm"
|
||||||
"git.urbach.dev/cli/q/src/token"
|
"git.urbach.dev/cli/q/src/token"
|
||||||
"git.urbach.dev/cli/q/src/types"
|
"git.urbach.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
@ -15,9 +15,9 @@ import (
|
||||||
// Function is the smallest unit of code.
|
// Function is the smallest unit of code.
|
||||||
type Function struct {
|
type Function struct {
|
||||||
ssa.IR
|
ssa.IR
|
||||||
|
ssa2asm.Compiler
|
||||||
Name string
|
Name string
|
||||||
Package string
|
Package string
|
||||||
UniqueName string
|
|
||||||
File *fs.File
|
File *fs.File
|
||||||
Input []*ssa.Parameter
|
Input []*ssa.Parameter
|
||||||
Output []*ssa.Parameter
|
Output []*ssa.Parameter
|
||||||
|
@ -25,19 +25,16 @@ type Function struct {
|
||||||
Identifiers map[string]ssa.Value
|
Identifiers map[string]ssa.Value
|
||||||
All *Environment
|
All *Environment
|
||||||
Dependencies set.Ordered[*Function]
|
Dependencies set.Ordered[*Function]
|
||||||
Assembler asm.Assembler
|
|
||||||
CPU *cpu.CPU
|
|
||||||
Type *types.Function
|
Type *types.Function
|
||||||
Err error
|
Err error
|
||||||
count count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFunction creates a new function.
|
// NewFunction creates a new function.
|
||||||
func NewFunction(name string, pkg string, file *fs.File) *Function {
|
func NewFunction(name string, pkg string, file *fs.File) *Function {
|
||||||
return &Function{
|
return &Function{
|
||||||
Name: name,
|
Name: name,
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
UniqueName: fmt.Sprintf("%s.%s", pkg, name),
|
|
||||||
File: file,
|
File: file,
|
||||||
Identifiers: make(map[string]ssa.Value, 8),
|
Identifiers: make(map[string]ssa.Value, 8),
|
||||||
IR: ssa.IR{
|
IR: ssa.IR{
|
||||||
|
@ -45,8 +42,11 @@ func NewFunction(name string, pkg string, file *fs.File) *Function {
|
||||||
{Instructions: make([]ssa.Value, 0, 8)},
|
{Instructions: make([]ssa.Value, 0, 8)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Assembler: asm.Assembler{
|
Compiler: ssa2asm.Compiler{
|
||||||
Instructions: make([]asm.Instruction, 0, 8),
|
UniqueName: fmt.Sprintf("%s.%s", pkg, name),
|
||||||
|
Assembler: asm.Assembler{
|
||||||
|
Instructions: make([]asm.Instruction, 0, 8),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
type counter = uint8
|
|
||||||
|
|
||||||
// count stores how often a certain statement appeared so we can generate a unique label from it.
|
|
||||||
type count struct {
|
|
||||||
data counter
|
|
||||||
}
|
|
|
@ -63,6 +63,21 @@ func (err *UnknownIdentifier) Error() string {
|
||||||
return fmt.Sprintf("Unknown identifier '%s'", err.Name)
|
return fmt.Sprintf("Unknown identifier '%s'", err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnknownStructField represents unknown struct fields.
|
||||||
|
type UnknownStructField struct {
|
||||||
|
StructName string
|
||||||
|
FieldName string
|
||||||
|
CorrectFieldName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *UnknownStructField) Error() string {
|
||||||
|
if err.CorrectFieldName != "" {
|
||||||
|
return fmt.Sprintf("Unknown struct field '%s' in '%s', did you mean '%s'?", err.FieldName, err.StructName, err.CorrectFieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Unknown struct field '%s' in '%s'", err.FieldName, err.StructName)
|
||||||
|
}
|
||||||
|
|
||||||
// UnusedValue error is created when a value is never used.
|
// UnusedValue error is created when a value is never used.
|
||||||
type UnusedValue struct {
|
type UnusedValue struct {
|
||||||
Value string
|
Value string
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.urbach.dev/cli/q/src/asm"
|
|
||||||
"git.urbach.dev/cli/q/src/ssa"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ssaToAsm converts the SSA IR to assembler instructions.
|
|
||||||
func (f *Function) ssaToAsm() {
|
|
||||||
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
|
|
||||||
|
|
||||||
if !f.IsLeaf() && f.UniqueName != "core.init" {
|
|
||||||
f.Assembler.Append(&asm.FunctionStart{})
|
|
||||||
}
|
|
||||||
|
|
||||||
for instr := range f.Values {
|
|
||||||
switch instr := instr.(type) {
|
|
||||||
case *ssa.Call:
|
|
||||||
arg := instr.Arguments[0].(*ssa.Function)
|
|
||||||
fn := f.All.Functions[arg.UniqueName]
|
|
||||||
|
|
||||||
if fn.IsExtern() {
|
|
||||||
f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.ExternCall)
|
|
||||||
f.Assembler.Append(&asm.CallExtern{Library: fn.Package, Function: fn.Name})
|
|
||||||
} else {
|
|
||||||
f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.Call)
|
|
||||||
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
|
|
||||||
}
|
|
||||||
|
|
||||||
case *ssa.Syscall:
|
|
||||||
f.ssaValuesToRegisters(instr.Arguments, f.CPU.Syscall)
|
|
||||||
f.Assembler.Append(&asm.Syscall{})
|
|
||||||
|
|
||||||
case *ssa.Return:
|
|
||||||
f.Assembler.Append(&asm.Return{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.IsLeaf() && f.UniqueName != "core.init" {
|
|
||||||
f.Assembler.Append(&asm.FunctionEnd{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.UniqueName != "core.exit" {
|
|
||||||
f.Assembler.Append(&asm.Return{})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/asm"
|
|
||||||
"git.urbach.dev/cli/q/src/cpu"
|
|
||||||
"git.urbach.dev/cli/q/src/ssa"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ssaValuesToRegisters generates assembler instructions to move the SSA values to the given registers.
|
|
||||||
func (f *Function) ssaValuesToRegisters(args []ssa.Value, registers []cpu.Register) {
|
|
||||||
extra := 0
|
|
||||||
|
|
||||||
for _, arg := range args {
|
|
||||||
switch arg.(type) {
|
|
||||||
case *ssa.Bytes:
|
|
||||||
extra++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, arg := range slices.Backward(args) {
|
|
||||||
switch arg := arg.(type) {
|
|
||||||
case *ssa.Int:
|
|
||||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
|
||||||
Destination: registers[i+extra],
|
|
||||||
Number: arg.Int,
|
|
||||||
})
|
|
||||||
case *ssa.Parameter:
|
|
||||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
|
||||||
Destination: registers[i+extra],
|
|
||||||
Source: f.CPU.Call[arg.Index],
|
|
||||||
})
|
|
||||||
case *ssa.Bytes:
|
|
||||||
f.count.data++
|
|
||||||
label := f.CreateLabel("data", f.count.data)
|
|
||||||
f.Assembler.SetData(label.Name, arg.Bytes)
|
|
||||||
|
|
||||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
|
||||||
Destination: registers[i+extra],
|
|
||||||
Number: len(arg.Bytes),
|
|
||||||
})
|
|
||||||
|
|
||||||
extra--
|
|
||||||
|
|
||||||
f.Assembler.Append(&asm.MoveRegisterLabel{
|
|
||||||
Destination: registers[i+extra],
|
|
||||||
Label: label.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,4 +5,5 @@ type CPU struct {
|
||||||
Call []Register
|
Call []Register
|
||||||
Syscall []Register
|
Syscall []Register
|
||||||
ExternCall []Register
|
ExternCall []Register
|
||||||
|
Return []Register
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ package cpu
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Register represents the number of the register.
|
// Register represents the number of the register.
|
||||||
type Register uint8
|
type Register int8
|
||||||
|
|
||||||
// String returns the human readable name of the register.
|
// String returns the human readable name of the register.
|
||||||
func (r Register) String() string {
|
func (r Register) String() string {
|
||||||
|
|
|
@ -12,6 +12,7 @@ type BinaryOp struct {
|
||||||
Left Value
|
Left Value
|
||||||
Right Value
|
Right Value
|
||||||
Op token.Kind
|
Op token.Kind
|
||||||
|
Id
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,10 @@ func (v *BinaryOp) IsConst() bool {
|
||||||
return true
|
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 {
|
func (v *BinaryOp) String() string {
|
||||||
return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right)
|
return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ type Block struct {
|
||||||
// Append adds a new instruction to the block.
|
// Append adds a new instruction to the block.
|
||||||
func (block *Block) Append(instr Value) Value {
|
func (block *Block) Append(instr Value) Value {
|
||||||
for _, dep := range instr.Dependencies() {
|
for _, dep := range instr.Dependencies() {
|
||||||
dep.AddUse(instr)
|
dep.AddUser(instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
block.Instructions = append(block.Instructions, instr)
|
block.Instructions = append(block.Instructions, instr)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bytes struct {
|
type Bytes struct {
|
||||||
|
Id
|
||||||
Bytes []byte
|
Bytes []byte
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
|
@ -31,10 +32,14 @@ func (v *Bytes) IsConst() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Bytes) Debug() string {
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Bytes) String() string {
|
func (v *Bytes) String() string {
|
||||||
return strconv.Quote(string(v.Bytes))
|
return strconv.Quote(string(v.Bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Bytes) Type() types.Type {
|
func (v *Bytes) Type() types.Type {
|
||||||
return types.String
|
return types.CString
|
||||||
}
|
}
|
|
@ -19,5 +19,5 @@ func TestBytes(t *testing.T) {
|
||||||
assert.False(t, hello.Equals(one))
|
assert.False(t, hello.Equals(one))
|
||||||
assert.True(t, hello.Equals(helloDup))
|
assert.True(t, hello.Equals(helloDup))
|
||||||
assert.Equal(t, hello.String(), "\"Hello\"")
|
assert.Equal(t, hello.String(), "\"Hello\"")
|
||||||
assert.True(t, types.Is(hello.Type(), types.String))
|
assert.True(t, types.Is(hello.Type(), types.CString))
|
||||||
}
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/types"
|
"git.urbach.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Call struct {
|
type Call struct {
|
||||||
|
Id
|
||||||
Arguments
|
Arguments
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
|
@ -27,14 +28,42 @@ func (v *Call) IsConst() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Call) String() string {
|
func (v *Call) Debug() string {
|
||||||
args := make([]string, 0, len(v.Arguments)-1)
|
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:] {
|
for i, arg := range args {
|
||||||
args = append(args, arg.String())
|
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 {
|
func (v *Call) Type() types.Type {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import "git.urbach.dev/cli/q/src/types"
|
||||||
type Function struct {
|
type Function struct {
|
||||||
UniqueName string
|
UniqueName string
|
||||||
Typ *types.Function
|
Typ *types.Function
|
||||||
|
IsExtern bool
|
||||||
|
Id
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
}
|
}
|
||||||
|
@ -27,6 +29,10 @@ func (v *Function) IsConst() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Function) Debug() string {
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Function) String() string {
|
func (v *Function) String() string {
|
||||||
return v.UniqueName
|
return v.UniqueName
|
||||||
}
|
}
|
||||||
|
|
11
src/ssa/ID.go
Normal file
11
src/ssa/ID.go
Normal 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)
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package ssa
|
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.
|
// IR is a list of basic blocks.
|
||||||
type IR struct {
|
type IR struct {
|
||||||
Blocks []*Block
|
Blocks []*Block
|
||||||
|
nextId int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBlock adds a new block to the function.
|
// 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)
|
return f.Blocks[len(f.Blocks)-1].Append(instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendInt adds a new integer value to the last block.
|
// AppendInt adds a new integer value to the last block.
|
||||||
func (f *IR) AppendInt(x int) *Int {
|
func (f *IR) AppendInt(x int) Value {
|
||||||
v := &Int{Int: x}
|
return f.Append(&Int{Int: x})
|
||||||
f.Append(v)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendFunction adds a new function value to the last block.
|
// AppendFunction adds a new function value to the last block.
|
||||||
func (f *IR) AppendFunction(name string, typ *types.Function) *Function {
|
func (f *IR) AppendFunction(name string, typ *types.Function, extern bool) Value {
|
||||||
v := &Function{UniqueName: name, Typ: typ}
|
return f.Append(&Function{UniqueName: name, Typ: typ, IsExtern: extern})
|
||||||
f.Append(v)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendBytes adds a new byte slice value to the last block.
|
// AppendBytes adds a new byte slice value to the last block.
|
||||||
func (f *IR) AppendBytes(s []byte) *Bytes {
|
func (f *IR) AppendBytes(s []byte) Value {
|
||||||
v := &Bytes{Bytes: s}
|
return f.Append(&Bytes{Bytes: s})
|
||||||
f.Append(v)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendString adds a new string value to the last block.
|
// AppendString adds a new string value to the last block.
|
||||||
func (f *IR) AppendString(s string) *Bytes {
|
func (f *IR) AppendString(s string) Value {
|
||||||
v := &Bytes{Bytes: []byte(s)}
|
return f.Append(&Bytes{Bytes: []byte(s)})
|
||||||
f.Append(v)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values yields on each value.
|
// Values yields on each value.
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
type Int struct {
|
type Int struct {
|
||||||
Int int
|
Int int
|
||||||
|
Id
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
}
|
}
|
||||||
|
@ -30,6 +31,10 @@ func (v *Int) IsConst() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Int) Debug() string {
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Int) String() string {
|
func (v *Int) String() string {
|
||||||
return fmt.Sprintf("%d", v.Int)
|
return fmt.Sprintf("%d", v.Int)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
type Liveness struct {
|
type Liveness struct {
|
||||||
alive int
|
users []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Liveness) AddUse(user Value) {
|
func (v *Liveness) AddUser(user Value) {
|
||||||
v.alive++
|
v.users = append(v.users, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Liveness) Alive() int {
|
func (v *Liveness) CountUsers() int {
|
||||||
return v.alive
|
return len(v.users)
|
||||||
}
|
}
|
6
src/ssa/NoLiveness.go
Normal file
6
src/ssa/NoLiveness.go
Normal 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 }
|
|
@ -10,6 +10,7 @@ type Parameter struct {
|
||||||
Index uint8
|
Index uint8
|
||||||
Name string
|
Name string
|
||||||
Typ types.Type
|
Typ types.Type
|
||||||
|
Id
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
}
|
}
|
||||||
|
@ -32,6 +33,10 @@ func (v *Parameter) IsConst() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Parameter) Debug() string {
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Parameter) String() string {
|
func (v *Parameter) String() string {
|
||||||
return fmt.Sprintf("in[%d]", v.Index)
|
return fmt.Sprintf("in[%d]", v.Index)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/types"
|
"git.urbach.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Return struct {
|
type Return struct {
|
||||||
|
Id
|
||||||
Arguments
|
Arguments
|
||||||
Source
|
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 {
|
func (a *Return) Equals(v Value) bool {
|
||||||
b, sameType := v.(*Return)
|
b, sameType := v.(*Return)
|
||||||
|
|
||||||
|
@ -39,18 +38,43 @@ func (v *Return) IsConst() bool {
|
||||||
return false
|
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 {
|
func (v *Return) String() string {
|
||||||
if len(v.Arguments) == 0 {
|
if len(v.Arguments) == 0 {
|
||||||
return "return"
|
return "return"
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0, len(v.Arguments))
|
tmp := strings.Builder{}
|
||||||
|
tmp.WriteString("return ")
|
||||||
|
|
||||||
for _, arg := range v.Arguments {
|
for i, arg := range v.Arguments {
|
||||||
args = append(args, arg.String())
|
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 {
|
func (v *Return) Type() types.Type {
|
||||||
|
|
|
@ -4,10 +4,14 @@ import "git.urbach.dev/cli/q/src/token"
|
||||||
|
|
||||||
type Source token.List
|
type Source token.List
|
||||||
|
|
||||||
func (v Source) Start() token.Position {
|
|
||||||
return v[0].Position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Source) End() token.Position {
|
func (v Source) End() token.Position {
|
||||||
return v[len(v)-1].End()
|
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
69
src/ssa/Struct.go
Normal 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
45
src/ssa/StructField.go
Normal 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
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.urbach.dev/cli/q/src/types"
|
"git.urbach.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Syscall struct {
|
type Syscall struct {
|
||||||
|
Id
|
||||||
Arguments
|
Arguments
|
||||||
Liveness
|
Liveness
|
||||||
Source
|
Source
|
||||||
|
@ -27,14 +28,37 @@ func (v *Syscall) IsConst() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Syscall) String() string {
|
func (v *Syscall) Debug() string {
|
||||||
args := make([]string, 0, len(v.Arguments))
|
tmp := strings.Builder{}
|
||||||
|
tmp.WriteString("syscall(")
|
||||||
|
|
||||||
for _, arg := range v.Arguments {
|
for i, arg := range v.Arguments {
|
||||||
args = append(args, arg.String())
|
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 {
|
func (v *Syscall) Type() types.Type {
|
||||||
|
|
|
@ -6,13 +6,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Value interface {
|
type Value interface {
|
||||||
AddUse(Value)
|
// Essentials
|
||||||
Alive() int
|
Debug() string
|
||||||
Dependencies() []Value
|
ID() int
|
||||||
End() token.Position
|
|
||||||
Equals(Value) bool
|
|
||||||
IsConst() bool
|
IsConst() bool
|
||||||
|
SetID(int)
|
||||||
String() string
|
String() string
|
||||||
Start() token.Position
|
|
||||||
Type() types.Type
|
Type() types.Type
|
||||||
|
|
||||||
|
// Arguments
|
||||||
|
Dependencies() []Value
|
||||||
|
Equals(Value) bool
|
||||||
|
|
||||||
|
// Liveness
|
||||||
|
AddUser(Value)
|
||||||
|
CountUsers() int
|
||||||
|
|
||||||
|
// Source
|
||||||
|
SetSource(token.List)
|
||||||
|
Start() token.Position
|
||||||
|
End() token.Position
|
||||||
}
|
}
|
13
src/ssa2asm/Compiler.go
Normal file
13
src/ssa2asm/Compiler.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package ssa2asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.urbach.dev/cli/q/src/asm"
|
||||||
|
"git.urbach.dev/cli/q/src/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Compiler struct {
|
||||||
|
UniqueName string
|
||||||
|
Assembler asm.Assembler
|
||||||
|
CPU *cpu.CPU
|
||||||
|
Count Count
|
||||||
|
}
|
25
src/ssa2asm/Compiler_test.go
Normal file
25
src/ssa2asm/Compiler_test.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package ssa2asm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.urbach.dev/cli/q/src/build"
|
||||||
|
"git.urbach.dev/cli/q/src/compiler"
|
||||||
|
"git.urbach.dev/go/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHelloExample(t *testing.T) {
|
||||||
|
b := build.New("../../examples/hello")
|
||||||
|
systems := []build.OS{build.Linux, build.Mac, build.Windows}
|
||||||
|
architectures := []build.Arch{build.ARM, build.X86}
|
||||||
|
|
||||||
|
for _, os := range systems {
|
||||||
|
b.OS = os
|
||||||
|
|
||||||
|
for _, arch := range architectures {
|
||||||
|
b.SetArch(arch)
|
||||||
|
_, err := compiler.Compile(b)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/ssa2asm/Count.go
Normal file
9
src/ssa2asm/Count.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package ssa2asm
|
||||||
|
|
||||||
|
// Counter is the data type for counters.
|
||||||
|
type Counter uint16
|
||||||
|
|
||||||
|
// Count stores how often a certain statement appeared so we can generate a unique label from it.
|
||||||
|
type Count struct {
|
||||||
|
Data Counter
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package core
|
package ssa2asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateLabel creates a label that is tied to this function by using a suffix.
|
// CreateLabel creates a label that is tied to this function by using a suffix.
|
||||||
func (f *Function) CreateLabel(prefix string, count counter) *asm.Label {
|
func (f *Compiler) CreateLabel(prefix string, count Counter) *asm.Label {
|
||||||
tmp := strings.Builder{}
|
tmp := strings.Builder{}
|
||||||
tmp.WriteString(prefix)
|
tmp.WriteString(prefix)
|
||||||
tmp.WriteString(" ")
|
tmp.WriteString(" ")
|
76
src/ssa2asm/GenerateAssembly.go
Normal file
76
src/ssa2asm/GenerateAssembly.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package ssa2asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.urbach.dev/cli/q/src/asm"
|
||||||
|
"git.urbach.dev/cli/q/src/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateAssembly converts the SSA IR to assembler instructions.
|
||||||
|
func (f *Compiler) GenerateAssembly(ir ssa.IR, isLeaf bool) {
|
||||||
|
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
|
||||||
|
|
||||||
|
if !isLeaf && f.UniqueName != "core.init" {
|
||||||
|
f.Assembler.Append(&asm.FunctionStart{})
|
||||||
|
}
|
||||||
|
|
||||||
|
for instr := range ir.Values {
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *ssa.Call:
|
||||||
|
fn := instr.Arguments[0].(*ssa.Function)
|
||||||
|
args := instr.Arguments[1:]
|
||||||
|
|
||||||
|
if fn.IsExtern {
|
||||||
|
for i := range slices.Backward(args) {
|
||||||
|
f.ValueToRegister(args[i], f.CPU.ExternCall[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
dot := strings.IndexByte(fn.UniqueName, '.')
|
||||||
|
library := fn.UniqueName[:dot]
|
||||||
|
function := fn.UniqueName[dot+1:]
|
||||||
|
f.Assembler.Append(&asm.CallExtern{Library: library, Function: function})
|
||||||
|
} else {
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
for i := range slices.Backward(args) {
|
||||||
|
structure, isStruct := args[i].(*ssa.Struct)
|
||||||
|
|
||||||
|
if isStruct {
|
||||||
|
for _, field := range structure.Arguments {
|
||||||
|
f.ValueToRegister(field, f.CPU.Call[offset+i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.ValueToRegister(args[i], f.CPU.Call[offset+i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ssa.Return:
|
||||||
|
for i := range slices.Backward(instr.Arguments) {
|
||||||
|
f.ValueToRegister(instr.Arguments[i], f.CPU.Return[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.Return{})
|
||||||
|
|
||||||
|
case *ssa.Syscall:
|
||||||
|
for i := range slices.Backward(instr.Arguments) {
|
||||||
|
f.ValueToRegister(instr.Arguments[i], f.CPU.Syscall[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.Syscall{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isLeaf && f.UniqueName != "core.init" {
|
||||||
|
f.Assembler.Append(&asm.FunctionEnd{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.UniqueName != "core.exit" {
|
||||||
|
f.Assembler.Append(&asm.Return{})
|
||||||
|
}
|
||||||
|
}
|
54
src/ssa2asm/ValueToRegister.go
Normal file
54
src/ssa2asm/ValueToRegister.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package ssa2asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.urbach.dev/cli/q/src/asm"
|
||||||
|
"git.urbach.dev/cli/q/src/cpu"
|
||||||
|
"git.urbach.dev/cli/q/src/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValueToRegister moves a value into the given `destination` register.
|
||||||
|
func (f *Compiler) ValueToRegister(instr ssa.Value, destination cpu.Register) {
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *ssa.Bytes:
|
||||||
|
f.Count.Data++
|
||||||
|
label := f.CreateLabel("data", f.Count.Data)
|
||||||
|
f.Assembler.SetData(label.Name, instr.Bytes)
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.MoveRegisterLabel{
|
||||||
|
Destination: destination,
|
||||||
|
Label: label.Name,
|
||||||
|
})
|
||||||
|
|
||||||
|
case *ssa.Int:
|
||||||
|
f.Assembler.Append(&asm.MoveRegisterNumber{
|
||||||
|
Destination: destination,
|
||||||
|
Number: instr.Int,
|
||||||
|
})
|
||||||
|
|
||||||
|
case *ssa.Parameter:
|
||||||
|
source := f.CPU.Call[instr.Index]
|
||||||
|
|
||||||
|
if source == destination {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||||
|
Destination: destination,
|
||||||
|
Source: source,
|
||||||
|
})
|
||||||
|
|
||||||
|
case *ssa.StructField:
|
||||||
|
parameter := instr.Struct.(*ssa.Parameter)
|
||||||
|
field := instr.Field
|
||||||
|
source := f.CPU.Call[parameter.Index+field.Index]
|
||||||
|
|
||||||
|
if source == destination {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||||
|
Destination: destination,
|
||||||
|
Source: source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ var (
|
||||||
Int8 = &Base{name: "int8", size: 1}
|
Int8 = &Base{name: "int8", size: 1}
|
||||||
Float64 = &Base{name: "float64", size: 8}
|
Float64 = &Base{name: "float64", size: 8}
|
||||||
Float32 = &Base{name: "float32", size: 4}
|
Float32 = &Base{name: "float32", size: 4}
|
||||||
String = &Array{Of: Byte}
|
|
||||||
UInt64 = &Base{name: "uint64", size: 8}
|
UInt64 = &Base{name: "uint64", size: 8}
|
||||||
UInt32 = &Base{name: "uint32", size: 4}
|
UInt32 = &Base{name: "uint32", size: 4}
|
||||||
UInt16 = &Base{name: "uint16", size: 2}
|
UInt16 = &Base{name: "uint16", size: 2}
|
||||||
|
@ -20,6 +19,19 @@ var (
|
||||||
Void = &Base{name: "void", size: 0}
|
Void = &Base{name: "void", size: 0}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CString = &Pointer{To: Byte}
|
||||||
|
String = &Struct{
|
||||||
|
Package: "",
|
||||||
|
UniqueName: "string",
|
||||||
|
name: "string",
|
||||||
|
Fields: []*Field{
|
||||||
|
{Name: "ptr", Type: CString, Index: 0, Offset: 0},
|
||||||
|
{Name: "len", Type: Int, Index: 1, Offset: 8},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Byte = UInt8
|
Byte = UInt8
|
||||||
Float = Float64
|
Float = Float64
|
||||||
|
|
17
src/types/Field.go
Normal file
17
src/types/Field.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "git.urbach.dev/cli/q/src/token"
|
||||||
|
|
||||||
|
// Field is a memory region in a data structure.
|
||||||
|
type Field struct {
|
||||||
|
Name string
|
||||||
|
Type Type
|
||||||
|
Position token.Position
|
||||||
|
Index uint8
|
||||||
|
Offset uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the name of the struct.
|
||||||
|
func (f *Field) String() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
|
@ -45,6 +45,8 @@ func Parse[T ~[]token.Token](tokens T, source []byte) Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch tokens[0].String(source) {
|
switch tokens[0].String(source) {
|
||||||
|
case "string":
|
||||||
|
return String
|
||||||
case "int":
|
case "int":
|
||||||
return Int
|
return Int
|
||||||
case "int64":
|
case "int64":
|
||||||
|
|
50
src/types/Struct.go
Normal file
50
src/types/Struct.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Struct is a structure in memory whose regions are addressable with named fields.
|
||||||
|
type Struct struct {
|
||||||
|
Package string
|
||||||
|
UniqueName string
|
||||||
|
Fields []*Field
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStruct creates a new struct.
|
||||||
|
func NewStruct(pkg string, name string) *Struct {
|
||||||
|
return &Struct{
|
||||||
|
Package: pkg,
|
||||||
|
UniqueName: pkg + "." + name,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddField adds a new field to the end of the struct.
|
||||||
|
func (s *Struct) AddField(field *Field) {
|
||||||
|
s.Fields = append(s.Fields, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByName returns the field with the given name if it exists.
|
||||||
|
func (s *Struct) FieldByName(name string) *Field {
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
if field.Name == name {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the struct.
|
||||||
|
func (s *Struct) Name() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the total size in bytes.
|
||||||
|
func (s *Struct) Size() int {
|
||||||
|
sum := 0
|
||||||
|
|
||||||
|
for _, field := range s.Fields {
|
||||||
|
sum += field.Type.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ func TestName(t *testing.T) {
|
||||||
assert.Equal(t, types.AnyPointer.Name(), "*any")
|
assert.Equal(t, types.AnyPointer.Name(), "*any")
|
||||||
assert.Equal(t, (&types.Pointer{To: types.Int}).Name(), "*int64")
|
assert.Equal(t, (&types.Pointer{To: types.Int}).Name(), "*int64")
|
||||||
assert.Equal(t, (&types.Array{Of: types.Int}).Name(), "[]int64")
|
assert.Equal(t, (&types.Array{Of: types.Int}).Name(), "[]int64")
|
||||||
assert.Equal(t, types.String.Name(), "[]uint8")
|
assert.Equal(t, types.String.Name(), "string")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSize(t *testing.T) {
|
func TestSize(t *testing.T) {
|
||||||
|
@ -24,7 +24,7 @@ func TestSize(t *testing.T) {
|
||||||
assert.Equal(t, types.Int64.Size(), 8)
|
assert.Equal(t, types.Int64.Size(), 8)
|
||||||
assert.Equal(t, types.AnyArray.Size(), 8)
|
assert.Equal(t, types.AnyArray.Size(), 8)
|
||||||
assert.Equal(t, types.AnyPointer.Size(), 8)
|
assert.Equal(t, types.AnyPointer.Size(), 8)
|
||||||
assert.Equal(t, types.String.Size(), 8)
|
assert.Equal(t, types.String.Size(), 16)
|
||||||
assert.Equal(t, (&types.Pointer{To: types.Int}).Size(), 8)
|
assert.Equal(t, (&types.Pointer{To: types.Int}).Size(), 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,4 +25,5 @@ var CPU = cpu.CPU{
|
||||||
Call: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
Call: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
||||||
Syscall: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
Syscall: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
||||||
ExternCall: []cpu.Register{R1, R2, R8, R9},
|
ExternCall: []cpu.Register{R1, R2, R8, R9},
|
||||||
|
Return: []cpu.Register{R0, R7, R6},
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue