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},
{"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"}},

View File

@ -13,4 +13,8 @@ print(address, length) {
write(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.
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)
}

View File

@ -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{}

View File

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

View File

@ -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"}
)

View File