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.
|
// 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)
|
functions, errors := scan(build.Files)
|
||||||
return compile(functions, errors)
|
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
|
Instructions []Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new assembler.
|
|
||||||
func New() *Assembler {
|
|
||||||
return &Assembler{
|
|
||||||
Instructions: make([]Instruction, 0, 8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize generates the final machine code.
|
// Finalize generates the final machine code.
|
||||||
func (a *Assembler) Finalize() ([]byte, []byte) {
|
func (a *Assembler) Finalize() ([]byte, []byte) {
|
||||||
code := make([]byte, 0, len(a.Instructions)*8)
|
code := make([]byte, 0, len(a.Instructions)*8)
|
||||||
|
@ -5,8 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 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) (map[string]*Function, error) {
|
func compile(functions <-chan *Function, errors <-chan error) (Result, error) {
|
||||||
allFunctions := map[string]*Function{}
|
result := Result{
|
||||||
|
Functions: map[string]*Function{},
|
||||||
|
}
|
||||||
|
|
||||||
for functions != nil || errors != nil {
|
for functions != nil || errors != nil {
|
||||||
select {
|
select {
|
||||||
@ -16,7 +18,7 @@ func compile(functions <-chan *Function, errors <-chan error) (map[string]*Funct
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return result, err
|
||||||
|
|
||||||
case function, ok := <-functions:
|
case function, ok := <-functions:
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -24,19 +26,21 @@ func compile(functions <-chan *Function, errors <-chan error) (map[string]*Funct
|
|||||||
continue
|
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 {
|
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.
|
// compileFunctions starts a goroutine for each function compilation and waits for completion.
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
"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/cpu"
|
||||||
"git.akyoto.dev/cli/q/src/build/fs"
|
"git.akyoto.dev/cli/q/src/build/fs"
|
||||||
"git.akyoto.dev/cli/q/src/build/token"
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
@ -231,6 +232,9 @@ func scanFile(path string, functions chan<- *Function) error {
|
|||||||
Syscall: x64.SyscallRegisters,
|
Syscall: x64.SyscallRegisters,
|
||||||
Return: x64.ReturnValueRegisters,
|
Return: x64.ReturnValueRegisters,
|
||||||
},
|
},
|
||||||
|
Assembler: asm.Assembler{
|
||||||
|
Instructions: make([]asm.Instruction, 0, 32),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
nameStart = -1
|
nameStart = -1
|
||||||
|
@ -43,7 +43,7 @@ func Build(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.Verbose {
|
if config.Verbose {
|
||||||
for _, function := range result {
|
for _, function := range result.Functions {
|
||||||
function.PrintAsm()
|
function.PrintAsm()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ func Build(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
path := b.Executable()
|
path := b.Executable()
|
||||||
code, data := build.Finalize(result)
|
code, data := result.Finalize()
|
||||||
err = build.Write(path, code, data)
|
err = build.Write(path, code, data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user