From 988a53866174d7fc09fde9a02f8057a946d62797 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 26 Jun 2024 20:16:18 +0200 Subject: [PATCH] Added build result struct --- src/build/Build.go | 2 +- src/build/Finalize.go | 36 -------------------------------- src/build/Result.go | 42 ++++++++++++++++++++++++++++++++++++++ src/build/asm/Assembler.go | 7 ------- src/build/compile.go | 20 ++++++++++-------- src/build/scan.go | 4 ++++ src/cli/Build.go | 4 ++-- 7 files changed, 61 insertions(+), 54 deletions(-) delete mode 100644 src/build/Finalize.go create mode 100644 src/build/Result.go diff --git a/src/build/Build.go b/src/build/Build.go index 1efb881..2821e2d 100644 --- a/src/build/Build.go +++ b/src/build/Build.go @@ -18,7 +18,7 @@ func New(files ...string) *Build { } // Run parses the input files and generates an executable file. -func (build *Build) Run() (map[string]*Function, error) { +func (build *Build) Run() (Result, error) { functions, errors := scan(build.Files) return compile(functions, errors) } diff --git a/src/build/Finalize.go b/src/build/Finalize.go deleted file mode 100644 index dadf7f6..0000000 --- a/src/build/Finalize.go +++ /dev/null @@ -1,36 +0,0 @@ -package build - -import ( - "git.akyoto.dev/cli/q/src/build/arch/x64" - "git.akyoto.dev/cli/q/src/build/asm" - "git.akyoto.dev/cli/q/src/build/os/linux" -) - -// Finalize generates the final machine code. -func Finalize(functions map[string]*Function) ([]byte, []byte) { - a := entry() - - main := functions["main"] - delete(functions, "main") - a.Merge(&main.Assembler) - - for _, f := range functions { - a.Merge(&f.Assembler) - } - - code, data := a.Finalize() - return code, data -} - -// entry returns the entry point of the executable. -// The only job of the entry function is to call `main` and exit cleanly. -// The reason we call `main` instead of using `main` itself is to place -// a return address on the stack, which allows return statements in `main`. -func entry() *asm.Assembler { - entry := asm.New() - entry.Call("main") - entry.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit) - entry.RegisterNumber(asm.MOVE, x64.SyscallRegisters[1], 0) - entry.Syscall() - return entry -} diff --git a/src/build/Result.go b/src/build/Result.go new file mode 100644 index 0000000..3b13dba --- /dev/null +++ b/src/build/Result.go @@ -0,0 +1,42 @@ +package build + +import ( + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/os/linux" +) + +// Result contains all the compiled functions in a build. +type Result struct { + Functions map[string]*Function + instructionCount int +} + +// Finalize generates the final machine code. +func (r Result) Finalize() ([]byte, []byte) { + // This will be the entry point of the executable. + // The only job of the entry function is to call `main` and exit cleanly. + // The reason we call `main` instead of using `main` itself is to place + // a return address on the stack, which allows return statements in `main`. + final := asm.Assembler{ + Instructions: make([]asm.Instruction, 0, r.instructionCount+4), + } + + final.Call("main") + final.RegisterNumber(asm.MOVE, x64.SyscallRegisters[0], linux.Exit) + 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 { + final.Merge(&f.Assembler) + } + + code, data := final.Finalize() + return code, data +} diff --git a/src/build/asm/Assembler.go b/src/build/asm/Assembler.go index 7c0ed62..9fe9229 100644 --- a/src/build/asm/Assembler.go +++ b/src/build/asm/Assembler.go @@ -11,13 +11,6 @@ type Assembler struct { Instructions []Instruction } -// New creates a new assembler. -func New() *Assembler { - return &Assembler{ - Instructions: make([]Instruction, 0, 8), - } -} - // Finalize generates the final machine code. func (a *Assembler) Finalize() ([]byte, []byte) { code := make([]byte, 0, len(a.Instructions)*8) diff --git a/src/build/compile.go b/src/build/compile.go index 6088e5a..110bd40 100644 --- a/src/build/compile.go +++ b/src/build/compile.go @@ -5,8 +5,10 @@ import ( ) // compile waits for the scan to finish and compiles all functions. -func compile(functions <-chan *Function, errors <-chan error) (map[string]*Function, error) { - allFunctions := map[string]*Function{} +func compile(functions <-chan *Function, errors <-chan error) (Result, error) { + result := Result{ + Functions: map[string]*Function{}, + } for functions != nil || errors != nil { select { @@ -16,7 +18,7 @@ func compile(functions <-chan *Function, errors <-chan error) (map[string]*Funct continue } - return nil, err + return result, err case function, ok := <-functions: if !ok { @@ -24,19 +26,21 @@ func compile(functions <-chan *Function, errors <-chan error) (map[string]*Funct continue } - allFunctions[function.Name] = function + result.Functions[function.Name] = function } } - compileFunctions(allFunctions) + compileFunctions(result.Functions) - for _, function := range allFunctions { + for _, function := range result.Functions { if function.Error != nil { - return nil, function.Error + return result, function.Error } + + result.instructionCount += len(function.Assembler.Instructions) } - return allFunctions, nil + return result, nil } // compileFunctions starts a goroutine for each function compilation and waits for completion. diff --git a/src/build/scan.go b/src/build/scan.go index a78ce93..fea378a 100644 --- a/src/build/scan.go +++ b/src/build/scan.go @@ -7,6 +7,7 @@ import ( "sync" "git.akyoto.dev/cli/q/src/build/arch/x64" + "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/token" @@ -231,6 +232,9 @@ func scanFile(path string, functions chan<- *Function) error { Syscall: x64.SyscallRegisters, Return: x64.ReturnValueRegisters, }, + Assembler: asm.Assembler{ + Instructions: make([]asm.Instruction, 0, 32), + }, } nameStart = -1 diff --git a/src/cli/Build.go b/src/cli/Build.go index 6120250..f9fe85d 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 { + for _, function := range result.Functions { function.PrintAsm() } } @@ -53,7 +53,7 @@ func Build(args []string) int { } path := b.Executable() - code, data := build.Finalize(result) + code, data := result.Finalize() err = build.Write(path, code, data) if err != nil {