From 526b92aa095cd17a29dfec19c78303a17e38c217 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 27 Jun 2024 17:36:45 +0200 Subject: [PATCH] Implemented dead code elimination --- errors_test.go | 1 + examples/hello/hello.q | 4 +++ src/build/Result.go | 12 +++------ src/build/compile.go | 40 +++++++++++++++++++++++++++--- src/cli/Build.go | 2 +- src/errors/CompileErrors.go | 9 ++++--- tests/errors/MissingMainFunction.q | 0 7 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 tests/errors/MissingMainFunction.q diff --git a/errors_test.go b/errors_test.go index 8eecad6..f386c66 100644 --- a/errors_test.go +++ b/errors_test.go @@ -27,6 +27,7 @@ func TestErrors(t *testing.T) { {"MissingBlockStart.q", errors.MissingBlockStart}, {"MissingGroupEnd.q", errors.MissingGroupEnd}, {"MissingGroupStart.q", errors.MissingGroupStart}, + {"MissingMainFunction.q", errors.MissingMainFunction}, {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}}, diff --git a/examples/hello/hello.q b/examples/hello/hello.q index c006280..07f7c6d 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -13,4 +13,8 @@ print(address, length) { write(fd, address, length) { syscall(1, fd, address, length) +} + +empty() { + } \ No newline at end of file diff --git a/src/build/Result.go b/src/build/Result.go index 3b13dba..615cbb8 100644 --- a/src/build/Result.go +++ b/src/build/Result.go @@ -8,7 +8,8 @@ import ( // Result contains all the compiled functions in a build. type Result struct { - Functions map[string]*Function + Used []*Function + Unused map[string]*Function instructionCount int } @@ -27,13 +28,8 @@ func (r Result) Finalize() ([]byte, []byte) { final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0) final.Syscall() - // Place the `main` function immediately after the entry point. - main := r.Functions["main"] - delete(r.Functions, "main") - final.Merge(&main.Assembler) - - // Merge all the remaining functions. - for _, f := range r.Functions { + // Merge all the called functions. + for _, f := range r.Used { final.Merge(&f.Assembler) } diff --git a/src/build/compile.go b/src/build/compile.go index 110bd40..325bcd5 100644 --- a/src/build/compile.go +++ b/src/build/compile.go @@ -2,12 +2,15 @@ package build import ( "sync" + + "git.akyoto.dev/cli/q/src/build/asm" + fail "git.akyoto.dev/cli/q/src/errors" ) // compile waits for the scan to finish and compiles all functions. func compile(functions <-chan *Function, errors <-chan error) (Result, error) { result := Result{ - Functions: map[string]*Function{}, + Unused: map[string]*Function{}, } for functions != nil || errors != nil { @@ -26,13 +29,13 @@ func compile(functions <-chan *Function, errors <-chan error) (Result, error) { continue } - result.Functions[function.Name] = function + result.Unused[function.Name] = function } } - compileFunctions(result.Functions) + compileFunctions(result.Unused) - for _, function := range result.Functions { + for _, function := range result.Unused { if function.Error != nil { return result, function.Error } @@ -40,9 +43,38 @@ func compile(functions <-chan *Function, errors <-chan error) (Result, error) { result.instructionCount += len(function.Assembler.Instructions) } + main, exists := result.Unused["main"] + + if !exists { + return result, fail.MissingMainFunction + } + + result.Used = append(result.Used, main) + delete(result.Unused, "main") + result.findCalls(main) + return result, nil } +func (result *Result) findCalls(f *Function) { + for _, x := range f.Assembler.Instructions { + if x.Mnemonic != asm.CALL { + continue + } + + name := x.Data.(*asm.Label).Name + called, exists := result.Unused[name] + + if !exists { + continue + } + + result.Used = append(result.Used, called) + delete(result.Unused, name) + result.findCalls(called) + } +} + // compileFunctions starts a goroutine for each function compilation and waits for completion. func compileFunctions(functions map[string]*Function) { wg := sync.WaitGroup{} diff --git a/src/cli/Build.go b/src/cli/Build.go index f9fe85d..2553996 100644 --- a/src/cli/Build.go +++ b/src/cli/Build.go @@ -43,7 +43,7 @@ func Build(args []string) int { } if config.Verbose { - for _, function := range result.Functions { + for _, function := range result.Used { function.PrintAsm() } } diff --git a/src/errors/CompileErrors.go b/src/errors/CompileErrors.go index 34515d0..13d5948 100644 --- a/src/errors/CompileErrors.go +++ b/src/errors/CompileErrors.go @@ -1,8 +1,9 @@ package errors var ( - InvalidStatement = &Base{"Invalid statement"} - InvalidExpression = &Base{"Invalid expression"} - MissingAssignValue = &Base{"Missing assignment value"} - NotImplemented = &Base{"Not implemented"} + InvalidStatement = &Base{"Invalid statement"} + InvalidExpression = &Base{"Invalid expression"} + MissingAssignValue = &Base{"Missing assignment value"} + MissingMainFunction = &Base{"Missing main function"} + NotImplemented = &Base{"Not implemented"} ) diff --git a/tests/errors/MissingMainFunction.q b/tests/errors/MissingMainFunction.q new file mode 100644 index 0000000..e69de29