Improved separation of concerns
This commit is contained in:
parent
d6db1b1096
commit
69245caf62
@ -16,6 +16,5 @@ const (
|
||||
|
||||
var (
|
||||
Assembler = false
|
||||
Comments = 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 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)
|
||||
|
||||
if err != nil {
|
||||
|
@ -2,7 +2,6 @@ package core
|
||||
|
||||
// Compile turns a function into machine code.
|
||||
func (f *Function) Compile() {
|
||||
defer close(f.finished)
|
||||
f.AddLabel(f.Name)
|
||||
f.Err = f.CompileTokens(f.Body)
|
||||
f.Return()
|
||||
|
@ -31,10 +31,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
||||
}
|
||||
|
||||
parameters := root.Children[1:]
|
||||
registers := f.cpu.Input[:len(parameters)]
|
||||
registers := f.CPU.Input[:len(parameters)]
|
||||
|
||||
if isSyscall {
|
||||
registers = f.cpu.Syscall[:len(parameters)]
|
||||
registers = f.CPU.Syscall[:len(parameters)]
|
||||
}
|
||||
|
||||
err := f.ExpressionsToRegisters(parameters, registers)
|
||||
@ -43,10 +43,10 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
||||
return err
|
||||
}
|
||||
|
||||
f.SaveRegister(f.cpu.Output[0])
|
||||
f.SaveRegister(f.CPU.Output[0])
|
||||
|
||||
// Push
|
||||
for _, register := range f.cpu.General {
|
||||
for _, register := range f.CPU.General {
|
||||
if f.CurrentScope().IsUsed(register) {
|
||||
f.Register(asm.PUSH, register)
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
||||
}
|
||||
|
||||
for _, register := range registers {
|
||||
if register == f.cpu.Output[0] && root.Parent != nil {
|
||||
if register == f.CPU.Output[0] && root.Parent != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -68,8 +68,8 @@ func (f *Function) CompileCall(root *expression.Expression) error {
|
||||
}
|
||||
|
||||
// Pop
|
||||
for i := len(f.cpu.General) - 1; i >= 0; i-- {
|
||||
register := f.cpu.General[i]
|
||||
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||
register := f.CPU.General[i]
|
||||
|
||||
if f.CurrentScope().IsUsed(register) {
|
||||
f.Register(asm.POP, register)
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||
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)
|
||||
err := f.ExpressionToRegister(node.Value, register)
|
||||
|
||||
|
@ -12,5 +12,5 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
||||
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 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)
|
||||
|
||||
err := f.ExpressionToRegister(value, tmp)
|
||||
|
@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/build/asm"
|
||||
"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/errors"
|
||||
"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) {
|
||||
err := f.CompileCall(node)
|
||||
|
||||
if register != f.cpu.Output[0] {
|
||||
f.RegisterRegister(asm.MOVE, register, f.cpu.Output[0])
|
||||
if register != f.CPU.Output[0] {
|
||||
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
||||
}
|
||||
|
||||
return err
|
||||
@ -34,11 +33,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
||||
final := register
|
||||
|
||||
if f.UsesRegister(right, register) {
|
||||
register = f.CurrentScope().MustFindFree(f.cpu.General)
|
||||
|
||||
if config.Comments {
|
||||
f.Comment("temporary register %s", register)
|
||||
}
|
||||
register = f.CurrentScope().MustFindFree(f.CPU.General)
|
||||
}
|
||||
|
||||
f.CurrentScope().Reserve(register)
|
||||
|
@ -1,26 +1,20 @@
|
||||
package core
|
||||
|
||||
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/scope"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
"git.akyoto.dev/cli/q/src/build/z"
|
||||
)
|
||||
|
||||
// Function represents the smallest unit of code.
|
||||
type Function struct {
|
||||
scope.Stack
|
||||
Name string
|
||||
File *fs.File
|
||||
Body []token.Token
|
||||
Assembler asm.Assembler
|
||||
Functions map[string]*Function
|
||||
Err error
|
||||
registerHistory []uint64
|
||||
finished chan struct{}
|
||||
cpu cpu.CPU
|
||||
count counter
|
||||
z.Compiler
|
||||
Name string
|
||||
File *fs.File
|
||||
Body []token.Token
|
||||
Functions map[string]*Function
|
||||
Err error
|
||||
count counter
|
||||
}
|
||||
|
||||
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
||||
|
@ -1,7 +1,7 @@
|
||||
package core
|
||||
|
||||
// identifierExists returns true if the identifier has been defined.
|
||||
func (f *Function) identifierExists(name string) bool {
|
||||
// IdentifierExists returns true if the identifier has been defined.
|
||||
func (f *Function) IdentifierExists(name string) bool {
|
||||
variable := f.VariableByName(name)
|
||||
|
||||
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/scope"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
"git.akyoto.dev/cli/q/src/build/z"
|
||||
)
|
||||
|
||||
// NewFunction creates a new function.
|
||||
@ -15,19 +16,20 @@ func NewFunction(name string, file *fs.File, body []token.Token) *Function {
|
||||
Name: name,
|
||||
File: file,
|
||||
Body: body,
|
||||
Assembler: asm.Assembler{
|
||||
Instructions: make([]asm.Instruction, 0, 8),
|
||||
Compiler: z.Compiler{
|
||||
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{}
|
||||
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 {
|
||||
registers.WriteString("⬤ ")
|
||||
} else {
|
||||
|
@ -13,12 +13,12 @@ func (f *Function) UsesRegister(expr *expression.Expression, register cpu.Regist
|
||||
}
|
||||
|
||||
if ast.IsFunctionCall(expr) {
|
||||
if register == f.cpu.Output[0] {
|
||||
if register == f.CPU.Output[0] {
|
||||
return true
|
||||
}
|
||||
|
||||
for i, parameter := range expr.Children[1:] {
|
||||
if register == f.cpu.Input[i] {
|
||||
if register == f.CPU.Input[i] {
|
||||
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 (
|
||||
"git.akyoto.dev/cli/q/src/build/ast"
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
)
|
||||
|
||||
@ -9,6 +10,11 @@ type Stack struct {
|
||||
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.
|
||||
func (stack *Stack) CurrentScope() *Scope {
|
||||
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 (
|
||||
"git.akyoto.dev/cli/q/src/build/asm"
|
||||
"git.akyoto.dev/cli/q/src/build/config"
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, general := range f.cpu.General {
|
||||
for _, general := range f.CPU.General {
|
||||
if register == general {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
variable := f.VariableInRegister(register)
|
||||
variable := f.VariableByRegister(register)
|
||||
|
||||
if variable == nil || variable.Alive == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
newRegister := f.CurrentScope().MustFindFree(f.cpu.General)
|
||||
newRegister := f.CurrentScope().MustFindFree(f.CPU.General)
|
||||
f.CurrentScope().Reserve(newRegister)
|
||||
|
||||
if config.Comments {
|
||||
f.Comment("save %s to %s", register, newRegister)
|
||||
}
|
||||
|
||||
f.RegisterRegister(asm.MOVE, newRegister, register)
|
||||
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] {
|
||||
case "-a", "--assembler":
|
||||
config.Assembler = true
|
||||
case "-c", "--comments":
|
||||
config.Comments = true
|
||||
case "-d", "--dry":
|
||||
config.Dry = true
|
||||
case "-v", "--verbose":
|
||||
config.Assembler = true
|
||||
config.Comments = true
|
||||
default:
|
||||
if strings.HasPrefix(args[i], "-") {
|
||||
return b, &errors.UnknownCLIParameter{Parameter: args[i]}
|
||||
|
Loading…
Reference in New Issue
Block a user