Added assembler merging

This commit is contained in:
Eduard Urbach 2023-10-31 14:50:53 +01:00
parent b6be9357bf
commit 5c12992fca
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
6 changed files with 122 additions and 63 deletions

View File

@ -62,6 +62,11 @@ func (a *Assembler) Finalize(verbose bool) ([]byte, []byte) {
return code, data
}
// Merge combines the contents of this assembler with another one.
func (a *Assembler) Merge(b *Assembler) {
a.Instructions = append(a.Instructions, b.Instructions...)
}
// MoveRegisterData moves a data section address into the given register.
func (a *Assembler) MoveRegisterData(reg register.ID, data []byte) {
a.Instructions = append(a.Instructions, Instruction{

View File

@ -5,12 +5,8 @@ import (
"os"
"path/filepath"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/elf"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/linux"
"git.akyoto.dev/cli/q/src/log"
"git.akyoto.dev/cli/q/src/register"
)
// Build describes a compiler build.
@ -30,7 +26,17 @@ func New(directory string) *Build {
// Run parses the input files and generates an executable file.
func (build *Build) Run() error {
code, data, err := build.Compile()
stat, err := os.Stat(build.Directory)
if err != nil {
return err
}
if !stat.IsDir() {
return &errors.InvalidDirectory{Path: build.Directory}
}
functions, err := build.Compile()
if err != nil {
return err
@ -40,60 +46,10 @@ func (build *Build) Run() error {
return nil
}
code, data := build.Finalize(functions)
return writeToDisk(build.Executable(), code, data)
}
// Compile compiles all the functions.
func (build *Build) Compile() ([]byte, []byte, error) {
stat, err := os.Stat(build.Directory)
if err != nil {
return nil, nil, err
}
if !stat.IsDir() {
return nil, nil, &errors.InvalidDirectory{Path: build.Directory}
}
functions, errors := Scan(build.Directory)
for functions != nil || errors != nil {
select {
case err, ok := <-errors:
if !ok {
errors = nil
continue
}
log.Info.Println(err)
case function, ok := <-functions:
if !ok {
functions = nil
continue
}
log.Info.Println(function)
}
}
a := asm.New()
hello := []byte("Hello\n")
a.MoveRegisterNumber(register.Syscall0, linux.Write)
a.MoveRegisterNumber(register.Syscall1, 1)
a.MoveRegisterData(register.Syscall2, hello)
a.MoveRegisterNumber(register.Syscall3, uint64(len(hello)))
a.Syscall()
a.MoveRegisterNumber(register.Syscall0, linux.Exit)
a.MoveRegisterNumber(register.Syscall1, 0)
a.Syscall()
code, data := a.Finalize(build.Verbose)
return code, data, nil
}
// Executable returns the path to the executable.
func (build *Build) Executable() string {
return filepath.Join(build.Directory, filepath.Base(build.Directory))

40
src/build/Compile.go Normal file
View File

@ -0,0 +1,40 @@
package build
import "sync"
// Compile compiles all the functions.
func (build *Build) Compile() (map[string]*Function, error) {
functions, errors := Scan(build.Directory)
wg := sync.WaitGroup{}
allFunctions := map[string]*Function{}
for functions != nil || errors != nil {
select {
case err, ok := <-errors:
if !ok {
errors = nil
continue
}
return nil, err
case function, ok := <-functions:
if !ok {
functions = nil
continue
}
wg.Add(1)
go func() {
defer wg.Done()
function.Compile()
}()
allFunctions[function.Name] = function
}
}
wg.Wait()
return allFunctions, nil
}

23
src/build/Finalize.go Normal file
View File

@ -0,0 +1,23 @@
package build
import (
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/linux"
"git.akyoto.dev/cli/q/src/register"
)
// Finalize generates the final machine code.
func (build *Build) Finalize(functions map[string]*Function) ([]byte, []byte) {
a := asm.New()
for _, f := range functions {
a.Merge(&f.Assembler)
}
a.MoveRegisterNumber(register.Syscall0, linux.Exit)
a.MoveRegisterNumber(register.Syscall1, 0)
a.Syscall()
code, data := a.Finalize(build.Verbose)
return code, data
}

View File

@ -1,7 +1,35 @@
package build
import "git.akyoto.dev/cli/q/src/token"
import (
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/linux"
"git.akyoto.dev/cli/q/src/register"
"git.akyoto.dev/cli/q/src/token"
)
// Function represents a function.
type Function struct {
Tokens token.List
Name string
Head token.List
Body token.List
Assembler asm.Assembler
}
// Compile turns a function into machine code.
func (f *Function) Compile() {
for i, t := range f.Body {
if t.Kind == token.Identifier && t.String() == "print" {
message := f.Body[i+2].Bytes
f.Assembler.MoveRegisterNumber(register.Syscall0, linux.Write)
f.Assembler.MoveRegisterNumber(register.Syscall1, 1)
f.Assembler.MoveRegisterData(register.Syscall2, message)
f.Assembler.MoveRegisterNumber(register.Syscall3, uint64(len(message)))
f.Assembler.Syscall()
}
}
}
// String returns the function name.
func (f *Function) String() string {
return f.Name
}

View File

@ -60,16 +60,17 @@ func scanFile(path string, functions chan<- *Function) error {
tokens := token.Tokenize(contents)
var (
groupLevel = 0
blockLevel = 0
functionStart = -1
groupLevel = 0
blockLevel = 0
headerStart = -1
bodyStart = -1
)
for i, t := range tokens {
switch t.Kind {
case token.Identifier:
if blockLevel == 0 && groupLevel == 0 {
functionStart = i
headerStart = i
}
case token.GroupStart:
@ -81,12 +82,18 @@ func scanFile(path string, functions chan<- *Function) error {
case token.BlockStart:
blockLevel++
if blockLevel == 1 {
bodyStart = i
}
case token.BlockEnd:
blockLevel--
if blockLevel == 0 {
function := &Function{
Tokens: tokens[functionStart : i+1],
Name: tokens[headerStart].String(),
Head: tokens[headerStart:bodyStart],
Body: tokens[bodyStart : i+1],
}
functions <- function