From 65684e823a7fa1f062e078fe4e82fbbb7b6c761f Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 14 Aug 2025 14:17:26 +0200 Subject: [PATCH] Implemented memory store instructions --- lib/mem/mem_linux.q | 2 +- lib/mem/mem_mac.q | 2 +- lib/mem/mem_unix.q | 6 +++-- lib/mem/mem_windows.q | 14 +++++++++--- src/arm/AddressMode.go | 1 + src/arm/Store.go | 16 ++++++++++++++ src/arm/Store_test.go | 22 +++++++++++++++++++ src/arm/extend.go | 9 ++++++++ src/asm/Instructions.go | 12 +++++++--- src/asm/compilerARM.go | 2 ++ src/asm/compilerX86.go | 2 ++ src/codegen/exec.go | 13 +++++++++++ src/core/compileAssign.go | 5 +++++ src/core/compileStore.go | 40 ++++++++++++++++++++++++++++++++++ src/ssa/Store.go | 46 +++++++++++++++++++++++++++++++++++++++ tests/memory.q | 12 +++++++++- tests/out-of-memory.q | 3 +-- tests/tests_test.go | 2 +- 18 files changed, 195 insertions(+), 14 deletions(-) create mode 100644 src/arm/extend.go create mode 100644 src/core/compileStore.go create mode 100644 src/ssa/Store.go diff --git a/lib/mem/mem_linux.q b/lib/mem/mem_linux.q index ca095f2..d58e327 100644 --- a/lib/mem/mem_linux.q +++ b/lib/mem/mem_linux.q @@ -1,4 +1,4 @@ -mmap(address int, length uint, protection int, flags int) -> *any { +mmap(address int, length uint, protection int, flags int) -> (address *byte) { return syscall(_mmap, address, length, protection, flags) } diff --git a/lib/mem/mem_mac.q b/lib/mem/mem_mac.q index 6fce1a7..7b7dd4a 100644 --- a/lib/mem/mem_mac.q +++ b/lib/mem/mem_mac.q @@ -1,4 +1,4 @@ -mmap(address int, length uint, protection int, flags int) -> *any { +mmap(address int, length uint, protection int, flags int) -> (address *byte) { return syscall(_mmap, address, length, protection, flags, 0, 0) } diff --git a/lib/mem/mem_unix.q b/lib/mem/mem_unix.q index 00fd01d..29e346e 100644 --- a/lib/mem/mem_unix.q +++ b/lib/mem/mem_unix.q @@ -1,8 +1,10 @@ -alloc(length int) -> (address *any) { +import run + +alloc(length int) -> (address *byte) { x := mmap(0, length, read|write, private|anonymous) if x < 0x1000 { - return 0 + run.crash() } return x diff --git a/lib/mem/mem_windows.q b/lib/mem/mem_windows.q index 8d0eec2..62081c1 100644 --- a/lib/mem/mem_windows.q +++ b/lib/mem/mem_windows.q @@ -1,5 +1,13 @@ -alloc(length int) -> (address *any) { - return kernel32.VirtualAlloc(0, length, commit|reserve, readwrite) +import run + +alloc(length int) -> (address *byte) { + x := kernel32.VirtualAlloc(0, length, commit|reserve, readwrite) + + if x == 0 { + run.crash() + } + + return x } free(address *any, length int) { @@ -15,7 +23,7 @@ const { extern { kernel32 { - VirtualAlloc(address int, size uint, flags uint32, protection uint32) -> (address *any) + VirtualAlloc(address int, size uint, flags uint32, protection uint32) -> (address *byte) VirtualFree(address *any, size uint, type uint32) -> (success bool) } } \ No newline at end of file diff --git a/src/arm/AddressMode.go b/src/arm/AddressMode.go index 5e23e8f..f4293ca 100644 --- a/src/arm/AddressMode.go +++ b/src/arm/AddressMode.go @@ -6,5 +6,6 @@ type AddressMode uint32 const ( UnscaledImmediate = AddressMode(0b00) PostIndex = AddressMode(0b01) + Offset = AddressMode(0b10) PreIndex = AddressMode(0b11) ) \ No newline at end of file diff --git a/src/arm/Store.go b/src/arm/Store.go index 08e77a6..52407ff 100644 --- a/src/arm/Store.go +++ b/src/arm/Store.go @@ -4,6 +4,22 @@ import ( "git.urbach.dev/cli/q/src/cpu" ) +// StoreDynamicRegister writes the contents of the register to a memory address with a dynamic offset. +func StoreDynamicRegister(source cpu.Register, base cpu.Register, mode AddressMode, offset cpu.Register, length byte) uint32 { + common := 1<<21 | (LSL << 13) | uint32(mode)<<10 | reg3(source, base, offset) + + switch length { + case 1: + return 0b00111<<27 | common + case 2: + return 0b01111<<27 | common + case 4: + return 0b10111<<27 | common + default: + return 0b11111<<27 | common + } +} + // StoreRegister writes the contents of the register to a memory address. func StoreRegister(source cpu.Register, base cpu.Register, mode AddressMode, offset int, length byte) uint32 { common := memory(source, base, mode, offset) diff --git a/src/arm/Store_test.go b/src/arm/Store_test.go index 24f694c..b1ccfab 100644 --- a/src/arm/Store_test.go +++ b/src/arm/Store_test.go @@ -8,6 +8,28 @@ import ( "git.urbach.dev/go/assert" ) +func TestStoreDynamicRegister(t *testing.T) { + usagePatterns := []struct { + Source cpu.Register + Base cpu.Register + Mode arm.AddressMode + Offset cpu.Register + Length byte + Code uint32 + }{ + {arm.X0, arm.X1, arm.Offset, arm.X2, 8, 0xF8226820}, + {arm.X0, arm.X1, arm.Offset, arm.X2, 4, 0xB8226820}, + {arm.X0, arm.X1, arm.Offset, arm.X2, 2, 0x78226820}, + {arm.X0, arm.X1, arm.Offset, arm.X2, 1, 0x38226820}, + } + + for _, pattern := range usagePatterns { + t.Logf("str %s, [%s, #%s] %db", pattern.Source, pattern.Base, pattern.Offset, pattern.Length) + code := arm.StoreDynamicRegister(pattern.Source, pattern.Base, pattern.Mode, pattern.Offset, pattern.Length) + assert.Equal(t, code, pattern.Code) + } +} + func TestStoreRegister(t *testing.T) { usagePatterns := []struct { Source cpu.Register diff --git a/src/arm/extend.go b/src/arm/extend.go new file mode 100644 index 0000000..da8486f --- /dev/null +++ b/src/arm/extend.go @@ -0,0 +1,9 @@ +package arm + +// extend values +const ( + UXTW = 0b010 + LSL = 0b011 + SXTW = 0b110 + SXTX = 0b111 +) \ No newline at end of file diff --git a/src/asm/Instructions.go b/src/asm/Instructions.go index 1db8089..6c261be 100644 --- a/src/asm/Instructions.go +++ b/src/asm/Instructions.go @@ -39,9 +39,15 @@ type ( Return struct{} ShiftLeft rrr ShiftRightSigned rrr - Subtract rrr - SubtractNumber rrn - StackFrameStart struct { + Store struct { + Base cpu.Register + Index cpu.Register + Value cpu.Register + Length byte + } + Subtract rrr + SubtractNumber rrn + StackFrameStart struct { FramePointer bool ExternCalls bool } diff --git a/src/asm/compilerARM.go b/src/asm/compilerARM.go index 1c89f1e..62216fa 100644 --- a/src/asm/compilerARM.go +++ b/src/asm/compilerARM.go @@ -181,6 +181,8 @@ func (c *compilerARM) Compile(instr Instruction) { c.append(arm.ShiftLeft(instr.Destination, instr.Source, instr.Operand)) case *ShiftRightSigned: c.append(arm.ShiftRightSigned(instr.Destination, instr.Source, instr.Operand)) + case *Store: + c.append(arm.StoreDynamicRegister(instr.Value, instr.Base, arm.Offset, instr.Index, instr.Length)) case *Subtract: c.append(arm.SubRegisterRegister(instr.Destination, instr.Source, instr.Operand)) case *StackFrameStart: diff --git a/src/asm/compilerX86.go b/src/asm/compilerX86.go index 0ad8477..317e100 100644 --- a/src/asm/compilerX86.go +++ b/src/asm/compilerX86.go @@ -276,6 +276,8 @@ func (c *compilerX86) Compile(instr Instruction) { c.code = x86.MoveRegisterRegister(c.code, x86.SP, x86.R5) c.code = x86.PopRegister(c.code, x86.R5) } + case *Store: + c.code = x86.StoreDynamicRegister(c.code, instr.Base, instr.Index, instr.Length, instr.Value) case *Syscall: c.code = x86.Syscall(c.code) case *Xor: diff --git a/src/codegen/exec.go b/src/codegen/exec.go index 4f3d9fa..238770d 100644 --- a/src/codegen/exec.go +++ b/src/codegen/exec.go @@ -8,6 +8,7 @@ import ( "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/cli/q/src/ssa" "git.urbach.dev/cli/q/src/token" + "git.urbach.dev/cli/q/src/types" ) // exec executes a step which appends it to the assembler's instruction list. @@ -278,6 +279,18 @@ func (f *Function) exec(step *Step) { Source: retVal.Register, }) + case *ssa.Store: + address := f.ValueToStep[instr.Address] + pointer := address.Value.Type().(*types.Pointer) + elementSize := pointer.To.Size() + + f.Assembler.Append(&asm.Store{ + Base: address.Register, + Index: f.ValueToStep[instr.Index].Register, + Value: f.ValueToStep[instr.Value].Register, + Length: byte(elementSize), + }) + case *ssa.Syscall: for i, arg := range instr.Arguments { if f.ValueToStep[arg].Register != f.CPU.Syscall.In[i] { diff --git a/src/core/compileAssign.go b/src/core/compileAssign.go index cb8fc56..08ed574 100644 --- a/src/core/compileAssign.go +++ b/src/core/compileAssign.go @@ -10,6 +10,11 @@ import ( // compileAssign compiles an assignment. func (f *Function) compileAssign(node *ast.Assign) error { left := node.Expression.Children[0] + + if left.Token.Kind == token.Array { + return f.compileStore(node) + } + name := left.String(f.File.Bytes) leftValue, exists := f.Block().FindIdentifier(name) diff --git a/src/core/compileStore.go b/src/core/compileStore.go new file mode 100644 index 0000000..ee1f8f9 --- /dev/null +++ b/src/core/compileStore.go @@ -0,0 +1,40 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/ast" + "git.urbach.dev/cli/q/src/ssa" +) + +// compileStore compiles an assignment to memory. +func (f *Function) compileStore(node *ast.Assign) error { + left := node.Expression.Children[0] + address := left.Children[0] + index := left.Children[1] + addressValue, err := f.evaluate(address) + + if err != nil { + return err + } + + indexValue, err := f.evaluate(index) + + if err != nil { + return err + } + + right := node.Expression.Children[1] + rightValue, err := f.evaluate(right) + + if err != nil { + return err + } + + f.Append(&ssa.Store{ + Address: addressValue, + Index: indexValue, + Value: rightValue, + Source: ssa.Source(node.Expression.Source()), + }) + + return nil +} \ No newline at end of file diff --git a/src/ssa/Store.go b/src/ssa/Store.go new file mode 100644 index 0000000..29acc73 --- /dev/null +++ b/src/ssa/Store.go @@ -0,0 +1,46 @@ +package ssa + +import ( + "fmt" +) + +// Store stores a value at a given index relative to the address. +type Store struct { + Void + Address Value + Index Value + Value Value + Source +} + +func (a *Store) Equals(v Value) bool { + b, sameType := v.(*Store) + + if !sameType { + return false + } + + return a.Address == b.Address && a.Index == b.Index && a.Value == b.Value +} + +func (v *Store) Inputs() []Value { + return []Value{v.Address, v.Index, v.Value} +} + +func (v *Store) Replace(old Value, new Value) { + if v.Address == old { + v.Address = new + } + + if v.Index == old { + v.Index = new + } + + if v.Value == old { + v.Value = new + } +} + +func (v *Store) String() string { + return fmt.Sprintf("store(%p, %p, %p)", v.Address, v.Index, v.Value) +} \ No newline at end of file diff --git a/tests/memory.q b/tests/memory.q index ebbb483..3b64598 100644 --- a/tests/memory.q +++ b/tests/memory.q @@ -1,8 +1,18 @@ import mem main() { - length := 1024 + length := 6 address := mem.alloc(length) assert address != 0 + fill(address) mem.free(address, length) +} + +fill(address *byte) { + address[0] = 'H' + address[1] = 'e' + address[2] = 'l' + address[3] = 'l' + address[4] = 'o' + address[5] = '\n' } \ No newline at end of file diff --git a/tests/out-of-memory.q b/tests/out-of-memory.q index 8559a3f..b465c37 100644 --- a/tests/out-of-memory.q +++ b/tests/out-of-memory.q @@ -1,6 +1,5 @@ import mem main() { - address := mem.alloc(0x4000000000000) - assert address == 0 + mem.alloc(0x4000000000000) } \ No newline at end of file diff --git a/tests/tests_test.go b/tests/tests_test.go index 7e10540..b2b153e 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -51,7 +51,7 @@ var tests = []run{ {"loop-limit-dynamic", "", "", 0}, {"loop-keepalive", "", "", 0}, {"memory", "", "", 0}, - {"out-of-memory", "", "", 0}, + {"out-of-memory", "", "", 1}, {"hello", "", "Hello\nHello\nHello\n", 0}, {"script", "", "Hello\n", 0}, } -- 2.51.2