Improved assembler performance

This commit is contained in:
2025-03-11 06:31:21 +01:00
parent d2ad8c8310
commit e7a06f5b26
37 changed files with 412 additions and 166 deletions

View File

@ -1,28 +1,10 @@
package asm package asm
import ( import "git.urbach.dev/cli/q/src/data"
"maps"
"git.urbach.dev/cli/q/src/data"
)
// Assembler contains a list of instructions. // Assembler contains a list of instructions.
type Assembler struct { type Assembler struct {
Data data.Data Data data.Data
Instructions []Instruction Instructions []Instruction
} Param Param
// 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)
} }

View File

@ -15,12 +15,12 @@ func (a *Assembler) CanSkip(mnemonic Mnemonic, left cpu.Register, right cpu.Regi
last := a.Instructions[len(a.Instructions)-1] last := a.Instructions[len(a.Instructions)-1]
if mnemonic == MOVE && last.Mnemonic == MOVE { if mnemonic == MOVE && last.Mnemonic == MOVE {
lastData, isRegReg := last.Data.(*RegisterRegister) if last.Type != TypeRegisterRegister {
if !isRegReg {
return false return false
} }
lastData := a.Param.RegisterRegister[last.Index]
if lastData.Destination == right && lastData.Source == left { if lastData.Destination == right && lastData.Source == left {
return true return true
} }

View File

@ -12,10 +12,10 @@ func (a *Assembler) CanSkipReturn() bool {
return true return true
} }
if last.Mnemonic == CALL { if last.Mnemonic == CALL && last.Type == TypeLabel {
label, isLabel := last.Data.(*Label) label := a.Param.Label[last.Index]
if isLabel && label.String() == "core.exit" { if label.String() == "core.exit" {
return true return true
} }
} }

28
src/asm/DataString.go Normal file
View File

@ -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 ""
}
}

3
src/asm/Index.go Normal file
View File

@ -0,0 +1,3 @@
package asm
type Index = uint16

View File

@ -1,9 +1,8 @@
package asm package asm
import "fmt"
// Instruction represents a single instruction which can be converted to machine code. // Instruction represents a single instruction which can be converted to machine code.
type Instruction struct { type Instruction struct {
Data fmt.Stringer
Mnemonic Mnemonic Mnemonic Mnemonic
Type Type
Index Index
} }

View File

@ -2,22 +2,12 @@ package asm
// Comment adds a comment at the current position. // Comment adds a comment at the current position.
func (a *Assembler) Comment(text string) { func (a *Assembler) Comment(text string) {
a.Instructions = append(a.Instructions, Instruction{ a.Label(COMMENT, text)
Mnemonic: COMMENT,
Data: &Label{
Name: text,
},
})
} }
// DLLCall calls a function in a DLL file. // DLLCall calls a function in a DLL file.
func (a *Assembler) DLLCall(name string) { func (a *Assembler) DLLCall(name string) {
a.Instructions = append(a.Instructions, Instruction{ a.Label(DLLCALL, name)
Mnemonic: DLLCALL,
Data: &Label{
Name: name,
},
})
} }
// Return returns back to the caller. // Return returns back to the caller.

View File

@ -14,8 +14,11 @@ func (data *Label) String() string {
func (a *Assembler) Label(mnemonic Mnemonic, name string) { func (a *Assembler) Label(mnemonic Mnemonic, name string) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &Label{ Type: TypeLabel,
Index: Index(len(a.Param.Label)),
})
a.Param.Label = append(a.Param.Label, Label{
Name: name, Name: name,
},
}) })
} }

View File

@ -56,6 +56,9 @@ func (mem *Memory) String() string {
func (a *Assembler) Memory(mnemonic Mnemonic, address Memory) { func (a *Assembler) Memory(mnemonic Mnemonic, address Memory) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &address, Type: TypeMemory,
Index: Index(len(a.Param.Memory)),
}) })
a.Param.Memory = append(a.Param.Memory, address)
} }

