Implemented register calls
This commit is contained in:
parent
d7f30d8319
commit
c3054369e3
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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{
|
||||||
|
@ -6,26 +6,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *compiler) call(x asm.Instruction) {
|
func (c *compiler) call(x asm.Instruction) {
|
||||||
c.code = x86.Call(c.code, 0x00_00_00_00)
|
switch data := x.Data.(type) {
|
||||||
size := 4
|
case *asm.Label:
|
||||||
label := x.Data.(*asm.Label)
|
c.code = x86.Call(c.code, 0x00_00_00_00)
|
||||||
|
size := 4
|
||||||
|
|
||||||
pointer := &pointer{
|
pointer := &pointer{
|
||||||
Position: Address(len(c.code) - size),
|
Position: Address(len(c.code) - size),
|
||||||
OpSize: 1,
|
OpSize: 1,
|
||||||
Size: uint8(size),
|
Size: uint8(size),
|
||||||
}
|
|
||||||
|
|
||||||
pointer.Resolve = func() Address {
|
|
||||||
destination, exists := c.codeLabels[label.Name]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
panic("unknown jump label")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
distance := destination - (pointer.Position + Address(pointer.Size))
|
pointer.Resolve = func() Address {
|
||||||
return distance
|
destination, exists := c.codeLabels[data.Name]
|
||||||
}
|
|
||||||
|
|
||||||
c.codePointers = append(c.codePointers, pointer)
|
if !exists {
|
||||||
|
panic("unknown jump label")
|
||||||
|
}
|
||||||
|
|
||||||
|
distance := destination - (pointer.Position + Address(pointer.Size))
|
||||||
|
return distance
|
||||||
|
}
|
||||||
|
|
||||||
|
c.codePointers = append(c.codePointers, pointer)
|
||||||
|
|
||||||
|
case *asm.Register:
|
||||||
|
c.code = x86.CallRegister(c.code, data.Register)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
8
src/register/Label.go
Normal 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()
|
||||||
|
}
|
@ -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
39
src/x86/Call_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
5
tests/errors/UnknownType.q
Normal file
5
tests/errors/UnknownType.q
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
main() {}
|
||||||
|
|
||||||
|
f(x unknown) -> int {
|
||||||
|
return x
|
||||||
|
}
|
5
tests/errors/UnknownType2.q
Normal file
5
tests/errors/UnknownType2.q
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
main() {}
|
||||||
|
|
||||||
|
f(x int) -> unknown {
|
||||||
|
return x
|
||||||
|
}
|
3
tests/errors/UnusedVariable2.q
Normal file
3
tests/errors/UnusedVariable2.q
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
main() {}
|
||||||
|
|
||||||
|
f(x int) {}
|
@ -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"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
tests/programs/function-pointer.q
Normal file
6
tests/programs/function-pointer.q
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import core
|
||||||
|
|
||||||
|
main() {
|
||||||
|
exit := core.exit
|
||||||
|
exit()
|
||||||
|
}
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user