diff --git a/examples/thread/thread.q b/examples/thread/thread.q index 22c9d05..8e33e07 100644 --- a/examples/thread/thread.q +++ b/examples/thread/thread.q @@ -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) } \ No newline at end of file diff --git a/lib/thread/thread_windows.q b/lib/thread/thread_windows.q new file mode 100644 index 0000000..0faef0d --- /dev/null +++ b/lib/thread/thread_windows.q @@ -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) +} \ No newline at end of file diff --git a/src/asm/Number.go b/src/asm/Number.go new file mode 100644 index 0000000..768b232 --- /dev/null +++ b/src/asm/Number.go @@ -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, + }, + }) +} diff --git a/src/asmc/compile.go b/src/asmc/compile.go index e671884..1e76cf4 100644 --- a/src/asmc/compile.go +++ b/src/asmc/compile.go @@ -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) } diff --git a/src/asmc/dllCall.go b/src/asmc/dllCall.go index 8c9856e..bd37582 100644 --- a/src/asmc/dllCall.go +++ b/src/asmc/dllCall.go @@ -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, diff --git a/src/compiler/finalize.go b/src/compiler/finalize.go index 343347e..dc21b8a 100644 --- a/src/compiler/finalize.go +++ b/src/compiler/finalize.go @@ -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") } diff --git a/src/core/CallExtern.go b/src/core/CallExtern.go index 57d3118..1413a45 100644 --- a/src/core/CallExtern.go +++ b/src/core/CallExtern.go @@ -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) diff --git a/src/core/CompileAssignArray.go b/src/core/CompileAssignArray.go index 2fee834..ec13b4c 100644 --- a/src/core/CompileAssignArray.go +++ b/src/core/CompileAssignArray.go @@ -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 diff --git a/src/core/CompileMemoryStore.go b/src/core/CompileMemoryStore.go index 1c84e72..1277268 100644 --- a/src/core/CompileMemoryStore.go +++ b/src/core/CompileMemoryStore.go @@ -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 } diff --git a/src/core/ExecuteLeaf.go b/src/core/ExecuteLeaf.go index 45cbe23..32feb13 100644 --- a/src/core/ExecuteLeaf.go +++ b/src/core/ExecuteLeaf.go @@ -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 diff --git a/src/core/ExpressionToMemory.go b/src/core/ExpressionToMemory.go index e73fe91..21f01f6 100644 --- a/src/core/ExpressionToMemory.go +++ b/src/core/ExpressionToMemory.go @@ -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 diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index d1a85ec..b143fac 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -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 diff --git a/src/core/Fold.go b/src/core/Fold.go index 983d42f..46615de 100644 --- a/src/core/Fold.go +++ b/src/core/Fold.go @@ -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 diff --git a/src/core/Number.go b/src/core/ToNumber.go similarity index 89% rename from src/core/Number.go rename to src/core/ToNumber.go index da39481..95d65a2 100644 --- a/src/core/Number.go +++ b/src/core/ToNumber.go @@ -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) diff --git a/src/core/TokenToRegister.go b/src/core/TokenToRegister.go index ed934cb..e8dd66e 100644 --- a/src/core/TokenToRegister.go +++ b/src/core/TokenToRegister.go @@ -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 diff --git a/src/register/Number.go b/src/register/Number.go new file mode 100644 index 0000000..b2c1188 --- /dev/null +++ b/src/register/Number.go @@ -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() +} diff --git a/src/x86/AlignStack.go b/src/x86/AlignStack.go deleted file mode 100644 index b2ce237..0000000 --- a/src/x86/AlignStack.go +++ /dev/null @@ -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) -} diff --git a/src/x86/Push.go b/src/x86/Push.go index b9c82b0..4bd6e5a 100644 --- a/src/x86/Push.go +++ b/src/x86/Push.go @@ -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 { diff --git a/src/x86/Push_test.go b/src/x86/Push_test.go index 29cedac..3552fc3 100644 --- a/src/x86/Push_test.go +++ b/src/x86/Push_test.go @@ -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 diff --git a/src/x86/Registers.go b/src/x86/Registers.go index 090a678..9ed0df0 100644 --- a/src/x86/Registers.go +++ b/src/x86/Registers.go @@ -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} diff --git a/src/x86/x86_test.go b/src/x86/x86_test.go index 12efd4d..393b2fb 100644 --- a/src/x86/x86_test.go +++ b/src/x86/x86_test.go @@ -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}) diff --git a/tests/programs/variables.q b/tests/programs/variables.q index aa461ab..8334626 100644 --- a/tests/programs/variables.q +++ b/tests/programs/variables.q @@ -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 } \ No newline at end of file