View File

@ -15,9 +15,12 @@ func (data *MemoryLabel) String() string {
func (a *Assembler) MemoryLabel(mnemonic Mnemonic, address Memory, label string) { func (a *Assembler) MemoryLabel(mnemonic Mnemonic, address Memory, label string) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &MemoryLabel{ Type: TypeMemoryLabel,
Index: Index(len(a.Param.MemoryLabel)),
})
a.Param.MemoryLabel = append(a.Param.MemoryLabel, MemoryLabel{
Address: address, Address: address,
Label: label, Label: label,
},
}) })
} }

View File

@ -17,9 +17,12 @@ func (data *MemoryNumber) String() string {
func (a *Assembler) MemoryNumber(mnemonic Mnemonic, address Memory, number int) { func (a *Assembler) MemoryNumber(mnemonic Mnemonic, address Memory, number int) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &MemoryNumber{ Type: TypeMemoryNumber,
Index: Index(len(a.Param.MemoryNumber)),
})
a.Param.MemoryNumber = append(a.Param.MemoryNumber, MemoryNumber{
Address: address, Address: address,
Number: number, Number: number,
},
}) })
} }

View File

@ -21,9 +21,12 @@ func (data *MemoryRegister) String() string {
func (a *Assembler) MemoryRegister(mnemonic Mnemonic, address Memory, register cpu.Register) { func (a *Assembler) MemoryRegister(mnemonic Mnemonic, address Memory, register cpu.Register) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &MemoryRegister{ Type: TypeMemoryRegister,
Index: Index(len(a.Param.MemoryRegister)),
})
a.Param.MemoryRegister = append(a.Param.MemoryRegister, MemoryRegister{
Address: address, Address: address,
Register: register, Register: register,
},
}) })
} }

46
src/asm/Merge.go Normal file
View File

@ -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...)
}

View File

@ -18,8 +18,11 @@ func (data *Number) String() string {
func (a *Assembler) Number(mnemonic Mnemonic, number int) { func (a *Assembler) Number(mnemonic Mnemonic, number int) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &Number{ Type: TypeNumber,
Index: Index(len(a.Param.Number)),
})
a.Param.Number = append(a.Param.Number, Number{
Number: number, Number: number,
},
}) })
} }

15
src/asm/Param.go Normal file
View File

@ -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
}

View File

@ -18,8 +18,11 @@ func (data *Register) String() string {
func (a *Assembler) Register(mnemonic Mnemonic, register cpu.Register) { func (a *Assembler) Register(mnemonic Mnemonic, register cpu.Register) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &Register{ Type: TypeRegister,
Index: Index(len(a.Param.Register)),
})
a.Param.Register = append(a.Param.Register, Register{
Register: register, Register: register,
},
}) })
} }

View File

@ -18,12 +18,15 @@ func (data *RegisterLabel) String() string {
} }
// RegisterLabel adds an instruction with a register and a label. // 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{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &RegisterLabel{ Type: TypeRegisterLabel,
Register: reg, Index: Index(len(a.Param.RegisterLabel)),
})
a.Param.RegisterLabel = append(a.Param.RegisterLabel, RegisterLabel{
Register: register,
Label: label, Label: label,
},
}) })
} }

View File

@ -18,12 +18,15 @@ func (data *RegisterNumber) String() string {
} }
// RegisterNumber adds an instruction with a register and a number. // 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{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &RegisterNumber{ Type: TypeRegisterNumber,
Register: reg, Index: Index(len(a.Param.RegisterNumber)),
})
a.Param.RegisterNumber = append(a.Param.RegisterNumber, RegisterNumber{
Register: register,
Number: number, Number: number,
},
}) })
} }

View File

@ -21,9 +21,12 @@ func (data *RegisterRegister) String() string {
func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) { func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic, Mnemonic: mnemonic,
Data: &RegisterRegister{ Type: TypeRegisterRegister,
Index: Index(len(a.Param.RegisterRegister)),
})
a.Param.RegisterRegister = append(a.Param.RegisterRegister, RegisterRegister{
Destination: left, Destination: left,
Source: right, Source: right,
},
}) })
} }

