Implemented an assembler
All checks were successful
/ test (push) Successful in 28s

This commit is contained in:
Eduard Urbach 2025-06-24 12:55:26 +02:00
parent 2b703e9af2
commit 70c2da4a4d
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
40 changed files with 821 additions and 117 deletions

View file

@ -1,21 +1,28 @@
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"
"git.urbach.dev/cli/q/src/token"
)
// Compile turns a function into machine code.
func (f *Function) Compile() {
registerCount := 0
extra := 0
for _, input := range f.Input {
f.Identifiers[input.Name] = f.AppendRegister(cpu.Register(registerCount))
registerCount++
for i, input := range f.Input {
if input.Name == "_" {
continue
}
f.Identifiers[input.Name] = f.AppendRegister(i + extra)
if input.TypeTokens[0].Kind == token.ArrayStart {
f.Identifiers[input.Name+".length"] = f.AppendRegister(cpu.Register(registerCount))
registerCount++
extra++
f.Identifiers[input.Name+".length"] = f.AppendRegister(i + extra)
}
}
@ -28,4 +35,83 @@ func (f *Function) Compile() {
}
f.Err = f.CheckDeadCode()
if f.Err != nil {
return
}
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:
f.mv(instr.Args[1:], f.CPU.Call)
switch arg := instr.Args[0].(type) {
case *ssa.Function:
f.Assembler.Instructions = append(f.Assembler.Instructions, &asm.Call{Label: arg.UniqueName})
}
case *ssa.Syscall:
f.mv(instr.Args, 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{})
}
switch f.Assembler.Instructions[len(f.Assembler.Instructions)-1].(type) {
case *asm.Return:
default:
f.Assembler.Append(&asm.Return{})
}
}
func (f *Function) mv(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.Assembler.SetData("data0", 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: "data0",
})
}
}
}

View file

@ -15,6 +15,10 @@ func (f *Function) CompileInstruction(instr token.List) error {
expr := expression.Parse(instr)
if expr == nil {
return nil
}
if expr.Token.Kind == token.Define {
name := expr.Children[0].String(f.File.Bytes)
value, err := f.Evaluate(expr.Children[1])

View file

@ -2,6 +2,7 @@ package core
import (
"fmt"
"slices"
"git.urbach.dev/cli/q/src/errors"
"git.urbach.dev/cli/q/src/expression"
@ -18,7 +19,16 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
value, exists := f.Identifiers[name]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
function, exists := f.All.Functions[f.File.Package+"."+name]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
}
f.Dependencies.Add(function)
v := f.AppendFunction(function.UniqueName)
v.Source = expr.Token
return v, nil
}
return value, nil
@ -66,7 +76,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
args := make([]ssa.Value, len(children))
for i, child := range children {
for i, child := range slices.Backward(children) {
value, err := f.Evaluate(child)
if err != nil {
@ -93,8 +103,17 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
}
case token.Dot:
name := fmt.Sprintf("%s.%s", expr.Children[0].String(f.File.Bytes), expr.Children[1].String(f.File.Bytes))
v := f.AppendFunction(name)
left := expr.Children[0]
right := expr.Children[1]
label := fmt.Sprintf("%s.%s", left.String(f.File.Bytes), right.String(f.File.Bytes))
function, exists := f.All.Functions[label]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
}
f.Dependencies.Add(function)
v := f.AppendFunction(function.UniqueName)
v.Source = expr.Children[1].Token
return v, nil
}

View file

@ -3,7 +3,10 @@ package core
import (
"fmt"
"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/set"
"git.urbach.dev/cli/q/src/ssa"
"git.urbach.dev/cli/q/src/token"
)
@ -11,14 +14,18 @@ import (
// Function is the smallest unit of code.
type Function struct {
ssa.IR
Name string
UniqueName string
File *fs.File
Input []*Parameter
Output []*Parameter
Body token.List
Identifiers map[string]ssa.Value
Err error
Name string
UniqueName string
File *fs.File
Input []*Parameter
Output []*Parameter
Body token.List
Identifiers map[string]ssa.Value
All *Environment
Dependencies set.Ordered[*Function]
Assembler asm.Assembler
CPU *cpu.CPU
Err error
}
// NewFunction creates a new function.
@ -33,6 +40,24 @@ func NewFunction(name string, file *fs.File) *Function {
{Instructions: make([]ssa.Value, 0, 8)},
},
},
Assembler: asm.Assembler{
Instructions: make([]asm.Instruction, 0, 8),
},
}
}
// EachDependency recursively finds all the calls to other functions.
// It avoids calling the same function twice with the help of a hashmap.
func (f *Function) EachDependency(traversed map[*Function]bool, call func(*Function)) {
call(f)
traversed[f] = true
for dep := range f.Dependencies.All() {
if traversed[dep] {
continue
}
dep.EachDependency(traversed, call)
}
}
@ -41,6 +66,11 @@ func (f *Function) IsExtern() bool {
return f.Body == nil
}
// IsLeaf returns true if the function doesn't call other functions.
func (f *Function) IsLeaf() bool {
return f.Dependencies.Count() == 0
}
// String returns the unique name.
func (f *Function) String() string {
return f.UniqueName