From 247b82b5290a5d38b6f0172f156cbd9dff75b1c1 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sun, 30 Jun 2024 11:41:59 +0200 Subject: [PATCH] Implemented compilation finished events --- README.md | 8 ++++++- src/build/Call.go | 44 +++++++++++++++++++++++++++++++++++++++ src/build/Execute.go | 4 ++++ src/build/Function.go | 6 ++++++ src/build/FunctionCall.go | 43 -------------------------------------- src/build/compile.go | 1 + src/build/compiler.go | 15 +++++++------ src/build/scan.go | 1 + 8 files changed, 72 insertions(+), 50 deletions(-) create mode 100644 src/build/Call.go delete mode 100644 src/build/FunctionCall.go diff --git a/README.md b/README.md index 3f36e89..e5e860f 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,13 @@ q build examples/hello q build examples/hello --dry ``` -To produce verbose output, add the `-v` flag which shows the generated assembly instructions: +Adding the `-a` or `--assembler` flag shows the generated assembly instructions: + +```shell +q build examples/hello -a +``` + +Adding the `-v` or `--verbose` flag shows verbose compiler information: ```shell q build examples/hello -v diff --git a/src/build/Call.go b/src/build/Call.go new file mode 100644 index 0000000..2051499 --- /dev/null +++ b/src/build/Call.go @@ -0,0 +1,44 @@ +package build + +import ( + "git.akyoto.dev/cli/q/src/build/config" + "git.akyoto.dev/cli/q/src/build/expression" +) + +// CompileFunctionCall executes a function call. +func (f *Function) CompileFunctionCall(expr *expression.Expression) error { + funcName := expr.Children[0].Token.Text() + + if funcName == "syscall" { + return f.CompileSyscall(expr) + } + + function := f.functions[funcName] + + if function != f { + function.Wait() + } + + parameters := expr.Children[1:] + registers := f.cpu.Call[:len(parameters)] + + err := f.ExpressionsToRegisters(parameters, registers) + + if config.Verbose { + f.Logf("call: %s", funcName) + } + + f.assembler.Call(funcName) + f.sideEffects += function.sideEffects + return err +} + +// CompileSyscall executes a syscall. +func (f *Function) CompileSyscall(expr *expression.Expression) error { + parameters := expr.Children[1:] + registers := f.cpu.Syscall[:len(parameters)] + err := f.ExpressionsToRegisters(parameters, registers) + f.assembler.Syscall() + f.sideEffects++ + return err +} diff --git a/src/build/Execute.go b/src/build/Execute.go index 8947218..bd50435 100644 --- a/src/build/Execute.go +++ b/src/build/Execute.go @@ -166,6 +166,10 @@ func (f *Function) ExpressionToRegister(root *expression.Expression, register cp // ExpressionsToRegisters moves multiple expressions into the specified registers. func (f *Function) ExpressionsToRegisters(expressions []*expression.Expression, registers []cpu.Register) error { + for _, register := range registers { + f.SaveRegister(register) + } + for i := len(expressions) - 1; i >= 0; i-- { expression := expressions[i] register := registers[i] diff --git a/src/build/Function.go b/src/build/Function.go index 8c271e0..8853605 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -20,6 +20,7 @@ type Function struct { // Compile turns a function into machine code. func (f *Function) Compile() { + defer close(f.finished) f.assembler.Label(f.Name) f.err = f.CompileTokens(f.Body) f.assembler.Return() @@ -124,6 +125,11 @@ func (f *Function) String() string { return f.Name } +// Wait will block until the compilation finishes. +func (f *Function) Wait() { + <-f.finished +} + // identifierExists returns true if the identifier has been defined. func (f *Function) identifierExists(name string) bool { _, exists := f.variables[name] diff --git a/src/build/FunctionCall.go b/src/build/FunctionCall.go deleted file mode 100644 index 8b62e25..0000000 --- a/src/build/FunctionCall.go +++ /dev/null @@ -1,43 +0,0 @@ -package build - -import ( - "git.akyoto.dev/cli/q/src/build/config" - "git.akyoto.dev/cli/q/src/build/cpu" - "git.akyoto.dev/cli/q/src/build/expression" -) - -// CompileFunctionCall executes a function call. -func (f *Function) CompileFunctionCall(expr *expression.Expression) error { - var ( - funcName = expr.Children[0].Token.Text() - parameters = expr.Children[1:] - isSyscall = funcName == "syscall" - registers []cpu.Register - ) - - if isSyscall { - registers = f.cpu.Syscall - } else { - registers = f.cpu.Call - } - - registers = registers[:len(parameters)] - - for _, register := range registers { - f.SaveRegister(register) - } - - err := f.ExpressionsToRegisters(parameters, registers) - - if config.Verbose { - f.Logf("call: %s", funcName) - } - - if isSyscall { - f.assembler.Syscall() - } else { - f.assembler.Call(funcName) - } - - return err -} diff --git a/src/build/compile.go b/src/build/compile.go index 2e1f4b3..0430873 100644 --- a/src/build/compile.go +++ b/src/build/compile.go @@ -27,6 +27,7 @@ func compile(functions <-chan *Function, errs <-chan error) (Result, error) { continue } + function.functions = allFunctions allFunctions[function.Name] = function } } diff --git a/src/build/compiler.go b/src/build/compiler.go index 3e727be..19b75a2 100644 --- a/src/build/compiler.go +++ b/src/build/compiler.go @@ -11,12 +11,15 @@ import ( // compiler is the data structure we embed in each function to preserve compilation state. type compiler struct { - assembler asm.Assembler - count counter - cpu cpu.CPU - debug []debug - err error - variables map[string]*Variable + assembler asm.Assembler + count counter + cpu cpu.CPU + debug []debug + err error + variables map[string]*Variable + functions map[string]*Function + sideEffects int + finished chan struct{} } // counter stores how often a certain statement appeared so we can generate a unique label from it. diff --git a/src/build/scan.go b/src/build/scan.go index 695a401..701a824 100644 --- a/src/build/scan.go +++ b/src/build/scan.go @@ -251,6 +251,7 @@ func scanFile(path string, functions chan<- *Function) error { Return: x64.ReturnValueRegisters, }, variables: map[string]*Variable{}, + finished: make(chan struct{}), }, }