Implemented dead code elimination

This commit is contained in:
Eduard Urbach 2024-06-27 17:36:45 +02:00
parent d86d411959
commit 526b92aa09
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
7 changed files with 51 additions and 17 deletions

View File

@ -27,6 +27,7 @@ func TestErrors(t *testing.T) {
{"MissingBlockStart.q", errors.MissingBlockStart}, {"MissingBlockStart.q", errors.MissingBlockStart},
{"MissingGroupEnd.q", errors.MissingGroupEnd}, {"MissingGroupEnd.q", errors.MissingGroupEnd},
{"MissingGroupStart.q", errors.MissingGroupStart}, {"MissingGroupStart.q", errors.MissingGroupStart},
{"MissingMainFunction.q", errors.MissingMainFunction},
{"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}},
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},

View File

@ -13,4 +13,8 @@ print(address, length) {
write(fd, address, length) { write(fd, address, length) {
syscall(1, fd, address, length) syscall(1, fd, address, length)
}
empty() {
} }

View File

@ -8,7 +8,8 @@ import (
// Result contains all the compiled functions in a build. // Result contains all the compiled functions in a build.
type Result struct { type Result struct {
Functions map[string]*Function Used []*Function
Unused map[string]*Function
instructionCount int instructionCount int
} }
@ -27,13 +28,8 @@ func (r Result) Finalize() ([]byte, []byte) {
final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0) final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0)
final.Syscall() final.Syscall()
// Place the `main` function immediately after the entry point. // Merge all the called functions.
main := r.Functions["main"] for _, f := range r.Used {
delete(r.Functions, "main")
final.Merge(&main.Assembler)
// Merge all the remaining functions.
for _, f := range r.Functions {
final.Merge(&f.Assembler) final.Merge(&f.Assembler)
} }

View File

@ -2,12 +2,15 @@ package build
import ( import (
"sync" "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. // compile waits for the scan to finish and compiles all functions.
func compile(functions <-chan *Function, errors <-chan error) (Result, error) { func compile(functions <-chan *Function, errors <-chan error) (Result, error) {
result := Result{ result := Result{
Functions: map[string]*Function{}, Unused: map[string]*Function{},
} }
for functions != nil || errors != nil { for functions != nil || errors != nil {
@ -26,13 +29,13 @@ func compile(functions <-chan *Function, errors <-chan error) (Result, error) {
continue 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 { if function.Error != nil {
return result, function.Error return result, function.Error
} }
@ -40,9 +43,38 @@ func compile(functions <-chan *Function, errors <-chan error) (Result, error) {
result.instructionCount += len(function.Assembler.Instructions) 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 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. // compileFunctions starts a goroutine for each function compilation and waits for completion.
func compileFunctions(functions map[string]*Function) { func compileFunctions(functions map[string]*Function) {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}

View File

@ -43,7 +43,7 @@ func Build(args []string) int {
} }
if config.Verbose { if config.Verbose {
for _, function := range result.Functions { for _, function := range result.Used {
function.PrintAsm() function.PrintAsm()
} }
} }

View File

@ -1,8 +1,9 @@
package errors package errors
var ( var (
InvalidStatement = &Base{"Invalid statement"} InvalidStatement = &Base{"Invalid statement"}
InvalidExpression = &Base{"Invalid expression"} InvalidExpression = &Base{"Invalid expression"}
MissingAssignValue = &Base{"Missing assignment value"} MissingAssignValue = &Base{"Missing assignment value"}
NotImplemented = &Base{"Not implemented"} MissingMainFunction = &Base{"Missing main function"}
NotImplemented = &Base{"Not implemented"}
) )

View File