12
src/asm/SetData.go Normal file
View File

@ -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)
}

17
src/asm/Type.go Normal file
View File

@ -0,0 +1,17 @@
package asm
type Type uint8
const (
TypeNone Type = iota
TypeLabel
TypeNumber
TypeRegister
TypeRegisterLabel
TypeRegisterNumber
TypeRegisterRegister
TypeMemory
TypeMemoryLabel
TypeMemoryNumber
TypeMemoryRegister
)

36
src/asm/bench_test.go Normal file
View File

@ -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)
}
}

View File

@ -7,7 +7,7 @@ import (
) )
// Finalize generates the final machine code. // 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() data, dataLabels := a.Data.Finalize()
if config.TargetOS == config.Windows && len(data) == 0 { if config.TargetOS == config.Windows && len(data) == 0 {
@ -15,6 +15,7 @@ func Finalize(a asm.Assembler, dlls dll.List) ([]byte, []byte) {
} }
c := compiler{ c := compiler{
assembler: a,
code: make([]byte, 0, len(a.Instructions)*8), code: make([]byte, 0, len(a.Instructions)*8),
codeLabels: make(map[string]Address, 32), codeLabels: make(map[string]Address, 32),
codePointers: make([]*pointer, 0, len(a.Instructions)*8), codePointers: make([]*pointer, 0, len(a.Instructions)*8),

View File

@ -8,8 +8,9 @@ import (
) )
func (c *compiler) call(x asm.Instruction) { func (c *compiler) call(x asm.Instruction) {
switch data := x.Data.(type) { switch x.Type {
case *asm.Label: case asm.TypeLabel:
data := c.assembler.Param.Label[x.Index]
c.code = x86.Call(c.code, 0x00_00_00_00) c.code = x86.Call(c.code, 0x00_00_00_00)
size := 4 size := 4
@ -32,10 +33,12 @@ func (c *compiler) call(x asm.Instruction) {
c.codePointers = append(c.codePointers, pointer) 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) 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) c.code = x86.CallAtMemory(c.code, data.Base, data.Offset)
} }
} }

View File

@ -13,8 +13,9 @@ import (
func (c *compiler) compileARM(x asm.Instruction) { func (c *compiler) compileARM(x asm.Instruction) {
switch x.Mnemonic { switch x.Mnemonic {
case asm.CALL: case asm.CALL:
switch data := x.Data.(type) { switch x.Type {
case *asm.Label: case asm.TypeLabel:
label := c.assembler.Param.Label[x.Index]
position := Address(len(c.code)) position := Address(len(c.code))
c.append(arm.Call(0)) c.append(arm.Call(0))
@ -25,10 +26,10 @@ func (c *compiler) compileARM(x asm.Instruction) {
} }
pointer.Resolve = func() Address { pointer.Resolve = func() Address {
destination, exists := c.codeLabels[data.Name] destination, exists := c.codeLabels[label.Name]
if !exists { if !exists {
panic(fmt.Sprintf("unknown jump label %s", data.Name)) panic(fmt.Sprintf("unknown jump label %s", label.Name))
} }
distance := (destination - position) / 4 distance := (destination - position) / 4
@ -39,13 +40,16 @@ func (c *compiler) compileARM(x asm.Instruction) {
} }
case asm.LABEL: 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(0xa9be7bfd)
c.append(0x910003fd) c.append(0x910003fd)
case asm.LOAD: case asm.LOAD:
switch operands := x.Data.(type) { switch x.Type {
case *asm.MemoryRegister: case asm.TypeMemoryRegister:
operands := c.assembler.Param.MemoryRegister[x.Index]
if operands.Address.OffsetRegister == math.MaxUint8 { if operands.Address.OffsetRegister == math.MaxUint8 {
c.append(arm.LoadRegister(operands.Register, operands.Address.Base, int16(operands.Address.Offset), operands.Address.Length)) c.append(arm.LoadRegister(operands.Register, operands.Address.Base, int16(operands.Address.Offset), operands.Address.Length))
} else { } else {
@ -55,14 +59,17 @@ func (c *compiler) compileARM(x asm.Instruction) {
} }
case asm.MOVE: case asm.MOVE:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterRegister: case asm.TypeRegisterRegister:
operands := c.assembler.Param.RegisterRegister[x.Index]
c.append(arm.MoveRegisterRegister(operands.Destination, operands.Source)) 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)) 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)) position := Address(len(c.code))
c.append(arm.LoadAddress(operands.Register, 0)) c.append(arm.LoadAddress(operands.Register, 0))

View File

@ -8,40 +8,49 @@ import (
func (c *compiler) compileX86(x asm.Instruction) { func (c *compiler) compileX86(x asm.Instruction) {
switch x.Mnemonic { switch x.Mnemonic {
case asm.ADD: case asm.ADD:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.AddRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.AddRegisterRegister(c.code, operands.Destination, operands.Source)
} }
case asm.AND: case asm.AND:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.AndRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.AndRegisterRegister(c.code, operands.Destination, operands.Source)
} }
case asm.SUB: case asm.SUB:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.SubRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.SubRegisterRegister(c.code, operands.Destination, operands.Source)
} }
case asm.MUL: case asm.MUL:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.MulRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.MulRegisterRegister(c.code, operands.Destination, operands.Source)
} }
case asm.DIV: case asm.DIV:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterRegister: case asm.TypeRegisterRegister:
operands := c.assembler.Param.RegisterRegister[x.Index]
if operands.Destination != x86.RAX { if operands.Destination != x86.RAX {
c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination)
} }
@ -55,8 +64,9 @@ func (c *compiler) compileX86(x asm.Instruction) {
} }
case asm.MODULO: case asm.MODULO:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterRegister: case asm.TypeRegisterRegister:
operands := c.assembler.Param.RegisterRegister[x.Index]
if operands.Destination != x86.RAX { if operands.Destination != x86.RAX {
c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination)
} }
@ -76,10 +86,12 @@ func (c *compiler) compileX86(x asm.Instruction) {
return return
case asm.COMPARE: case asm.COMPARE:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.CompareRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.CompareRegisterRegister(c.code, operands.Destination, operands.Source)
} }
@ -90,7 +102,7 @@ func (c *compiler) compileX86(x asm.Instruction) {
c.jump(x) c.jump(x)
case asm.LABEL: case asm.LABEL:
label := x.Data.(*asm.Label) label := c.assembler.Param.Label[x.Index]
c.codeLabels[label.Name] = Address(len(c.code)) c.codeLabels[label.Name] = Address(len(c.code))
case asm.LOAD: case asm.LOAD:
@ -100,30 +112,36 @@ func (c *compiler) compileX86(x asm.Instruction) {
c.move(x) c.move(x)
case asm.NEGATE: case asm.NEGATE:
switch operands := x.Data.(type) { switch x.Type {
case *asm.Register: case asm.TypeRegister:
operands := c.assembler.Param.Register[x.Index]
c.code = x86.NegateRegister(c.code, operands.Register) c.code = x86.NegateRegister(c.code, operands.Register)
} }
case asm.OR: case asm.OR:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.OrRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.OrRegisterRegister(c.code, operands.Destination, operands.Source)
} }
case asm.POP: case asm.POP:
switch operands := x.Data.(type) { switch x.Type {
case *asm.Register: case asm.TypeRegister:
operands := c.assembler.Param.Register[x.Index]
c.code = x86.PopRegister(c.code, operands.Register) c.code = x86.PopRegister(c.code, operands.Register)
} }
case asm.PUSH: case asm.PUSH:
switch operands := x.Data.(type) { switch x.Type {
case *asm.Number: case asm.TypeNumber:
operands := c.assembler.Param.Number[x.Index]
c.code = x86.PushNumber(c.code, int32(operands.Number)) 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) 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) c.code = x86.Return(c.code)
case asm.SHIFTL: case asm.SHIFTL:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111) c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111)
} }
case asm.SHIFTRS: case asm.SHIFTRS:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.ShiftRightSignedNumber(c.code, operands.Register, byte(operands.Number)&0b111111) 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) c.code = x86.Syscall(c.code)
case asm.XOR: case asm.XOR:
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.XorRegisterNumber(c.code, operands.Register, operands.Number) 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) c.code = x86.XorRegisterRegister(c.code, operands.Destination, operands.Source)
} }

