Implemented jumps and jump related optimizations
All checks were successful
/ test (push) Successful in 15s
All checks were successful
/ test (push) Successful in 15s
This commit is contained in:
parent
abbc4d9ae2
commit
312e3b2e31
5 changed files with 120 additions and 6 deletions
|
@ -17,9 +17,18 @@ type Assembler struct {
|
||||||
|
|
||||||
// Append adds another instruction.
|
// Append adds another instruction.
|
||||||
func (a *Assembler) Append(instr Instruction) {
|
func (a *Assembler) Append(instr Instruction) {
|
||||||
|
if a.Skip(instr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
a.Instructions = append(a.Instructions, instr)
|
a.Instructions = append(a.Instructions, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Last returns the last instruction.
|
||||||
|
func (a *Assembler) Last() Instruction {
|
||||||
|
return a.Instructions[len(a.Instructions)-1]
|
||||||
|
}
|
||||||
|
|
||||||
// Compile compiles the instructions to machine code.
|
// Compile compiles the instructions to machine code.
|
||||||
func (a *Assembler) Compile(b *build.Build) (code []byte, data []byte) {
|
func (a *Assembler) Compile(b *build.Build) (code []byte, data []byte) {
|
||||||
data, dataLabels := a.Data.Finalize()
|
data, dataLabels := a.Data.Finalize()
|
||||||
|
@ -65,7 +74,10 @@ func (a *Assembler) Compile(b *build.Build) (code []byte, data []byte) {
|
||||||
// Merge combines the contents of this assembler with another one.
|
// Merge combines the contents of this assembler with another one.
|
||||||
func (a *Assembler) Merge(b *Assembler) {
|
func (a *Assembler) Merge(b *Assembler) {
|
||||||
maps.Copy(a.Data, b.Data)
|
maps.Copy(a.Data, b.Data)
|
||||||
a.Instructions = append(a.Instructions, b.Instructions...)
|
|
||||||
|
for _, instr := range b.Instructions {
|
||||||
|
a.Append(instr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetData sets the data for the given label.
|
// SetData sets the data for the given label.
|
||||||
|
@ -76,3 +88,60 @@ func (a *Assembler) SetData(label string, bytes []byte) {
|
||||||
|
|
||||||
a.Data.Insert(label, bytes)
|
a.Data.Insert(label, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLast sets the last instruction.
|
||||||
|
func (a *Assembler) SetLast(instr Instruction) {
|
||||||
|
a.Instructions[len(a.Instructions)-1] = instr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip returns true if appending the instruction can be skipped.
|
||||||
|
func (a *Assembler) Skip(instr Instruction) bool {
|
||||||
|
if len(a.Instructions) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *FunctionEnd:
|
||||||
|
// Call + FunctionEnd can be replaced by a single Jump
|
||||||
|
call, isCall := a.Last().(*Call)
|
||||||
|
|
||||||
|
if isCall {
|
||||||
|
a.SetLast(&Jump{Label: call.Label})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Label:
|
||||||
|
// Jump + Label can be replaced by just the Label if both addresses are equal
|
||||||
|
jump, isJump := a.Last().(*Jump)
|
||||||
|
|
||||||
|
if isJump && jump.Label == instr.Name {
|
||||||
|
a.SetLast(instr)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Return:
|
||||||
|
// Call + Return can be replaced by a single Jump
|
||||||
|
call, isCall := a.Last().(*Call)
|
||||||
|
|
||||||
|
if isCall {
|
||||||
|
a.SetLast(&Jump{Label: call.Label})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump + Return is unnecessary
|
||||||
|
_, isJump := a.Last().(*Jump)
|
||||||
|
|
||||||
|
if isJump {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return + Return is unnecessary
|
||||||
|
_, isReturn := a.Last().(*Return)
|
||||||
|
|
||||||
|
if isReturn {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -11,6 +11,10 @@ type Call struct {
|
||||||
type FunctionStart struct{}
|
type FunctionStart struct{}
|
||||||
type FunctionEnd struct{}
|
type FunctionEnd struct{}
|
||||||
|
|
||||||
|
type Jump struct {
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
type Label struct {
|
type Label struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,25 @@ func (c *compilerARM) Compile(instr Instruction) {
|
||||||
offset := (address - start) / 4
|
offset := (address - start) / 4
|
||||||
binary.LittleEndian.PutUint32(c.code[start:start+4], arm.Call(offset))
|
binary.LittleEndian.PutUint32(c.code[start:start+4], arm.Call(offset))
|
||||||
})
|
})
|
||||||
|
case *Jump:
|
||||||
|
start := len(c.code)
|
||||||
|
c.append(arm.Jump(0))
|
||||||
|
|
||||||
|
c.Defer(func() {
|
||||||
|
address, exists := c.labels[instr.Label]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
panic("unknown label: " + instr.Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := (address - start) / 4
|
||||||
|
|
||||||
|
if offset != (offset & 0b11_11111111_11111111_11111111) {
|
||||||
|
panic("not implemented: long jumps")
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.LittleEndian.PutUint32(c.code[start:start+4], arm.Jump(offset))
|
||||||
|
})
|
||||||
case *FunctionStart:
|
case *FunctionStart:
|
||||||
c.append(arm.StorePair(arm.FP, arm.LR, arm.SP, -16))
|
c.append(arm.StorePair(arm.FP, arm.LR, arm.SP, -16))
|
||||||
c.append(arm.MoveRegisterRegister(arm.FP, arm.SP))
|
c.append(arm.MoveRegisterRegister(arm.FP, arm.SP))
|
||||||
|
@ -59,5 +78,7 @@ func (c *compilerARM) Compile(instr Instruction) {
|
||||||
c.append(arm.Return())
|
c.append(arm.Return())
|
||||||
case *Syscall:
|
case *Syscall:
|
||||||
c.append(arm.Syscall())
|
c.append(arm.Syscall())
|
||||||
|
default:
|
||||||
|
panic("unknown instruction")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package asm
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
"git.urbach.dev/cli/q/src/sizeof"
|
||||||
"git.urbach.dev/cli/q/src/x86"
|
"git.urbach.dev/cli/q/src/x86"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +27,27 @@ func (c *compilerX86) Compile(instr Instruction) {
|
||||||
offset := address - end
|
offset := address - end
|
||||||
binary.LittleEndian.PutUint32(c.code[end-4:end], uint32(offset))
|
binary.LittleEndian.PutUint32(c.code[end-4:end], uint32(offset))
|
||||||
})
|
})
|
||||||
|
case *FunctionStart:
|
||||||
|
case *FunctionEnd:
|
||||||
|
case *Jump:
|
||||||
|
c.code = x86.Jump8(c.code, 0)
|
||||||
|
end := len(c.code)
|
||||||
|
|
||||||
|
c.Defer(func() {
|
||||||
|
address, exists := c.labels[instr.Label]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
panic("unknown label: " + instr.Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := address - end
|
||||||
|
|
||||||
|
if sizeof.Signed(offset) > 1 {
|
||||||
|
panic("not implemented: long jumps")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.code[end-1] = byte(offset)
|
||||||
|
})
|
||||||
case *Label:
|
case *Label:
|
||||||
c.labels[instr.Name] = len(c.code)
|
c.labels[instr.Name] = len(c.code)
|
||||||
case *MoveRegisterLabel:
|
case *MoveRegisterLabel:
|
||||||
|
@ -50,5 +72,7 @@ func (c *compilerX86) Compile(instr Instruction) {
|
||||||
c.code = x86.Return(c.code)
|
c.code = x86.Return(c.code)
|
||||||
case *Syscall:
|
case *Syscall:
|
||||||
c.code = x86.Syscall(c.code)
|
c.code = x86.Syscall(c.code)
|
||||||
|
default:
|
||||||
|
panic("unknown instruction")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -93,11 +93,7 @@ func (f *Function) Compile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.UniqueName != "core.exit" {
|
if f.UniqueName != "core.exit" {
|
||||||
switch f.Assembler.Instructions[len(f.Assembler.Instructions)-1].(type) {
|
f.Assembler.Append(&asm.Return{})
|
||||||
case *asm.Return:
|
|
||||||
default:
|
|
||||||
f.Assembler.Append(&asm.Return{})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue