From e7a06f5b266ec6652549254ac2b7c545f2c1b933 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Tue, 11 Mar 2025 06:31:21 +0100 Subject: [PATCH] Improved assembler performance --- src/asm/Assembler.go | 22 +------- src/asm/CanSkip.go | 6 +-- src/asm/CanSkipReturn.go | 6 +-- src/asm/DataString.go | 28 ++++++++++ src/asm/Index.go | 3 ++ src/asm/Instruction.go | 5 +- src/asm/Instructions.go | 14 +---- src/asm/Label.go | 9 ++-- src/asm/Memory.go | 5 +- src/asm/MemoryLabel.go | 11 ++-- src/asm/MemoryNumber.go | 11 ++-- src/asm/MemoryRegister.go | 11 ++-- src/asm/Merge.go | 46 +++++++++++++++++ src/asm/Number.go | 9 ++-- src/asm/Param.go | 15 ++++++ src/asm/Register.go | 9 ++-- src/asm/RegisterLabel.go | 13 +++-- src/asm/RegisterNumber.go | 13 +++-- src/asm/RegisterRegister.go | 11 ++-- src/asm/SetData.go | 12 +++++ src/asm/Type.go | 17 +++++++ src/asm/bench_test.go | 36 +++++++++++++ src/asmc/Finalize.go | 3 +- src/asmc/call.go | 11 ++-- src/asmc/compileARM.go | 29 +++++++---- src/asmc/compileX86.go | 96 +++++++++++++++++++++-------------- src/asmc/compiler.go | 6 ++- src/asmc/dllCall.go | 2 +- src/asmc/jump.go | 2 +- src/asmc/load.go | 6 ++- src/asmc/move.go | 11 ++-- src/asmc/store.go | 16 +++--- src/compiler/Compile.go | 3 +- src/compiler/Result.go | 17 +++---- src/compiler/Statistics.go | 35 +++++++++++++ src/compiler/finalize.go | 16 +++++- src/core/PrintInstructions.go | 13 ++--- 37 files changed, 412 insertions(+), 166 deletions(-) create mode 100644 src/asm/DataString.go create mode 100644 src/asm/Index.go create mode 100644 src/asm/Merge.go create mode 100644 src/asm/Param.go create mode 100644 src/asm/SetData.go create mode 100644 src/asm/Type.go create mode 100644 src/asm/bench_test.go create mode 100644 src/compiler/Statistics.go diff --git a/src/asm/Assembler.go b/src/asm/Assembler.go index 8e32655..5605499 100644 --- a/src/asm/Assembler.go +++ b/src/asm/Assembler.go @@ -1,28 +1,10 @@ package asm -import ( - "maps" - - "git.urbach.dev/cli/q/src/data" -) +import "git.urbach.dev/cli/q/src/data" // Assembler contains a list of instructions. type Assembler struct { Data data.Data Instructions []Instruction -} - -// Merge combines the contents of this assembler with another one. -func (a *Assembler) Merge(b Assembler) { - maps.Copy(a.Data, b.Data) - a.Instructions = append(a.Instructions, b.Instructions...) -} - -// SetData sets the data for the given label. -func (a *Assembler) SetData(label string, bytes []byte) { - if a.Data == nil { - a.Data = data.Data{} - } - - a.Data.Insert(label, bytes) + Param Param } diff --git a/src/asm/CanSkip.go b/src/asm/CanSkip.go index ab58c65..f99b4a0 100644 --- a/src/asm/CanSkip.go +++ b/src/asm/CanSkip.go @@ -15,12 +15,12 @@ func (a *Assembler) CanSkip(mnemonic Mnemonic, left cpu.Register, right cpu.Regi last := a.Instructions[len(a.Instructions)-1] if mnemonic == MOVE && last.Mnemonic == MOVE { - lastData, isRegReg := last.Data.(*RegisterRegister) - - if !isRegReg { + if last.Type != TypeRegisterRegister { return false } + lastData := a.Param.RegisterRegister[last.Index] + if lastData.Destination == right && lastData.Source == left { return true } diff --git a/src/asm/CanSkipReturn.go b/src/asm/CanSkipReturn.go index 56e5096..0cf0bce 100644 --- a/src/asm/CanSkipReturn.go +++ b/src/asm/CanSkipReturn.go @@ -12,10 +12,10 @@ func (a *Assembler) CanSkipReturn() bool { return true } - if last.Mnemonic == CALL { - label, isLabel := last.Data.(*Label) + if last.Mnemonic == CALL && last.Type == TypeLabel { + label := a.Param.Label[last.Index] - if isLabel && label.String() == "core.exit" { + if label.String() == "core.exit" { return true } } diff --git a/src/asm/DataString.go b/src/asm/DataString.go new file mode 100644 index 0000000..144ca0e --- /dev/null +++ b/src/asm/DataString.go @@ -0,0 +1,28 @@ +package asm + +func (x Instruction) DataString(a *Assembler) string { + switch x.Type { + case TypeLabel: + return a.Param.Label[x.Index].String() + case TypeNumber: + return a.Param.Number[x.Index].String() + case TypeRegister: + return a.Param.Register[x.Index].String() + case TypeRegisterLabel: + return a.Param.RegisterLabel[x.Index].String() + case TypeRegisterNumber: + return a.Param.RegisterNumber[x.Index].String() + case TypeRegisterRegister: + return a.Param.RegisterRegister[x.Index].String() + case TypeMemory: + return a.Param.Memory[x.Index].String() + case TypeMemoryLabel: + return a.Param.MemoryLabel[x.Index].String() + case TypeMemoryNumber: + return a.Param.MemoryNumber[x.Index].String() + case TypeMemoryRegister: + return a.Param.MemoryRegister[x.Index].String() + default: + return "" + } +} diff --git a/src/asm/Index.go b/src/asm/Index.go new file mode 100644 index 0000000..ad942cc --- /dev/null +++ b/src/asm/Index.go @@ -0,0 +1,3 @@ +package asm + +type Index = uint16 diff --git a/src/asm/Instruction.go b/src/asm/Instruction.go index 2447bae..aace1ba 100644 --- a/src/asm/Instruction.go +++ b/src/asm/Instruction.go @@ -1,9 +1,8 @@ package asm -import "fmt" - // Instruction represents a single instruction which can be converted to machine code. type Instruction struct { - Data fmt.Stringer Mnemonic Mnemonic + Type Type + Index Index } diff --git a/src/asm/Instructions.go b/src/asm/Instructions.go index e203e3f..1066e10 100644 --- a/src/asm/Instructions.go +++ b/src/asm/Instructions.go @@ -2,22 +2,12 @@ package asm // Comment adds a comment at the current position. func (a *Assembler) Comment(text string) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: COMMENT, - Data: &Label{ - Name: text, - }, - }) + a.Label(COMMENT, text) } // DLLCall calls a function in a DLL file. func (a *Assembler) DLLCall(name string) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: DLLCALL, - Data: &Label{ - Name: name, - }, - }) + a.Label(DLLCALL, name) } // Return returns back to the caller. diff --git a/src/asm/Label.go b/src/asm/Label.go index 1b463bc..b55bd96 100644 --- a/src/asm/Label.go +++ b/src/asm/Label.go @@ -14,8 +14,11 @@ func (data *Label) String() string { func (a *Assembler) Label(mnemonic Mnemonic, name string) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &Label{ - Name: name, - }, + Type: TypeLabel, + Index: Index(len(a.Param.Label)), + }) + + a.Param.Label = append(a.Param.Label, Label{ + Name: name, }) } diff --git a/src/asm/Memory.go b/src/asm/Memory.go index 0af7c38..9ffd5d3 100644 --- a/src/asm/Memory.go +++ b/src/asm/Memory.go @@ -56,6 +56,9 @@ func (mem *Memory) String() string { func (a *Assembler) Memory(mnemonic Mnemonic, address Memory) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &address, + Type: TypeMemory, + Index: Index(len(a.Param.Memory)), }) + + a.Param.Memory = append(a.Param.Memory, address) } diff --git a/src/asm/MemoryLabel.go b/src/asm/MemoryLabel.go index 3ea1b9b..1402330 100644 --- a/src/asm/MemoryLabel.go +++ b/src/asm/MemoryLabel.go @@ -15,9 +15,12 @@ func (data *MemoryLabel) String() string { func (a *Assembler) MemoryLabel(mnemonic Mnemonic, address Memory, label string) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &MemoryLabel{ - Address: address, - Label: label, - }, + Type: TypeMemoryLabel, + Index: Index(len(a.Param.MemoryLabel)), + }) + + a.Param.MemoryLabel = append(a.Param.MemoryLabel, MemoryLabel{ + Address: address, + Label: label, }) } diff --git a/src/asm/MemoryNumber.go b/src/asm/MemoryNumber.go index 47c39d5..453f1de 100644 --- a/src/asm/MemoryNumber.go +++ b/src/asm/MemoryNumber.go @@ -17,9 +17,12 @@ func (data *MemoryNumber) String() string { func (a *Assembler) MemoryNumber(mnemonic Mnemonic, address Memory, number int) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &MemoryNumber{ - Address: address, - Number: number, - }, + Type: TypeMemoryNumber, + Index: Index(len(a.Param.MemoryNumber)), + }) + + a.Param.MemoryNumber = append(a.Param.MemoryNumber, MemoryNumber{ + Address: address, + Number: number, }) } diff --git a/src/asm/MemoryRegister.go b/src/asm/MemoryRegister.go index 5e76305..09d21b5 100644 --- a/src/asm/MemoryRegister.go +++ b/src/asm/MemoryRegister.go @@ -21,9 +21,12 @@ func (data *MemoryRegister) String() string { func (a *Assembler) MemoryRegister(mnemonic Mnemonic, address Memory, register cpu.Register) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &MemoryRegister{ - Address: address, - Register: register, - }, + Type: TypeMemoryRegister, + Index: Index(len(a.Param.MemoryRegister)), + }) + + a.Param.MemoryRegister = append(a.Param.MemoryRegister, MemoryRegister{ + Address: address, + Register: register, }) } diff --git a/src/asm/Merge.go b/src/asm/Merge.go new file mode 100644 index 0000000..68a5e1e --- /dev/null +++ b/src/asm/Merge.go @@ -0,0 +1,46 @@ +package asm + +import "maps" + +// Merge combines the contents of this assembler with another one. +func (a *Assembler) Merge(b *Assembler) { + maps.Copy(a.Data, b.Data) + from := len(a.Instructions) + a.Instructions = append(a.Instructions, b.Instructions...) + + for i := from; i < len(a.Instructions); i++ { + switch a.Instructions[i].Type { + case TypeLabel: + a.Instructions[i].Index += Index(len(a.Param.Label)) + case TypeNumber: + a.Instructions[i].Index += Index(len(a.Param.Number)) + case TypeRegister: + a.Instructions[i].Index += Index(len(a.Param.Register)) + case TypeRegisterLabel: + a.Instructions[i].Index += Index(len(a.Param.RegisterLabel)) + case TypeRegisterNumber: + a.Instructions[i].Index += Index(len(a.Param.RegisterNumber)) + case TypeRegisterRegister: + a.Instructions[i].Index += Index(len(a.Param.RegisterRegister)) + case TypeMemory: + a.Instructions[i].Index += Index(len(a.Param.Memory)) + case TypeMemoryLabel: + a.Instructions[i].Index += Index(len(a.Param.MemoryLabel)) + case TypeMemoryNumber: + a.Instructions[i].Index += Index(len(a.Param.MemoryNumber)) + case TypeMemoryRegister: + a.Instructions[i].Index += Index(len(a.Param.MemoryRegister)) + } + } + + a.Param.Label = append(a.Param.Label, b.Param.Label...) + a.Param.Number = append(a.Param.Number, b.Param.Number...) + a.Param.Register = append(a.Param.Register, b.Param.Register...) + a.Param.RegisterLabel = append(a.Param.RegisterLabel, b.Param.RegisterLabel...) + a.Param.RegisterNumber = append(a.Param.RegisterNumber, b.Param.RegisterNumber...) + a.Param.RegisterRegister = append(a.Param.RegisterRegister, b.Param.RegisterRegister...) + a.Param.Memory = append(a.Param.Memory, b.Param.Memory...) + a.Param.MemoryLabel = append(a.Param.MemoryLabel, b.Param.MemoryLabel...) + a.Param.MemoryNumber = append(a.Param.MemoryNumber, b.Param.MemoryNumber...) + a.Param.MemoryRegister = append(a.Param.MemoryRegister, b.Param.MemoryRegister...) +} diff --git a/src/asm/Number.go b/src/asm/Number.go index 3f0271c..4e58004 100644 --- a/src/asm/Number.go +++ b/src/asm/Number.go @@ -18,8 +18,11 @@ func (data *Number) String() string { func (a *Assembler) Number(mnemonic Mnemonic, number int) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &Number{ - Number: number, - }, + Type: TypeNumber, + Index: Index(len(a.Param.Number)), + }) + + a.Param.Number = append(a.Param.Number, Number{ + Number: number, }) } diff --git a/src/asm/Param.go b/src/asm/Param.go new file mode 100644 index 0000000..efb5f1b --- /dev/null +++ b/src/asm/Param.go @@ -0,0 +1,15 @@ +package asm + +// Param stores all the parameters for the instructions. +type Param struct { + Label []Label + Number []Number + Register []Register + RegisterLabel []RegisterLabel + RegisterNumber []RegisterNumber + RegisterRegister []RegisterRegister + Memory []Memory + MemoryLabel []MemoryLabel + MemoryNumber []MemoryNumber + MemoryRegister []MemoryRegister +} diff --git a/src/asm/Register.go b/src/asm/Register.go index 91be5e0..fee9080 100644 --- a/src/asm/Register.go +++ b/src/asm/Register.go @@ -18,8 +18,11 @@ func (data *Register) String() string { func (a *Assembler) Register(mnemonic Mnemonic, register cpu.Register) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &Register{ - Register: register, - }, + Type: TypeRegister, + Index: Index(len(a.Param.Register)), + }) + + a.Param.Register = append(a.Param.Register, Register{ + Register: register, }) } diff --git a/src/asm/RegisterLabel.go b/src/asm/RegisterLabel.go index 402954b..f5cb956 100644 --- a/src/asm/RegisterLabel.go +++ b/src/asm/RegisterLabel.go @@ -18,12 +18,15 @@ func (data *RegisterLabel) String() string { } // RegisterLabel adds an instruction with a register and a label. -func (a *Assembler) RegisterLabel(mnemonic Mnemonic, reg cpu.Register, label string) { +func (a *Assembler) RegisterLabel(mnemonic Mnemonic, register cpu.Register, label string) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &RegisterLabel{ - Register: reg, - Label: label, - }, + Type: TypeRegisterLabel, + Index: Index(len(a.Param.RegisterLabel)), + }) + + a.Param.RegisterLabel = append(a.Param.RegisterLabel, RegisterLabel{ + Register: register, + Label: label, }) } diff --git a/src/asm/RegisterNumber.go b/src/asm/RegisterNumber.go index f12bb81..2a5f711 100644 --- a/src/asm/RegisterNumber.go +++ b/src/asm/RegisterNumber.go @@ -18,12 +18,15 @@ func (data *RegisterNumber) String() string { } // RegisterNumber adds an instruction with a register and a number. -func (a *Assembler) RegisterNumber(mnemonic Mnemonic, reg cpu.Register, number int) { +func (a *Assembler) RegisterNumber(mnemonic Mnemonic, register cpu.Register, number int) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &RegisterNumber{ - Register: reg, - Number: number, - }, + Type: TypeRegisterNumber, + Index: Index(len(a.Param.RegisterNumber)), + }) + + a.Param.RegisterNumber = append(a.Param.RegisterNumber, RegisterNumber{ + Register: register, + Number: number, }) } diff --git a/src/asm/RegisterRegister.go b/src/asm/RegisterRegister.go index dadafd1..f6dd7e5 100644 --- a/src/asm/RegisterRegister.go +++ b/src/asm/RegisterRegister.go @@ -21,9 +21,12 @@ func (data *RegisterRegister) String() string { func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) { a.Instructions = append(a.Instructions, Instruction{ Mnemonic: mnemonic, - Data: &RegisterRegister{ - Destination: left, - Source: right, - }, + Type: TypeRegisterRegister, + Index: Index(len(a.Param.RegisterRegister)), + }) + + a.Param.RegisterRegister = append(a.Param.RegisterRegister, RegisterRegister{ + Destination: left, + Source: right, }) } diff --git a/src/asm/SetData.go b/src/asm/SetData.go new file mode 100644 index 0000000..257ded9 --- /dev/null +++ b/src/asm/SetData.go @@ -0,0 +1,12 @@ +package asm + +import "git.urbach.dev/cli/q/src/data" + +// SetData sets the data for the given label. +func (a *Assembler) SetData(label string, bytes []byte) { + if a.Data == nil { + a.Data = data.Data{} + } + + a.Data.Insert(label, bytes) +} diff --git a/src/asm/Type.go b/src/asm/Type.go new file mode 100644 index 0000000..25714f4 --- /dev/null +++ b/src/asm/Type.go @@ -0,0 +1,17 @@ +package asm + +type Type uint8 + +const ( + TypeNone Type = iota + TypeLabel + TypeNumber + TypeRegister + TypeRegisterLabel + TypeRegisterNumber + TypeRegisterRegister + TypeMemory + TypeMemoryLabel + TypeMemoryNumber + TypeMemoryRegister +) diff --git a/src/asm/bench_test.go b/src/asm/bench_test.go new file mode 100644 index 0000000..c060327 --- /dev/null +++ b/src/asm/bench_test.go @@ -0,0 +1,36 @@ +package asm_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/asm" +) + +func BenchmarkMerge(b *testing.B) { + n := 100 + all := make([]*asm.Assembler, 0, n) + + for range n { + f := &asm.Assembler{} + f.Number(asm.PUSH, 1) + f.Number(asm.PUSH, 2) + f.Number(asm.PUSH, 3) + all = append(all, f) + } + + for b.Loop() { + final := asm.Assembler{} + + for _, f := range all { + final.Merge(f) + } + } +} + +func BenchmarkNumber(b *testing.B) { + a := asm.Assembler{} + + for b.Loop() { + a.Number(asm.PUSH, 42) + } +} diff --git a/src/asmc/Finalize.go b/src/asmc/Finalize.go index 3f35552..e2b935c 100644 --- a/src/asmc/Finalize.go +++ b/src/asmc/Finalize.go @@ -7,7 +7,7 @@ import ( ) // Finalize generates the final machine code. -func Finalize(a asm.Assembler, dlls dll.List) ([]byte, []byte) { +func Finalize(a *asm.Assembler, dlls dll.List) ([]byte, []byte) { data, dataLabels := a.Data.Finalize() if config.TargetOS == config.Windows && len(data) == 0 { @@ -15,6 +15,7 @@ func Finalize(a asm.Assembler, dlls dll.List) ([]byte, []byte) { } c := compiler{ + assembler: a, code: make([]byte, 0, len(a.Instructions)*8), codeLabels: make(map[string]Address, 32), codePointers: make([]*pointer, 0, len(a.Instructions)*8), diff --git a/src/asmc/call.go b/src/asmc/call.go index 3bdc411..c941bb6 100644 --- a/src/asmc/call.go +++ b/src/asmc/call.go @@ -8,8 +8,9 @@ import ( ) func (c *compiler) call(x asm.Instruction) { - switch data := x.Data.(type) { - case *asm.Label: + switch x.Type { + case asm.TypeLabel: + data := c.assembler.Param.Label[x.Index] c.code = x86.Call(c.code, 0x00_00_00_00) size := 4 @@ -32,10 +33,12 @@ func (c *compiler) call(x asm.Instruction) { c.codePointers = append(c.codePointers, pointer) - case *asm.Register: + case asm.TypeRegister: + data := c.assembler.Param.Register[x.Index] c.code = x86.CallRegister(c.code, data.Register) - case *asm.Memory: + case asm.TypeMemory: + data := c.assembler.Param.Memory[x.Index] c.code = x86.CallAtMemory(c.code, data.Base, data.Offset) } } diff --git a/src/asmc/compileARM.go b/src/asmc/compileARM.go index 058f1f0..1c94725 100644 --- a/src/asmc/compileARM.go +++ b/src/asmc/compileARM.go @@ -13,8 +13,9 @@ import ( func (c *compiler) compileARM(x asm.Instruction) { switch x.Mnemonic { case asm.CALL: - switch data := x.Data.(type) { - case *asm.Label: + switch x.Type { + case asm.TypeLabel: + label := c.assembler.Param.Label[x.Index] position := Address(len(c.code)) c.append(arm.Call(0)) @@ -25,10 +26,10 @@ func (c *compiler) compileARM(x asm.Instruction) { } pointer.Resolve = func() Address { - destination, exists := c.codeLabels[data.Name] + destination, exists := c.codeLabels[label.Name] if !exists { - panic(fmt.Sprintf("unknown jump label %s", data.Name)) + panic(fmt.Sprintf("unknown jump label %s", label.Name)) } distance := (destination - position) / 4 @@ -39,13 +40,16 @@ func (c *compiler) compileARM(x asm.Instruction) { } case asm.LABEL: - c.codeLabels[x.Data.(*asm.Label).Name] = Address(len(c.code)) + label := c.assembler.Param.Label[x.Index] + c.codeLabels[label.Name] = Address(len(c.code)) c.append(0xa9be7bfd) c.append(0x910003fd) case asm.LOAD: - switch operands := x.Data.(type) { - case *asm.MemoryRegister: + switch x.Type { + case asm.TypeMemoryRegister: + operands := c.assembler.Param.MemoryRegister[x.Index] + if operands.Address.OffsetRegister == math.MaxUint8 { c.append(arm.LoadRegister(operands.Register, operands.Address.Base, int16(operands.Address.Offset), operands.Address.Length)) } else { @@ -55,14 +59,17 @@ func (c *compiler) compileARM(x asm.Instruction) { } case asm.MOVE: - switch operands := x.Data.(type) { - case *asm.RegisterRegister: + switch x.Type { + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.append(arm.MoveRegisterRegister(operands.Destination, operands.Source)) - case *asm.RegisterNumber: + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.append(arm.MoveRegisterNumber(operands.Register, operands.Number)) - case *asm.RegisterLabel: + case asm.TypeRegisterLabel: + operands := c.assembler.Param.RegisterLabel[x.Index] position := Address(len(c.code)) c.append(arm.LoadAddress(operands.Register, 0)) diff --git a/src/asmc/compileX86.go b/src/asmc/compileX86.go index 13ee381..f873cdd 100644 --- a/src/asmc/compileX86.go +++ b/src/asmc/compileX86.go @@ -8,40 +8,49 @@ import ( func (c *compiler) compileX86(x asm.Instruction) { switch x.Mnemonic { case asm.ADD: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.AddRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.AddRegisterRegister(c.code, operands.Destination, operands.Source) } case asm.AND: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.AndRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.AndRegisterRegister(c.code, operands.Destination, operands.Source) } case asm.SUB: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.SubRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.SubRegisterRegister(c.code, operands.Destination, operands.Source) } case asm.MUL: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.MulRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.MulRegisterRegister(c.code, operands.Destination, operands.Source) } case asm.DIV: - switch operands := x.Data.(type) { - case *asm.RegisterRegister: + switch x.Type { + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] if operands.Destination != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) } @@ -55,8 +64,9 @@ func (c *compiler) compileX86(x asm.Instruction) { } case asm.MODULO: - switch operands := x.Data.(type) { - case *asm.RegisterRegister: + switch x.Type { + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] if operands.Destination != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) } @@ -76,10 +86,12 @@ func (c *compiler) compileX86(x asm.Instruction) { return case asm.COMPARE: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.CompareRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.CompareRegisterRegister(c.code, operands.Destination, operands.Source) } @@ -90,7 +102,7 @@ func (c *compiler) compileX86(x asm.Instruction) { c.jump(x) case asm.LABEL: - label := x.Data.(*asm.Label) + label := c.assembler.Param.Label[x.Index] c.codeLabels[label.Name] = Address(len(c.code)) case asm.LOAD: @@ -100,30 +112,36 @@ func (c *compiler) compileX86(x asm.Instruction) { c.move(x) case asm.NEGATE: - switch operands := x.Data.(type) { - case *asm.Register: + switch x.Type { + case asm.TypeRegister: + operands := c.assembler.Param.Register[x.Index] c.code = x86.NegateRegister(c.code, operands.Register) } case asm.OR: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.OrRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.OrRegisterRegister(c.code, operands.Destination, operands.Source) } case asm.POP: - switch operands := x.Data.(type) { - case *asm.Register: + switch x.Type { + case asm.TypeRegister: + operands := c.assembler.Param.Register[x.Index] c.code = x86.PopRegister(c.code, operands.Register) } case asm.PUSH: - switch operands := x.Data.(type) { - case *asm.Number: + switch x.Type { + case asm.TypeNumber: + operands := c.assembler.Param.Number[x.Index] c.code = x86.PushNumber(c.code, int32(operands.Number)) - case *asm.Register: + case asm.TypeRegister: + operands := c.assembler.Param.Register[x.Index] c.code = x86.PushRegister(c.code, operands.Register) } @@ -131,14 +149,16 @@ func (c *compiler) compileX86(x asm.Instruction) { c.code = x86.Return(c.code) case asm.SHIFTL: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111) } case asm.SHIFTRS: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.ShiftRightSignedNumber(c.code, operands.Register, byte(operands.Number)&0b111111) } @@ -149,10 +169,12 @@ func (c *compiler) compileX86(x asm.Instruction) { c.code = x86.Syscall(c.code) case asm.XOR: - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.XorRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.XorRegisterRegister(c.code, operands.Destination, operands.Source) } diff --git a/src/asmc/compiler.go b/src/asmc/compiler.go index 056704b..890b2d6 100644 --- a/src/asmc/compiler.go +++ b/src/asmc/compiler.go @@ -1,8 +1,12 @@ package asmc -import "git.urbach.dev/cli/q/src/dll" +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/dll" +) type compiler struct { + assembler *asm.Assembler code []byte data []byte codeLabels map[string]Address diff --git a/src/asmc/dllCall.go b/src/asmc/dllCall.go index 22492e8..203fbc1 100644 --- a/src/asmc/dllCall.go +++ b/src/asmc/dllCall.go @@ -8,10 +8,10 @@ import ( ) func (c *compiler) dllCall(x asm.Instruction) { + label := c.assembler.Param.Label[x.Index] c.code = x86.CallAt(c.code, 0x00_00_00_00) next := Address(len(c.code)) position := next - 4 - label := x.Data.(*asm.Label) pointer := &pointer{ Position: Address(position), diff --git a/src/asmc/jump.go b/src/asmc/jump.go index 9964c8b..2b2f159 100644 --- a/src/asmc/jump.go +++ b/src/asmc/jump.go @@ -25,8 +25,8 @@ func (c *compiler) jump(x asm.Instruction) { c.code = x86.Jump8(c.code, 0x00) } + label := c.assembler.Param.Label[x.Index] size := 1 - label := x.Data.(*asm.Label) pointer := &pointer{ Position: Address(len(c.code) - size), diff --git a/src/asmc/load.go b/src/asmc/load.go index 8b28fa8..d6691f7 100644 --- a/src/asmc/load.go +++ b/src/asmc/load.go @@ -8,8 +8,10 @@ import ( ) func (c *compiler) load(x asm.Instruction) { - switch operands := x.Data.(type) { - case *asm.MemoryRegister: + switch x.Type { + case asm.TypeMemoryRegister: + operands := c.assembler.Param.MemoryRegister[x.Index] + if operands.Address.OffsetRegister == math.MaxUint8 { c.code = x86.LoadRegister(c.code, operands.Register, operands.Address.Base, operands.Address.Offset, operands.Address.Length) } else { diff --git a/src/asmc/move.go b/src/asmc/move.go index c3687fb..256ede6 100644 --- a/src/asmc/move.go +++ b/src/asmc/move.go @@ -8,14 +8,17 @@ import ( ) func (c *compiler) move(x asm.Instruction) { - switch operands := x.Data.(type) { - case *asm.RegisterNumber: + switch x.Type { + case asm.TypeRegisterNumber: + operands := c.assembler.Param.RegisterNumber[x.Index] c.code = x86.MoveRegisterNumber(c.code, operands.Register, operands.Number) - case *asm.RegisterRegister: + case asm.TypeRegisterRegister: + operands := c.assembler.Param.RegisterRegister[x.Index] c.code = x86.MoveRegisterRegister(c.code, operands.Destination, operands.Source) - case *asm.RegisterLabel: + case asm.TypeRegisterLabel: + operands := c.assembler.Param.RegisterLabel[x.Index] start := Address(len(c.code)) c.code = x86.LoadAddress(c.code, operands.Register, 0x00_00_00_00) end := Address(len(c.code)) diff --git a/src/asmc/store.go b/src/asmc/store.go index aa81960..f67b9b0 100644 --- a/src/asmc/store.go +++ b/src/asmc/store.go @@ -9,14 +9,17 @@ import ( ) func (c *compiler) store(x asm.Instruction) { - switch operands := x.Data.(type) { - case *asm.MemoryNumber: + switch x.Type { + case asm.TypeMemoryNumber: + operands := c.assembler.Param.MemoryNumber[x.Index] + if operands.Address.OffsetRegister == math.MaxUint8 { c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) } else { c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) } - case *asm.MemoryLabel: + case asm.TypeMemoryLabel: + operands := c.assembler.Param.MemoryLabel[x.Index] start := len(c.code) if operands.Address.OffsetRegister == math.MaxUint8 { @@ -27,14 +30,13 @@ func (c *compiler) store(x asm.Instruction) { size := 4 opSize := len(c.code) - size - start - memLabel := x.Data.(*asm.MemoryLabel) c.codePointers = append(c.codePointers, &pointer{ Position: Address(len(c.code) - size), OpSize: uint8(opSize), Size: uint8(size), Resolve: func() Address { - destination, exists := c.codeLabels[memLabel.Label] + destination, exists := c.codeLabels[operands.Label] if !exists { panic("unknown label") @@ -43,7 +45,9 @@ func (c *compiler) store(x asm.Instruction) { return config.BaseAddress + c.codeStart + destination }, }) - case *asm.MemoryRegister: + case asm.TypeMemoryRegister: + operands := c.assembler.Param.MemoryRegister[x.Index] + if operands.Address.OffsetRegister == math.MaxUint8 { c.code = x86.StoreRegister(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) } else { diff --git a/src/compiler/Compile.go b/src/compiler/Compile.go index 6fd88c1..cffb1d6 100644 --- a/src/compiler/Compile.go +++ b/src/compiler/Compile.go @@ -105,8 +105,7 @@ func Compile(constants <-chan *core.Constant, files <-chan *fs.File, functions < return result, function.Err } - result.InstructionCount += len(function.Assembler.Instructions) - result.DataCount += len(function.Assembler.Data) + result.Count(function) } // Check for unused imports in all files diff --git a/src/compiler/Result.go b/src/compiler/Result.go index b9bbb7b..04634ba 100644 --- a/src/compiler/Result.go +++ b/src/compiler/Result.go @@ -7,13 +7,12 @@ import ( // Result contains everything we need to write an executable file to disk. type Result struct { - Init *core.Function - Main *core.Function - Functions map[string]*core.Function - Traversed map[*core.Function]bool - Code []byte - Data []byte - DLLs dll.List - InstructionCount int - DataCount int + Init *core.Function + Main *core.Function + Functions map[string]*core.Function + Traversed map[*core.Function]bool + Code []byte + Data []byte + DLLs dll.List + Statistics } diff --git a/src/compiler/Statistics.go b/src/compiler/Statistics.go new file mode 100644 index 0000000..2c2cbbe --- /dev/null +++ b/src/compiler/Statistics.go @@ -0,0 +1,35 @@ +package compiler + +import "git.urbach.dev/cli/q/src/core" + +// Statistics contains counters for all functions in the build. +type Statistics struct { + InstructionCount int + DataCount int + LabelCount int + NumberCount int + RegisterCount int + RegisterLabelCount int + RegisterNumberCount int + RegisterRegisterCount int + MemoryCount int + MemoryLabelCount int + MemoryNumberCount int + MemoryRegisterCount int +} + +// Count adds statistics for this function. +func (s *Statistics) Count(function *core.Function) { + s.InstructionCount += len(function.Assembler.Instructions) + s.DataCount += len(function.Assembler.Data) + s.LabelCount += len(function.Assembler.Param.Label) + s.NumberCount += len(function.Assembler.Param.Number) + s.RegisterCount += len(function.Assembler.Param.Register) + s.RegisterLabelCount += len(function.Assembler.Param.RegisterLabel) + s.RegisterNumberCount += len(function.Assembler.Param.RegisterNumber) + s.RegisterRegisterCount += len(function.Assembler.Param.RegisterRegister) + s.MemoryCount += len(function.Assembler.Param.Memory) + s.MemoryLabelCount += len(function.Assembler.Param.MemoryLabel) + s.MemoryNumberCount += len(function.Assembler.Param.MemoryNumber) + s.MemoryRegisterCount += len(function.Assembler.Param.MemoryRegister) +} diff --git a/src/compiler/finalize.go b/src/compiler/finalize.go index 6437913..45d6c54 100644 --- a/src/compiler/finalize.go +++ b/src/compiler/finalize.go @@ -17,6 +17,18 @@ func (r *Result) finalize() { final := asm.Assembler{ Instructions: make([]asm.Instruction, 0, r.InstructionCount+8), Data: make(map[string][]byte, r.DataCount), + Param: asm.Param{ + Label: make([]asm.Label, 0, r.LabelCount), + Number: make([]asm.Number, 0, r.NumberCount), + Register: make([]asm.Register, 0, r.RegisterCount), + RegisterLabel: make([]asm.RegisterLabel, 0, r.RegisterLabelCount), + RegisterNumber: make([]asm.RegisterNumber, 0, r.RegisterNumberCount), + RegisterRegister: make([]asm.RegisterRegister, 0, r.RegisterRegisterCount), + Memory: make([]asm.Memory, 0, r.MemoryCount), + MemoryLabel: make([]asm.MemoryLabel, 0, r.MemoryLabelCount), + MemoryNumber: make([]asm.MemoryNumber, 0, r.MemoryNumberCount), + MemoryRegister: make([]asm.MemoryRegister, 0, r.MemoryRegisterCount), + }, } r.Traversed = make(map[*core.Function]bool, len(r.Functions)) @@ -24,7 +36,7 @@ func (r *Result) finalize() { // This will place the init function immediately after the entry point // and also add everything the init function calls recursively. r.eachFunction(r.Init, r.Traversed, func(f *core.Function) { - final.Merge(f.Assembler) + final.Merge(&f.Assembler) for _, library := range f.DLLs { for _, fn := range library.Functions { @@ -50,5 +62,5 @@ func (r *Result) finalize() { final.DLLCall("kernel32.ExitProcess") } - r.Code, r.Data = asmc.Finalize(final, r.DLLs) + r.Code, r.Data = asmc.Finalize(&final, r.DLLs) } diff --git a/src/core/PrintInstructions.go b/src/core/PrintInstructions.go index cc81953..a5d5f4c 100644 --- a/src/core/PrintInstructions.go +++ b/src/core/PrintInstructions.go @@ -26,19 +26,16 @@ func (f *Function) PrintInstructions() { switch x.Mnemonic { case asm.LABEL: - ansi.Yellow.Printf("%-44s", x.Data.String()+":") + label := f.Assembler.Param.Label[x.Index] + ansi.Yellow.Printf("%-44s", label.String()+":") case asm.COMMENT: - ansi.Dim.Printf("%-44s", x.Data.String()) + label := f.Assembler.Param.Label[x.Index] + ansi.Dim.Printf("%-44s", label.String()) default: ansi.Green.Printf("%-12s", x.Mnemonic.String()) - - if x.Data != nil { - fmt.Printf("%-32s", x.Data.String()) - } else { - fmt.Printf("%-32s", "") - } + fmt.Printf("%-32s", x.DataString(&f.Assembler)) } registers := bytes.Buffer{}