This commit is contained in:
parent
2b703e9af2
commit
70c2da4a4d
40 changed files with 821 additions and 117 deletions
|
@ -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",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue