Improved separation of concerns
This commit is contained in:
parent
d6db1b1096
commit
69245caf62
@ -16,6 +16,5 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Assembler = false
|
Assembler = false
|
||||||
Comments = false
|
|
||||||
Dry = false
|
Dry = false
|
||||||
)
|
)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/build/config"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/scope"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddVariable adds a new variable to the current scope.
|
|
||||||
func (f *Function) AddVariable(variable *scope.Variable) {
|
|
||||||
if config.Comments {
|
|
||||||
f.Comment("%s = %s (%d uses)", variable.Name, variable.Register, variable.Alive)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.CurrentScope().AddVariable(variable)
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Comment adds a comment to the assembler.
|
|
||||||
func (f *Function) Comment(format string, args ...any) {
|
|
||||||
f.Assembler.Comment(fmt.Sprintf(format, args...))
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
@ -31,10 +31,10 @@ func (f *Function) Compare(comparison *expression.Expression) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.Execute(comparison.Token, f.cpu.Output[0], right)
|
return f.Execute(comparison.Token, f.CPU.Output[0], right)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp := f.CurrentScope().MustFindFree(f.cpu.General)
|
tmp := f.CurrentScope().MustFindFree(f.CPU.General)
|
||||||
err := f.ExpressionToRegister(left, tmp)
|
err := f.ExpressionToRegister(left, tmp)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,7 +2,6 @@ package core
|
|||||||
|
|
||||||
// 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)
|
|
||||||
f.AddLabel(f.Name)
|
f.AddLabel(f.Name)
|
||||||
f.Err = f.CompileTokens(f.Body)
|
f.Err = f.CompileTokens(f.Body)
|
||||||
f.Return()
|
f.Return()
|
||||||
|
@ -31,10 +31,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameters := root.Children[1:]
|
parameters := root.Children[1:]
|
||||||
registers := f.cpu.Input[:len(parameters)]
|
registers := f.CPU.Input[:len(parameters)]
|
||||||
|
|
||||||
if isSyscall {
|
if isSyscall {
|
||||||
registers = f.cpu.Syscall[:len(parameters)]
|
registers = f.CPU.Syscall[:len(parameters)]
|
||||||
}
|
}
|
||||||
|
|
||||||
err := f.ExpressionsToRegisters(parameters, registers)
|
err := f.ExpressionsToRegisters(parameters, registers)
|
||||||
@ -43,10 +43,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.SaveRegister(f.cpu.Output[0])
|
f.SaveRegister(f.CPU.Output[0])
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
for _, register := range f.cpu.General {
|
for _, register := range f.CPU.General {
|
||||||
if f.CurrentScope().IsUsed(register) {
|
if f.CurrentScope().IsUsed(register) {
|
||||||
f.Register(asm.PUSH, register)
|
f.Register(asm.PUSH, register)
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, register := range registers {
|
for _, register := range registers {
|
||||||
if register == f.cpu.Output[0] && root.Parent != nil {
|
if register == f.CPU.Output[0] && root.Parent != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +68,8 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pop
|
// Pop
|
||||||
for i := len(f.cpu.General) - 1; i >= 0; i-- {
|
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||||
register := f.cpu.General[i]
|
register := f.CPU.General[i]
|
||||||
|
|
||||||
if f.CurrentScope().IsUsed(register) {
|
if f.CurrentScope().IsUsed(register) {
|
||||||
f.Register(asm.POP, register)
|
f.Register(asm.POP, register)
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
func (f *Function) CompileDefinition(node *ast.Define) error {
|
func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||||
name := node.Name.Text(f.File.Bytes)
|
name := node.Name.Text(f.File.Bytes)
|
||||||
|
|
||||||
if f.identifierExists(name) {
|
if f.IdentifierExists(name) {
|
||||||
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, node.Name.Position)
|
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, node.Name.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
return errors.New(&errors.UnusedVariable{Name: name}, f.File, node.Name.Position)
|
return errors.New(&errors.UnusedVariable{Name: name}, f.File, node.Name.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
register := f.CurrentScope().MustFindFree(f.cpu.General)
|
register := f.CurrentScope().MustFindFree(f.CPU.General)
|
||||||
f.CurrentScope().Reserve(register)
|
f.CurrentScope().Reserve(register)
|
||||||
err := f.ExpressionToRegister(node.Value, register)
|
err := f.ExpressionToRegister(node.Value, register)
|
||||||
|
|
||||||
|
@ -12,5 +12,5 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.ExpressionToRegister(node.Value, f.cpu.Output[0])
|
return f.ExpressionToRegister(node.Value, f.CPU.Output[0])
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value *
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.ExecuteRegisterRegister(operation, register, f.cpu.Output[0])
|
return f.ExecuteRegisterRegister(operation, register, f.CPU.Output[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp := f.CurrentScope().MustFindFree(f.cpu.General)
|
tmp := f.CurrentScope().MustFindFree(f.CPU.General)
|
||||||
defer f.CurrentScope().Free(tmp)
|
defer f.CurrentScope().Free(tmp)
|
||||||
|
|
||||||
err := f.ExpressionToRegister(value, tmp)
|
err := f.ExpressionToRegister(value, tmp)
|
||||||
|
@ -3,7 +3,6 @@ package core
|
|||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
"git.akyoto.dev/cli/q/src/build/ast"
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
"git.akyoto.dev/cli/q/src/build/config"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
"git.akyoto.dev/cli/q/src/build/errors"
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
"git.akyoto.dev/cli/q/src/build/expression"
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
@ -18,8 +17,8 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
if ast.IsFunctionCall(node) {
|
if ast.IsFunctionCall(node) {
|
||||||
err := f.CompileCall(node)
|
err := f.CompileCall(node)
|
||||||
|
|
||||||
if register != f.cpu.Output[0] {
|
if register != f.CPU.Output[0] {
|
||||||
f.RegisterRegister(asm.MOVE, register, f.cpu.Output[0])
|
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -34,11 +33,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
final := register
|
final := register
|
||||||
|
|
||||||
if f.UsesRegister(right, register) {
|
if f.UsesRegister(right, register) {
|
||||||
register = f.CurrentScope().MustFindFree(f.cpu.General)
|
register = f.CurrentScope().MustFindFree(f.CPU.General)
|
||||||
|
|
||||||
if config.Comments {
|
|
||||||
f.Comment("temporary register %s", register)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.CurrentScope().Reserve(register)
|
f.CurrentScope().Reserve(register)
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/fs"
|
"git.akyoto.dev/cli/q/src/build/fs"
|
||||||
"git.akyoto.dev/cli/q/src/build/scope"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/token"
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/z"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Function represents the smallest unit of code.
|
// Function represents the smallest unit of code.
|
||||||
type Function struct {
|
type Function struct {
|
||||||
scope.Stack
|
z.Compiler
|
||||||
Name string
|
Name string
|
||||||
File *fs.File
|
File *fs.File
|
||||||
Body []token.Token
|
Body []token.Token
|
||||||
Assembler asm.Assembler
|
Functions map[string]*Function
|
||||||
Functions map[string]*Function
|
Err error
|
||||||
Err error
|
count counter
|
||||||
registerHistory []uint64
|
|
||||||
finished chan struct{}
|
|
||||||
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.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
// identifierExists returns true if the identifier has been defined.
|
// IdentifierExists returns true if the identifier has been defined.
|
||||||
func (f *Function) identifierExists(name string) bool {
|
func (f *Function) IdentifierExists(name string) bool {
|
||||||
variable := f.VariableByName(name)
|
variable := f.VariableByName(name)
|
||||||
|
|
||||||
if variable != nil {
|
if variable != nil {
|
@ -1,103 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
|
||||||
"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.CurrentScope().Use(f.cpu.Output[0])
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) Jump(mnemonic asm.Mnemonic, label string) {
|
|
||||||
f.Assembler.Label(mnemonic, label)
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) MemoryNumber(mnemonic asm.Mnemonic, a asm.Memory, b int) {
|
|
||||||
f.Assembler.MemoryNumber(mnemonic, a, b)
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) Register(mnemonic asm.Mnemonic, a cpu.Register) {
|
|
||||||
f.Assembler.Register(mnemonic, a)
|
|
||||||
|
|
||||||
if mnemonic == asm.POP {
|
|
||||||
f.CurrentScope().Use(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) RegisterNumber(mnemonic asm.Mnemonic, a cpu.Register, b int) {
|
|
||||||
if f.CurrentScope().IsUsed(a) && isDestructive(mnemonic) {
|
|
||||||
f.SaveRegister(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Assembler.RegisterNumber(mnemonic, a, b)
|
|
||||||
|
|
||||||
if mnemonic == asm.MOVE {
|
|
||||||
f.CurrentScope().Use(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) RegisterLabel(mnemonic asm.Mnemonic, register cpu.Register, label string) {
|
|
||||||
if f.CurrentScope().IsUsed(register) && isDestructive(mnemonic) {
|
|
||||||
f.SaveRegister(register)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Assembler.RegisterLabel(mnemonic, register, label)
|
|
||||||
|
|
||||||
if mnemonic == asm.MOVE {
|
|
||||||
f.CurrentScope().Use(register)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) RegisterRegister(mnemonic asm.Mnemonic, a cpu.Register, b cpu.Register) {
|
|
||||||
if mnemonic == asm.MOVE && a == b {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.CurrentScope().IsUsed(a) && isDestructive(mnemonic) {
|
|
||||||
f.SaveRegister(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Assembler.RegisterRegister(mnemonic, a, b)
|
|
||||||
|
|
||||||
if mnemonic == asm.MOVE {
|
|
||||||
f.CurrentScope().Use(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) Return() {
|
|
||||||
f.Assembler.Return()
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) Syscall() {
|
|
||||||
f.Assembler.Syscall()
|
|
||||||
f.CurrentScope().Use(f.cpu.Output[0])
|
|
||||||
f.postInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDestructive(mnemonic asm.Mnemonic) bool {
|
|
||||||
switch mnemonic {
|
|
||||||
case asm.MOVE, asm.ADD, asm.SUB, asm.MUL, asm.DIV:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||||||
"git.akyoto.dev/cli/q/src/build/fs"
|
"git.akyoto.dev/cli/q/src/build/fs"
|
||||||
"git.akyoto.dev/cli/q/src/build/scope"
|
"git.akyoto.dev/cli/q/src/build/scope"
|
||||||
"git.akyoto.dev/cli/q/src/build/token"
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/z"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewFunction creates a new function.
|
// NewFunction creates a new function.
|
||||||
@ -15,19 +16,20 @@ func NewFunction(name string, file *fs.File, body []token.Token) *Function {
|
|||||||
Name: name,
|
Name: name,
|
||||||
File: file,
|
File: file,
|
||||||
Body: body,
|
Body: body,
|
||||||
Assembler: asm.Assembler{
|
Compiler: z.Compiler{
|
||||||
Instructions: make([]asm.Instruction, 0, 8),
|
Assembler: asm.Assembler{
|
||||||
|
Instructions: make([]asm.Instruction, 0, 8),
|
||||||
|
},
|
||||||
|
Stack: scope.Stack{
|
||||||
|
Scopes: []*scope.Scope{{}},
|
||||||
|
},
|
||||||
|
CPU: cpu.CPU{
|
||||||
|
All: x64.AllRegisters,
|
||||||
|
Input: x64.CallRegisters,
|
||||||
|
General: x64.GeneralRegisters,
|
||||||
|
Syscall: x64.SyscallRegisters,
|
||||||
|
Output: x64.ReturnValueRegisters,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Stack: scope.Stack{
|
|
||||||
Scopes: []*scope.Scope{{}},
|
|
||||||
},
|
|
||||||
cpu: cpu.CPU{
|
|
||||||
All: x64.AllRegisters,
|
|
||||||
Input: x64.CallRegisters,
|
|
||||||
General: x64.GeneralRegisters,
|
|
||||||
Syscall: x64.SyscallRegisters,
|
|
||||||
Output: x64.ReturnValueRegisters,
|
|
||||||
},
|
|
||||||
finished: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,9 @@ func (f *Function) PrintInstructions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registers := bytes.Buffer{}
|
registers := bytes.Buffer{}
|
||||||
used := f.registerHistory[i]
|
used := f.RegisterHistory[i]
|
||||||
|
|
||||||
for _, reg := range f.cpu.All {
|
for _, reg := range f.CPU.All {
|
||||||
if used&(1<<reg) != 0 {
|
if used&(1<<reg) != 0 {
|
||||||
registers.WriteString("⬤ ")
|
registers.WriteString("⬤ ")
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,12 +13,12 @@ func (f *Function) UsesRegister(expr *expression.Expression, register cpu.Regist
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ast.IsFunctionCall(expr) {
|
if ast.IsFunctionCall(expr) {
|
||||||
if register == f.cpu.Output[0] {
|
if register == f.CPU.Output[0] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, parameter := range expr.Children[1:] {
|
for i, parameter := range expr.Children[1:] {
|
||||||
if register == f.cpu.Input[i] {
|
if register == f.CPU.Input[i] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/build/scope"
|
|
||||||
|
|
||||||
// VariableByName returns the variable with the given name or `nil` if it doesn't exist.
|
|
||||||
func (f *Function) VariableByName(name string) *scope.Variable {
|
|
||||||
return f.CurrentScope().VariableByName(name)
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/scope"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VariableInRegister returns the variable that occupies the given register or `nil` if none occupy the register.
|
|
||||||
func (f *Function) VariableInRegister(register cpu.Register) *scope.Variable {
|
|
||||||
for _, v := range f.CurrentScope().Variables {
|
|
||||||
if v.Register == register {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/build/config"
|
|
||||||
|
|
||||||
func (f *Function) postInstruction() {
|
|
||||||
if !config.Assembler {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.registerHistory = append(f.registerHistory, f.CurrentScope().Used)
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package scope
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/build/ast"
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
"git.akyoto.dev/cli/q/src/build/token"
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -9,6 +10,11 @@ type Stack struct {
|
|||||||
Scopes []*Scope
|
Scopes []*Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddVariable adds a new variable to the current scope.
|
||||||
|
func (stack *Stack) AddVariable(variable *Variable) {
|
||||||
|
stack.CurrentScope().AddVariable(variable)
|
||||||
|
}
|
||||||
|
|
||||||
// CurrentScope returns the current scope.
|
// CurrentScope returns the current scope.
|
||||||
func (stack *Stack) CurrentScope() *Scope {
|
func (stack *Stack) CurrentScope() *Scope {
|
||||||
return stack.Scopes[len(stack.Scopes)-1]
|
return stack.Scopes[len(stack.Scopes)-1]
|
||||||
@ -69,3 +75,19 @@ func (stack *Stack) UseVariable(variable *Variable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VariableByName returns the variable with the given name or `nil` if it doesn't exist.
|
||||||
|
func (stack *Stack) VariableByName(name string) *Variable {
|
||||||
|
return stack.CurrentScope().VariableByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VariableByRegister returns the variable that occupies the given register or `nil` if none occupy the register.
|
||||||
|
func (stack *Stack) VariableByRegister(register cpu.Register) *Variable {
|
||||||
|
for _, v := range stack.CurrentScope().Variables {
|
||||||
|
if v.Register == register {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
8
src/build/z/AddLabel.go
Normal file
8
src/build/z/AddLabel.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
|
||||||
|
func (f *Compiler) AddLabel(label string) {
|
||||||
|
f.Assembler.Label(asm.LABEL, label)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
7
src/build/z/Call.go
Normal file
7
src/build/z/Call.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
func (f *Compiler) Call(label string) {
|
||||||
|
f.Assembler.Call(label)
|
||||||
|
f.CurrentScope().Use(f.CPU.Output[0])
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
8
src/build/z/Comment.go
Normal file
8
src/build/z/Comment.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (f *Compiler) Comment(format string, args ...any) {
|
||||||
|
f.Assembler.Comment(fmt.Sprintf(format, args...))
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
15
src/build/z/Compiler.go
Normal file
15
src/build/z/Compiler.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/scope"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compiler is a register usage aware assembler.
|
||||||
|
type Compiler struct {
|
||||||
|
scope.Stack
|
||||||
|
Assembler asm.Assembler
|
||||||
|
CPU cpu.CPU
|
||||||
|
RegisterHistory []uint64
|
||||||
|
}
|
8
src/build/z/Jump.go
Normal file
8
src/build/z/Jump.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
|
||||||
|
func (f *Compiler) Jump(mnemonic asm.Mnemonic, label string) {
|
||||||
|
f.Assembler.Label(mnemonic, label)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
8
src/build/z/MemoryNumber.go
Normal file
8
src/build/z/MemoryNumber.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
|
||||||
|
func (f *Compiler) MemoryNumber(mnemonic asm.Mnemonic, a asm.Memory, b int) {
|
||||||
|
f.Assembler.MemoryNumber(mnemonic, a, b)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
16
src/build/z/Register.go
Normal file
16
src/build/z/Register.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Compiler) Register(mnemonic asm.Mnemonic, a cpu.Register) {
|
||||||
|
f.Assembler.Register(mnemonic, a)
|
||||||
|
|
||||||
|
if mnemonic == asm.POP {
|
||||||
|
f.CurrentScope().Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
20
src/build/z/RegisterLabel.go
Normal file
20
src/build/z/RegisterLabel.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Compiler) RegisterLabel(mnemonic asm.Mnemonic, register cpu.Register, label string) {
|
||||||
|
if f.CurrentScope().IsUsed(register) && isDestructive(mnemonic) {
|
||||||
|
f.SaveRegister(register)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.RegisterLabel(mnemonic, register, label)
|
||||||
|
|
||||||
|
if mnemonic == asm.MOVE {
|
||||||
|
f.CurrentScope().Use(register)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
20
src/build/z/RegisterNumber.go
Normal file
20
src/build/z/RegisterNumber.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Compiler) RegisterNumber(mnemonic asm.Mnemonic, a cpu.Register, b int) {
|
||||||
|
if f.CurrentScope().IsUsed(a) && isDestructive(mnemonic) {
|
||||||
|
f.SaveRegister(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.RegisterNumber(mnemonic, a, b)
|
||||||
|
|
||||||
|
if mnemonic == asm.MOVE {
|
||||||
|
f.CurrentScope().Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
24
src/build/z/RegisterRegister.go
Normal file
24
src/build/z/RegisterRegister.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Compiler) RegisterRegister(mnemonic asm.Mnemonic, a cpu.Register, b cpu.Register) {
|
||||||
|
if mnemonic == asm.MOVE && a == b {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.CurrentScope().IsUsed(a) && isDestructive(mnemonic) {
|
||||||
|
f.SaveRegister(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Assembler.RegisterRegister(mnemonic, a, b)
|
||||||
|
|
||||||
|
if mnemonic == asm.MOVE {
|
||||||
|
f.CurrentScope().Use(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
6
src/build/z/Return.go
Normal file
6
src/build/z/Return.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
func (f *Compiler) Return() {
|
||||||
|
f.Assembler.Return()
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
@ -1,36 +1,30 @@
|
|||||||
package core
|
package z
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
"git.akyoto.dev/cli/q/src/build/config"
|
|
||||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SaveRegister attempts to move a variable occupying this register to another register.
|
// SaveRegister attempts to move a variable occupying this register to another register.
|
||||||
func (f *Function) SaveRegister(register cpu.Register) {
|
func (f *Compiler) SaveRegister(register cpu.Register) {
|
||||||
if !f.CurrentScope().IsUsed(register) {
|
if !f.CurrentScope().IsUsed(register) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, general := range f.cpu.General {
|
for _, general := range f.CPU.General {
|
||||||
if register == general {
|
if register == general {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
variable := f.VariableInRegister(register)
|
variable := f.VariableByRegister(register)
|
||||||
|
|
||||||
if variable == nil || variable.Alive == 0 {
|
if variable == nil || variable.Alive == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newRegister := f.CurrentScope().MustFindFree(f.cpu.General)
|
newRegister := f.CurrentScope().MustFindFree(f.CPU.General)
|
||||||
f.CurrentScope().Reserve(newRegister)
|
f.CurrentScope().Reserve(newRegister)
|
||||||
|
|
||||||
if config.Comments {
|
|
||||||
f.Comment("save %s to %s", register, newRegister)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.RegisterRegister(asm.MOVE, newRegister, register)
|
f.RegisterRegister(asm.MOVE, newRegister, register)
|
||||||
variable.Register = newRegister
|
variable.Register = newRegister
|
||||||
}
|
}
|
7
src/build/z/Syscall.go
Normal file
7
src/build/z/Syscall.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
func (f *Compiler) Syscall() {
|
||||||
|
f.Assembler.Syscall()
|
||||||
|
f.CurrentScope().Use(f.CPU.Output[0])
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
12
src/build/z/isDestructive.go
Normal file
12
src/build/z/isDestructive.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
|
||||||
|
func isDestructive(mnemonic asm.Mnemonic) bool {
|
||||||
|
switch mnemonic {
|
||||||
|
case asm.MOVE, asm.ADD, asm.SUB, asm.MUL, asm.DIV:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
11
src/build/z/postInstruction.go
Normal file
11
src/build/z/postInstruction.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package z
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/config"
|
||||||
|
|
||||||
|
func (f *Compiler) postInstruction() {
|
||||||
|
if !config.Assembler {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.RegisterHistory = append(f.RegisterHistory, f.CurrentScope().Used)
|
||||||
|
}
|
@ -37,13 +37,10 @@ func buildWithArgs(args []string) (*build.Build, error) {
|
|||||||
switch args[i] {
|
switch args[i] {
|
||||||
case "-a", "--assembler":
|
case "-a", "--assembler":
|
||||||
config.Assembler = true
|
config.Assembler = true
|
||||||
case "-c", "--comments":
|
|
||||||
config.Comments = true
|
|
||||||
case "-d", "--dry":
|
case "-d", "--dry":
|
||||||
config.Dry = true
|
config.Dry = true
|
||||||
case "-v", "--verbose":
|
case "-v", "--verbose":
|
||||||
config.Assembler = true
|
config.Assembler = true
|
||||||
config.Comments = true
|
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(args[i], "-") {
|
if strings.HasPrefix(args[i], "-") {
|
||||||
return b, &errors.UnknownCLIParameter{Parameter: args[i]}
|
return b, &errors.UnknownCLIParameter{Parameter: args[i]}
|
||||||
|
Loading…
Reference in New Issue
Block a user