Improved Windows ABI support
This commit is contained in:
parent
0a1a8f741d
commit
d0bcd8cf9f
@ -1,7 +1,6 @@
|
||||
import io
|
||||
import sys
|
||||
import thread
|
||||
import time
|
||||
|
||||
main() {
|
||||
thread.create(work)
|
||||
@ -12,7 +11,6 @@ main() {
|
||||
|
||||
work() {
|
||||
io.out("[ ] start\n")
|
||||
time.sleep(10 * 1000 * 1000)
|
||||
io.out("[x] end\n")
|
||||
sys.exit(0)
|
||||
}
|
7
lib/thread/thread_windows.q
Normal file
7
lib/thread/thread_windows.q
Normal file
@ -0,0 +1,7 @@
|
||||
extern kernel32 {
|
||||
CreateThread(attributes Int, stackSize Int, address *Any, parameter Int) -> Int
|
||||
}
|
||||
|
||||
create(func *Any) -> Int {
|
||||
return kernel32.CreateThread(0, 4096, func, 0)
|
||||
}
|
25
src/asm/Number.go
Normal file
25
src/asm/Number.go
Normal file
@ -0,0 +1,25 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Number operates with just a number.
|
||||
type Number struct {
|
||||
Number int
|
||||
}
|
||||
|
||||
// String returns a human readable version.
|
||||
func (data *Number) String() string {
|
||||
return fmt.Sprintf("%d", data.Number)
|
||||
}
|
||||
|
||||
// Number adds an instruction with a number.
|
||||
func (a *Assembler) Number(mnemonic Mnemonic, number int) {
|
||||
a.Instructions = append(a.Instructions, Instruction{
|
||||
Mnemonic: mnemonic,
|
||||
Data: &Number{
|
||||
Number: number,
|
||||
},
|
||||
})
|
||||
}
|
@ -123,6 +123,8 @@ func (c *compiler) compile(x asm.Instruction) {
|
||||
|
||||
case asm.PUSH:
|
||||
switch operands := x.Data.(type) {
|
||||
case *asm.Number:
|
||||
c.code = x86.PushNumber(c.code, operands.Number)
|
||||
case *asm.Register:
|
||||
c.code = x86.PushRegister(c.code, operands.Register)
|
||||
}
|
||||
|
@ -9,15 +9,10 @@ import (
|
||||
|
||||
func (c *compiler) dllCall(x asm.Instruction) {
|
||||
size := 4
|
||||
// TODO: R15 could be in use.
|
||||
c.code = x86.MoveRegisterRegister(c.code, x86.R15, x86.RSP)
|
||||
c.code = x86.AlignStack(c.code)
|
||||
c.code = x86.SubRegisterNumber(c.code, x86.RSP, 32)
|
||||
c.code = x86.CallAtAddress(c.code, 0x00_00_00_00)
|
||||
position := len(c.code) - size
|
||||
c.code = x86.MoveRegisterRegister(c.code, x86.RSP, x86.R15)
|
||||
|
||||
label := x.Data.(*asm.Label)
|
||||
|
||||
pointer := &pointer{
|
||||
Position: Address(position),
|
||||
OpSize: 2,
|
||||
|
@ -33,6 +33,7 @@ func (r *Result) finalize() {
|
||||
final.Syscall()
|
||||
case config.Windows:
|
||||
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 0)
|
||||
final.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||
final.DLLCall("kernel32.ExitProcess")
|
||||
}
|
||||
|
||||
@ -67,6 +68,7 @@ func (r *Result) finalize() {
|
||||
final.Syscall()
|
||||
case config.Windows:
|
||||
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 1)
|
||||
final.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||
final.DLLCall("kernel32.ExitProcess")
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,15 @@ func (f *Function) CallExtern(fn *Function, parameters []*expression.Expression)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.Register(asm.PUSH, x86.RBP)
|
||||
f.RegisterRegister(asm.MOVE, x86.RBP, x86.RSP)
|
||||
f.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||
f.Number(asm.PUSH, 0)
|
||||
f.Number(asm.PUSH, 0)
|
||||
f.RegisterNumber(asm.SUB, x86.RSP, 32)
|
||||
f.DLLCall(fmt.Sprintf("%s.%s", fn.Package, fn.Name))
|
||||
f.RegisterRegister(asm.MOVE, x86.RSP, x86.RBP)
|
||||
f.Register(asm.POP, x86.RBP)
|
||||
|
||||
for _, register := range registers {
|
||||
f.FreeRegister(register)
|
||||
|
@ -34,7 +34,7 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error {
|
||||
index := left.Children[1]
|
||||
|
||||
if index.Token.IsNumeric() {
|
||||
offset, err := f.Number(index.Token)
|
||||
offset, err := f.ToNumber(index.Token)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -12,7 +12,12 @@ import (
|
||||
func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
||||
parameters := root.Children[1:]
|
||||
name := parameters[0].Token.Text(f.File.Bytes)
|
||||
numBytes, _ := f.Number(parameters[1].Token)
|
||||
numBytes, err := f.ToNumber(parameters[1].Token)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value := parameters[2]
|
||||
variable := f.VariableByName(name)
|
||||
|
||||
@ -28,6 +33,6 @@ func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
||||
Length: byte(numBytes),
|
||||
}
|
||||
|
||||
_, err := f.ExpressionToMemory(value, memory)
|
||||
_, err = f.ExpressionToMemory(value, memory)
|
||||
return err
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope
|
||||
return f.ExecuteRegisterRegister(operation, register, variable.Register)
|
||||
|
||||
case token.Number, token.Rune:
|
||||
number, err := f.Number(operand)
|
||||
number, err := f.ToNumber(operand)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -30,7 +30,7 @@ func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Me
|
||||
}
|
||||
|
||||
if node.Token.IsNumeric() {
|
||||
number, err := f.Number(node.Token)
|
||||
number, err := f.ToNumber(node.Token)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -59,7 +59,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
||||
}
|
||||
|
||||
if index.Token.IsNumeric() {
|
||||
offset, err := f.Number(index.Token)
|
||||
offset, err := f.ToNumber(index.Token)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -18,7 +18,7 @@ func (f *Function) Fold(expr *expression.Expression) error {
|
||||
|
||||
if expr.IsLeaf() {
|
||||
if expr.Token.IsNumeric() {
|
||||
value, err := f.Number(expr.Token)
|
||||
value, err := f.ToNumber(expr.Token)
|
||||
expr.Value = value
|
||||
expr.IsFolded = true
|
||||
return err
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"git.akyoto.dev/cli/q/src/token"
|
||||
)
|
||||
|
||||
// Number tries to convert the token into a numeric value.
|
||||
func (f *Function) Number(t token.Token) (int, error) {
|
||||
// ToNumber tries to convert the token into a numeric value.
|
||||
func (f *Function) ToNumber(t token.Token) (int, error) {
|
||||
switch t.Kind {
|
||||
case token.Number:
|
||||
digits := t.Text(f.File.Bytes)
|
@ -34,7 +34,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.
|
||||
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||
|
||||
case token.Number, token.Rune:
|
||||
number, err := f.Number(t)
|
||||
number, err := f.ToNumber(t)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
8
src/register/Number.go
Normal file
8
src/register/Number.go
Normal file
@ -0,0 +1,8 @@
|
||||
package register
|
||||
|
||||
import "git.akyoto.dev/cli/q/src/asm"
|
||||
|
||||
func (f *Machine) Number(mnemonic asm.Mnemonic, number int) {
|
||||
f.Assembler.Number(mnemonic, number)
|
||||
f.postInstruction()
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package x86
|
||||
|
||||
// AlignStack aligns RSP on a 16-byte boundary.
|
||||
func AlignStack(code []byte) []byte {
|
||||
return append(code, 0x48, 0x83, 0xE4, 0xF0)
|
||||
}
|
@ -1,6 +1,31 @@
|
||||
package x86
|
||||
|
||||
import "git.akyoto.dev/cli/q/src/cpu"
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/cpu"
|
||||
"git.akyoto.dev/cli/q/src/sizeof"
|
||||
)
|
||||
|
||||
// PushNumber pushes a number onto the stack.
|
||||
func PushNumber(code []byte, number int) []byte {
|
||||
length := sizeof.Signed(number)
|
||||
|
||||
if length >= 8 {
|
||||
panic("x86 does not support pushing 64-bit numbers")
|
||||
}
|
||||
|
||||
if length >= 2 {
|
||||
return append(
|
||||
code,
|
||||
0x68,
|
||||
byte(number),
|
||||
byte(number>>8),
|
||||
byte(number>>16),
|
||||
byte(number>>24),
|
||||
)
|
||||
}
|
||||
|
||||
return append(code, 0x6A, byte(number))
|
||||
}
|
||||
|
||||
// PushRegister pushes the value inside the register onto the stack.
|
||||
func PushRegister(code []byte, register cpu.Register) []byte {
|
||||
|
@ -8,6 +8,28 @@ import (
|
||||
"git.akyoto.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestPushNumber(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Number int
|
||||
Code []byte
|
||||
}{
|
||||
{0, []byte{0x6A, 0x00}},
|
||||
{1, []byte{0x6A, 0x01}},
|
||||
{-1, []byte{0x6A, 0xFF}},
|
||||
{127, []byte{0x6A, 0x7F}},
|
||||
{128, []byte{0x68, 0x80, 0x00, 0x00, 0x00}},
|
||||
{0xFF, []byte{0x68, 0xFF, 0x00, 0x00, 0x00}},
|
||||
{0xFFFF, []byte{0x68, 0xFF, 0xFF, 0x00, 0x00}},
|
||||
{0x7FFFFFFF, []byte{0x68, 0xFF, 0xFF, 0xFF, 0x7F}},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("push %d", pattern.Number)
|
||||
code := x86.PushNumber(nil, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Register cpu.Register
|
||||
|
@ -25,7 +25,7 @@ var (
|
||||
AllRegisters = []cpu.Register{RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15}
|
||||
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
||||
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
||||
GeneralRegisters = []cpu.Register{RBX, R12, R13, R14, R15, RCX, R11, RBP}
|
||||
GeneralRegisters = []cpu.Register{RBX, R12, R13, R14, R15, RCX, R11}
|
||||
InputRegisters = SyscallInputRegisters
|
||||
OutputRegisters = SyscallInputRegisters
|
||||
WindowsInputRegisters = []cpu.Register{RCX, RDX, R8, R9}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
)
|
||||
|
||||
func TestX86(t *testing.T) {
|
||||
assert.DeepEqual(t, x86.AlignStack(nil), []byte{0x48, 0x83, 0xE4, 0xF0})
|
||||
assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00})
|
||||
assert.DeepEqual(t, x86.CallAtAddress(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00})
|
||||
assert.DeepEqual(t, x86.ExtendRAXToRDX(nil), []byte{0x48, 0x99})
|
||||
|
@ -6,10 +6,12 @@ main() {
|
||||
e := 5
|
||||
f := 6
|
||||
g := 7
|
||||
h := 8
|
||||
|
||||
assert a != b
|
||||
assert c != d
|
||||
assert e != f
|
||||
assert g != h
|
||||
assert a != 0
|
||||
assert b != 0
|
||||
assert c != 0
|
||||
assert d != 0
|
||||
assert e != 0
|
||||
assert f != 0
|
||||
assert g != 0
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user