Added build result struct
This commit is contained in:
parent
3268f7a7ee
commit
988a538661
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
42
src/build/Result.go
Normal file
42
src/build/Result.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user