View File

@ -1,8 +1,12 @@
package asmc 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 { type compiler struct {
assembler *asm.Assembler
code []byte code []byte
data []byte data []byte
codeLabels map[string]Address codeLabels map[string]Address

View File

@ -8,10 +8,10 @@ import (
) )
func (c *compiler) dllCall(x asm.Instruction) { func (c *compiler) dllCall(x asm.Instruction) {
label := c.assembler.Param.Label[x.Index]
c.code = x86.CallAt(c.code, 0x00_00_00_00) c.code = x86.CallAt(c.code, 0x00_00_00_00)
next := Address(len(c.code)) next := Address(len(c.code))
position := next - 4 position := next - 4
label := x.Data.(*asm.Label)
pointer := &pointer{ pointer := &pointer{
Position: Address(position), Position: Address(position),

View File

@ -25,8 +25,8 @@ func (c *compiler) jump(x asm.Instruction) {
c.code = x86.Jump8(c.code, 0x00) c.code = x86.Jump8(c.code, 0x00)
} }
label := c.assembler.Param.Label[x.Index]
size := 1 size := 1
label := x.Data.(*asm.Label)
pointer := &pointer{ pointer := &pointer{
Position: Address(len(c.code) - size), Position: Address(len(c.code) - size),

View File

@ -8,8 +8,10 @@ import (
) )
func (c *compiler) load(x asm.Instruction) { func (c *compiler) load(x asm.Instruction) {
switch operands := x.Data.(type) { switch x.Type {
case *asm.MemoryRegister: case asm.TypeMemoryRegister:
operands := c.assembler.Param.MemoryRegister[x.Index]
if operands.Address.OffsetRegister == math.MaxUint8 { if operands.Address.OffsetRegister == math.MaxUint8 {
c.code = x86.LoadRegister(c.code, operands.Register, operands.Address.Base, operands.Address.Offset, operands.Address.Length) c.code = x86.LoadRegister(c.code, operands.Register, operands.Address.Base, operands.Address.Offset, operands.Address.Length)
} else { } else {

View File

@ -8,14 +8,17 @@ import (
) )
func (c *compiler) move(x asm.Instruction) { func (c *compiler) move(x asm.Instruction) {
switch operands := x.Data.(type) { switch x.Type {
case *asm.RegisterNumber: case asm.TypeRegisterNumber:
operands := c.assembler.Param.RegisterNumber[x.Index]
c.code = x86.MoveRegisterNumber(c.code, operands.Register, operands.Number) 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) 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)) start := Address(len(c.code))
c.code = x86.LoadAddress(c.code, operands.Register, 0x00_00_00_00) c.code = x86.LoadAddress(c.code, operands.Register, 0x00_00_00_00)
end := Address(len(c.code)) end := Address(len(c.code))

View File

@ -9,14 +9,17 @@ import (
) )
func (c *compiler) store(x asm.Instruction) { func (c *compiler) store(x asm.Instruction) {
switch operands := x.Data.(type) { switch x.Type {
case *asm.MemoryNumber: case asm.TypeMemoryNumber:
operands := c.assembler.Param.MemoryNumber[x.Index]
if operands.Address.OffsetRegister == math.MaxUint8 { if operands.Address.OffsetRegister == math.MaxUint8 {
c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number)
} else { } else {
c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) 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) start := len(c.code)
if operands.Address.OffsetRegister == math.MaxUint8 { if operands.Address.OffsetRegister == math.MaxUint8 {
@ -27,14 +30,13 @@ func (c *compiler) store(x asm.Instruction) {
size := 4 size := 4
opSize := len(c.code) - size - start opSize := len(c.code) - size - start
memLabel := x.Data.(*asm.MemoryLabel)
c.codePointers = append(c.codePointers, &pointer{ c.codePointers = append(c.codePointers, &pointer{
Position: Address(len(c.code) - size), Position: Address(len(c.code) - size),
OpSize: uint8(opSize), OpSize: uint8(opSize),
Size: uint8(size), Size: uint8(size),
Resolve: func() Address { Resolve: func() Address {
destination, exists := c.codeLabels[memLabel.Label] destination, exists := c.codeLabels[operands.Label]
if !exists { if !exists {
panic("unknown label") panic("unknown label")
@ -43,7 +45,9 @@ func (c *compiler) store(x asm.Instruction) {
return config.BaseAddress + c.codeStart + destination 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 { if operands.Address.OffsetRegister == math.MaxUint8 {
c.code = x86.StoreRegister(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) c.code = x86.StoreRegister(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register)
} else { } else {

View File

@ -105,8 +105,7 @@ func Compile(constants <-chan *core.Constant, files <-chan *fs.File, functions <
return result, function.Err return result, function.Err
} }
result.InstructionCount += len(function.Assembler.Instructions) result.Count(function)
result.DataCount += len(function.Assembler.Data)
} }
// Check for unused imports in all files // Check for unused imports in all files

View File

@ -14,6 +14,5 @@ type Result struct {
Code []byte Code []byte
Data []byte Data []byte
DLLs dll.List DLLs dll.List
InstructionCount int Statistics
DataCount int
} }

View File

@ -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)
}

View File

@ -17,6 +17,18 @@ func (r *Result) finalize() {
final := asm.Assembler{ final := asm.Assembler{
Instructions: make([]asm.Instruction, 0, r.InstructionCount+8), Instructions: make([]asm.Instruction, 0, r.InstructionCount+8),
Data: make(map[string][]byte, r.DataCount), 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)) 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 // This will place the init function immediately after the entry point
// and also add everything the init function calls recursively. // and also add everything the init function calls recursively.
r.eachFunction(r.Init, r.Traversed, func(f *core.Function) { r.eachFunction(r.Init, r.Traversed, func(f *core.Function) {
final.Merge(f.Assembler) final.Merge(&f.Assembler)
for _, library := range f.DLLs { for _, library := range f.DLLs {
for _, fn := range library.Functions { for _, fn := range library.Functions {
@ -50,5 +62,5 @@ func (r *Result) finalize() {
final.DLLCall("kernel32.ExitProcess") final.DLLCall("kernel32.ExitProcess")
} }
r.Code, r.Data = asmc.Finalize(final, r.DLLs) r.Code, r.Data = asmc.Finalize(&final, r.DLLs)
} }

View File

@ -26,19 +26,16 @@ func (f *Function) PrintInstructions() {
switch x.Mnemonic { switch x.Mnemonic {
case asm.LABEL: 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: case asm.COMMENT:
ansi.Dim.Printf("%-44s", x.Data.String()) label := f.Assembler.Param.Label[x.Index]
ansi.Dim.Printf("%-44s", label.String())
default: default:
ansi.Green.Printf("%-12s", x.Mnemonic.String()) ansi.Green.Printf("%-12s", x.Mnemonic.String())
fmt.Printf("%-32s", x.DataString(&f.Assembler))
if x.Data != nil {
fmt.Printf("%-32s", x.Data.String())
} else {
fmt.Printf("%-32s", "")
}
} }
registers := bytes.Buffer{} registers := bytes.Buffer{}