Improved code generation
This commit is contained in:
parent
1204591cdc
commit
4386392844
@ -68,16 +68,6 @@ func (a *Assembler) Call(name string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump jumps to a position that is identified by a label.
|
|
||||||
func (a *Assembler) Jump(name string) {
|
|
||||||
a.Instructions = append(a.Instructions, Instruction{
|
|
||||||
Mnemonic: JUMP,
|
|
||||||
Data: &Label{
|
|
||||||
Name: name,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return returns back to the caller.
|
// Return returns back to the caller.
|
||||||
func (a *Assembler) Return() {
|
func (a *Assembler) Return() {
|
||||||
if len(a.Instructions) > 0 && a.Instructions[len(a.Instructions)-1].Mnemonic == RETURN {
|
if len(a.Instructions) > 0 && a.Instructions[len(a.Instructions)-1].Mnemonic == RETURN {
|
||||||
|
@ -4,10 +4,6 @@ import "git.akyoto.dev/cli/q/src/build/cpu"
|
|||||||
|
|
||||||
// unnecessary returns true if the register/register operation can be skipped.
|
// unnecessary returns true if the register/register operation can be skipped.
|
||||||
func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool {
|
func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu.Register) bool {
|
||||||
if mnemonic == MOVE && left == right {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.Instructions) == 0 {
|
if len(a.Instructions) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ func (f *Function) Compare(comparison *expression.Expression) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.cpu.Use(tmp)
|
|
||||||
defer f.cpu.Free(tmp)
|
defer f.cpu.Free(tmp)
|
||||||
return f.Execute(comparison.Token, tmp, right)
|
return f.Execute(comparison.Token, tmp, right)
|
||||||
}
|
}
|
||||||
|
@ -37,22 +37,18 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
|
|
||||||
f.SaveRegister(f.cpu.Output[0])
|
f.SaveRegister(f.cpu.Output[0])
|
||||||
|
|
||||||
for _, register := range registers {
|
|
||||||
f.SaveRegister(register)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
for _, register := range f.cpu.General {
|
for _, register := range f.cpu.General {
|
||||||
if f.cpu.IsUsed(register) {
|
if f.cpu.IsUsed(register) {
|
||||||
f.assembler.Register(asm.PUSH, register)
|
f.Register(asm.PUSH, register)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
if isSyscall {
|
if isSyscall {
|
||||||
f.assembler.Syscall()
|
f.Syscall()
|
||||||
} else {
|
} else {
|
||||||
f.assembler.Call(funcName)
|
f.Call(funcName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop
|
// Pop
|
||||||
@ -60,7 +56,7 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
register := f.cpu.General[i]
|
register := f.cpu.General[i]
|
||||||
|
|
||||||
if f.cpu.IsUsed(register) {
|
if f.cpu.IsUsed(register) {
|
||||||
f.assembler.Register(asm.POP, register)
|
f.Register(asm.POP, register)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab
|
|||||||
f.JumpIfTrue(left.Token.Text(), successLabel)
|
f.JumpIfTrue(left.Token.Text(), successLabel)
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
f.assembler.Label(asm.LABEL, leftFailLabel)
|
f.AddLabel(leftFailLabel)
|
||||||
right := condition.Children[1]
|
right := condition.Children[1]
|
||||||
err = f.CompileCondition(right, successLabel, failLabel)
|
err = f.CompileCondition(right, successLabel, failLabel)
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab
|
|||||||
f.JumpIfFalse(left.Token.Text(), failLabel)
|
f.JumpIfFalse(left.Token.Text(), failLabel)
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
f.assembler.Label(asm.LABEL, leftSuccessLabel)
|
f.AddLabel(leftSuccessLabel)
|
||||||
right := condition.Children[1]
|
right := condition.Children[1]
|
||||||
err = f.CompileCondition(right, successLabel, failLabel)
|
err = f.CompileCondition(right, successLabel, failLabel)
|
||||||
|
|
||||||
@ -79,17 +79,17 @@ func (f *Function) CompileCondition(condition *expression.Expression, successLab
|
|||||||
func (f *Function) JumpIfFalse(operator string, label string) {
|
func (f *Function) JumpIfFalse(operator string, label string) {
|
||||||
switch operator {
|
switch operator {
|
||||||
case "==":
|
case "==":
|
||||||
f.assembler.Label(asm.JNE, label)
|
f.Jump(asm.JNE, label)
|
||||||
case "!=":
|
case "!=":
|
||||||
f.assembler.Label(asm.JE, label)
|
f.Jump(asm.JE, label)
|
||||||
case ">":
|
case ">":
|
||||||
f.assembler.Label(asm.JLE, label)
|
f.Jump(asm.JLE, label)
|
||||||
case "<":
|
case "<":
|
||||||
f.assembler.Label(asm.JGE, label)
|
f.Jump(asm.JGE, label)
|
||||||
case ">=":
|
case ">=":
|
||||||
f.assembler.Label(asm.JL, label)
|
f.Jump(asm.JL, label)
|
||||||
case "<=":
|
case "<=":
|
||||||
f.assembler.Label(asm.JG, label)
|
f.Jump(asm.JG, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,16 +97,16 @@ func (f *Function) JumpIfFalse(operator string, label string) {
|
|||||||
func (f *Function) JumpIfTrue(operator string, label string) {
|
func (f *Function) JumpIfTrue(operator string, label string) {
|
||||||
switch operator {
|
switch operator {
|
||||||
case "==":
|
case "==":
|
||||||
f.assembler.Label(asm.JE, label)
|
f.Jump(asm.JE, label)
|
||||||
case "!=":
|
case "!=":
|
||||||
f.assembler.Label(asm.JNE, label)
|
f.Jump(asm.JNE, label)
|
||||||
case ">":
|
case ">":
|
||||||
f.assembler.Label(asm.JG, label)
|
f.Jump(asm.JG, label)
|
||||||
case "<":
|
case "<":
|
||||||
f.assembler.Label(asm.JL, label)
|
f.Jump(asm.JL, label)
|
||||||
case ">=":
|
case ">=":
|
||||||
f.assembler.Label(asm.JGE, label)
|
f.Jump(asm.JGE, label)
|
||||||
case "<=":
|
case "<=":
|
||||||
f.assembler.Label(asm.JLE, label)
|
f.Jump(asm.JLE, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
|
|
||||||
func (f *Function) AddVariable(variable *Variable) {
|
func (f *Function) AddVariable(variable *Variable) {
|
||||||
if config.Comments {
|
if config.Comments {
|
||||||
f.assembler.Comment(fmt.Sprintf("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive))
|
f.Comment(fmt.Sprintf("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive))
|
||||||
}
|
}
|
||||||
|
|
||||||
f.variables[variable.Name] = variable
|
f.variables[variable.Name] = variable
|
||||||
@ -60,7 +60,7 @@ func (f *Function) useVariable(variable *Variable) {
|
|||||||
|
|
||||||
if variable.Alive == 0 {
|
if variable.Alive == 0 {
|
||||||
if config.Comments {
|
if config.Comments {
|
||||||
f.assembler.Comment(fmt.Sprintf("%s died (%s)", variable.Name, variable.Register))
|
f.Comment(fmt.Sprintf("%s died (%s)", variable.Name, variable.Register))
|
||||||
}
|
}
|
||||||
|
|
||||||
f.cpu.Free(variable.Register)
|
f.cpu.Free(variable.Register)
|
||||||
|
@ -3,7 +3,6 @@ package core
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/ast"
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,8 +16,8 @@ func (f *Function) CompileIf(branch *ast.If) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.assembler.Label(asm.LABEL, success)
|
f.AddLabel(success)
|
||||||
defer f.assembler.Label(asm.LABEL, fail)
|
defer f.AddLabel(fail)
|
||||||
f.count.branch++
|
f.count.branch++
|
||||||
return f.CompileAST(branch.Body)
|
return f.CompileAST(branch.Body)
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
// CompileLoop compiles a loop instruction.
|
// CompileLoop compiles a loop instruction.
|
||||||
func (f *Function) CompileLoop(loop *ast.Loop) error {
|
func (f *Function) CompileLoop(loop *ast.Loop) error {
|
||||||
label := fmt.Sprintf("%s_loop_%d", f.Name, f.count.loop)
|
label := fmt.Sprintf("%s_loop_%d", f.Name, f.count.loop)
|
||||||
f.assembler.Label(asm.LABEL, label)
|
f.AddLabel(label)
|
||||||
defer f.assembler.Jump(label)
|
defer f.Jump(asm.JUMP, label)
|
||||||
f.count.loop++
|
f.count.loop++
|
||||||
return f.CompileAST(loop.Body)
|
return f.CompileAST(loop.Body)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
// CompileReturn compiles a return instruction.
|
// CompileReturn compiles a return instruction.
|
||||||
func (f *Function) CompileReturn(node *ast.Return) error {
|
func (f *Function) CompileReturn(node *ast.Return) error {
|
||||||
defer f.assembler.Return()
|
defer f.Return()
|
||||||
|
|
||||||
if node.Value == nil {
|
if node.Value == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -24,7 +24,6 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value *
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmp := f.cpu.MustFindFree(f.cpu.General)
|
tmp := f.cpu.MustFindFree(f.cpu.General)
|
||||||
f.cpu.Use(tmp)
|
|
||||||
defer f.cpu.Free(tmp)
|
defer f.cpu.Free(tmp)
|
||||||
|
|
||||||
err := f.ExpressionToRegister(value, tmp)
|
err := f.ExpressionToRegister(value, tmp)
|
||||||
|
@ -11,22 +11,22 @@ import (
|
|||||||
func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Register, number int) error {
|
func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Register, number int) error {
|
||||||
switch operation.Text() {
|
switch operation.Text() {
|
||||||
case "+", "+=":
|
case "+", "+=":
|
||||||
f.assembler.RegisterNumber(asm.ADD, register, number)
|
f.RegisterNumber(asm.ADD, register, number)
|
||||||
|
|
||||||
case "-", "-=":
|
case "-", "-=":
|
||||||
f.assembler.RegisterNumber(asm.SUB, register, number)
|
f.RegisterNumber(asm.SUB, register, number)
|
||||||
|
|
||||||
case "*", "*=":
|
case "*", "*=":
|
||||||
f.assembler.RegisterNumber(asm.MUL, register, number)
|
f.RegisterNumber(asm.MUL, register, number)
|
||||||
|
|
||||||
case "/", "/=":
|
case "/", "/=":
|
||||||
f.assembler.RegisterNumber(asm.DIV, register, number)
|
f.RegisterNumber(asm.DIV, register, number)
|
||||||
|
|
||||||
case "==", "!=", "<", "<=", ">", ">=":
|
case "==", "!=", "<", "<=", ">", ">=":
|
||||||
f.assembler.RegisterNumber(asm.COMPARE, register, number)
|
f.RegisterNumber(asm.COMPARE, register, number)
|
||||||
|
|
||||||
case "=":
|
case "=":
|
||||||
f.assembler.RegisterNumber(asm.MOVE, register, number)
|
f.RegisterNumber(asm.MOVE, register, number)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position)
|
return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position)
|
||||||
|
@ -11,22 +11,22 @@ import (
|
|||||||
func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error {
|
func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error {
|
||||||
switch operation.Text() {
|
switch operation.Text() {
|
||||||
case "+", "+=":
|
case "+", "+=":
|
||||||
f.assembler.RegisterRegister(asm.ADD, destination, source)
|
f.RegisterRegister(asm.ADD, destination, source)
|
||||||
|
|
||||||
case "-", "-=":
|
case "-", "-=":
|
||||||
f.assembler.RegisterRegister(asm.SUB, destination, source)
|
f.RegisterRegister(asm.SUB, destination, source)
|
||||||
|
|
||||||
case "*", "*=":
|
case "*", "*=":
|
||||||
f.assembler.RegisterRegister(asm.MUL, destination, source)
|
f.RegisterRegister(asm.MUL, destination, source)
|
||||||
|
|
||||||
case "/", "/=":
|
case "/", "/=":
|
||||||
f.assembler.RegisterRegister(asm.DIV, destination, source)
|
f.RegisterRegister(asm.DIV, destination, source)
|
||||||
|
|
||||||
case "==", "!=", "<", "<=", ">", ">=":
|
case "==", "!=", "<", "<=", ">", ">=":
|
||||||
f.assembler.RegisterRegister(asm.COMPARE, destination, source)
|
f.RegisterRegister(asm.COMPARE, destination, source)
|
||||||
|
|
||||||
case "=":
|
case "=":
|
||||||
f.assembler.RegisterRegister(asm.MOVE, destination, source)
|
f.RegisterRegister(asm.MOVE, destination, source)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position)
|
return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position)
|
||||||
|
@ -18,7 +18,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
err := f.CompileCall(node)
|
err := f.CompileCall(node)
|
||||||
|
|
||||||
if register != f.cpu.Output[0] {
|
if register != f.cpu.Output[0] {
|
||||||
f.assembler.RegisterRegister(asm.MOVE, register, f.cpu.Output[0])
|
f.RegisterRegister(asm.MOVE, register, f.cpu.Output[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -34,8 +34,6 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
|
|
||||||
if f.UsesRegister(right, register) {
|
if f.UsesRegister(right, register) {
|
||||||
register = f.cpu.MustFindFree(f.cpu.General)
|
register = f.cpu.MustFindFree(f.cpu.General)
|
||||||
} else {
|
|
||||||
f.SaveRegister(register)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.cpu.Reserve(register)
|
f.cpu.Reserve(register)
|
||||||
@ -45,11 +43,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.cpu.Use(register)
|
|
||||||
err = f.Execute(node.Token, register, right)
|
err = f.Execute(node.Token, register, right)
|
||||||
|
|
||||||
if register != final {
|
if register != final {
|
||||||
f.assembler.RegisterRegister(asm.MOVE, final, register)
|
f.RegisterRegister(asm.MOVE, final, register)
|
||||||
f.cpu.Free(register)
|
f.cpu.Free(register)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ func NewFunction(name string, file *fs.File, body token.List) *Function {
|
|||||||
// Compile turns a function into machine code.
|
// Compile turns a function into machine code.
|
||||||
func (f *Function) Compile() {
|
func (f *Function) Compile() {
|
||||||
defer close(f.finished)
|
defer close(f.finished)
|
||||||
f.assembler.Label(asm.LABEL, f.Name)
|
f.AddLabel(f.Name)
|
||||||
f.err = f.CompileTokens(f.Body)
|
f.err = f.CompileTokens(f.Body)
|
||||||
f.assembler.Return()
|
f.Return()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileTokens compiles a token list.
|
// CompileTokens compiles a token list.
|
||||||
|
98
src/build/core/Instructions.go
Normal file
98
src/build/core/Instructions.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/config"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Function) AddLabel(label string) {
|
||||||
|
f.assembler.Label(asm.LABEL, label)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Call(label string) {
|
||||||
|
f.assembler.Call(label)
|
||||||
|
f.cpu.Use(f.cpu.Output[0])
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Comment(comment string) {
|
||||||
|
f.assembler.Comment(comment)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Jump(mnemonic asm.Mnemonic, label string) {
|
||||||
|
f.assembler.Label(mnemonic, label)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Register(mnemonic asm.Mnemonic, a cpu.Register) {
|
||||||
|
f.assembler.Register(mnemonic, a)
|
||||||
|
|
||||||
|
if mnemonic == asm.POP {
|
||||||
|
f.cpu.Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) RegisterNumber(mnemonic asm.Mnemonic, a cpu.Register, b int) {
|
||||||
|
if f.cpu.IsUsed(a) && isDestructive(mnemonic) {
|
||||||
|
f.SaveRegister(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.assembler.RegisterNumber(mnemonic, a, b)
|
||||||
|
|
||||||
|
if mnemonic == asm.MOVE {
|
||||||
|
f.cpu.Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) RegisterRegister(mnemonic asm.Mnemonic, a cpu.Register, b cpu.Register) {
|
||||||
|
if mnemonic == asm.MOVE && a == b {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.cpu.IsUsed(a) && isDestructive(mnemonic) {
|
||||||
|
f.SaveRegister(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.assembler.RegisterRegister(mnemonic, a, b)
|
||||||
|
|
||||||
|
if mnemonic == asm.MOVE {
|
||||||
|
f.cpu.Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Return() {
|
||||||
|
f.assembler.Return()
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) Syscall() {
|
||||||
|
f.assembler.Syscall()
|
||||||
|
f.cpu.Use(f.cpu.Output[0])
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) postInstruction() {
|
||||||
|
if !config.Assembler {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.registerHistory = append(f.registerHistory, f.cpu.Used)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDestructive(mnemonic asm.Mnemonic) bool {
|
||||||
|
switch mnemonic {
|
||||||
|
case asm.MOVE, asm.ADD, asm.SUB, asm.MUL, asm.DIV:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,12 @@ func (f *Function) SaveRegister(register cpu.Register) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, general := range f.cpu.General {
|
||||||
|
if register == general {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var variable *Variable
|
var variable *Variable
|
||||||
|
|
||||||
for _, v := range f.variables {
|
for _, v := range f.variables {
|
||||||
@ -29,13 +35,12 @@ func (f *Function) SaveRegister(register cpu.Register) {
|
|||||||
|
|
||||||
newRegister := f.cpu.MustFindFree(f.cpu.General)
|
newRegister := f.cpu.MustFindFree(f.cpu.General)
|
||||||
f.cpu.Reserve(newRegister)
|
f.cpu.Reserve(newRegister)
|
||||||
f.cpu.Use(newRegister)
|
|
||||||
|
|
||||||
if config.Comments {
|
if config.Comments {
|
||||||
f.assembler.Comment(fmt.Sprintf("save %s to %s", register, newRegister))
|
f.Comment(fmt.Sprintf("save %s to %s", register, newRegister))
|
||||||
}
|
}
|
||||||
|
|
||||||
f.assembler.RegisterRegister(asm.MOVE, newRegister, register)
|
f.RegisterRegister(asm.MOVE, newRegister, register)
|
||||||
f.cpu.Free(register)
|
f.cpu.Free(register)
|
||||||
variable.Register = newRegister
|
variable.Register = newRegister
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
|
|||||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.assembler.RegisterRegister(asm.MOVE, register, variable.Register)
|
|
||||||
f.useVariable(variable)
|
f.useVariable(variable)
|
||||||
|
f.RegisterRegister(asm.MOVE, register, variable.Register)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case token.Number:
|
case token.Number:
|
||||||
@ -33,7 +33,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.assembler.RegisterNumber(asm.MOVE, register, n)
|
f.RegisterNumber(asm.MOVE, register, n)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case token.String:
|
case token.String:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
@ -10,13 +11,14 @@ import (
|
|||||||
|
|
||||||
// state is the data structure we embed in each function to preserve compilation state.
|
// state is the data structure we embed in each function to preserve compilation state.
|
||||||
type state struct {
|
type state struct {
|
||||||
err error
|
err error
|
||||||
variables map[string]*Variable
|
variables map[string]*Variable
|
||||||
functions map[string]*Function
|
functions map[string]*Function
|
||||||
finished chan struct{}
|
registerHistory []uint64
|
||||||
assembler asm.Assembler
|
finished chan struct{}
|
||||||
cpu cpu.CPU
|
assembler asm.Assembler
|
||||||
count counter
|
cpu cpu.CPU
|
||||||
|
count counter
|
||||||
}
|
}
|
||||||
|
|
||||||
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
||||||
@ -28,30 +30,42 @@ type counter struct {
|
|||||||
|
|
||||||
// PrintInstructions shows the assembly instructions.
|
// PrintInstructions shows the assembly instructions.
|
||||||
func (s *state) PrintInstructions() {
|
func (s *state) PrintInstructions() {
|
||||||
ansi.Dim.Println("╭────────────────────────────────────────────────────╮")
|
ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮")
|
||||||
|
|
||||||
for _, x := range s.assembler.Instructions {
|
for i, x := range s.assembler.Instructions {
|
||||||
ansi.Dim.Print("│ ")
|
ansi.Dim.Print("│ ")
|
||||||
|
|
||||||
switch x.Mnemonic {
|
switch x.Mnemonic {
|
||||||
case asm.LABEL:
|
case asm.LABEL:
|
||||||
ansi.Yellow.Printf("%-50s", x.Data.String()+":")
|
ansi.Yellow.Printf("%-44s", x.Data.String()+":")
|
||||||
|
|
||||||
case asm.COMMENT:
|
case asm.COMMENT:
|
||||||
ansi.Dim.Printf("%-50s", x.Data.String())
|
ansi.Dim.Printf("%-44s", x.Data.String())
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ansi.Green.Printf("%-12s", x.Mnemonic.String())
|
ansi.Green.Printf("%-12s", x.Mnemonic.String())
|
||||||
|
|
||||||
if x.Data != nil {
|
if x.Data != nil {
|
||||||
fmt.Printf("%-38s", x.Data.String())
|
fmt.Printf("%-32s", x.Data.String())
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%-38s", "")
|
fmt.Printf("%-32s", "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registers := bytes.Buffer{}
|
||||||
|
used := s.registerHistory[i]
|
||||||
|
|
||||||
|
for _, reg := range s.cpu.All {
|
||||||
|
if used&(1<<reg) != 0 {
|
||||||
|
registers.WriteString("⬤ ")
|
||||||
|
} else {
|
||||||
|
registers.WriteString("◯ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ansi.Dim.Print(registers.String())
|
||||||
ansi.Dim.Print(" │\n")
|
ansi.Dim.Print(" │\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
ansi.Dim.Println("╰────────────────────────────────────────────────────╯")
|
ansi.Dim.Println("╰──────────────────────────────────────────────────────────────────────────────╯")
|
||||||
}
|
}
|
||||||
|
@ -7,34 +7,34 @@ type CPU struct {
|
|||||||
Syscall []Register
|
Syscall []Register
|
||||||
Input []Register
|
Input []Register
|
||||||
Output []Register
|
Output []Register
|
||||||
reserved uint64
|
Reserved uint64
|
||||||
used uint64
|
Used uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free will reset the reserved and used status which means the register can be allocated again.
|
// Free will reset the reserved and used status which means the register can be allocated again.
|
||||||
func (c *CPU) Free(reg Register) {
|
func (c *CPU) Free(reg Register) {
|
||||||
c.used &= ^(1 << reg)
|
c.Used &= ^(1 << reg)
|
||||||
c.reserved &= ^(1 << reg)
|
c.Reserved &= ^(1 << reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsReserved returns true if the register was marked for future use.
|
// IsReserved returns true if the register was marked for future use.
|
||||||
func (c *CPU) IsReserved(reg Register) bool {
|
func (c *CPU) IsReserved(reg Register) bool {
|
||||||
return c.reserved&(1<<reg) != 0
|
return c.Reserved&(1<<reg) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUsed returns true if the register is currently in use and holds a value.
|
// IsUsed returns true if the register is currently in use and holds a value.
|
||||||
func (c *CPU) IsUsed(reg Register) bool {
|
func (c *CPU) IsUsed(reg Register) bool {
|
||||||
return c.used&(1<<reg) != 0
|
return c.Used&(1<<reg) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve reserves a register for future use.
|
// Reserve reserves a register for future use.
|
||||||
func (c *CPU) Reserve(reg Register) {
|
func (c *CPU) Reserve(reg Register) {
|
||||||
c.reserved |= (1 << reg)
|
c.Reserved |= (1 << reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use marks a register to be currently in use which means it must be preserved across function calls.
|
// Use marks a register to be currently in use which means it must be preserved across function calls.
|
||||||
func (c *CPU) Use(reg Register) {
|
func (c *CPU) Use(reg Register) {
|
||||||
c.used |= (1 << reg)
|
c.Used |= (1 << reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindFree tries to find a free register in the given slice of registers.
|
// FindFree tries to find a free register in the given slice of registers.
|
||||||
|
12
tests/programs/param-multi.q
Normal file
12
tests/programs/param-multi.q
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
main() {
|
||||||
|
syscall(60, f(1, 2, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
f(x, y, z) {
|
||||||
|
w := g(4, 5, 6)
|
||||||
|
return x + y + z + w
|
||||||
|
}
|
||||||
|
|
||||||
|
g(x, y, z) {
|
||||||
|
return x + y + z
|
||||||
|
}
|
@ -21,14 +21,15 @@ var programs = []struct {
|
|||||||
{"square-sum", "", 25},
|
{"square-sum", "", 25},
|
||||||
{"chained-calls", "", 9},
|
{"chained-calls", "", 9},
|
||||||
{"nested-calls", "", 4},
|
{"nested-calls", "", 4},
|
||||||
{"overwrite", "", 3},
|
{"param", "", 3},
|
||||||
|
{"param-multi", "", 21},
|
||||||
{"reuse", "", 3},
|
{"reuse", "", 3},
|
||||||
{"return", "", 6},
|
{"return", "", 6},
|
||||||
{"reassign", "", 2},
|
{"reassign", "", 2},
|
||||||
{"branch", "", 0},
|
{"branch", "", 0},
|
||||||
{"branch-and", "", 0},
|
{"branch-and", "", 0},
|
||||||
{"branch-or", "", 0},
|
{"branch-or", "", 0},
|
||||||
{"branch-combined", "", 0},
|
{"branch-both", "", 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrograms(t *testing.T) {
|
func TestPrograms(t *testing.T) {
|
||||||
@ -42,7 +43,7 @@ func TestPrograms(t *testing.T) {
|
|||||||
func BenchmarkPrograms(b *testing.B) {
|
func BenchmarkPrograms(b *testing.B) {
|
||||||
for _, test := range programs {
|
for _, test := range programs {
|
||||||
b.Run(test.Name, func(b *testing.B) {
|
b.Run(test.Name, func(b *testing.B) {
|
||||||
compiler := build.New(filepath.Join("programs", test.Name))
|
compiler := build.New(filepath.Join("programs", test.Name+".q"))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := compiler.Run()
|
_, err := compiler.Run()
|
||||||
|
Loading…
Reference in New Issue
Block a user