Implemented register calls

This commit is contained in:
Eduard Urbach 2025-03-02 17:53:18 +01:00
parent d7f30d8319
commit c3054369e3
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
16 changed files with 133 additions and 43 deletions

View File

@ -21,11 +21,11 @@ func (a *Assembler) CanSkip(mnemonic Mnemonic, left cpu.Register, right cpu.Regi
return false return false
} }
if lastData.Destination == left && lastData.Source == right { if lastData.Destination == right && lastData.Source == left {
return true return true
} }
if lastData.Destination == right && lastData.Source == left { if lastData.Destination == left && lastData.Source == right {
return true return true
} }
} }

View File

@ -10,16 +10,6 @@ func (a *Assembler) Comment(text string) {
}) })
} }
// Call calls a function whose position is identified by a label.
func (a *Assembler) Call(name string) {
a.Instructions = append(a.Instructions, Instruction{
Mnemonic: CALL,
Data: &Label{
Name: name,
},
})
}
// 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.Instructions = append(a.Instructions, Instruction{

View File

@ -6,9 +6,10 @@ import (
) )
func (c *compiler) call(x asm.Instruction) { func (c *compiler) call(x asm.Instruction) {
switch data := x.Data.(type) {
case *asm.Label:
c.code = x86.Call(c.code, 0x00_00_00_00) c.code = x86.Call(c.code, 0x00_00_00_00)
size := 4 size := 4
label := x.Data.(*asm.Label)
pointer := &pointer{ pointer := &pointer{
Position: Address(len(c.code) - size), Position: Address(len(c.code) - size),
@ -17,7 +18,7 @@ func (c *compiler) call(x asm.Instruction) {
} }
pointer.Resolve = func() Address { pointer.Resolve = func() Address {
destination, exists := c.codeLabels[label.Name] destination, exists := c.codeLabels[data.Name]
if !exists { if !exists {
panic("unknown jump label") panic("unknown jump label")
@ -28,4 +29,8 @@ func (c *compiler) call(x asm.Instruction) {
} }
c.codePointers = append(c.codePointers, pointer) c.codePointers = append(c.codePointers, pointer)
case *asm.Register:
c.code = x86.CallRegister(c.code, data.Register)
}
} }

View File

@ -16,8 +16,13 @@ func (r *Result) eachFunction(caller *core.Function, traversed map[*core.Functio
continue continue
} }
name := x.Data.(*asm.Label).Name label, isLabel := x.Data.(*asm.Label)
callee, exists := r.Functions[name]
if !isLabel {
continue
}
callee, exists := r.Functions[label.Name]
if !exists { if !exists {
continue continue

View File

@ -19,7 +19,7 @@ func (f *Function) CallSafe(fn *Function, registers []cpu.Register) {
} }
} }
f.Call(fn.UniqueName) f.Label(asm.CALL, fn.UniqueName)
for _, register := range slices.Backward(f.CPU.General) { for _, register := range slices.Backward(f.CPU.General) {
if f.RegisterIsUsed(register) { if f.RegisterIsUsed(register) {

View File

@ -1,6 +1,7 @@
package core package core
import ( import (
"git.urbach.dev/cli/q/src/asm"
"git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/errors"
"git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/expression"
"git.urbach.dev/cli/q/src/types" "git.urbach.dev/cli/q/src/types"
@ -45,13 +46,20 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error
} }
if f.UniqueName == "core.init" && pkg == "main" && name == "main" { if f.UniqueName == "core.init" && pkg == "main" && name == "main" {
f.Call("main.main") f.Label(asm.CALL, "main.main")
return nil, nil return nil, nil
} }
fn, exists = f.All.Functions[pkg+"."+name] fn, exists = f.All.Functions[pkg+"."+name]
if !exists { if !exists {
variable := f.VariableByName(name)
if variable != nil {
f.Register(asm.CALL, variable.Value.Register)
return nil, nil
}
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position) return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
} }

View File

@ -1,7 +0,0 @@
package register
func (f *Machine) Call(label string) {
f.Assembler.Call(label)
f.UseRegister(f.CPU.Output[0])
f.postInstruction()
}

8
src/register/Label.go Normal file
View File

@ -0,0 +1,8 @@
package register
import "git.urbach.dev/cli/q/src/asm"
func (f *Machine) Label(mnemonic asm.Mnemonic, label string) {
f.Assembler.Label(mnemonic, label)
f.postInstruction()
}

View File

@ -1,5 +1,7 @@
package x86 package x86
import "git.urbach.dev/cli/q/src/cpu"
// Call places the return address on the top of the stack and continues // Call places the return address on the top of the stack and continues
// program flow at the new address. // program flow at the new address.
// The address is relative to the next instruction. // The address is relative to the next instruction.
@ -14,8 +16,25 @@ func Call(code []byte, address uint32) []byte {
) )
} }
// CallAtAddress places the return address on the top of the stack and // Calls a function whose address is stored in the given register.
// continues program flow at the address stored at the given memory address. func CallRegister(code []byte, register cpu.Register) []byte {
if register > 0b111 {
return append(
code,
0x41,
0xFF,
0xD0+byte(register&0b111),
)
}
return append(
code,
0xFF,
0xD0+byte(register),
)
}
// CallAtAddress calls a function at the address stored at the given memory address.
// The memory address is relative to the next instruction. // The memory address is relative to the next instruction.
func CallAtAddress(code []byte, address uint32) []byte { func CallAtAddress(code []byte, address uint32) []byte {
return append( return append(

39
src/x86/Call_test.go Normal file
View File

@ -0,0 +1,39 @@
package x86_test
import (
"testing"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/x86"
"git.urbach.dev/go/assert"
)
func TestCallRegister(t *testing.T) {
usagePatterns := []struct {
Register cpu.Register
Code []byte
}{
{x86.RAX, []byte{0xFF, 0xD0}},
{x86.RCX, []byte{0xFF, 0xD1}},
{x86.RDX, []byte{0xFF, 0xD2}},
{x86.RBX, []byte{0xFF, 0xD3}},
{x86.RSP, []byte{0xFF, 0xD4}},
{x86.RBP, []byte{0xFF, 0xD5}},
{x86.RSI, []byte{0xFF, 0xD6}},
{x86.RDI, []byte{0xFF, 0xD7}},
{x86.R8, []byte{0x41, 0xFF, 0xD0}},
{x86.R9, []byte{0x41, 0xFF, 0xD1}},
{x86.R10, []byte{0x41, 0xFF, 0xD2}},
{x86.R11, []byte{0x41, 0xFF, 0xD3}},
{x86.R12, []byte{0x41, 0xFF, 0xD4}},
{x86.R13, []byte{0x41, 0xFF, 0xD5}},
{x86.R14, []byte{0x41, 0xFF, 0xD6}},
{x86.R15, []byte{0x41, 0xFF, 0xD7}},
}
for _, pattern := range usagePatterns {
t.Logf("call %s", pattern.Register)
code := x86.CallRegister(nil, pattern.Register)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -0,0 +1,5 @@
main() {}
f(x unknown) -> int {
return x
}

View File

@ -0,0 +1,5 @@
main() {}
f(x int) -> unknown {
return x
}

View File

@ -0,0 +1,3 @@
main() {}
f(x int) {}

View File

@ -58,10 +58,13 @@ var errs = []struct {
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},
{"UnknownIdentifier3.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier3.q", &errors.UnknownIdentifier{Name: "x"}},
{"UnknownPackage.q", &errors.UnknownPackage{Name: "sys"}}, {"UnknownPackage.q", &errors.UnknownPackage{Name: "sys"}},
{"UnknownType.q", &errors.UnknownType{Name: "unknown"}},
{"UnknownType2.q", &errors.UnknownType{Name: "unknown"}},
{"UnknownStructField.q", &errors.UnknownStructField{StructName: "A", FieldName: "x"}}, {"UnknownStructField.q", &errors.UnknownStructField{StructName: "A", FieldName: "x"}},
{"UntypedExpression.q", errors.UntypedExpression}, {"UntypedExpression.q", errors.UntypedExpression},
{"UnusedImport.q", &errors.UnusedImport{Package: "sys"}}, {"UnusedImport.q", &errors.UnusedImport{Package: "sys"}},
{"UnusedVariable.q", &errors.UnusedVariable{Name: "x"}}, {"UnusedVariable.q", &errors.UnusedVariable{Name: "x"}},
{"UnusedVariable2.q", &errors.UnusedVariable{Name: "x"}},
{"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}},
} }

View File

@ -0,0 +1,6 @@
import core
main() {
exit := core.exit
exit()
}

View File

@ -68,6 +68,7 @@ var programs = []struct {
{"struct", 0}, {"struct", 0},
{"len", 0}, {"len", 0},
{"cast", 0}, {"cast", 0},
{"function-pointer", 0},
} }
func TestPrograms(t *testing.T) { func TestPrograms(t *testing.T) {