diff --git a/examples/shell/shell.q b/examples/shell/shell.q index e434a35..0b094f6 100644 --- a/examples/shell/shell.q +++ b/examples/shell/shell.q @@ -16,9 +16,7 @@ main() { return } - // TODO: Indexing by register - command[7] = '\0' - + command[n-1] = '\0' pid := sys.fork() if pid == 0 { diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index 22bebac..d60556d 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -3,6 +3,7 @@ package asm import ( "encoding/binary" "fmt" + "math" "slices" "strings" @@ -287,9 +288,17 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { case STORE: switch operands := x.Data.(type) { case *MemoryNumber: - code = x64.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) + if operands.Address.OffsetRegister == math.MaxUint8 { + code = x64.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) + } else { + code = x64.StoreDynamicOffsetNumber(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) + } case *MemoryRegister: - code = x64.StoreRegister(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) + if operands.Address.OffsetRegister == math.MaxUint8 { + code = x64.StoreRegister(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) + } else { + code = x64.StoreDynamicOffsetRegister(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Register) + } } case SYSCALL: diff --git a/src/asm/Memory.go b/src/asm/Memory.go index 3e98a71..1c0cc4e 100644 --- a/src/asm/Memory.go +++ b/src/asm/Memory.go @@ -3,7 +3,8 @@ package asm import "git.akyoto.dev/cli/q/src/cpu" type Memory struct { - Base cpu.Register - Offset byte - Length byte + Base cpu.Register + Offset byte + OffsetRegister cpu.Register + Length byte } diff --git a/src/asm/MemoryNumber.go b/src/asm/MemoryNumber.go index 0923bd1..3e7a54a 100644 --- a/src/asm/MemoryNumber.go +++ b/src/asm/MemoryNumber.go @@ -12,7 +12,7 @@ type MemoryNumber struct { // String returns a human readable version. func (data *MemoryNumber) String() string { - return fmt.Sprintf("%dB [%s+%d], %d", data.Address.Length, data.Address.Base, data.Address.Offset, data.Number) + return fmt.Sprintf("%dB [%s+%s+%d], %d", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Number) } // MemoryNumber adds an instruction with a memory address and a number. diff --git a/src/asm/MemoryRegister.go b/src/asm/MemoryRegister.go index 21232ce..ae232e5 100644 --- a/src/asm/MemoryRegister.go +++ b/src/asm/MemoryRegister.go @@ -14,7 +14,7 @@ type MemoryRegister struct { // String returns a human readable version. func (data *MemoryRegister) String() string { - return fmt.Sprintf("%dB [%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.Offset, data.Register) + return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Register) } // MemoryRegister adds an instruction with a memory address and a number. diff --git a/src/core/CompileAssignArray.go b/src/core/CompileAssignArray.go index 8fb040b..339f3f6 100644 --- a/src/core/CompileAssignArray.go +++ b/src/core/CompileAssignArray.go @@ -1,9 +1,13 @@ package core import ( + "math" + "git.akyoto.dev/cli/q/src/asm" "git.akyoto.dev/cli/q/src/ast" + "git.akyoto.dev/cli/q/src/cpu" "git.akyoto.dev/cli/q/src/errors" + "git.akyoto.dev/cli/q/src/token" ) // CompileAssignArray compiles an assign statement for array elements. @@ -20,19 +24,33 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error { defer f.UseVariable(variable) - index := left.Children[1] - offset, err := f.Number(index.Token) - - if err != nil { - return err - } - memory := asm.Memory{ - Base: variable.Register, - Offset: byte(offset), - Length: byte(1), + Base: variable.Register, + Offset: 0, + OffsetRegister: cpu.Register(math.MaxUint8), + Length: byte(1), } - _, err = f.ExpressionToMemory(right, memory) + index := left.Children[1] + + if index.Token.Kind == token.Number { + offset, err := f.Number(index.Token) + + if err != nil { + return err + } + + memory.Offset = byte(offset) + } else { + _, indexRegister, err := f.Evaluate(index) + + if err != nil { + return err + } + + memory.OffsetRegister = indexRegister + } + + _, err := f.ExpressionToMemory(right, memory) return err } diff --git a/src/x64/StoreDynamicOffset.go b/src/x64/StoreDynamicOffset.go new file mode 100644 index 0000000..c070aca --- /dev/null +++ b/src/x64/StoreDynamicOffset.go @@ -0,0 +1,130 @@ +package x64 + +import ( + "encoding/binary" + + "git.akyoto.dev/cli/q/src/cpu" +) + +// StoreDynamicOffsetNumber stores a number into the memory address at `destination` with a register offset. +func StoreDynamicOffsetNumber(code []byte, destination cpu.Register, offset cpu.Register, length byte, number int) []byte { + var ( + w = byte(0) + r = byte(0) + x = byte(0) + b = byte(0) + opCode = byte(0xC7) + mod = AddressMemory + ) + + if length == 1 { + opCode = 0xC6 + } + + if offset == RSP { + tmp := offset + offset = destination + destination = tmp + } + + if length == 8 { + w = 1 + } + + if offset > 0b111 { + x = 1 + offset &= 0b111 + } + + if destination > 0b111 { + b = 1 + destination &= 0b111 + } + + if destination == RBP || destination == R13 { + mod = AddressMemoryOffset8 + } + + if length == 2 { + code = append(code, 0x66) + } + + code = append(code, REX(w, r, x, b)) + code = append(code, opCode) + code = append(code, ModRM(mod, 0b000, 0b100)) + code = append(code, SIB(Scale1, byte(offset), byte(destination))) + + if mod == AddressMemoryOffset8 { + code = append(code, 0x00) + } + + switch length { + case 8, 4: + return binary.LittleEndian.AppendUint32(code, uint32(number)) + + case 2: + return binary.LittleEndian.AppendUint16(code, uint16(number)) + } + + return append(code, byte(number)) +} + +// StoreDynamicOffsetRegister stores the contents of the `source` register into the memory address at `destination` with a register offset. +func StoreDynamicOffsetRegister(code []byte, destination cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte { + var ( + w = byte(0) + r = byte(0) + x = byte(0) + b = byte(0) + opCode = byte(0x89) + mod = AddressMemory + ) + + if length == 1 { + opCode = 0x88 + } + + if offset == RSP { + tmp := offset + offset = destination + destination = tmp + } + + if length == 8 { + w = 1 + } + + if source > 0b111 { + r = 1 + source &= 0b111 + } + + if offset > 0b111 { + x = 1 + offset &= 0b111 + } + + if destination > 0b111 { + b = 1 + destination &= 0b111 + } + + if destination == RBP || destination == R13 { + mod = AddressMemoryOffset8 + } + + if length == 2 { + code = append(code, 0x66) + } + + code = append(code, REX(w, r, x, b)) + code = append(code, opCode) + code = append(code, ModRM(mod, byte(source), 0b100)) + code = append(code, SIB(Scale1, byte(offset), byte(destination))) + + if mod == AddressMemoryOffset8 { + code = append(code, 0x00) + } + + return code +} diff --git a/src/x64/StoreDynamicOffset_test.go b/src/x64/StoreDynamicOffset_test.go new file mode 100644 index 0000000..dfb72b3 --- /dev/null +++ b/src/x64/StoreDynamicOffset_test.go @@ -0,0 +1,171 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/cpu" + "git.akyoto.dev/cli/q/src/x64" + "git.akyoto.dev/go/assert" +) + +func TestStoreDynamicOffsetNumber(t *testing.T) { + usagePatterns := []struct { + RegisterTo cpu.Register + Offset cpu.Register + Length byte + Number int + Code []byte + }{ + {x64.RAX, x64.R15, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, x64.R15, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RAX, x64.R15, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x38, 0x7F, 0x00}}, + {x64.RAX, x64.R15, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x38, 0x7F}}, + {x64.RCX, x64.R14, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, x64.R14, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RCX, x64.R14, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x31, 0x7F, 0x00}}, + {x64.RCX, x64.R14, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x31, 0x7F}}, + {x64.RDX, x64.R13, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, x64.R13, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDX, x64.R13, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x2A, 0x7F, 0x00}}, + {x64.RDX, x64.R13, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x2A, 0x7F}}, + {x64.RBX, x64.R12, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x23, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, x64.R12, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x23, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBX, x64.R12, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x23, 0x7F, 0x00}}, + {x64.RBX, x64.R12, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x23, 0x7F}}, + {x64.RSP, x64.R11, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, x64.R11, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSP, x64.R11, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x64.RSP, x64.R11, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x1C, 0x7F}}, + {x64.RBP, x64.R10, 8, 0x7F, []byte{0x4A, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, x64.R10, 4, 0x7F, []byte{0x42, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RBP, x64.R10, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00}}, + {x64.RBP, x64.R10, 1, 0x7F, []byte{0x42, 0xC6, 0x44, 0x15, 0x00, 0x7F}}, + {x64.RSI, x64.R9, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, x64.R9, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RSI, x64.R9, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x0E, 0x7F, 0x00}}, + {x64.RSI, x64.R9, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x0E, 0x7F}}, + {x64.RDI, x64.R8, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, x64.R8, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.RDI, x64.R8, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x07, 0x7F, 0x00}}, + {x64.RDI, x64.R8, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x07, 0x7F}}, + {x64.R8, x64.RDI, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, x64.RDI, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R8, x64.RDI, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x38, 0x7F, 0x00}}, + {x64.R8, x64.RDI, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x38, 0x7F}}, + {x64.R9, x64.RSI, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, x64.RSI, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R9, x64.RSI, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x31, 0x7F, 0x00}}, + {x64.R9, x64.RSI, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x31, 0x7F}}, + {x64.R10, x64.RBP, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, x64.RBP, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R10, x64.RBP, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x2A, 0x7F, 0x00}}, + {x64.R10, x64.RBP, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x2A, 0x7F}}, + {x64.R11, x64.RSP, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, x64.RSP, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R11, x64.RSP, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x64.R11, x64.RSP, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x1C, 0x7F}}, + {x64.R12, x64.RBX, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, x64.RBX, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R12, x64.RBX, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x64.R12, x64.RBX, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x1C, 0x7F}}, + {x64.R13, x64.RDX, 8, 0x7F, []byte{0x49, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, x64.RDX, 4, 0x7F, []byte{0x41, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R13, x64.RDX, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00}}, + {x64.R13, x64.RDX, 1, 0x7F, []byte{0x41, 0xC6, 0x44, 0x15, 0x00, 0x7F}}, + {x64.R14, x64.RCX, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, x64.RCX, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R14, x64.RCX, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x0E, 0x7F, 0x00}}, + {x64.R14, x64.RCX, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x0E, 0x7F}}, + {x64.R15, x64.RAX, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, x64.RAX, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x64.R15, x64.RAX, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x07, 0x7F, 0x00}}, + {x64.R15, x64.RAX, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x07, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%s], %d", pattern.Length, pattern.RegisterTo, pattern.Offset, pattern.Number) + code := x64.StoreDynamicOffsetNumber(nil, pattern.RegisterTo, pattern.Offset, pattern.Length, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestStoreDynamicOffsetRegister(t *testing.T) { + usagePatterns := []struct { + RegisterTo cpu.Register + Offset cpu.Register + Length byte + RegisterFrom cpu.Register + Code []byte + }{ + {x64.RAX, x64.R15, 8, x64.R15, []byte{0x4E, 0x89, 0x3C, 0x38}}, + {x64.RAX, x64.R15, 4, x64.R15, []byte{0x46, 0x89, 0x3C, 0x38}}, + {x64.RAX, x64.R15, 2, x64.R15, []byte{0x66, 0x46, 0x89, 0x3C, 0x38}}, + {x64.RAX, x64.R15, 1, x64.R15, []byte{0x46, 0x88, 0x3C, 0x38}}, + {x64.RCX, x64.R14, 8, x64.R14, []byte{0x4E, 0x89, 0x34, 0x31}}, + {x64.RCX, x64.R14, 4, x64.R14, []byte{0x46, 0x89, 0x34, 0x31}}, + {x64.RCX, x64.R14, 2, x64.R14, []byte{0x66, 0x46, 0x89, 0x34, 0x31}}, + {x64.RCX, x64.R14, 1, x64.R14, []byte{0x46, 0x88, 0x34, 0x31}}, + {x64.RDX, x64.R13, 8, x64.R13, []byte{0x4E, 0x89, 0x2C, 0x2A}}, + {x64.RDX, x64.R13, 4, x64.R13, []byte{0x46, 0x89, 0x2C, 0x2A}}, + {x64.RDX, x64.R13, 2, x64.R13, []byte{0x66, 0x46, 0x89, 0x2C, 0x2A}}, + {x64.RDX, x64.R13, 1, x64.R13, []byte{0x46, 0x88, 0x2C, 0x2A}}, + {x64.RBX, x64.R12, 8, x64.R12, []byte{0x4E, 0x89, 0x24, 0x23}}, + {x64.RBX, x64.R12, 4, x64.R12, []byte{0x46, 0x89, 0x24, 0x23}}, + {x64.RBX, x64.R12, 2, x64.R12, []byte{0x66, 0x46, 0x89, 0x24, 0x23}}, + {x64.RBX, x64.R12, 1, x64.R12, []byte{0x46, 0x88, 0x24, 0x23}}, + {x64.RSP, x64.R11, 8, x64.R11, []byte{0x4E, 0x89, 0x1C, 0x1C}}, + {x64.RSP, x64.R11, 4, x64.R11, []byte{0x46, 0x89, 0x1C, 0x1C}}, + {x64.RSP, x64.R11, 2, x64.R11, []byte{0x66, 0x46, 0x89, 0x1C, 0x1C}}, + {x64.RSP, x64.R11, 1, x64.R11, []byte{0x46, 0x88, 0x1C, 0x1C}}, + {x64.RBP, x64.R10, 8, x64.R10, []byte{0x4E, 0x89, 0x54, 0x15, 0x00}}, + {x64.RBP, x64.R10, 4, x64.R10, []byte{0x46, 0x89, 0x54, 0x15, 0x00}}, + {x64.RBP, x64.R10, 2, x64.R10, []byte{0x66, 0x46, 0x89, 0x54, 0x15, 0x00}}, + {x64.RBP, x64.R10, 1, x64.R10, []byte{0x46, 0x88, 0x54, 0x15, 0x00}}, + {x64.RSI, x64.R9, 8, x64.R9, []byte{0x4E, 0x89, 0x0C, 0x0E}}, + {x64.RSI, x64.R9, 4, x64.R9, []byte{0x46, 0x89, 0x0C, 0x0E}}, + {x64.RSI, x64.R9, 2, x64.R9, []byte{0x66, 0x46, 0x89, 0x0C, 0x0E}}, + {x64.RSI, x64.R9, 1, x64.R9, []byte{0x46, 0x88, 0x0C, 0x0E}}, + {x64.RDI, x64.R8, 8, x64.R8, []byte{0x4E, 0x89, 0x04, 0x07}}, + {x64.RDI, x64.R8, 4, x64.R8, []byte{0x46, 0x89, 0x04, 0x07}}, + {x64.RDI, x64.R8, 2, x64.R8, []byte{0x66, 0x46, 0x89, 0x04, 0x07}}, + {x64.RDI, x64.R8, 1, x64.R8, []byte{0x46, 0x88, 0x04, 0x07}}, + {x64.R8, x64.RDI, 8, x64.RDI, []byte{0x49, 0x89, 0x3C, 0x38}}, + {x64.R8, x64.RDI, 4, x64.RDI, []byte{0x41, 0x89, 0x3C, 0x38}}, + {x64.R8, x64.RDI, 2, x64.RDI, []byte{0x66, 0x41, 0x89, 0x3C, 0x38}}, + {x64.R8, x64.RDI, 1, x64.RDI, []byte{0x41, 0x88, 0x3C, 0x38}}, + {x64.R9, x64.RSI, 8, x64.RSI, []byte{0x49, 0x89, 0x34, 0x31}}, + {x64.R9, x64.RSI, 4, x64.RSI, []byte{0x41, 0x89, 0x34, 0x31}}, + {x64.R9, x64.RSI, 2, x64.RSI, []byte{0x66, 0x41, 0x89, 0x34, 0x31}}, + {x64.R9, x64.RSI, 1, x64.RSI, []byte{0x41, 0x88, 0x34, 0x31}}, + {x64.R10, x64.RBP, 8, x64.RBP, []byte{0x49, 0x89, 0x2C, 0x2A}}, + {x64.R10, x64.RBP, 4, x64.RBP, []byte{0x41, 0x89, 0x2C, 0x2A}}, + {x64.R10, x64.RBP, 2, x64.RBP, []byte{0x66, 0x41, 0x89, 0x2C, 0x2A}}, + {x64.R10, x64.RBP, 1, x64.RBP, []byte{0x41, 0x88, 0x2C, 0x2A}}, + {x64.R11, x64.RSP, 8, x64.RSP, []byte{0x4A, 0x89, 0x24, 0x1C}}, + {x64.R11, x64.RSP, 4, x64.RSP, []byte{0x42, 0x89, 0x24, 0x1C}}, + {x64.R11, x64.RSP, 2, x64.RSP, []byte{0x66, 0x42, 0x89, 0x24, 0x1C}}, + {x64.R11, x64.RSP, 1, x64.RSP, []byte{0x42, 0x88, 0x24, 0x1C}}, + {x64.R12, x64.RBX, 8, x64.RBX, []byte{0x49, 0x89, 0x1C, 0x1C}}, + {x64.R12, x64.RBX, 4, x64.RBX, []byte{0x41, 0x89, 0x1C, 0x1C}}, + {x64.R12, x64.RBX, 2, x64.RBX, []byte{0x66, 0x41, 0x89, 0x1C, 0x1C}}, + {x64.R12, x64.RBX, 1, x64.RBX, []byte{0x41, 0x88, 0x1C, 0x1C}}, + {x64.R13, x64.RDX, 8, x64.RDX, []byte{0x49, 0x89, 0x54, 0x15, 0x00}}, + {x64.R13, x64.RDX, 4, x64.RDX, []byte{0x41, 0x89, 0x54, 0x15, 0x00}}, + {x64.R13, x64.RDX, 2, x64.RDX, []byte{0x66, 0x41, 0x89, 0x54, 0x15, 0x00}}, + {x64.R13, x64.RDX, 1, x64.RDX, []byte{0x41, 0x88, 0x54, 0x15, 0x00}}, + {x64.R14, x64.RCX, 8, x64.RCX, []byte{0x49, 0x89, 0x0C, 0x0E}}, + {x64.R14, x64.RCX, 4, x64.RCX, []byte{0x41, 0x89, 0x0C, 0x0E}}, + {x64.R14, x64.RCX, 2, x64.RCX, []byte{0x66, 0x41, 0x89, 0x0C, 0x0E}}, + {x64.R14, x64.RCX, 1, x64.RCX, []byte{0x41, 0x88, 0x0C, 0x0E}}, + {x64.R15, x64.RAX, 8, x64.RAX, []byte{0x49, 0x89, 0x04, 0x07}}, + {x64.R15, x64.RAX, 4, x64.RAX, []byte{0x41, 0x89, 0x04, 0x07}}, + {x64.R15, x64.RAX, 2, x64.RAX, []byte{0x66, 0x41, 0x89, 0x04, 0x07}}, + {x64.R15, x64.RAX, 1, x64.RAX, []byte{0x41, 0x88, 0x04, 0x07}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%s], %s", pattern.Length, pattern.RegisterTo, pattern.Offset, pattern.RegisterFrom) + code := x64.StoreDynamicOffsetRegister(nil, pattern.RegisterTo, pattern.Offset, pattern.Length, pattern.RegisterFrom) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/tests/programs/array.q b/tests/programs/array.q new file mode 100644 index 0000000..6b4d16a --- /dev/null +++ b/tests/programs/array.q @@ -0,0 +1,44 @@ +import mem + +main() { + a := mem.alloc(5) + + assert a[0] == 0 + assert a[1] == 0 + assert a[2] == 0 + assert a[3] == 0 + assert a[4] == 0 + + a[0] = 0 + a[1] = 1 + a[2] = 2 + a[3] = 3 + a[4] = 4 + + assert a[0] == 0 + assert a[1] == 1 + assert a[2] == 2 + assert a[3] == 3 + assert a[4] == 4 + + i := 0 + a[i] = i * 2 + + i += 1 + a[i] = i * 2 + + i += 1 + a[i] = i * 2 + + i += 1 + a[i] = i * 2 + + i += 1 + a[i] = i * 2 + + assert a[0] == 0 + assert a[1] == 2 + assert a[2] == 4 + assert a[3] == 6 + assert a[4] == 8 +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 986cf81..dfeba4d 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -31,6 +31,7 @@ var programs = []struct { {"binary", "", "", 0}, {"octal", "", "", 0}, {"hexadecimal", "", "", 0}, + {"array", "", "", 0}, {"escape-rune", "", "", 0}, {"escape-string", "", "", 0}, {"bitwise-and", "", "", 0},