From d94fc1e482d859e25540005c4de6a19dd1cc2e24 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 09:04:24 +0000 Subject: [PATCH 01/18] fix(interp): clamp fuel without dead overwrite The overflow guard assigned the clamped value, then an unconditional fuel = int64(ticks) overwrote it, so ticks > 2^63-1 wrapped negative. Collapse to fuel = int64(min(ticks, 1<<63-1)). https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/interp.go | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/interp/interp.go b/interp/interp.go index b9ee242..783f37a 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -156,11 +156,7 @@ func New(prog *program.Program, opts ...func(*option)) *Interpreter { var fuel int64 = -1 if opt.fuel > 0 { ticks := (opt.fuel-1)/uint64(opt.tick) + 1 - m := uint64(1<<63 - 1) - if ticks > m { - fuel = int64(m) - } - fuel = int64(ticks) + fuel = int64(min(ticks, 1<<63-1)) } var threshold int64 = int64(opt.threshold) @@ -319,11 +315,11 @@ func (i *Interpreter) Context() context.Context { } func (i *Interpreter) Func() int { - return i.frame().addr + return i.fr.addr } func (i *Interpreter) IP() int { - return i.frame().ip + return i.fr.ip } func (i *Interpreter) FP() int { @@ -374,7 +370,7 @@ func (i *Interpreter) SetGlobal(idx int, val types.Boxed) error { } func (i *Interpreter) Local(idx int) (types.Boxed, error) { - f := i.frame() + f := i.fr addr := f.bp + idx if addr < 0 || addr >= i.sp { return 0, ErrSegmentationFault @@ -383,7 +379,7 @@ func (i *Interpreter) Local(idx int) (types.Boxed, error) { } func (i *Interpreter) SetLocal(idx int, val types.Boxed) error { - f := i.frame() + f := i.fr addr := f.bp + idx if addr < 0 || addr >= i.sp { return ErrSegmentationFault @@ -551,12 +547,8 @@ func (i *Interpreter) jit(addr int) error { return nil } -func (i *Interpreter) frame() *frame { - return i.fr -} - func (i *Interpreter) error(r any) error { - ip := i.frame().ip + ip := i.fr.ip switch e := r.(type) { case error: return fmt.Errorf("%w: at=%d", e, ip) @@ -665,21 +657,13 @@ func (i *Interpreter) unbox(val types.Boxed) types.Value { } func (i *Interpreter) alloc(val types.Value) int { - if len(i.free) > 0 { - addr := i.free[len(i.free)-1] - i.free = i.free[:len(i.free)-1] - i.heap[addr] = val - i.rc[addr] = 1 + if addr, ok := i.reuse(val); ok { return addr } if len(i.heap) == cap(i.heap) { i.gc() - if len(i.free) > 0 { - addr := i.free[len(i.free)-1] - i.free = i.free[:len(i.free)-1] - i.heap[addr] = val - i.rc[addr] = 1 + if addr, ok := i.reuse(val); ok { return addr } @@ -701,6 +685,17 @@ func (i *Interpreter) alloc(val types.Value) int { return len(i.heap) - 1 } +func (i *Interpreter) reuse(val types.Value) (int, bool) { + if len(i.free) == 0 { + return 0, false + } + addr := i.free[len(i.free)-1] + i.free = i.free[:len(i.free)-1] + i.heap[addr] = val + i.rc[addr] = 1 + return addr, true +} + func (i *Interpreter) keep(val types.Value) int { roots := i.trace(val) defer i.unroot(roots) From 2703aa7f57cb4c15dae389636454fbbab7625ebb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 09:04:30 +0000 Subject: [PATCH 02/18] refactor: drop redundant helpers and dedup renderers - interp: inline trivial frame() accessor into i.fr - interp: extract alloc free-list reuse into one reuse() helper - types: drop MapKey.String's unused typ param and dead string branch - cli: merge printLocals/printGlobals into printIndexed - cli: replace hasJIT with a prof.JIT zero-value comparison https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- cli/repl.go | 41 +++++++---------------------------------- interp/jit.go | 2 +- types/map.go | 7 ++----- 3 files changed, 10 insertions(+), 40 deletions(-) diff --git a/cli/repl.go b/cli/repl.go index 1bf273d..8d37932 100644 --- a/cli/repl.go +++ b/cli/repl.go @@ -509,9 +509,9 @@ func (r *REPL) debugLoop(ctx context.Context, scanner *bufio.Scanner, vm *interp case "stack": printStack(r.out, vm) case "locals": - printLocals(r.out, vm) + printIndexed(r.out, vm, "local", vm.Local) case "globals": - printGlobals(r.out, vm) + printIndexed(r.out, vm, "global", vm.Global) case "frames": printFrames(r.out, vm) case "breaks": @@ -602,33 +602,17 @@ func (r *REPL) printErr(err error) { fmt.Fprintf(r.out, "error: %v\n", err) } -func printLocals(out io.Writer, vm *interp.Interpreter) { +func printIndexed(out io.Writer, vm *interp.Interpreter, label string, get func(int) (types.Boxed, error)) { var parts []string for i := 0; ; i++ { - v, err := vm.Local(i) + v, err := get(i) if err != nil { break } - parts = append(parts, fmt.Sprintf("local[%d]=%s", i, formatValue(v, vm))) + parts = append(parts, fmt.Sprintf("%s[%d]=%s", label, i, formatValue(v, vm))) } if len(parts) == 0 { - fmt.Fprintln(out, "(no locals)") - return - } - fmt.Fprintln(out, strings.Join(parts, "\n")) -} - -func printGlobals(out io.Writer, vm *interp.Interpreter) { - var parts []string - for i := 0; ; i++ { - v, err := vm.Global(i) - if err != nil { - break - } - parts = append(parts, fmt.Sprintf("global[%d]=%s", i, formatValue(v, vm))) - } - if len(parts) == 0 { - fmt.Fprintln(out, "(no globals)") + fmt.Fprintf(out, "(no %ss)\n", label) return } fmt.Fprintln(out, strings.Join(parts, "\n")) @@ -682,7 +666,7 @@ func printProfile(out io.Writer, snap prof.Snapshot) { fmt.Fprintf(out, "%s\t%d\t%s\n", opcodeLabel(op.Code), op.Samples, formatPercent(op.Percent)) } } - if hasJIT(snap.JIT) { + if snap.JIT != (prof.JIT{}) { jit := snap.JIT fmt.Fprintln(out, "jit:") fmt.Fprintln(out, "attempts\temits\tlinks\tskips\taborts\terrors\tbytes\ttime") @@ -703,17 +687,6 @@ func opcodeLabel(code byte) string { return fmt.Sprintf("0x%02X", code) } -func hasJIT(jit prof.JIT) bool { - return jit.Attempts != 0 || - jit.Emits != 0 || - jit.Links != 0 || - jit.Skips != 0 || - jit.Aborts != 0 || - jit.Errors != 0 || - jit.Bytes != 0 || - jit.Time != 0 -} - // normalize converts "@N" absolute byte targets in branch instructions to relative // offsets from ip, and strips any "NNNN:\t" offset prefix. Returns the line unchanged // if no "@" tokens are present. diff --git a/interp/jit.go b/interp/jit.go index a6dfa5b..e90d9bb 100644 --- a/interp/jit.go +++ b/interp/jit.go @@ -776,7 +776,7 @@ func (c *jitCompiler) closure(fn asm.Caller, pregs []asm.PReg, sig *asm.Signatur } func (c *jitCompiler) scratch(i *Interpreter, scratch []uint64) { - f := i.frame() + f := i.fr scratch[rStack] = uint64(uintptr(unsafe.Pointer(&i.stack[f.bp]))) scratch[rHeap] = uint64(uintptr(unsafe.Pointer(unsafe.SliceData(i.heap)))) scratch[rGlobals] = uint64(uintptr(unsafe.Pointer(unsafe.SliceData(i.globals)))) diff --git a/types/map.go b/types/map.go index d5c1707..ae5f262 100644 --- a/types/map.go +++ b/types/map.go @@ -160,7 +160,7 @@ func (m *Map) Clear(fn func(MapEntry)) { func (m *Map) String() string { parts := make([]string, 0, m.Len()) m.Range(func(key MapKey, entry MapEntry) { - parts = append(parts, fmt.Sprintf("%s: %s", key.String(m.Typ.Key), entry.Value.String())) + parts = append(parts, fmt.Sprintf("%s: %s", key.String(), entry.Value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) @@ -466,10 +466,7 @@ func (m *MapF64) Refs() []Ref { return refs } -func (k MapKey) String(typ Type) string { - if typ.Equals(TypeString) && k.Kind == KindRef { - return BoxRef(int(k.Bits)).String() - } +func (k MapKey) String() string { switch k.Kind { case KindI32: return BoxI32(int32(k.Bits)).String() From c0b399181c04ef4025638f5080967be4070e833a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:11:16 +0000 Subject: [PATCH 03/18] refactor(asm): drop dead public symbols Remove exported symbols with no production callers (test-only): Encode, Memory.Write (+ErrCodeTooLarge), RegMask.{Empty,List,And,Or, Not,Sub}, RegAlloc.{Get,Clone}, RegInfo.IsReserved, InvalidRegID. Excise their dedicated tests; rewrite TestRegAlloc_Reserve/Reset and TestMemory_Writable to assert via the public API instead of the removed Get/Write methods. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- asm/encoder.go | 12 ----------- asm/memory.go | 9 --------- asm/memory_test.go | 25 ----------------------- asm/reg.go | 2 -- asm/regalloc.go | 22 --------------------- asm/regalloc_test.go | 23 +++------------------- asm/reginfo.go | 7 ------- asm/reginfo_test.go | 11 ----------- asm/regmask.go | 30 ---------------------------- asm/regmask_test.go | 47 -------------------------------------------- 10 files changed, 3 insertions(+), 185 deletions(-) diff --git a/asm/encoder.go b/asm/encoder.go index 8dcafb5..67ae25a 100644 --- a/asm/encoder.go +++ b/asm/encoder.go @@ -7,15 +7,3 @@ type Encoder interface { } var ErrInvalidOperand = errors.New("invalid operand") - -func Encode(encoder Encoder, insts []Instruction) ([]byte, error) { - buf := make([]byte, 0, len(insts)*4) - for _, inst := range insts { - b, err := encoder.Encode(inst) - if err != nil { - return nil, err - } - buf = append(buf, b...) - } - return buf, nil -} diff --git a/asm/memory.go b/asm/memory.go index 7eebb47..e399517 100644 --- a/asm/memory.go +++ b/asm/memory.go @@ -10,7 +10,6 @@ import ( var ( ErrInvalidSize = errors.New("invalid size") - ErrCodeTooLarge = errors.New("code too large for memory") ErrMmapFailed = errors.New("mmap failed") ErrMprotectFailed = errors.New("mprotect failed") ErrMunmapFailed = errors.New("munmap failed") @@ -37,14 +36,6 @@ func Alloc(size int) (Memory, error) { return data, nil } -func (m Memory) Write(code []byte) error { - if len(code) > len(m) { - return fmt.Errorf("%w: code size %d exceeds memory size %d", ErrCodeTooLarge, len(code), len(m)) - } - copy(m, code) - return nil -} - func (m Memory) Executable() error { if len(m) == 0 { return nil diff --git a/asm/memory_test.go b/asm/memory_test.go index b366e42..f5aae48 100644 --- a/asm/memory_test.go +++ b/asm/memory_test.go @@ -23,28 +23,6 @@ func TestAlloc(t *testing.T) { }) } -func TestWrite(t *testing.T) { - t.Run("valid", func(t *testing.T) { - m, err := Alloc(64) - require.NoError(t, err) - defer m.Free() - - code := []byte{0x90, 0x90, 0x90} - err = m.Write(code) - require.NoError(t, err) - require.Equal(t, Memory(code), m[:len(code)]) - }) - - t.Run("too large", func(t *testing.T) { - m, err := Alloc(4) - require.NoError(t, err) - defer m.Free() - - err = m.Write(make([]byte, len(m)+1)) - require.ErrorIs(t, err, ErrCodeTooLarge) - }) -} - func TestExecutable(t *testing.T) { m, err := Alloc(64) require.NoError(t, err) @@ -69,9 +47,6 @@ func TestMemory_Writable(t *testing.T) { require.NoError(t, m.Executable()) require.NoError(t, m.Writable()) - - code := []byte{0x90} - require.NoError(t, m.Write(code)) } func TestMemory_Ptr(t *testing.T) { diff --git a/asm/reg.go b/asm/reg.go index 4cd3b22..820bdc1 100644 --- a/asm/reg.go +++ b/asm/reg.go @@ -20,8 +20,6 @@ type VReg struct { width RegWidth } -const InvalidRegID = 255 - type RegType uint8 const ( diff --git a/asm/regalloc.go b/asm/regalloc.go index b74ae9b..7ea7722 100644 --- a/asm/regalloc.go +++ b/asm/regalloc.go @@ -101,11 +101,6 @@ func (ra *RegAlloc) Free(vreg VReg) { } } -func (ra *RegAlloc) Get(vreg VReg) (PReg, bool) { - p, ok := ra.phys[vreg.ID()] - return p, ok -} - func (ra *RegAlloc) Block(preg PReg) { switch preg.Type() { case RegTypeFloat: @@ -126,20 +121,3 @@ func (ra *RegAlloc) Reset() { ra.blockedInt = 0 ra.blockedFloat = 0 } - -func (ra *RegAlloc) Clone() *RegAlloc { - clone := &RegAlloc{ - info: ra.info, - phys: make(map[int32]PReg), - intAvail: ra.intAvail, - floatAvail: ra.floatAvail, - blockedInt: ra.blockedInt, - blockedFloat: ra.blockedFloat, - } - - for k, v := range ra.phys { - clone.phys[k] = v - } - - return clone -} diff --git a/asm/regalloc_test.go b/asm/regalloc_test.go index 501374d..9a93c16 100644 --- a/asm/regalloc_test.go +++ b/asm/regalloc_test.go @@ -49,8 +49,8 @@ func TestRegAlloc_Reserve(t *testing.T) { v := NewVReg(0, RegTypeInt, Width64) p := NewPReg(2, RegTypeInt, Width64) require.NoError(t, ra.Reserve(v, p)) - got, ok := ra.Get(v) - require.True(t, ok) + got, err := ra.Alloc(v) + require.NoError(t, err) require.Equal(t, uint8(2), got.ID()) }) t.Run("conflict", func(t *testing.T) { @@ -83,30 +83,13 @@ func TestRegAlloc_Free(t *testing.T) { }) } -func TestRegAlloc_Get(t *testing.T) { - t.Run("not found", func(t *testing.T) { - ra := NewRegAlloc(NewRegInfo(4, 2, nil, nil)) - _, ok := ra.Get(NewVReg(0, RegTypeInt, Width64)) - require.False(t, ok) - }) - t.Run("found after alloc", func(t *testing.T) { - ra := NewRegAlloc(NewRegInfo(4, 2, nil, nil)) - v := NewVReg(0, RegTypeInt, Width64) - ra.Alloc(v) - _, ok := ra.Get(v) - require.True(t, ok) - }) -} - func TestRegAlloc_Reset(t *testing.T) { ra := NewRegAlloc(NewRegInfo(1, 0, nil, nil)) v := NewVReg(0, RegTypeInt, Width64) ra.Alloc(v) ra.Reset() - _, ok := ra.Get(v) - require.False(t, ok) - + // The single register is available again after reset. _, err := ra.Alloc(NewVReg(1, RegTypeInt, Width64)) require.NoError(t, err) } diff --git a/asm/reginfo.go b/asm/reginfo.go index 94b5334..e3f94be 100644 --- a/asm/reginfo.go +++ b/asm/reginfo.go @@ -16,13 +16,6 @@ func NewRegInfo(numInt, numFloat uint8, intRes, fltRes []uint8) RegInfo { } } -func (ri RegInfo) IsReserved(reg PReg) bool { - if reg.Type() == RegTypeInt { - return ri.IntReserved.Contains(reg.ID()) - } - return ri.FltReserved.Contains(reg.ID()) -} - func (ri RegInfo) Allocatable(typ RegType) RegMask { var count uint8 var reserved RegMask diff --git a/asm/reginfo_test.go b/asm/reginfo_test.go index 63a62c5..b237d05 100644 --- a/asm/reginfo_test.go +++ b/asm/reginfo_test.go @@ -12,17 +12,6 @@ func TestNewRegInfo(t *testing.T) { require.Equal(t, uint8(4), ri.NumFloat) } -func TestRegInfo_IsReserved(t *testing.T) { - ri := NewRegInfo(8, 4, []uint8{6, 7}, []uint8{3}) - - require.True(t, ri.IsReserved(NewPReg(6, RegTypeInt, Width64))) - require.True(t, ri.IsReserved(NewPReg(7, RegTypeInt, Width64))) - require.False(t, ri.IsReserved(NewPReg(0, RegTypeInt, Width64))) - - require.True(t, ri.IsReserved(NewPReg(3, RegTypeFloat, Width64))) - require.False(t, ri.IsReserved(NewPReg(0, RegTypeFloat, Width64))) -} - func TestRegInfo_Allocatable(t *testing.T) { ri := NewRegInfo(4, 2, []uint8{3}, []uint8{1}) diff --git a/asm/regmask.go b/asm/regmask.go index 3f19646..9d83bcf 100644 --- a/asm/regmask.go +++ b/asm/regmask.go @@ -30,10 +30,6 @@ func (m RegMask) Contains(id uint8) bool { return id < 64 && (m&(1< Date: Fri, 29 May 2026 14:13:21 +0000 Subject: [PATCH 04/18] refactor(transform): collapse constant-folding boilerplate Add a `replace` helper beside `fold` that folds [ip,ip+width) to an instruction and returns the next ip. Each of the ~70 fold cases drops from a 4-5 line fold/width/advance sequence to a single call, leaving only the genuine per-case difference (the arithmetic and result opcode). Div-by-zero guards and the right-align NOP-pad invariant are preserved unchanged. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- transform/cf.go | 451 +++++++++++------------------------------------- 1 file changed, 99 insertions(+), 352 deletions(-) diff --git a/transform/cf.go b/transform/cf.go index 5af0c4d..6e09718 100644 --- a/transform/cf.go +++ b/transform/cf.go @@ -58,180 +58,103 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { } v1 := int32(i1.Operand(0)) i2 := instr.Instruction(fn.Code[ip+i0.Width()+i1.Width():]) + w := i0.Width() + i1.Width() + i2.Width() switch i2.Opcode() { case instr.I32_ADD: - inst := instr.New(instr.I32_CONST, uint64(v0+v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0+v1))) continue case instr.I32_SUB: - inst := instr.New(instr.I32_CONST, uint64(v0-v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0-v1))) continue case instr.I32_MUL: - inst := instr.New(instr.I32_CONST, uint64(v0*v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0*v1))) continue case instr.I32_DIV_S: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I32_CONST, uint64(v0/v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0/v1))) continue case instr.I32_DIV_U: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I32_CONST, uint64(uint32(v0)/uint32(v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(uint32(v0)/uint32(v1)))) continue case instr.I32_REM_S: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I32_CONST, uint64(v0%v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0%v1))) continue case instr.I32_REM_U: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I32_CONST, uint64(uint32(v0)%uint32(v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(uint32(v0)%uint32(v1)))) continue case instr.I32_SHL: - inst := instr.New(instr.I32_CONST, uint64(v0<<(v1&0x1F))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0<<(v1&0x1F)))) continue case instr.I32_SHR_S: - inst := instr.New(instr.I32_CONST, uint64(v0>>(v1&0x1F))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0>>(v1&0x1F)))) continue case instr.I32_SHR_U: - inst := instr.New(instr.I32_CONST, uint64(uint32(v0)>>(uint32(v1)&0x1F))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(uint32(v0)>>(uint32(v1)&0x1F)))) continue case instr.I32_XOR: - inst := instr.New(instr.I32_CONST, uint64(v0^v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0^v1))) continue case instr.I32_AND: - inst := instr.New(instr.I32_CONST, uint64(v0&v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0&v1))) continue case instr.I32_OR: - inst := instr.New(instr.I32_CONST, uint64(v0|v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(v0|v1))) continue case instr.I32_EQ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1)))) continue case instr.I32_NE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1)))) continue case instr.I32_LT_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1)))) continue case instr.I32_LT_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) < uint32(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) < uint32(v1))))) continue case instr.I32_GT_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1)))) continue case instr.I32_GT_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) > uint32(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) > uint32(v1))))) continue case instr.I32_LE_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1)))) continue case instr.I32_LE_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) <= uint32(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) <= uint32(v1))))) continue case instr.I32_GE_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1)))) continue case instr.I32_GE_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) >= uint32(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint32(v0) >= uint32(v1))))) continue default: } case instr.I32_EQZ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == 0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(types.Bool(v0 == 0)))) continue case instr.I32_TO_F32_S: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0)))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0))))) continue case instr.I32_TO_F32_U: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(uint32(v0))))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(uint32(v0)))))) continue default: } @@ -248,180 +171,103 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { } v1 := int64(i1.Operand(0)) i2 := instr.Instruction(fn.Code[ip+i0.Width()+i1.Width():]) + w := i0.Width() + i1.Width() + i2.Width() switch i2.Opcode() { case instr.I64_ADD: - inst := instr.New(instr.I64_CONST, uint64(v0+v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0+v1))) continue case instr.I64_SUB: - inst := instr.New(instr.I64_CONST, uint64(v0-v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0-v1))) continue case instr.I64_MUL: - inst := instr.New(instr.I64_CONST, uint64(v0*v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0*v1))) continue case instr.I64_DIV_S: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I64_CONST, uint64(v0/v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0/v1))) continue case instr.I64_DIV_U: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I64_CONST, uint64(v0)/uint64(v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0)/uint64(v1))) continue case instr.I64_REM_S: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I64_CONST, uint64(v0%v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0%v1))) continue case instr.I64_REM_U: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.I64_CONST, uint64(v0)%uint64(v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0)%uint64(v1))) continue case instr.I64_SHL: - inst := instr.New(instr.I64_CONST, uint64(v0<<(v1&0x3F))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0<<(v1&0x3F)))) continue case instr.I64_SHR_S: - inst := instr.New(instr.I64_CONST, uint64(v0>>(v1&0x3F))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0>>(v1&0x3F)))) continue case instr.I64_SHR_U: - inst := instr.New(instr.I64_CONST, uint64(v0)>>(uint64(v1)&0x3F)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I64_CONST, uint64(v0)>>(uint64(v1)&0x3F))) continue case instr.I64_EQ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1)))) continue case instr.I64_NE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1)))) continue case instr.I64_LT_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1)))) continue case instr.I64_LT_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) < uint64(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) < uint64(v1))))) continue case instr.I64_GT_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1)))) continue case instr.I64_GT_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) > uint64(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) > uint64(v1))))) continue case instr.I64_LE_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1)))) continue case instr.I64_LE_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) <= uint64(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) <= uint64(v1))))) continue case instr.I64_GE_S: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1)))) continue case instr.I64_GE_U: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) >= uint64(v1)))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(uint64(v0) >= uint64(v1))))) continue default: } case instr.I64_EQZ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == 0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(types.Bool(v0 == 0)))) continue case instr.I64_TO_I32: - inst := instr.New(instr.I32_CONST, uint64(int32(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(int32(v0)))) continue case instr.I64_TO_F32_S: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0)))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0))))) continue case instr.I64_TO_F32_U: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(uint64(v0))))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(uint64(v0)))))) continue case instr.I64_TO_F64_S: - inst := instr.New(instr.F64_CONST, math.Float64bits(float64(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F64_CONST, math.Float64bits(float64(v0)))) continue case instr.I64_TO_F64_U: - inst := instr.New(instr.F64_CONST, math.Float64bits(float64(uint64(v0)))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F64_CONST, math.Float64bits(float64(uint64(v0))))) continue default: } @@ -438,84 +284,49 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { } v1 := math.Float32frombits(uint32(i1.Operand(0))) i2 := instr.Instruction(fn.Code[ip+i0.Width()+i1.Width():]) + w := i0.Width() + i1.Width() + i2.Width() switch i2.Opcode() { case instr.F32_ADD: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(v0+v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F32_CONST, uint64(math.Float32bits(v0+v1)))) continue case instr.F32_SUB: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(v0-v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F32_CONST, uint64(math.Float32bits(v0-v1)))) continue case instr.F32_MUL: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(v0*v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F32_CONST, uint64(math.Float32bits(v0*v1)))) continue case instr.F32_DIV: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(v0/v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F32_CONST, uint64(math.Float32bits(v0/v1)))) continue case instr.F32_EQ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1)))) continue case instr.F32_NE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1)))) continue case instr.F32_LT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1)))) continue case instr.F32_GT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1)))) continue case instr.F32_LE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1)))) continue case instr.F32_GE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1)))) continue default: } case instr.F32_TO_I32_U: - inst := instr.New(instr.I32_CONST, uint64(uint32(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(uint32(v0)))) continue case instr.F32_TO_I32_S: - inst := instr.New(instr.I32_CONST, uint64(int32(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(int32(v0)))) continue default: } @@ -532,102 +343,58 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { } v1 := math.Float64frombits(i1.Operand(0)) i2 := instr.Instruction(fn.Code[ip+i0.Width()+i1.Width():]) + w := i0.Width() + i1.Width() + i2.Width() switch i2.Opcode() { case instr.F64_ADD: - inst := instr.New(instr.F64_CONST, math.Float64bits(v0+v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F64_CONST, math.Float64bits(v0+v1))) continue case instr.F64_SUB: - inst := instr.New(instr.F64_CONST, math.Float64bits(v0-v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F64_CONST, math.Float64bits(v0-v1))) continue case instr.F64_MUL: - inst := instr.New(instr.F64_CONST, math.Float64bits(v0*v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F64_CONST, math.Float64bits(v0*v1))) continue case instr.F64_DIV: if v1 == 0 { ip += i0.Width() continue } - inst := instr.New(instr.F64_CONST, math.Float64bits(v0/v1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.F64_CONST, math.Float64bits(v0/v1))) continue case instr.F64_EQ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1)))) continue case instr.F64_NE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1)))) continue case instr.F64_LT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1)))) continue case instr.F64_GT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1)))) continue case instr.F64_LE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1)))) continue case instr.F64_GE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1)))) continue default: } case instr.F64_TO_I32_S: - inst := instr.New(instr.I32_CONST, uint64(uint32(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(uint32(v0)))) continue case instr.F64_TO_I32_U: - inst := instr.New(instr.I32_CONST, uint64(int32(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I32_CONST, uint64(int32(v0)))) continue case instr.F64_TO_I64_S: - inst := instr.New(instr.I64_CONST, uint64(int64(v0))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I64_CONST, uint64(int64(v0)))) continue case instr.F64_TO_I64_U: - inst := instr.New(instr.I64_CONST, uint64(v0)) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.I64_CONST, uint64(v0))) continue case instr.F64_TO_F32: - inst := instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0)))) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.F32_CONST, uint64(math.Float32bits(float32(v0))))) continue default: } @@ -646,51 +413,31 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { break } i2 := instr.Instruction(fn.Code[ip+i0.Width()+i1.Width():]) + w := i0.Width() + i1.Width() + i2.Width() switch v1 := prog.Constants[addr1].(type) { case types.String: switch i2.Opcode() { case instr.STRING_CONCAT: prog.Constants = append(prog.Constants, v0+v1) - inst := instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1)) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue case instr.STRING_EQ: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 == v1)))) continue case instr.STRING_NE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 != v1)))) continue case instr.STRING_LT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 < v1)))) continue case instr.STRING_GT: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 > v1)))) continue case instr.STRING_LE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 <= v1)))) continue case instr.STRING_GE: - inst := instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1))) - width := i0.Width() + i1.Width() + i2.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, w, instr.New(instr.I32_CONST, uint64(types.Bool(v0 >= v1)))) continue default: } @@ -698,10 +445,7 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { } case instr.STRING_ENCODE_UTF32: prog.Constants = append(prog.Constants, types.I32Array(v0)) - inst := instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1)) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue default: } @@ -709,10 +453,7 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { switch i1.Opcode() { case instr.STRING_NEW_UTF32: prog.Constants = append(prog.Constants, types.String(v0)) - inst := instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1)) - width := i0.Width() + i1.Width() - p.fold(fn.Code[ip:ip+width], inst) - ip += width - inst.Width() + ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue default: } @@ -727,6 +468,12 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { return prog, nil } +// replace folds the [ip, ip+width) range to inst and returns the next ip. +func (p *ConstantFoldingPass) replace(code []byte, ip, width int, inst instr.Instruction) int { + p.fold(code[ip:ip+width], inst) + return ip + width - inst.Width() +} + func (p *ConstantFoldingPass) fold(code []byte, inst instr.Instruction) { for i := range code { code[i] = byte(instr.NOP) From 30933d1916fbe7e6c6ba7af367ab3afd2e0288a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:16:56 +0000 Subject: [PATCH 05/18] refactor(pass): drop unused New helper and tidy pass conformance - Remove the production-dead pass[T]/New (the Manager dispatches by reflection on Run); tests build inline passes via a test-local funcPass[T] adapter instead. - NewBasicBlocksPass returns the concrete *BasicBlocksPass, matching the other pass constructors; the Pass[T] assertion still documents conformance. - Unify the two invalidJumpError helpers: drop transform's and inline its uses against analysis.ErrInvalidJump; move analysis's helper to the private-function slot below link. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- analysis/blocks.go | 10 +++++----- optimize/optimizer_test.go | 6 ++---- pass/manager_test.go | 25 +++++++++++++++++-------- pass/pass.go | 12 ------------ transform/dce.go | 23 +++-------------------- 5 files changed, 27 insertions(+), 49 deletions(-) diff --git a/analysis/blocks.go b/analysis/blocks.go index d0d4992..feb9bf0 100644 --- a/analysis/blocks.go +++ b/analysis/blocks.go @@ -23,7 +23,7 @@ var ErrInvalidJump = errors.New("invalid jump") var _ pass.Pass[[]*BasicBlock] = (*BasicBlocksPass)(nil) -func NewBasicBlocksPass() pass.Pass[[]*BasicBlock] { +func NewBasicBlocksPass() *BasicBlocksPass { return &BasicBlocksPass{} } @@ -139,10 +139,6 @@ func (p *BasicBlocksPass) Run(m *pass.Manager) ([]*BasicBlock, error) { return blocks, nil } -func invalidJumpError(ip, target int) error { - return fmt.Errorf("%w: at=%d target=%d", ErrInvalidJump, ip, target) -} - func (p *BasicBlocksPass) link(blocks []*BasicBlock, src, dst int) bool { for i, b := range blocks { if b.Start <= dst && dst < b.End { @@ -153,3 +149,7 @@ func (p *BasicBlocksPass) link(blocks []*BasicBlock, src, dst int) bool { } return false } + +func invalidJumpError(ip, target int) error { + return fmt.Errorf("%w: at=%d target=%d", ErrInvalidJump, ip, target) +} diff --git a/optimize/optimizer_test.go b/optimize/optimizer_test.go index 3b2db2f..7e0ff8b 100644 --- a/optimize/optimizer_test.go +++ b/optimize/optimizer_test.go @@ -3,8 +3,8 @@ package optimize import ( "testing" + "github.com/siyul-park/minivm/analysis" "github.com/siyul-park/minivm/instr" - "github.com/siyul-park/minivm/pass" "github.com/siyul-park/minivm/program" "github.com/siyul-park/minivm/types" "github.com/stretchr/testify/require" @@ -17,9 +17,7 @@ func TestOptimizer_Level(t *testing.T) { func TestOptimizer_Register(t *testing.T) { o := NewOptimizer(O0) - err := o.Register(pass.New(func(_ *pass.Manager) (*program.Program, error) { - return nil, nil - })) + err := o.Register(analysis.NewBasicBlocksPass()) require.NoError(t, err) } diff --git a/pass/manager_test.go b/pass/manager_test.go index 8b7c28c..88cad9f 100644 --- a/pass/manager_test.go +++ b/pass/manager_test.go @@ -8,21 +8,30 @@ import ( "github.com/stretchr/testify/require" ) +// funcPass adapts a closure into a Pass for tests. +type funcPass[T any] struct { + run func(*Manager) (T, error) +} + +func (p funcPass[T]) Run(m *Manager) (T, error) { + return p.run(m) +} + func TestManager_Register(t *testing.T) { m := NewManager() - err := m.Register(New[*program.Program](func(m *Manager) (*program.Program, error) { + err := m.Register(funcPass[*program.Program]{run: func(m *Manager) (*program.Program, error) { var prog *program.Program if err := m.Load(&prog); err != nil { return nil, err } return prog, nil - })) + }}) require.NoError(t, err) } func TestManager_Convert(t *testing.T) { m := NewManager() - _ = m.Register(New[*program.Program](func(m *Manager) (*program.Program, error) { + _ = m.Register(funcPass[*program.Program]{run: func(m *Manager) (*program.Program, error) { var prog *program.Program if err := m.Load(&prog); err != nil { return nil, err @@ -32,14 +41,14 @@ func TestManager_Convert(t *testing.T) { return nil, err } return prog, nil - })) - _ = m.Register(New[*types.Function](func(m *Manager) (*types.Function, error) { + }}) + _ = m.Register(funcPass[*types.Function]{run: func(m *Manager) (*types.Function, error) { var fn *types.Function if err := m.Load(&fn); err != nil { return nil, err } return fn, nil - })) + }}) err := m.Run(program.New(nil)) require.NoError(t, err) @@ -47,13 +56,13 @@ func TestManager_Convert(t *testing.T) { func TestManager_Run(t *testing.T) { m := NewManager() - _ = m.Register(New[*program.Program](func(m *Manager) (*program.Program, error) { + _ = m.Register(funcPass[*program.Program]{run: func(m *Manager) (*program.Program, error) { var prog *program.Program if err := m.Load(&prog); err != nil { return nil, err } return prog, nil - })) + }}) err := m.Run(program.New(nil)) require.NoError(t, err) diff --git a/pass/pass.go b/pass/pass.go index 90d63b3..7630b26 100644 --- a/pass/pass.go +++ b/pass/pass.go @@ -3,15 +3,3 @@ package pass type Pass[T any] interface { Run(*Manager) (T, error) } - -type pass[T any] struct { - run func(*Manager) (T, error) -} - -func New[T any](run func(*Manager) (T, error)) Pass[T] { - return &pass[T]{run: run} -} - -func (p *pass[T]) Run(m *Manager) (T, error) { - return p.run(m) -} diff --git a/transform/dce.go b/transform/dce.go index 2be416f..69d1028 100644 --- a/transform/dce.go +++ b/transform/dce.go @@ -3,8 +3,6 @@ package transform import ( "fmt" - "github.com/siyul-park/minivm/types" - "github.com/siyul-park/minivm/analysis" "github.com/siyul-park/minivm/instr" "github.com/siyul-park/minivm/pass" @@ -25,18 +23,7 @@ func (p *DeadCodeEliminationPass) Run(m *pass.Manager) (*program.Program, error) return nil, err } - var fns []*types.Function - fns = append(fns, &types.Function{ - Typ: &types.FunctionType{}, - Code: prog.Code, - }) - for _, v := range prog.Constants { - if fn, ok := v.(*types.Function); ok { - fns = append(fns, fn) - } - } - - for i, fn := range fns { + for i, fn := range functions(prog) { code := fn.Code var blocks []*analysis.BasicBlock @@ -88,7 +75,7 @@ func (p *DeadCodeEliminationPass) Run(m *pass.Manager) (*program.Program, error) target++ } if target < 0 || target >= len(offsets) { - return nil, invalidJumpError(read) + return nil, fmt.Errorf("%w: at=%d", analysis.ErrInvalidJump, read) } inst.SetOperand(0, uint64(offsets[target]-write-inst.Width())) case instr.BR_TABLE: @@ -101,7 +88,7 @@ func (p *DeadCodeEliminationPass) Run(m *pass.Manager) (*program.Program, error) target++ } if target < 0 || target >= len(offsets) { - return nil, invalidJumpError(read) + return nil, fmt.Errorf("%w: at=%d", analysis.ErrInvalidJump, read) } inst.SetOperand(j+1, uint64(offsets[target]-write-width)) } @@ -121,7 +108,3 @@ func (p *DeadCodeEliminationPass) Run(m *pass.Manager) (*program.Program, error) return prog, nil } - -func invalidJumpError(at int) error { - return fmt.Errorf("%w: at=%d", analysis.ErrInvalidJump, at) -} From 1439b3e90e4903f6a6d56597ca302c6829e0c97d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:16:56 +0000 Subject: [PATCH 06/18] refactor(transform): dedup index remap and per-pass function lists - Extract a generic dedup helper for the parallel constant/type index-remap blocks in cd.go (init, mark-used, transitive collapse, compact), called once each for constants (==) and types (Equals). - Extract a shared `functions` helper returning a program's executable functions (implicit root over prog.Code plus *types.Function constants), replacing the identical inline construction in cf.go, dce.go, and cd.go. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- transform/cd.go | 76 +++++++------------------------------------- transform/cf.go | 13 +------- transform/program.go | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 77 deletions(-) create mode 100644 transform/program.go diff --git a/transform/cd.go b/transform/cd.go index 6f692bc..44afcf7 100644 --- a/transform/cd.go +++ b/transform/cd.go @@ -21,86 +21,31 @@ func (p *ConstantDeduplicationPass) Run(m *pass.Manager) (*program.Program, erro return nil, err } - var codes [][]byte - codes = append(codes, prog.Code) - for _, v := range prog.Constants { - if fn, ok := v.(*types.Function); ok { - codes = append(codes, fn.Code) - } - } + fns := functions(prog) constants := prog.Constants typs := prog.Types - constIndex := make([]int, len(constants)) - typeIndex := make([]int, len(typs)) - for i := 0; i < len(constIndex); i++ { - constIndex[i] = -1 - } - for i := 0; i < len(typeIndex); i++ { - typeIndex[i] = -1 - } - - for _, code := range codes { + constUsed := make([]bool, len(constants)) + typeUsed := make([]bool, len(typs)) + for _, fn := range fns { + code := fn.Code ip := 0 for ip < len(code) { inst := instr.Instruction(code[ip:]) switch inst.Opcode() { case instr.CONST_GET: - idx := inst.Operand(0) - constIndex[idx] = int(idx) + constUsed[inst.Operand(0)] = true case instr.ARRAY_NEW, instr.ARRAY_NEW_DEFAULT, instr.STRUCT_NEW, instr.STRUCT_NEW_DEFAULT: - idx := inst.Operand(0) - typeIndex[idx] = int(idx) + typeUsed[inst.Operand(0)] = true default: } ip += inst.Width() } } - for i := 0; i < len(constants); i++ { - if constIndex[i] == -1 { - continue - } - for j := i + 1; j < len(constants); j++ { - if constants[j] == constants[i] { - constIndex[j] = constIndex[i] - } - } - } - for i := 0; i < len(typs); i++ { - if typeIndex[i] == -1 { - continue - } - for j := i + 1; j < len(typs); j++ { - if typs[j].Equals(typs[i]) { - typeIndex[j] = typeIndex[i] - } - } - } - - constSize := 0 - typesSize := 0 - for i := 0; i < len(constIndex); i++ { - if constIndex[i] != -1 { - if constIndex[i] != i { - constIndex[i] = constIndex[constIndex[i]] - } else { - constIndex[i] = constSize - constSize++ - } - } - } - for i := 0; i < len(typeIndex); i++ { - if typeIndex[i] != -1 { - if typeIndex[i] != i { - typeIndex[i] = typeIndex[typeIndex[i]] - } else { - typeIndex[i] = typesSize - typesSize++ - } - } - } + constIndex, constSize := dedup(constants, constUsed, func(a, b types.Value) bool { return a == b }) + typeIndex, typesSize := dedup(typs, typeUsed, func(a, b types.Type) bool { return a.Equals(b) }) for i, v := range constIndex { if v >= 0 { @@ -122,7 +67,8 @@ func (p *ConstantDeduplicationPass) Run(m *pass.Manager) (*program.Program, erro typs = nil } - for _, code := range codes { + for _, fn := range fns { + code := fn.Code ip := 0 for ip < len(code) { inst := instr.Instruction(code[ip:]) diff --git a/transform/cf.go b/transform/cf.go index 6e09718..3795535 100644 --- a/transform/cf.go +++ b/transform/cf.go @@ -24,18 +24,7 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { return nil, err } - var fns []*types.Function - fns = append(fns, &types.Function{ - Typ: &types.FunctionType{}, - Code: prog.Code, - }) - for _, v := range prog.Constants { - if fn, ok := v.(*types.Function); ok { - fns = append(fns, fn) - } - } - - for _, fn := range fns { + for _, fn := range functions(prog) { var blocks []*analysis.BasicBlock if err := m.Convert(fn, &blocks); err != nil { return nil, err diff --git a/transform/program.go b/transform/program.go new file mode 100644 index 0000000..7662798 --- /dev/null +++ b/transform/program.go @@ -0,0 +1,57 @@ +package transform + +import ( + "github.com/siyul-park/minivm/types" + + "github.com/siyul-park/minivm/program" +) + +// functions returns a program's executable functions: an implicit root over +// prog.Code followed by every *types.Function constant. +func functions(prog *program.Program) []*types.Function { + fns := []*types.Function{{Typ: &types.FunctionType{}, Code: prog.Code}} + for _, v := range prog.Constants { + if fn, ok := v.(*types.Function); ok { + fns = append(fns, fn) + } + } + return fns +} + +// dedup builds a compaction index for items: each referenced entry (used[i]) +// is renumbered into a dense range with equal entries collapsed to one slot, +// while unreferenced entries map to -1. Returns the index and compacted size. +func dedup[T any](items []T, used []bool, eq func(a, b T) bool) ([]int, int) { + index := make([]int, len(items)) + for i := range index { + index[i] = -1 + if used[i] { + index[i] = i + } + } + + for i := range items { + if index[i] == -1 { + continue + } + for j := i + 1; j < len(items); j++ { + if eq(items[j], items[i]) { + index[j] = index[i] + } + } + } + + size := 0 + for i := range index { + if index[i] == -1 { + continue + } + if index[i] != i { + index[i] = index[index[i]] + } else { + index[i] = size + size++ + } + } + return index, size +} From 1e201f9b9026f5489886061b3b5cfebf4f09c0d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:19:51 +0000 Subject: [PATCH 07/18] refactor(interp): collapse redundant debugger state fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the stop/stoppedFlag pair with a nilable *Stop, and the skip/skipFunc/skipIP/skipDepth quartet with a nilable *skipPoint (nil = not stopped / not skipping), removing the zero-value-Stop ambiguity that forced the bool. Regroup fields per the config → data → state → counter layering. The lazy init() guard stays: a zero-value Debugger is a supported public-API path (exercised by TestDebugger_Breakpoints). https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/debugger.go | 54 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/interp/debugger.go b/interp/debugger.go index 22331df..71ccd70 100644 --- a/interp/debugger.go +++ b/interp/debugger.go @@ -23,21 +23,28 @@ type Breakpoint struct { } type Debugger struct { + mode debugMode + breakpoints map[int]*Breakpoint - next int - mode debugMode - stop Stop - stoppedFlag bool - skip bool - skipFunc int - skipIP int - skipDepth int + + stop *Stop + skip *skipPoint pausedDepth int depth int + + next int } type debugMode int +// skipPoint marks the instruction a resumed debugger steps over once so it +// does not immediately re-trigger at the position it stopped on. +type skipPoint struct { + fn int + ip int + depth int +} + const ( debugContinue debugMode = iota debugStep @@ -65,11 +72,12 @@ func NewDebugger() *Debugger { func (d *Debugger) Hook(i *Interpreter) error { fn, ip, fp := i.Func(), i.IP(), i.FP() - if d.skip && d.skipFunc == fn && d.skipIP == ip && d.skipDepth == fp { - d.skip = false - return nil + if s := d.skip; s != nil { + d.skip = nil + if s.fn == fn && s.ip == ip && s.depth == fp { + return nil + } } - d.skip = false if bp := d.breakpoint(i, fn, ip); bp != nil { bp.Hits++ @@ -92,7 +100,10 @@ func (d *Debugger) Hook(i *Interpreter) error { } func (d *Debugger) Stop() Stop { - return d.stop + if d.stop == nil { + return Stop{} + } + return *d.stop } func (d *Debugger) Continue() { @@ -184,27 +195,26 @@ func (d *Debugger) breakpoint(i *Interpreter, fn, ip int) *Breakpoint { } func (d *Debugger) stopped(fn, ip, depth, bp int) error { - d.stop = Stop{ + d.stop = &Stop{ Func: fn, IP: ip, Breakpoint: bp, } - d.stoppedFlag = true d.pausedDepth = depth d.mode = debugContinue return ErrStopped } func (d *Debugger) resume() { - if !d.stoppedFlag { + if d.stop == nil { return } - d.skip = true - d.skipFunc = d.stop.Func - d.skipIP = d.stop.IP - d.skipDepth = d.stopDepth() - d.stop = Stop{} - d.stoppedFlag = false + d.skip = &skipPoint{ + fn: d.stop.Func, + ip: d.stop.IP, + depth: d.stopDepth(), + } + d.stop = nil } func (d *Debugger) stopDepth() int { From 579c6e27a07f28971c6ea5fe3bd9afac022dcfc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:25:57 +0000 Subject: [PATCH 08/18] refactor(interp): encapsulate boxed-value refcounting Add retainVal/releaseVal wrappers beside retain/release that perform the KindRef guard once, and route the 44 opcode handlers in threaded.go through them instead of repeating `if v.Kind() == types.KindRef { i.release(v.Ref()) }` at every site. Guarded variants keep their non-Kind condition (`if old != val { i.releaseVal(old) }`, `if ok { ... }`). The raw-addr releases and the single retains(v, n) site are left as-is. retainVal inlines at all sites; releaseVal does not (it calls the loop-bearing release), but Fib35 shows no measurable change. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/interp.go | 14 ++++ interp/threaded.go | 176 ++++++++++++++++----------------------------- 2 files changed, 75 insertions(+), 115 deletions(-) diff --git a/interp/interp.go b/interp/interp.go index 783f37a..e41f9b7 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -702,6 +702,20 @@ func (i *Interpreter) keep(val types.Value) int { return i.alloc(val) } +// retainVal increments v's refcount when it holds a heap reference. +func (i *Interpreter) retainVal(v types.Boxed) { + if v.Kind() == types.KindRef { + i.retain(v.Ref()) + } +} + +// releaseVal decrements v's refcount when it holds a heap reference. +func (i *Interpreter) releaseVal(v types.Boxed) { + if v.Kind() == types.KindRef { + i.release(v.Ref()) + } +} + func (i *Interpreter) retain(addr int) { i.rc[addr]++ } diff --git a/interp/threaded.go b/interp/threaded.go index 46e5532..8c20cee 100644 --- a/interp/threaded.go +++ b/interp/threaded.go @@ -46,9 +46,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } val := i.stack[i.sp-1] - if val.Kind() == types.KindRef { - i.release(val.Ref()) - } + i.releaseVal(val) i.sp-- i.fr.ip++ } @@ -63,9 +61,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackOverflow) } val := i.stack[i.sp-1] - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) i.stack[i.sp] = val i.sp++ i.fr.ip++ @@ -139,9 +135,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } else { result = v1 } - if result.Kind() == types.KindRef { - i.release(result.Ref()) - } + i.releaseVal(result) i.stack[i.sp-3] = result i.sp -= 2 i.fr.ip++ @@ -286,9 +280,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.globals[idx] - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 3 @@ -315,8 +307,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } } old := i.globals[idx] - if old != val && old.Kind() == types.KindRef { - i.release(old.Ref()) + if old != val { + i.releaseVal(old) } i.globals[idx] = val i.sp-- @@ -344,8 +336,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } } old := i.globals[idx] - if old != val && old.Kind() == types.KindRef { - i.release(old.Ref()) + if old != val { + i.releaseVal(old) } i.globals[idx] = val i.fr.ip += 3 @@ -410,9 +402,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.stack[addr] - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 2 @@ -436,8 +426,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } val := i.stack[i.sp-1] old := i.stack[addr] - if old != val && old.Kind() == types.KindRef { - i.release(old.Ref()) + if old != val { + i.releaseVal(old) } i.stack[addr] = val i.sp-- @@ -462,8 +452,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } val := i.stack[i.sp-1] old := i.stack[addr] - if old != val && old.Kind() == types.KindRef { - i.release(old.Ref()) + if old != val { + i.releaseVal(old) } i.stack[addr] = val i.fr.ip += 2 @@ -541,9 +531,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.fr.upvals[idx] - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 2 @@ -561,8 +549,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } val := i.stack[i.sp-1] old := i.fr.upvals[idx] - if old != val && old.Kind() == types.KindRef { - i.release(old.Ref()) + if old != val { + i.releaseVal(old) } i.fr.upvals[idx] = val i.sp-- @@ -2266,9 +2254,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrIndexOutOfRange) } elem := arr.Elems[idx] - if elem.Kind() == types.KindRef { - i.retain(elem.Ref()) - } + i.retainVal(elem) val = elem default: panic(ErrTypeMismatch) @@ -2319,9 +2305,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } elem := arr.Elems[idx] arr.Elems[idx] = val - if elem.Kind() == types.KindRef { - i.release(elem.Ref()) - } + i.releaseVal(elem) default: panic(ErrTypeMismatch) } @@ -2388,9 +2372,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if val.Kind() == types.KindRef { i.retains(val.Ref(), size-1) } - if elem.Kind() == types.KindRef { - i.release(elem.Ref()) - } + i.releaseVal(elem) default: panic(ErrTypeMismatch) } @@ -2440,14 +2422,10 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } elems := arr.Elems for _, v := range elems[src : src+size] { - if v.Kind() == types.KindRef { - i.retain(v.Ref()) - } + i.retainVal(v) } for _, v := range elems[dst : dst+size] { - if v.Kind() == types.KindRef { - i.release(v.Ref()) - } + i.releaseVal(v) } copy(elems[dst:dst+size], elems[src:src+size]) default: @@ -2545,9 +2523,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val = types.BoxF64(math.Float64frombits(s.Data[idx])) case types.KindRef: val = types.Boxed(s.Data[idx]) - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) default: panic(ErrTypeMismatch) } @@ -2564,9 +2540,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val = i.boxI64(int64(s.Raw(idx))) case types.KindRef: val = types.Boxed(s.Raw(idx)) - if val.Kind() == types.KindRef { - i.retain(val.Ref()) - } + i.retainVal(val) default: panic(ErrTypeMismatch) } @@ -2610,9 +2584,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ s.Data[idx] = math.Float64bits(val.F64()) case types.KindRef: old := types.Boxed(s.Data[idx]) - if old.Kind() == types.KindRef { - i.release(old.Ref()) - } + i.releaseVal(old) s.Data[idx] = uint64(val) default: panic(ErrTypeMismatch) @@ -2630,9 +2602,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ s.SetRaw(idx, uint64(i.unboxI64(val))) case types.KindRef: old := types.Boxed(s.Raw(idx)) - if old.Kind() == types.KindRef { - i.release(old.Ref()) - } + i.releaseVal(old) s.SetRaw(idx, uint64(val)) default: panic(ErrTypeMismatch) @@ -2678,23 +2648,23 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ switch m := m.(type) { case *types.MapI32: old, ok := m.Set(key.I32(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapI64: old, ok := m.Set(i.unboxI64(key), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF32: old, ok := m.Set(key.F32(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF64: old, ok := m.Set(key.F64(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.Map: var k types.MapKey @@ -2739,9 +2709,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if drop { i.release(keyRef) } - if old.Value.Kind() == types.KindRef { - i.release(old.Value.Ref()) - } + i.releaseVal(old.Value) } default: panic(ErrTypeMismatch) @@ -2901,9 +2869,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ default: panic(ErrTypeMismatch) } - if result.Kind() == types.KindRef { - i.retain(result.Ref()) - } + i.retainVal(result) i.release(addr) i.sp-- i.stack[i.sp-1] = result @@ -2990,9 +2956,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ default: panic(ErrTypeMismatch) } - if result.Kind() == types.KindRef { - i.retain(result.Ref()) - } + i.retainVal(result) i.release(addr) i.stack[i.sp-2] = result i.stack[i.sp-1] = types.BoxBool(found) @@ -3015,23 +2979,23 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ switch m := i.heap[addr].(type) { case *types.MapI32: old, ok := m.Set(key.I32(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapI64: old, ok := m.Set(i.unboxI64(key), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF32: old, ok := m.Set(key.F32(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF64: old, ok := m.Set(key.F64(), value) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.Map: var k types.MapKey @@ -3076,9 +3040,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if drop { i.release(keyRef) } - if old.Value.Kind() == types.KindRef { - i.release(old.Value.Ref()) - } + i.releaseVal(old.Value) } default: panic(ErrTypeMismatch) @@ -3103,23 +3065,23 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ switch m := i.heap[addr].(type) { case *types.MapI32: old, ok := m.Delete(key.I32()) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapI64: old, ok := m.Delete(i.unboxI64(key)) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF32: old, ok := m.Delete(key.F32()) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.MapF64: old, ok := m.Delete(key.F64()) - if ok && old.Kind() == types.KindRef { - i.release(old.Ref()) + if ok { + i.releaseVal(old) } case *types.Map: var k types.MapKey @@ -3155,12 +3117,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } old, ok := m.Delete(k) if ok { - if old.Key.Kind() == types.KindRef { - i.release(old.Key.Ref()) - } - if old.Value.Kind() == types.KindRef { - i.release(old.Value.Ref()) - } + i.releaseVal(old.Key) + i.releaseVal(old.Value) } if drop { i.release(keyRef) @@ -3187,36 +3145,24 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ switch m := i.heap[addr].(type) { case *types.MapI32: m.Clear(func(value types.Boxed) { - if value.Kind() == types.KindRef { - i.release(value.Ref()) - } + i.releaseVal(value) }) case *types.MapI64: m.Clear(func(value types.Boxed) { - if value.Kind() == types.KindRef { - i.release(value.Ref()) - } + i.releaseVal(value) }) case *types.MapF32: m.Clear(func(value types.Boxed) { - if value.Kind() == types.KindRef { - i.release(value.Ref()) - } + i.releaseVal(value) }) case *types.MapF64: m.Clear(func(value types.Boxed) { - if value.Kind() == types.KindRef { - i.release(value.Ref()) - } + i.releaseVal(value) }) case *types.Map: m.Clear(func(entry types.MapEntry) { - if entry.Key.Kind() == types.KindRef { - i.release(entry.Key.Ref()) - } - if entry.Value.Kind() == types.KindRef { - i.release(entry.Value.Ref()) - } + i.releaseVal(entry.Key) + i.releaseVal(entry.Value) }) default: panic(ErrTypeMismatch) From 17c53f77d01b0f45919378fed52bb3bccc7ae99b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:30:38 +0000 Subject: [PATCH 09/18] refactor(interp): tidy jit driver - Lift the adjacent/absorbed closures out of traces() into jitPlan methods, dropping closure capture and reading top-down. - Extract a shared jitSeg.pin helper for the duplicated pin-loop + Site call in finalize/pinReturn (the empty-params guard is kept). - Regroup jitSeg fields per the config/infra/data/state layering. - Rename jitRun.c to compiler so chains read as s.r.compiler.profile (the jitSeg parent field stays r to avoid colliding with the run() method). https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/jit.go | 115 ++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/interp/jit.go b/interp/jit.go index e90d9bb..08e9dcb 100644 --- a/interp/jit.go +++ b/interp/jit.go @@ -44,9 +44,9 @@ type jitTrace struct { } type jitRun struct { - c *jitCompiler - a *asm.Assembler - code []byte + compiler *jitCompiler + a *asm.Assembler + code []byte labels map[int]int entries map[int]jitEntry @@ -70,15 +70,15 @@ type jitSeg struct { r *jitRun assembler *asm.Assembler + code []byte constants []types.Boxed labels map[int]int - start int - end int - ip int - force bool - + start int + end int + ip int + force bool stack []asm.VReg params []asm.VReg facts map[int]types.Kind @@ -136,10 +136,10 @@ func (c *jitCompiler) Compile(code []byte) []func(*Interpreter) { c.assembler.Reset() r := &jitRun{ - c: c, - a: c.assembler, - code: plan.code, - entries: make(map[int]jitEntry), + compiler: c, + a: c.assembler, + code: plan.code, + entries: make(map[int]jitEntry), } r.assign(plan.blocks) objs := r.compile(plan) @@ -270,23 +270,13 @@ func (c *jitCompiler) traces(plan jitPlan) []jitTrace { eligible[b] = true } - // adjacent reports whether b flows straight into its single successor: one - // successor block that begins exactly where b ends. - adjacent := func(b *analysis.BasicBlock) (*analysis.BasicBlock, bool) { - if len(b.Succs) != 1 { - return nil, false - } - next := plan.blocks[b.Succs[0]] - return next, b.End == next.Start - } - used := make(map[*analysis.BasicBlock]bool, len(plan.hot)) traces := make([]jitTrace, 0, len(plan.hot)) appendTrace := func(first *analysis.BasicBlock) { trace := jitTrace{blocks: []*analysis.BasicBlock{first}} used[first] = true for current := first; ; { - next, ok := adjacent(current) + next, ok := plan.adjacent(current) if !ok || used[next] || !eligible[next] { break } @@ -297,23 +287,8 @@ func (c *jitCompiler) traces(plan jitPlan) []jitTrace { traces = append(traces, trace) } - // absorbed reports whether an eligible block flows straight into b, in which - // case b becomes a trace body rather than a head. - absorbed := func(b *analysis.BasicBlock) bool { - for _, p := range b.Preds { - if p < 0 || p >= len(plan.blocks) { - continue - } - prev := plan.blocks[p] - if next, ok := adjacent(prev); ok && eligible[prev] && next == b { - return true - } - } - return false - } - for _, first := range plan.hot { - if !used[first] && !absorbed(first) { + if !used[first] && !plan.absorbed(first, eligible) { appendTrace(first) } } @@ -325,6 +300,31 @@ func (c *jitCompiler) traces(plan jitPlan) []jitTrace { return traces } +// adjacent reports whether b flows straight into its single successor: one +// successor block that begins exactly where b ends. +func (p jitPlan) adjacent(b *analysis.BasicBlock) (*analysis.BasicBlock, bool) { + if len(b.Succs) != 1 { + return nil, false + } + next := p.blocks[b.Succs[0]] + return next, b.End == next.Start +} + +// absorbed reports whether an eligible block flows straight into b, in which +// case b becomes a trace body rather than a head. +func (p jitPlan) absorbed(b *analysis.BasicBlock, eligible map[*analysis.BasicBlock]bool) bool { + for _, pred := range b.Preds { + if pred < 0 || pred >= len(p.blocks) { + continue + } + prev := p.blocks[pred] + if next, ok := p.adjacent(prev); ok && eligible[prev] && next == b { + return true + } + } + return false +} + func (r *jitRun) assign(blocks []*analysis.BasicBlock) { r.labels = make(map[int]int, len(blocks)) for _, b := range blocks { @@ -418,7 +418,7 @@ func (r *jitRun) segment(start, end int, force bool, boundaries []int) (*asm.Rel r: r, assembler: r.a, code: r.code, - constants: r.c.constants, + constants: r.compiler.constants, labels: r.labels, start: start, end: end, @@ -586,7 +586,7 @@ func (s *jitSeg) can(end, count int) (bool, bool) { if count == 0 { return false, false } - if !s.force && count < s.r.c.cutoff { + if !s.force && count < s.r.compiler.cutoff { return false, false } if !s.force && s.hot(s.start, end) == 0 { @@ -596,15 +596,15 @@ func (s *jitSeg) can(end, count int) (bool, bool) { } func (s *jitSeg) hot(start, end int) uint64 { - return s.r.c.profile.Range(s.r.c.addr, start, end) + return s.r.compiler.profile.Range(s.r.compiler.addr, start, end) } func (s *jitSeg) abort(skip bool) { s.assembler.Abort() if skip { - s.r.c.profile.JITSkip() + s.r.compiler.profile.JITSkip() } else { - s.r.c.profile.JITAbort() + s.r.compiler.profile.JITAbort() } } @@ -614,11 +614,11 @@ func (s *jitSeg) compile(next int, stop bool) (*asm.RelocObject, int, bool) { obj, err := s.assembler.Compile() if err != nil { s.assembler.Abort() - s.r.c.profile.JITError() + s.r.compiler.profile.JITError() return nil, next, false } - s.r.c.profile.JITEmit(obj.Chunk.Size()) + s.r.compiler.profile.JITEmit(obj.Chunk.Size()) return obj, next, stop } @@ -626,11 +626,8 @@ func (s *jitSeg) compile(next int, stop bool) (*asm.RelocObject, int, bool) { // registers them as Site(0). The only place the assembler learns the VM's // eval-stack-derived call convention. func (s *jitSeg) finalize() { - for i, v := range s.params { - _ = s.assembler.Pin(v, asm.NewPReg(uint8(i), v.Type(), v.Width())) - } if len(s.params) > 0 { - s.assembler.Site(0, s.params) + s.pin(s.params, 0) } } @@ -639,11 +636,17 @@ func (s *jitSeg) finalize() { // specific ret() helpers. func (s *jitSeg) pinReturn() int { idx := s.assembler.Index() - for i, v := range s.stack { + s.pin(s.stack, idx) + return idx +} + +// pin maps each reg to the ABI slot at its index and registers them as the +// Site for the given instruction index. +func (s *jitSeg) pin(regs []asm.VReg, site int) { + for i, v := range regs { _ = s.assembler.Pin(v, asm.NewPReg(uint8(i), v.Type(), v.Width())) } - s.assembler.Site(idx, s.stack) - return idx + s.assembler.Site(site, regs) } func (s *jitSeg) edge(target int) (int, int) { @@ -659,7 +662,7 @@ func (s *jitSeg) edge(target int) (int, int) { } func (s *jitSeg) global(idx int) (int16, bool) { - if idx < 0 || idx >= len(s.r.c.globals) { + if idx < 0 || idx >= len(s.r.compiler.globals) { return 0, false } offset := int16(idx * 8) @@ -670,7 +673,7 @@ func (s *jitSeg) global(idx int) (int16, bool) { } func (s *jitSeg) local(idx int) (types.Type, bool) { - c := s.r.c + c := s.r.compiler if c.addr <= 0 || c.addr >= len(c.heap) { return nil, false } @@ -691,7 +694,7 @@ func (s *jitSeg) local(idx int) (types.Type, bool) { } func (s *jitSeg) regKind(r asm.Reg) (types.Kind, bool) { - return s.r.c.regKind(r) + return s.r.compiler.regKind(r) } func (c *jitCompiler) closure(fn asm.Caller, pregs []asm.PReg, sig *asm.Signature) func(*Interpreter) { From 7d7200572d1c0b538f5c7d993f6dfbc42645d89b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:31:59 +0000 Subject: [PATCH 10/18] refactor(types): dedup array/struct formatters and fix slot order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Route Array.String and Struct.String through the single formatSlice helper (deleting the parallel joinElems generic), so the `Type{e0, e1, …}` rendering lives in one place. Reorder array.go to the fixed declaration slots: types, values, constructors, methods, then the private formatSlice helper. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- types/array.go | 64 ++++++++++++++++++++----------------------------- types/struct.go | 14 +++-------- 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/types/array.go b/types/array.go index 28df10a..f80d735 100644 --- a/types/array.go +++ b/types/array.go @@ -37,6 +37,14 @@ var _ Value = F64Array(nil) var _ Traceable = (*Array)(nil) var _ Type = (*ArrayType)(nil) +func NewArray(typ *ArrayType, elems ...Boxed) *Array { + return &Array{Typ: typ, Elems: elems} +} + +func NewArrayType(elem Type) *ArrayType { + return &ArrayType{Elem: elem, ElemKind: elem.Kind()} +} + func (a I32Array) Kind() Kind { return KindRef } func (a I32Array) Type() Type { return TypeI32Array } func (a I32Array) String() string { @@ -61,14 +69,12 @@ func (a F64Array) String() string { return formatSlice(TypeF64Array, len(a), func(i int) string { return fmt.Sprintf("%g", a[i]) }) } -func NewArray(typ *ArrayType, elems ...Boxed) *Array { - return &Array{Typ: typ, Elems: elems} +func (a *Array) Kind() Kind { return KindRef } +func (a *Array) Type() Type { return a.Typ } +func (a *Array) String() string { + return formatSlice(a.Type(), len(a.Elems), func(i int) string { return a.Elems[i].String() }) } -func (a *Array) Kind() Kind { return KindRef } -func (a *Array) Type() Type { return a.Typ } -func (a *Array) String() string { return joinElems(a.Type(), a.Elems) } - func (a *Array) Refs() []Ref { var refs []Ref for _, e := range a.Elems { @@ -82,38 +88,6 @@ func (a *Array) Refs() []Ref { return refs } -func formatSlice(typ Type, n int, elem func(int) string) string { - var sb strings.Builder - sb.WriteString(typ.String()) - sb.WriteByte('{') - for i := range n { - if i > 0 { - sb.WriteString(", ") - } - sb.WriteString(elem(i)) - } - sb.WriteByte('}') - return sb.String() -} - -func joinElems[T interface{ String() string }](typ Type, elems []T) string { - var sb strings.Builder - sb.WriteString(typ.String()) - sb.WriteByte('{') - for i, e := range elems { - if i > 0 { - sb.WriteString(", ") - } - sb.WriteString(e.String()) - } - sb.WriteByte('}') - return sb.String() -} - -func NewArrayType(elem Type) *ArrayType { - return &ArrayType{Elem: elem, ElemKind: elem.Kind()} -} - func (t *ArrayType) Kind() Kind { return KindRef } func (t *ArrayType) String() string { @@ -133,3 +107,17 @@ func (t *ArrayType) Equals(other Type) bool { } return false } + +func formatSlice(typ Type, n int, elem func(int) string) string { + var sb strings.Builder + sb.WriteString(typ.String()) + sb.WriteByte('{') + for i := range n { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(elem(i)) + } + sb.WriteByte('}') + return sb.String() +} diff --git a/types/struct.go b/types/struct.go index f8d9a7e..9464941 100644 --- a/types/struct.go +++ b/types/struct.go @@ -106,17 +106,9 @@ func (s *Struct) Type() Type { } func (s *Struct) String() string { - var sb strings.Builder - sb.WriteString(s.Typ.String()) - sb.WriteString("{") - for i, f := range s.Typ.Fields { - if i > 0 { - sb.WriteString(", ") - } - sb.WriteString(s.field(i, f).String()) - } - sb.WriteString("}") - return sb.String() + return formatSlice(s.Typ, len(s.Typ.Fields), func(i int) string { + return s.field(i, s.Typ.Fields[i]).String() + }) } func (s *Struct) Refs() []Ref { From cdf8f974d56223726192b2b939127bf4618063b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:09:05 +0000 Subject: [PATCH 11/18] refactor(types): merge typed maps into generic TypedMap[K] Replace MapI32/MapI64/MapF32/MapF64 (4 structs, ~40 methods) with a single generic TypedMap[K comparable]; a boxKey helper renders native keys for String. Float maps now use native float32/float64 keys: the explicit -0.0 bit canonicalization is dropped (Go map semantics still collapse -0.0/+0.0, and NaN keys are unretrievable, matching the approved decision). The boxed Map/MapKey/MapEntry (reference- and string-keyed, with its heap-boxed-I64 key disambiguation) is intentionally left as-is. Updated NewMapForType dispatch, the typed cases in threaded.go and marshal.go, and collapsed the per-type map tests into TestTypedMap_* (adding -0.0 and NaN cases). https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/interp_test.go | 8 +- interp/marshal.go | 24 +-- interp/threaded.go | 56 +++--- types/map.go | 373 ++++++++----------------------------- types/map_test.go | 422 ++++++++++-------------------------------- 5 files changed, 215 insertions(+), 668 deletions(-) diff --git a/interp/interp_test.go b/interp/interp_test.go index 3dbefd3..234609c 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -3899,7 +3899,7 @@ func TestInterpreter_Marshal(t *testing.T) { i32, err := i.Marshal(map[int32]int32{1: 2}) require.NoError(t, err) - mI32, ok := i32.(*types.MapI32) + mI32, ok := i32.(*types.TypedMap[int32]) require.True(t, ok) gotI32, ok := mI32.Get(1) require.True(t, ok) @@ -3907,7 +3907,7 @@ func TestInterpreter_Marshal(t *testing.T) { i64, err := i.Marshal(map[int64]string{1: "a"}) require.NoError(t, err) - mI64, ok := i64.(*types.MapI64) + mI64, ok := i64.(*types.TypedMap[int64]) require.True(t, ok) gotI64, ok := mI64.Get(1) require.True(t, ok) @@ -3917,7 +3917,7 @@ func TestInterpreter_Marshal(t *testing.T) { f64, err := i.Marshal(map[float64]int32{math.Copysign(0, -1): 1}) require.NoError(t, err) - mF64, ok := f64.(*types.MapF64) + mF64, ok := f64.(*types.TypedMap[float64]) require.True(t, ok) gotF64, ok := mF64.Get(0) require.True(t, ok) @@ -4656,7 +4656,7 @@ func BenchmarkInterpreter_Release(b *testing.B) { if err != nil { break } - m := types.NewMapI32(typ, 1) + m := types.NewTypedMap[int32](typ, 1) m.Set(1, types.BoxRef(child)) var addr int addr, err = i.Alloc(m) diff --git a/interp/marshal.go b/interp/marshal.go index 606b35f..e33f19a 100644 --- a/interp/marshal.go +++ b/interp/marshal.go @@ -788,7 +788,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { out := types.NewMapForType(mt, v.Len()) switch m := out.(type) { - case *types.MapI32: + case *types.TypedMap[int32]: iter := v.MapRange() for iter.Next() { key, err := s.boxAs(iter.Key(), mt.Key) @@ -801,7 +801,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(key.I32(), value) } - case *types.MapI64: + case *types.TypedMap[int64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -818,7 +818,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(keyInt, value) } - case *types.MapF32: + case *types.TypedMap[float32]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -835,7 +835,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(float32(keyFloat), value) } - case *types.MapF64: + case *types.TypedMap[float64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -1111,13 +1111,13 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { return func(s *unmarshalState, src types.Value, dst reflect.Value) error { size := 0 switch m := src.(type) { - case *types.MapI32: + case *types.TypedMap[int32]: size = m.Len() - case *types.MapI64: + case *types.TypedMap[int64]: size = m.Len() - case *types.MapF32: + case *types.TypedMap[float32]: size = m.Len() - case *types.MapF64: + case *types.TypedMap[float64]: size = m.Len() case *types.Map: size = m.Len() @@ -1143,7 +1143,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { out.SetMapIndex(k, v) } switch m := src.(type) { - case *types.MapI32: + case *types.TypedMap[int32]: m.Range(func(key int32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1152,7 +1152,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I32(key), elemValue) }) - case *types.MapI64: + case *types.TypedMap[int64]: m.Range(func(key int64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1161,7 +1161,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I64(key), elemValue) }) - case *types.MapF32: + case *types.TypedMap[float32]: m.Range(func(key float32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1170,7 +1170,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.F32(key), elemValue) }) - case *types.MapF64: + case *types.TypedMap[float64]: m.Range(func(key float64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { diff --git a/interp/threaded.go b/interp/threaded.go index 8c20cee..9045dd1 100644 --- a/interp/threaded.go +++ b/interp/threaded.go @@ -2646,22 +2646,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ key := i.stack[base+j*2] value := i.stack[base+j*2+1] switch m := m.(type) { - case *types.MapI32: + case *types.TypedMap[int32]: old, ok := m.Set(key.I32(), value) if ok { i.releaseVal(old) } - case *types.MapI64: + case *types.TypedMap[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { i.releaseVal(old) } - case *types.MapF32: + case *types.TypedMap[float32]: old, ok := m.Set(key.F32(), value) if ok { i.releaseVal(old) } - case *types.MapF64: + case *types.TypedMap[float64]: old, ok := m.Set(key.F64(), value) if ok { i.releaseVal(old) @@ -2765,13 +2765,13 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() n := 0 switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: n = m.Len() - case *types.MapI64: + case *types.TypedMap[int64]: n = m.Len() - case *types.MapF32: + case *types.TypedMap[float32]: n = m.Len() - case *types.MapF64: + case *types.TypedMap[float64]: n = m.Len() case *types.Map: n = m.Len() @@ -2797,28 +2797,28 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var result types.Boxed switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: value, ok := m.Get(key.I32()) if ok { result = value } else { result = m.Zero } - case *types.MapI64: + case *types.TypedMap[int64]: value, ok := m.Get(i.unboxI64(key)) if ok { result = value } else { result = m.Zero } - case *types.MapF32: + case *types.TypedMap[float32]: value, ok := m.Get(key.F32()) if ok { result = value } else { result = m.Zero } - case *types.MapF64: + case *types.TypedMap[float64]: value, ok := m.Get(key.F64()) if ok { result = value @@ -2891,22 +2891,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ var result types.Boxed var found bool switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: result, found = m.Get(key.I32()) if !found { result = m.Zero } - case *types.MapI64: + case *types.TypedMap[int64]: result, found = m.Get(i.unboxI64(key)) if !found { result = m.Zero } - case *types.MapF32: + case *types.TypedMap[float32]: result, found = m.Get(key.F32()) if !found { result = m.Zero } - case *types.MapF64: + case *types.TypedMap[float64]: result, found = m.Get(key.F64()) if !found { result = m.Zero @@ -2977,22 +2977,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: old, ok := m.Set(key.I32(), value) if ok { i.releaseVal(old) } - case *types.MapI64: + case *types.TypedMap[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { i.releaseVal(old) } - case *types.MapF32: + case *types.TypedMap[float32]: old, ok := m.Set(key.F32(), value) if ok { i.releaseVal(old) } - case *types.MapF64: + case *types.TypedMap[float64]: old, ok := m.Set(key.F64(), value) if ok { i.releaseVal(old) @@ -3063,22 +3063,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: old, ok := m.Delete(key.I32()) if ok { i.releaseVal(old) } - case *types.MapI64: + case *types.TypedMap[int64]: old, ok := m.Delete(i.unboxI64(key)) if ok { i.releaseVal(old) } - case *types.MapF32: + case *types.TypedMap[float32]: old, ok := m.Delete(key.F32()) if ok { i.releaseVal(old) } - case *types.MapF64: + case *types.TypedMap[float64]: old, ok := m.Delete(key.F64()) if ok { i.releaseVal(old) @@ -3143,19 +3143,19 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.MapI32: + case *types.TypedMap[int32]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.MapI64: + case *types.TypedMap[int64]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.MapF32: + case *types.TypedMap[float32]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.MapF64: + case *types.TypedMap[float64]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) diff --git a/types/map.go b/types/map.go index ae5f262..7045ae8 100644 --- a/types/map.go +++ b/types/map.go @@ -7,34 +7,16 @@ import ( "strings" ) -type Map struct { - Typ *MapType - Zero Boxed - entries map[MapKey]MapEntry -} - -type MapI32 struct { +type TypedMap[K comparable] struct { Typ *MapType Zero Boxed - entries map[int32]Boxed + entries map[K]Boxed } -type MapI64 struct { - Typ *MapType - Zero Boxed - entries map[int64]Boxed -} - -type MapF32 struct { - Typ *MapType - Zero Boxed - entries map[uint32]Boxed -} - -type MapF64 struct { +type Map struct { Typ *MapType Zero Boxed - entries map[uint64]Boxed + entries map[MapKey]MapEntry } type MapKey struct { @@ -58,13 +40,17 @@ type MapType struct { var ( _ Traceable = (*Map)(nil) - _ Traceable = (*MapI32)(nil) - _ Traceable = (*MapI64)(nil) - _ Traceable = (*MapF32)(nil) - _ Traceable = (*MapF64)(nil) + _ Traceable = (*TypedMap[int32])(nil) + _ Traceable = (*TypedMap[int64])(nil) + _ Traceable = (*TypedMap[float32])(nil) + _ Traceable = (*TypedMap[float64])(nil) _ Type = (*MapType)(nil) ) +func NewTypedMap[K comparable](typ *MapType, capacity int) *TypedMap[K] { + return &TypedMap[K]{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[K]Boxed, capacity)} +} + func NewMap(typ *MapType) *Map { return NewMapWithCapacity(typ, 0) } @@ -80,34 +66,18 @@ func NewMapWithCapacity(typ *MapType, capacity int) *Map { func NewMapForType(typ *MapType, capacity int) Value { switch typ.KeyKind { case KindI32: - return NewMapI32(typ, capacity) + return NewTypedMap[int32](typ, capacity) case KindI64: - return NewMapI64(typ, capacity) + return NewTypedMap[int64](typ, capacity) case KindF32: - return NewMapF32(typ, capacity) + return NewTypedMap[float32](typ, capacity) case KindF64: - return NewMapF64(typ, capacity) + return NewTypedMap[float64](typ, capacity) default: return NewMapWithCapacity(typ, capacity) } } -func NewMapI32(typ *MapType, capacity int) *MapI32 { - return &MapI32{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[int32]Boxed, capacity)} -} - -func NewMapI64(typ *MapType, capacity int) *MapI64 { - return &MapI64{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[int64]Boxed, capacity)} -} - -func NewMapF32(typ *MapType, capacity int) *MapF32 { - return &MapF32{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[uint32]Boxed, capacity)} -} - -func NewMapF64(typ *MapType, capacity int) *MapF64 { - return &MapF64{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[uint64]Boxed, capacity)} -} - func NewMapType(key Type, elem Type) *MapType { return &MapType{ Key: key, @@ -119,95 +89,24 @@ func NewMapType(key Type, elem Type) *MapType { } } -func (m *Map) Kind() Kind { return KindRef } - -func (m *Map) Type() Type { return m.Typ } - -func (m *Map) Len() int { return len(m.entries) } - -func (m *Map) Get(key MapKey) (MapEntry, bool) { - entry, ok := m.entries[key] - return entry, ok -} - -func (m *Map) Set(key MapKey, entry MapEntry) (MapEntry, bool) { - old, ok := m.entries[key] - m.entries[key] = entry - return old, ok -} - -func (m *Map) Delete(key MapKey) (MapEntry, bool) { - old, ok := m.entries[key] - if ok { - delete(m.entries, key) - } - return old, ok -} - -func (m *Map) Range(fn func(MapKey, MapEntry)) { - for key, entry := range m.entries { - fn(key, entry) - } -} - -func (m *Map) Clear(fn func(MapEntry)) { - for key, entry := range m.entries { - fn(entry) - delete(m.entries, key) - } -} - -func (m *Map) String() string { - parts := make([]string, 0, m.Len()) - m.Range(func(key MapKey, entry MapEntry) { - parts = append(parts, fmt.Sprintf("%s: %s", key.String(), entry.Value.String())) - }) - sort.Strings(parts) - return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) -} +func (m *TypedMap[K]) Kind() Kind { return KindRef } -func (m *Map) Refs() []Ref { - traceKeys := m.Typ.TraceKeys - traceValues := m.Typ.TraceValues - if !traceKeys && !traceValues { - return nil - } - var refs []Ref - for _, entry := range m.entries { - if traceKeys && entry.Key.Kind() == KindRef { - if refs == nil { - refs = make([]Ref, 0, m.Len()*2) - } - refs = append(refs, Ref(entry.Key.Ref())) - } - if traceValues && entry.Value.Kind() == KindRef { - if refs == nil { - refs = make([]Ref, 0, m.Len()*2) - } - refs = append(refs, Ref(entry.Value.Ref())) - } - } - return refs -} - -func (m *MapI32) Kind() Kind { return KindRef } - -func (m *MapI32) Type() Type { return m.Typ } +func (m *TypedMap[K]) Type() Type { return m.Typ } -func (m *MapI32) Len() int { return len(m.entries) } +func (m *TypedMap[K]) Len() int { return len(m.entries) } -func (m *MapI32) Get(key int32) (Boxed, bool) { +func (m *TypedMap[K]) Get(key K) (Boxed, bool) { value, ok := m.entries[key] return value, ok } -func (m *MapI32) Set(key int32, value Boxed) (Boxed, bool) { +func (m *TypedMap[K]) Set(key K, value Boxed) (Boxed, bool) { old, ok := m.entries[key] m.entries[key] = value return old, ok } -func (m *MapI32) Delete(key int32) (Boxed, bool) { +func (m *TypedMap[K]) Delete(key K) (Boxed, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -215,29 +114,29 @@ func (m *MapI32) Delete(key int32) (Boxed, bool) { return old, ok } -func (m *MapI32) Range(fn func(int32, Boxed)) { +func (m *TypedMap[K]) Range(fn func(K, Boxed)) { for key, value := range m.entries { fn(key, value) } } -func (m *MapI32) Clear(fn func(Boxed)) { +func (m *TypedMap[K]) Clear(fn func(Boxed)) { for key, value := range m.entries { fn(value) delete(m.entries, key) } } -func (m *MapI32) String() string { +func (m *TypedMap[K]) String() string { parts := make([]string, 0, m.Len()) - m.Range(func(key int32, value Boxed) { - parts = append(parts, fmt.Sprintf("%s: %s", I32(key), value.String())) + m.Range(func(key K, value Boxed) { + parts = append(parts, fmt.Sprintf("%s: %s", boxKey(any(key)), value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *MapI32) Refs() []Ref { +func (m *TypedMap[K]) Refs() []Ref { if !m.Typ.TraceValues { return nil } @@ -253,24 +152,24 @@ func (m *MapI32) Refs() []Ref { return refs } -func (m *MapI64) Kind() Kind { return KindRef } +func (m *Map) Kind() Kind { return KindRef } -func (m *MapI64) Type() Type { return m.Typ } +func (m *Map) Type() Type { return m.Typ } -func (m *MapI64) Len() int { return len(m.entries) } +func (m *Map) Len() int { return len(m.entries) } -func (m *MapI64) Get(key int64) (Boxed, bool) { - value, ok := m.entries[key] - return value, ok +func (m *Map) Get(key MapKey) (MapEntry, bool) { + entry, ok := m.entries[key] + return entry, ok } -func (m *MapI64) Set(key int64, value Boxed) (Boxed, bool) { +func (m *Map) Set(key MapKey, entry MapEntry) (MapEntry, bool) { old, ok := m.entries[key] - m.entries[key] = value + m.entries[key] = entry return old, ok } -func (m *MapI64) Delete(key int64) (Boxed, bool) { +func (m *Map) Delete(key MapKey) (MapEntry, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -278,189 +177,47 @@ func (m *MapI64) Delete(key int64) (Boxed, bool) { return old, ok } -func (m *MapI64) Range(fn func(int64, Boxed)) { - for key, value := range m.entries { - fn(key, value) - } -} - -func (m *MapI64) Clear(fn func(Boxed)) { - for key, value := range m.entries { - fn(value) - delete(m.entries, key) - } -} - -func (m *MapI64) String() string { - parts := make([]string, 0, m.Len()) - m.Range(func(key int64, value Boxed) { - parts = append(parts, fmt.Sprintf("%s: %s", I64(key), value.String())) - }) - sort.Strings(parts) - return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) -} - -func (m *MapI64) Refs() []Ref { - if !m.Typ.TraceValues { - return nil - } - var refs []Ref - for _, value := range m.entries { - if value.Kind() == KindRef { - if refs == nil { - refs = make([]Ref, 0, m.Len()) - } - refs = append(refs, Ref(value.Ref())) - } - } - return refs -} - -func (m *MapF32) Kind() Kind { return KindRef } - -func (m *MapF32) Type() Type { return m.Typ } - -func (m *MapF32) Len() int { return len(m.entries) } - -func (m *MapF32) Get(key float32) (Boxed, bool) { - bits := math.Float32bits(key) - if bits == 1<<31 { - bits = 0 - } - value, ok := m.entries[bits] - return value, ok -} - -func (m *MapF32) Set(key float32, value Boxed) (Boxed, bool) { - bits := math.Float32bits(key) - if bits == 1<<31 { - bits = 0 - } - old, ok := m.entries[bits] - m.entries[bits] = value - return old, ok -} - -func (m *MapF32) Delete(key float32) (Boxed, bool) { - bits := math.Float32bits(key) - if bits == 1<<31 { - bits = 0 - } - old, ok := m.entries[bits] - if ok { - delete(m.entries, bits) - } - return old, ok -} - -func (m *MapF32) Range(fn func(float32, Boxed)) { - for key, value := range m.entries { - fn(math.Float32frombits(key), value) +func (m *Map) Range(fn func(MapKey, MapEntry)) { + for key, entry := range m.entries { + fn(key, entry) } } -func (m *MapF32) Clear(fn func(Boxed)) { - for key, value := range m.entries { - fn(value) +func (m *Map) Clear(fn func(MapEntry)) { + for key, entry := range m.entries { + fn(entry) delete(m.entries, key) } } -func (m *MapF32) String() string { +func (m *Map) String() string { parts := make([]string, 0, m.Len()) - m.Range(func(key float32, value Boxed) { - parts = append(parts, fmt.Sprintf("%s: %s", F32(key), value.String())) + m.Range(func(key MapKey, entry MapEntry) { + parts = append(parts, fmt.Sprintf("%s: %s", key.String(), entry.Value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *MapF32) Refs() []Ref { - if !m.Typ.TraceValues { +func (m *Map) Refs() []Ref { + traceKeys := m.Typ.TraceKeys + traceValues := m.Typ.TraceValues + if !traceKeys && !traceValues { return nil } var refs []Ref - for _, value := range m.entries { - if value.Kind() == KindRef { + for _, entry := range m.entries { + if traceKeys && entry.Key.Kind() == KindRef { if refs == nil { - refs = make([]Ref, 0, m.Len()) + refs = make([]Ref, 0, m.Len()*2) } - refs = append(refs, Ref(value.Ref())) + refs = append(refs, Ref(entry.Key.Ref())) } - } - return refs -} - -func (m *MapF64) Kind() Kind { return KindRef } - -func (m *MapF64) Type() Type { return m.Typ } - -func (m *MapF64) Len() int { return len(m.entries) } - -func (m *MapF64) Get(key float64) (Boxed, bool) { - bits := math.Float64bits(key) - if bits == 1<<63 { - bits = 0 - } - value, ok := m.entries[bits] - return value, ok -} - -func (m *MapF64) Set(key float64, value Boxed) (Boxed, bool) { - bits := math.Float64bits(key) - if bits == 1<<63 { - bits = 0 - } - old, ok := m.entries[bits] - m.entries[bits] = value - return old, ok -} - -func (m *MapF64) Delete(key float64) (Boxed, bool) { - bits := math.Float64bits(key) - if bits == 1<<63 { - bits = 0 - } - old, ok := m.entries[bits] - if ok { - delete(m.entries, bits) - } - return old, ok -} - -func (m *MapF64) Range(fn func(float64, Boxed)) { - for key, value := range m.entries { - fn(math.Float64frombits(key), value) - } -} - -func (m *MapF64) Clear(fn func(Boxed)) { - for key, value := range m.entries { - fn(value) - delete(m.entries, key) - } -} - -func (m *MapF64) String() string { - parts := make([]string, 0, m.Len()) - m.Range(func(key float64, value Boxed) { - parts = append(parts, fmt.Sprintf("%s: %s", F64(key), value.String())) - }) - sort.Strings(parts) - return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) -} - -func (m *MapF64) Refs() []Ref { - if !m.Typ.TraceValues { - return nil - } - var refs []Ref - for _, value := range m.entries { - if value.Kind() == KindRef { + if traceValues && entry.Value.Kind() == KindRef { if refs == nil { - refs = make([]Ref, 0, m.Len()) + refs = make([]Ref, 0, m.Len()*2) } - refs = append(refs, Ref(value.Ref())) + refs = append(refs, Ref(entry.Value.Ref())) } } return refs @@ -503,3 +260,19 @@ func (t *MapType) Equals(other Type) bool { } return t.Key.Equals(o.Key) && t.Elem.Equals(o.Elem) } + +// boxKey renders a native map key through its boxed value's String form. +func boxKey(k any) string { + switch v := k.(type) { + case int32: + return I32(v).String() + case int64: + return I64(v).String() + case float32: + return F32(v).String() + case float64: + return F64(v).String() + default: + return fmt.Sprint(v) + } +} diff --git a/types/map_test.go b/types/map_test.go index 1bbbfe8..f97f3b5 100644 --- a/types/map_test.go +++ b/types/map_test.go @@ -159,10 +159,10 @@ func TestNewMapForType(t *testing.T) { typ *MapType want any }{ - {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*MapI32)(nil)}, - {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*MapI64)(nil)}, - {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*MapF32)(nil)}, - {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*MapF64)(nil)}, + {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*TypedMap[int32])(nil)}, + {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*TypedMap[int64])(nil)}, + {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*TypedMap[float32])(nil)}, + {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*TypedMap[float64])(nil)}, {name: "ref", typ: NewMapType(TypeRef, TypeI32), want: (*Map)(nil)}, {name: "string", typ: NewMapType(TypeString, TypeI32), want: (*Map)(nil)}, {name: "struct", typ: NewMapType(structType, TypeI32), want: (*Map)(nil)}, @@ -174,267 +174,120 @@ func TestNewMapForType(t *testing.T) { } } -func TestMapI32_Get(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - m.Set(1, BoxI32(2)) - - got, ok := m.Get(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - - _, ok = m.Get(2) - require.False(t, ok) -} - -func TestMapI32_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMapI32(NewMapType(TypeI32, TypeI32), 0).Kind()) +func TestTypedMap_Kind(t *testing.T) { + require.Equal(t, KindRef, NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0).Kind()) } -func TestMapI32_Type(t *testing.T) { +func TestTypedMap_Type(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - require.Equal(t, typ, NewMapI32(typ, 0).Type()) + require.Equal(t, typ, NewTypedMap[int32](typ, 0).Type()) } -func TestMapI32_Len(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) +func TestTypedMap_Len(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) require.Equal(t, 0, m.Len()) m.Set(1, BoxI32(2)) require.Equal(t, 1, m.Len()) } -func TestMapI32_Set(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - - old, ok := m.Set(1, BoxI32(2)) - require.False(t, ok) - require.Equal(t, Boxed(0), old) - - old, ok = m.Set(1, BoxI32(3)) - require.True(t, ok) - require.Equal(t, BoxI32(2), old) -} - -func TestMapI32_Delete(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - m.Set(1, BoxI32(2)) - - old, ok := m.Delete(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), old) - require.Equal(t, 0, m.Len()) -} - -func TestMapI32_Range(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - m.Set(1, BoxI32(2)) - - var keys []int32 - m.Range(func(key int32, _ Boxed) { - keys = append(keys, key) +func TestTypedMap_Get(t *testing.T) { + t.Run("i32", func(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + m.Set(1, BoxI32(2)) + got, ok := m.Get(1) + require.True(t, ok) + require.Equal(t, BoxI32(2), got) + _, ok = m.Get(2) + require.False(t, ok) }) - require.Equal(t, []int32{1}, keys) -} - -func TestMapI32_Clear(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - m.Set(1, BoxI32(2)) - - var values []Boxed - m.Clear(func(value Boxed) { - values = append(values, value) - }) - require.Equal(t, []Boxed{BoxI32(2)}, values) - require.Equal(t, 0, m.Len()) -} - -func TestMapI32_String(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI32), 0) - m.Set(1, BoxI32(2)) - require.Equal(t, "map[i32]i32{1: 2}", m.String()) -} - -func TestMapI32_Refs(t *testing.T) { - t.Run("inline i64 value", func(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeI64), 0) - m.Set(1, BoxI64(2)) - var refs []Ref - allocs := testing.AllocsPerRun(100, func() { - refs = m.Refs() - }) - require.Empty(t, refs) - require.Zero(t, allocs) + t.Run("i64 wide key", func(t *testing.T) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) + m.Set(1<<50, BoxI32(2)) + got, ok := m.Get(1 << 50) + require.True(t, ok) + require.Equal(t, BoxI32(2), got) + _, ok = m.Get(2) + require.False(t, ok) }) - t.Run("ref value", func(t *testing.T) { - m := NewMapI32(NewMapType(TypeI32, TypeString), 0) - m.Set(1, BoxRef(2)) - require.Equal(t, []Ref{2}, m.Refs()) + t.Run("f64", func(t *testing.T) { + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m.Set(1.5, BoxI32(2)) + got, ok := m.Get(1.5) + require.True(t, ok) + require.Equal(t, BoxI32(2), got) }) } -func TestMapI64_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMapI64(NewMapType(TypeI64, TypeI32), 0).Kind()) -} - -func TestMapI64_Type(t *testing.T) { - typ := NewMapType(TypeI64, TypeI32) - require.Equal(t, typ, NewMapI64(typ, 0).Type()) -} - -func TestMapI64_Len(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - require.Equal(t, 0, m.Len()) - m.Set(1, BoxI32(2)) - require.Equal(t, 1, m.Len()) -} - -func TestMapI64_Get(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - m.Set(1<<50, BoxI32(2)) - - got, ok := m.Get(1 << 50) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - - _, ok = m.Get(2) - require.False(t, ok) -} - -func TestMapI64_Set(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - - old, ok := m.Set(1<<50, BoxI32(2)) - require.False(t, ok) - require.Equal(t, Boxed(0), old) - - old, ok = m.Set(1<<50, BoxI32(3)) - require.True(t, ok) - require.Equal(t, BoxI32(2), old) -} - -func TestMapI64_Delete(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - m.Set(1, BoxI32(2)) +func TestTypedMap_Set(t *testing.T) { + t.Run("overwrite returns old", func(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + old, ok := m.Set(1, BoxI32(2)) + require.False(t, ok) + require.Equal(t, Boxed(0), old) - old, ok := m.Delete(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), old) - require.Equal(t, 0, m.Len()) -} - -func TestMapI64_Range(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - m.Set(1, BoxI32(2)) - - var keys []int64 - m.Range(func(key int64, _ Boxed) { - keys = append(keys, key) + old, ok = m.Set(1, BoxI32(3)) + require.True(t, ok) + require.Equal(t, BoxI32(2), old) }) - require.Equal(t, []int64{1}, keys) -} -func TestMapI64_Clear(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - m.Set(1, BoxI32(2)) + t.Run("f32 -0.0 collapses to +0.0", func(t *testing.T) { + m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) + m.Set(float32(math.Copysign(0, -1)), BoxI32(1)) + m.Set(0, BoxI32(2)) - var values []Boxed - m.Clear(func(value Boxed) { - values = append(values, value) + got, ok := m.Get(float32(math.Copysign(0, -1))) + require.True(t, ok) + require.Equal(t, BoxI32(2), got) + require.Equal(t, 1, m.Len()) }) - require.Equal(t, []Boxed{BoxI32(2)}, values) - require.Equal(t, 0, m.Len()) -} - -func TestMapI64_String(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) - m.Set(1, BoxI32(2)) - require.Equal(t, "map[i64]i32{1: 2}", m.String()) -} -func TestMapI64_Refs(t *testing.T) { - t.Run("inline i64 value", func(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeI64), 0) - m.Set(1, BoxI64(2)) + t.Run("f64 -0.0 collapses to +0.0", func(t *testing.T) { + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m.Set(math.Copysign(0, -1), BoxI32(1)) + m.Set(0, BoxI32(2)) - var refs []Ref - allocs := testing.AllocsPerRun(100, func() { - refs = m.Refs() - }) - require.Empty(t, refs) - require.Zero(t, allocs) + got, ok := m.Get(math.Copysign(0, -1)) + require.True(t, ok) + require.Equal(t, BoxI32(2), got) + require.Equal(t, 1, m.Len()) }) - t.Run("ref value", func(t *testing.T) { - m := NewMapI64(NewMapType(TypeI64, TypeString), 0) - m.Set(1, BoxRef(2)) - require.Equal(t, []Ref{2}, m.Refs()) + t.Run("f64 NaN is not retrievable", func(t *testing.T) { + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m.Set(math.NaN(), BoxI32(1)) + _, ok := m.Get(math.NaN()) + require.False(t, ok) }) } -func TestMapF32_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMapF32(NewMapType(TypeF32, TypeI32), 0).Kind()) -} - -func TestMapF32_Type(t *testing.T) { - typ := NewMapType(TypeF32, TypeI32) - require.Equal(t, typ, NewMapF32(typ, 0).Type()) -} - -func TestMapF32_Len(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) - require.Equal(t, 0, m.Len()) - m.Set(1, BoxI32(2)) - require.Equal(t, 1, m.Len()) -} - -func TestMapF32_Get(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) - m.Set(1, BoxI32(2)) - - got, ok := m.Get(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - - _, ok = m.Get(2) - require.False(t, ok) -} - -func TestMapF32_Set(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) - - m.Set(float32(math.Copysign(0, -1)), BoxI32(1)) - m.Set(0, BoxI32(2)) - - got, ok := m.Get(float32(math.Copysign(0, -1))) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - require.Equal(t, 1, m.Len()) -} - -func TestMapF32_Delete(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) +func TestTypedMap_Delete(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) old, ok := m.Delete(1) require.True(t, ok) require.Equal(t, BoxI32(2), old) require.Equal(t, 0, m.Len()) + + _, ok = m.Delete(1) + require.False(t, ok) } -func TestMapF32_Range(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) +func TestTypedMap_Range(t *testing.T) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1, BoxI32(2)) - var keys []float32 - m.Range(func(key float32, _ Boxed) { + var keys []int64 + m.Range(func(key int64, _ Boxed) { keys = append(keys, key) }) - require.Equal(t, []float32{1}, keys) + require.Equal(t, []int64{1}, keys) } -func TestMapF32_Clear(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) +func TestTypedMap_Clear(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) var values []Boxed @@ -445,114 +298,35 @@ func TestMapF32_Clear(t *testing.T) { require.Equal(t, 0, m.Len()) } -func TestMapF32_String(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI32), 0) - m.Set(1, BoxI32(2)) - require.Equal(t, "map[f32]i32{1: 2}", m.String()) -} - -func TestMapF32_Refs(t *testing.T) { - t.Run("inline i64 value", func(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeI64), 0) - m.Set(1, BoxI64(2)) - - var refs []Ref - allocs := testing.AllocsPerRun(100, func() { - refs = m.Refs() - }) - require.Empty(t, refs) - require.Zero(t, allocs) +func TestTypedMap_String(t *testing.T) { + t.Run("i32", func(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + m.Set(1, BoxI32(2)) + require.Equal(t, "map[i32]i32{1: 2}", m.String()) }) - t.Run("ref value", func(t *testing.T) { - m := NewMapF32(NewMapType(TypeF32, TypeString), 0) - m.Set(1, BoxRef(2)) - require.Equal(t, []Ref{2}, m.Refs()) + t.Run("i64", func(t *testing.T) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) + m.Set(1, BoxI32(2)) + require.Equal(t, "map[i64]i32{1: 2}", m.String()) }) -} - -func TestMapF64_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMapF64(NewMapType(TypeF64, TypeI32), 0).Kind()) -} -func TestMapF64_Type(t *testing.T) { - typ := NewMapType(TypeF64, TypeI32) - require.Equal(t, typ, NewMapF64(typ, 0).Type()) -} - -func TestMapF64_Len(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - require.Equal(t, 0, m.Len()) - m.Set(1, BoxI32(2)) - require.Equal(t, 1, m.Len()) -} - -func TestMapF64_Get(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - m.Set(1, BoxI32(2)) - - got, ok := m.Get(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - - _, ok = m.Get(2) - require.False(t, ok) -} - -func TestMapF64_Set(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - - m.Set(math.Copysign(0, -1), BoxI32(1)) - m.Set(0, BoxI32(2)) - - got, ok := m.Get(math.Copysign(0, -1)) - require.True(t, ok) - require.Equal(t, BoxI32(2), got) - require.Equal(t, 1, m.Len()) -} - -func TestMapF64_Delete(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - m.Set(1, BoxI32(2)) - - old, ok := m.Delete(1) - require.True(t, ok) - require.Equal(t, BoxI32(2), old) - require.Equal(t, 0, m.Len()) -} - -func TestMapF64_Range(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - m.Set(1, BoxI32(2)) - - var keys []float64 - m.Range(func(key float64, _ Boxed) { - keys = append(keys, key) + t.Run("f32", func(t *testing.T) { + m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) + m.Set(1, BoxI32(2)) + require.Equal(t, "map[f32]i32{1: 2}", m.String()) }) - require.Equal(t, []float64{1}, keys) -} - -func TestMapF64_Clear(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - m.Set(1, BoxI32(2)) - var values []Boxed - m.Clear(func(value Boxed) { - values = append(values, value) + t.Run("f64", func(t *testing.T) { + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m.Set(1, BoxI32(2)) + require.Equal(t, "map[f64]i32{1: 2}", m.String()) }) - require.Equal(t, []Boxed{BoxI32(2)}, values) - require.Equal(t, 0, m.Len()) -} - -func TestMapF64_String(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI32), 0) - m.Set(1, BoxI32(2)) - require.Equal(t, "map[f64]i32{1: 2}", m.String()) } -func TestMapF64_Refs(t *testing.T) { +func TestTypedMap_Refs(t *testing.T) { t.Run("inline i64 value", func(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeI64), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref @@ -564,7 +338,7 @@ func TestMapF64_Refs(t *testing.T) { }) t.Run("ref value", func(t *testing.T) { - m := NewMapF64(NewMapType(TypeF64, TypeString), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeString), 0) m.Set(1, BoxRef(2)) require.Equal(t, []Ref{2}, m.Refs()) }) @@ -607,8 +381,8 @@ func TestMapType_Equals(t *testing.T) { require.False(t, NewMapType(TypeI32, TypeI32).Equals(NewMapType(TypeI32, TypeI64))) } -func BenchmarkMapI64_Get(b *testing.B) { - m := NewMapI64(NewMapType(TypeI64, TypeI32), 0) +func BenchmarkTypedMap_Get(b *testing.B) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(42, BoxI32(7)) b.ReportAllocs() @@ -650,7 +424,7 @@ func BenchmarkMap_Refs(b *testing.B) { }) b.Run("inline i64", func(b *testing.B) { - m := NewMapI32(NewMapType(TypeI32, TypeI64), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref From 9b8d0df74033e7616fd4c337d0ff775dd822f208 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:12:34 +0000 Subject: [PATCH 12/18] refactor(asm): drop arm64 test for removed Encode helper TestEncode_HelpsInterface only exercised the standalone asm.Encode batch helper removed earlier; it has no production caller. The arm64-gated file is invisible to the host build, so this surfaced only under GOARCH=arm64 vet. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- asm/arm64/encoder_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/asm/arm64/encoder_test.go b/asm/arm64/encoder_test.go index 5c8a796..f391d01 100644 --- a/asm/arm64/encoder_test.go +++ b/asm/arm64/encoder_test.go @@ -196,15 +196,3 @@ func TestEncoder_Encode(t *testing.T) { require.ErrorIs(t, err, asm.ErrInvalidOperand) }) } - -func TestEncode_HelpsInterface(t *testing.T) { - encoder := NewEncoder() - insts := []asm.Instruction{ - ADD(X0, X1, X2), - ADDI(X0, X1, 5), - } - - bytes, err := asm.Encode(encoder, insts) - require.NoError(t, err) - require.Equal(t, []byte{0x20, 0x00, 0x02, 0x8B, 0x20, 0x14, 0x00, 0x91}, bytes) -} From 21d59cadd9606a5544f8e907404754edb0cfb1e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:12:34 +0000 Subject: [PATCH 13/18] refactor(types): merge typed arrays into generic TypedArray[T] Replace I32Array/I64Array/F32Array/F64Array (4 bare slice types, 12 methods) with a single generic TypedArray[T int32|int64|float32| float64]. Type() and String() dispatch on T via a zero-value type switch and a formatElem helper; both reuse the shared formatSlice. The boxed Array (Boxed elems + Refs) is left as-is. Updated the array opcode cases in threaded.go, marshal.go, and the constant-folding UTF32 cases; split the array tests into TestTypedArray_* (generic) and TestArray_* (boxed). https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/interp_test.go | 36 +++++++-------- interp/marshal.go | 20 ++++----- interp/threaded.go | 60 ++++++++++++------------- transform/cf.go | 4 +- transform/cf_test.go | 4 +- types/array.go | 63 +++++++++++++------------- types/array_test.go | 101 +++++++++++++----------------------------- types/value_test.go | 2 +- 8 files changed, 127 insertions(+), 163 deletions(-) diff --git a/interp/interp_test.go b/interp/interp_test.go index 234609c..4a16791 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -1464,7 +1464,7 @@ var tests = []test{ }, program.WithConstants(types.String("foo")), ), - values: []types.Value{types.I32Array("foo")}, + values: []types.Value{types.TypedArray[int32]("foo")}, }, // --- array: ARRAY_NEW, ARRAY_NEW_DEFAULT, ARRAY_GET, ARRAY_SET, ARRAY_FILL --- { @@ -1476,7 +1476,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{types.I32Array{1}}, + values: []types.Value{types.TypedArray[int32]{1}}, }, { program: program.New( @@ -1487,7 +1487,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{types.I64Array{1}}, + values: []types.Value{types.TypedArray[int64]{1}}, }, { program: program.New( @@ -1498,7 +1498,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{types.F32Array{42}}, + values: []types.Value{types.TypedArray[float32]{42}}, }, { program: program.New( @@ -1509,7 +1509,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{types.F64Array{42}}, + values: []types.Value{types.TypedArray[float64]{42}}, }, { program: program.New( @@ -1530,7 +1530,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{make(types.I32Array, 1)}, + values: []types.Value{make(types.TypedArray[int32], 1)}, }, { program: program.New( @@ -1540,7 +1540,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{make(types.I64Array, 1)}, + values: []types.Value{make(types.TypedArray[int64], 1)}, }, { program: program.New( @@ -1550,7 +1550,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{make(types.F32Array, 1)}, + values: []types.Value{make(types.TypedArray[float32], 1)}, }, { program: program.New( @@ -1560,7 +1560,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{make(types.F64Array, 1)}, + values: []types.Value{make(types.TypedArray[float64], 1)}, }, { program: program.New( @@ -2885,7 +2885,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.I32Array{10, 20, 30}), + program.WithConstants(types.TypedArray[int32]{10, 20, 30}), ), WithTick(1), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -2901,7 +2901,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.I32Array{10, 20, 30}), + program.WithConstants(types.TypedArray[int32]{10, 20, 30}), ), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -3817,19 +3817,19 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]int32{1, 2}) require.NoError(t, err) - require.Equal(t, types.I32Array{1, 2}, got) + require.Equal(t, types.TypedArray[int32]{1, 2}, got) got, err = i.Marshal([]uint32{math.MaxUint32}) require.NoError(t, err) - require.Equal(t, types.I32Array{-1}, got) + require.Equal(t, types.TypedArray[int32]{-1}, got) got, err = i.Marshal([]int{1, 2}) require.NoError(t, err) - require.Equal(t, types.I64Array{1, 2}, got) + require.Equal(t, types.TypedArray[int64]{1, 2}, got) got, err = i.Marshal([]uint64{math.MaxUint64}) require.NoError(t, err) - require.Equal(t, types.I64Array{-1}, got) + require.Equal(t, types.TypedArray[int64]{-1}, got) }) t.Run("reference slice", func(t *testing.T) { @@ -4289,15 +4289,15 @@ func TestInterpreter_Unmarshal(t *testing.T) { defer i.Close() var out []int32 - require.NoError(t, i.Unmarshal(types.I32Array{1, 2}, &out)) + require.NoError(t, i.Unmarshal(types.TypedArray[int32]{1, 2}, &out)) require.Equal(t, []int32{1, 2}, out) var u32 []uint32 - require.NoError(t, i.Unmarshal(types.I32Array{-1}, &u32)) + require.NoError(t, i.Unmarshal(types.TypedArray[int32]{-1}, &u32)) require.Equal(t, []uint32{math.MaxUint32}, u32) var u64 []uint64 - require.NoError(t, i.Unmarshal(types.I64Array{-1}, &u64)) + require.NoError(t, i.Unmarshal(types.TypedArray[int64]{-1}, &u64)) require.Equal(t, []uint64{math.MaxUint64}, u64) }) diff --git a/interp/marshal.go b/interp/marshal.go index e33f19a..8b5b5e6 100644 --- a/interp/marshal.go +++ b/interp/marshal.go @@ -594,25 +594,25 @@ func (s *unmarshalState) value(val types.Value, dst reflect.Value) error { func (s *unmarshalState) elems(value types.Value) ([]types.Value, error) { switch v := value.(type) { - case types.I32Array: + case types.TypedArray[int32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I32(elem) } return out, nil - case types.I64Array: + case types.TypedArray[int64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I64(elem) } return out, nil - case types.F32Array: + case types.TypedArray[float32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F32(elem) } return out, nil - case types.F64Array: + case types.TypedArray[float64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F64(elem) @@ -736,37 +736,37 @@ func (m *codec) marshalArray(elem *marshalPlan) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { switch elemKind { case reflect.Int8, reflect.Int16, reflect.Int32: - out := make(types.I32Array, v.Len()) + out := make(types.TypedArray[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Int()) } return out, nil case reflect.Uint8, reflect.Uint16, reflect.Uint32: - out := make(types.I32Array, v.Len()) + out := make(types.TypedArray[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Uint()) } return out, nil case reflect.Int, reflect.Int64: - out := make(types.I64Array, v.Len()) + out := make(types.TypedArray[int64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Int() } return out, nil case reflect.Uint, reflect.Uint64, reflect.Uintptr: - out := make(types.I64Array, v.Len()) + out := make(types.TypedArray[int64], v.Len()) for idx := range out { out[idx] = int64(v.Index(idx).Uint()) } return out, nil case reflect.Float32: - out := make(types.F32Array, v.Len()) + out := make(types.TypedArray[float32], v.Len()) for idx := range out { out[idx] = float32(v.Index(idx).Float()) } return out, nil case reflect.Float64: - out := make(types.F64Array, v.Len()) + out := make(types.TypedArray[float64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Float() } diff --git a/interp/threaded.go b/interp/threaded.go index 9045dd1..16f9a84 100644 --- a/interp/threaded.go +++ b/interp/threaded.go @@ -1899,7 +1899,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp == 0 { panic(ErrStackUnderflow) } - val := unboxRef[types.I32Array](i, i.stack[i.sp-1]) + val := unboxRef[types.TypedArray[int32]](i, i.stack[i.sp-1]) i.stack[i.sp-1] = types.BoxRef(int(i.intern(string(types.String(val))))) i.fr.ip++ } @@ -2013,7 +2013,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } val := unboxRef[types.String](i, i.stack[i.sp-1]) - i.stack[i.sp-1] = types.BoxRef(i.alloc(types.I32Array(val))) + i.stack[i.sp-1] = types.BoxRef(i.alloc(types.TypedArray[int32](val))) i.fr.ip++ } }, @@ -2041,7 +2041,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.I32Array, size) + val := make(types.TypedArray[int32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].I32() } @@ -2058,7 +2058,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.I64Array, size) + val := make(types.TypedArray[int64], size) for j := 0; j < size; j++ { val[j] = i.unboxI64(i.stack[i.sp-size-j-1]) } @@ -2075,7 +2075,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.F32Array, size) + val := make(types.TypedArray[float32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F32() } @@ -2092,7 +2092,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.F64Array, size) + val := make(types.TypedArray[float64], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F64() } @@ -2141,7 +2141,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.I32Array, size) + val := make(types.TypedArray[int32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2151,7 +2151,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.I64Array, size) + val := make(types.TypedArray[int64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2161,7 +2161,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.F32Array, size) + val := make(types.TypedArray[float32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2171,7 +2171,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.F64Array, size) + val := make(types.TypedArray[float64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2198,13 +2198,13 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } var n int32 switch arr := i.unbox(i.stack[i.sp-1]).(type) { - case types.I32Array: + case types.TypedArray[int32]: n = int32(len(arr)) - case types.I64Array: + case types.TypedArray[int64]: n = int32(len(arr)) - case types.F32Array: + case types.TypedArray[float32]: n = int32(len(arr)) - case types.F64Array: + case types.TypedArray[float64]: n = int32(len(arr)) case *types.Array: n = int32(len(arr.Elems)) @@ -2229,22 +2229,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var val types.Boxed switch arr := i.heap[addr].(type) { - case types.I32Array: + case types.TypedArray[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxI32(int32(arr[idx])) - case types.I64Array: + case types.TypedArray[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = i.boxI64(int64(arr[idx])) - case types.F32Array: + case types.TypedArray[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxF32(float32(arr[idx])) - case types.F64Array: + case types.TypedArray[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } @@ -2279,22 +2279,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.I32Array: + case types.TypedArray[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.I32() - case types.I64Array: + case types.TypedArray[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = i.unboxI64(val) - case types.F32Array: + case types.TypedArray[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.F32() - case types.F64Array: + case types.TypedArray[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } @@ -2329,7 +2329,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.I32Array: + case types.TypedArray[int32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2337,7 +2337,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.I64Array: + case types.TypedArray[int64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2345,7 +2345,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.F32Array: + case types.TypedArray[float32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2353,7 +2353,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.F64Array: + case types.TypedArray[float64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2396,22 +2396,22 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.I32Array: + case types.TypedArray[int32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.I64Array: + case types.TypedArray[int64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.F32Array: + case types.TypedArray[float32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.F64Array: + case types.TypedArray[float64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } diff --git a/transform/cf.go b/transform/cf.go index 3795535..9c7db72 100644 --- a/transform/cf.go +++ b/transform/cf.go @@ -433,12 +433,12 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { default: } case instr.STRING_ENCODE_UTF32: - prog.Constants = append(prog.Constants, types.I32Array(v0)) + prog.Constants = append(prog.Constants, types.TypedArray[int32](v0)) ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue default: } - case types.I32Array: + case types.TypedArray[int32]: switch i1.Opcode() { case instr.STRING_NEW_UTF32: prog.Constants = append(prog.Constants, types.String(v0)) diff --git a/transform/cf_test.go b/transform/cf_test.go index b22c723..f6c60e0 100644 --- a/transform/cf_test.go +++ b/transform/cf_test.go @@ -1705,7 +1705,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 2), }, - program.WithConstants(types.String("foo"), types.I32Array("foo"), types.String("foo")), + program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo"), types.String("foo")), ), }, { @@ -1849,7 +1849,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 1), }, - program.WithConstants(types.String("foo"), types.I32Array("foo")), + program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo")), ), }, } diff --git a/types/array.go b/types/array.go index f80d735..244d1f9 100644 --- a/types/array.go +++ b/types/array.go @@ -5,13 +5,7 @@ import ( "strings" ) -type I32Array []int32 - -type I64Array []int64 - -type F32Array []float32 - -type F64Array []float64 +type TypedArray[T int32 | int64 | float32 | float64] []T type Array struct { Typ *ArrayType @@ -30,10 +24,10 @@ var ( TypeF64Array = NewArrayType(TypeF64) ) -var _ Value = I32Array(nil) -var _ Value = I64Array(nil) -var _ Value = F32Array(nil) -var _ Value = F64Array(nil) +var _ Value = TypedArray[int32](nil) +var _ Value = TypedArray[int64](nil) +var _ Value = TypedArray[float32](nil) +var _ Value = TypedArray[float64](nil) var _ Traceable = (*Array)(nil) var _ Type = (*ArrayType)(nil) @@ -45,28 +39,24 @@ func NewArrayType(elem Type) *ArrayType { return &ArrayType{Elem: elem, ElemKind: elem.Kind()} } -func (a I32Array) Kind() Kind { return KindRef } -func (a I32Array) Type() Type { return TypeI32Array } -func (a I32Array) String() string { - return formatSlice(TypeI32Array, len(a), func(i int) string { return fmt.Sprintf("%d", a[i]) }) -} - -func (a I64Array) Kind() Kind { return KindRef } -func (a I64Array) Type() Type { return TypeI64Array } -func (a I64Array) String() string { - return formatSlice(TypeI64Array, len(a), func(i int) string { return fmt.Sprintf("%d", a[i]) }) -} - -func (a F32Array) Kind() Kind { return KindRef } -func (a F32Array) Type() Type { return TypeF32Array } -func (a F32Array) String() string { - return formatSlice(TypeF32Array, len(a), func(i int) string { return fmt.Sprintf("%g", a[i]) }) +func (a TypedArray[T]) Kind() Kind { return KindRef } + +func (a TypedArray[T]) Type() Type { + var zero T + switch any(zero).(type) { + case int32: + return TypeI32Array + case int64: + return TypeI64Array + case float32: + return TypeF32Array + default: + return TypeF64Array + } } -func (a F64Array) Kind() Kind { return KindRef } -func (a F64Array) Type() Type { return TypeF64Array } -func (a F64Array) String() string { - return formatSlice(TypeF64Array, len(a), func(i int) string { return fmt.Sprintf("%g", a[i]) }) +func (a TypedArray[T]) String() string { + return formatSlice(a.Type(), len(a), func(i int) string { return formatElem(a[i]) }) } func (a *Array) Kind() Kind { return KindRef } @@ -108,6 +98,17 @@ func (t *ArrayType) Equals(other Type) bool { return false } +func formatElem[T int32 | int64 | float32 | float64](v T) string { + switch x := any(v).(type) { + case int32: + return fmt.Sprintf("%d", x) + case int64: + return fmt.Sprintf("%d", x) + default: + return fmt.Sprintf("%g", x) + } +} + func formatSlice(typ Type, n int, elem func(int) string) string { var sb strings.Builder sb.WriteString(typ.String()) diff --git a/types/array_test.go b/types/array_test.go index 2b00419..5b02037 100644 --- a/types/array_test.go +++ b/types/array_test.go @@ -8,63 +8,42 @@ import ( ) func TestArray_Kind(t *testing.T) { - tests := []struct { - val Value - kind Kind - }{ - { - val: I32Array{}, - kind: KindRef, - }, - { - val: I64Array{}, - kind: KindRef, - }, - { - val: F32Array{}, - kind: KindRef, - }, - { - val: F64Array{}, - kind: KindRef, - }, - { - val: NewArray(NewArrayType(TypeRef)), - kind: KindRef, - }, + require.Equal(t, KindRef, NewArray(NewArrayType(TypeRef)).Kind()) +} + +func TestArray_Type(t *testing.T) { + typ := NewArrayType(TypeRef) + require.Equal(t, typ, NewArray(typ).Type()) +} + +func TestArray_String(t *testing.T) { + a := NewArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)) + require.Equal(t, "[]ref{1, 2, 3}", a.String()) +} + +func TestTypedArray_Kind(t *testing.T) { + tests := []Value{ + TypedArray[int32]{}, + TypedArray[int64]{}, + TypedArray[float32]{}, + TypedArray[float64]{}, } - for _, tt := range tests { - t.Run(fmt.Sprint(tt.val), func(t *testing.T) { - require.Equal(t, tt.kind, tt.val.Kind()) + for _, val := range tests { + t.Run(fmt.Sprint(val), func(t *testing.T) { + require.Equal(t, KindRef, val.Kind()) }) } } -func TestArray_Type(t *testing.T) { +func TestTypedArray_Type(t *testing.T) { tests := []struct { val Value typ Type }{ - { - val: I32Array{}, - typ: TypeI32Array, - }, - { - val: I64Array{}, - typ: TypeI64Array, - }, - { - val: F32Array{}, - typ: TypeF32Array, - }, - { - val: F64Array{}, - typ: TypeF64Array, - }, - { - val: NewArray(NewArrayType(TypeRef)), - typ: NewArrayType(TypeRef), - }, + {val: TypedArray[int32]{}, typ: TypeI32Array}, + {val: TypedArray[int64]{}, typ: TypeI64Array}, + {val: TypedArray[float32]{}, typ: TypeF32Array}, + {val: TypedArray[float64]{}, typ: TypeF64Array}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { @@ -73,31 +52,15 @@ func TestArray_Type(t *testing.T) { } } -func TestArray_String(t *testing.T) { +func TestTypedArray_String(t *testing.T) { tests := []struct { val Value str string }{ - { - val: I32Array{1, 2, 3}, - str: "[]i32{1, 2, 3}", - }, - { - val: I64Array{1, 2, 3}, - str: "[]i64{1, 2, 3}", - }, - { - val: F32Array{1, 2, 3}, - str: "[]f32{1, 2, 3}", - }, - { - val: F64Array{1, 2, 3}, - str: "[]f64{1, 2, 3}", - }, - { - val: NewArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)), - str: "[]ref{1, 2, 3}", - }, + {val: TypedArray[int32]{1, 2, 3}, str: "[]i32{1, 2, 3}"}, + {val: TypedArray[int64]{1, 2, 3}, str: "[]i64{1, 2, 3}"}, + {val: TypedArray[float32]{1, 2, 3}, str: "[]f32{1, 2, 3}"}, + {val: TypedArray[float64]{1, 2, 3}, str: "[]f64{1, 2, 3}"}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { diff --git a/types/value_test.go b/types/value_test.go index ce093f6..361a4e9 100644 --- a/types/value_test.go +++ b/types/value_test.go @@ -37,7 +37,7 @@ func TestIsNull(t *testing.T) { {Ref(1), false}, {BoxRef(1), false}, {I32(0), false}, - {I32Array{0}, false}, + {TypedArray[int32]{0}, false}, {nil, false}, } for _, tt := range tests { From c0cc1261f4ad40db1a1e58f0fd55eee019bbd099 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:41:02 +0000 Subject: [PATCH 14/18] refactor(asm/arm64): decoders to methods, table/helper-driven encode families MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7a: convert the ~16 operand decoders (decodeReg*, decodeMemOp, decodeCmp, decodeMovImm, …), used only by (*Encoder).Encode, into *Encoder methods (§1.5). Pure value helpers (intBase, enc, reg, sameKind, encR*, logicalImmediate, encodeFloatUnary, …) stay package-level. 7b: collapse the repeated encode families through shared helpers/tables, each preserving the exact opcode constants and bit math: - MOVZ/MOVK/MOVN -> encodeMovImmediate(op32, op64) - SXTB/H/W, UXTB/H/W -> extendOpcodes table - CMP/CMN/TST -> encodeCompareReg; CMPI/CMNI -> encodeCompareImm - LDR/LDRB/LDRSB/LDRH/LDRSH/LDRSW -> encodeLoad(base, scale); STR/STRB/STRH -> encodeStore(base, scale) - FADD/FSUB/FMUL/FDIV -> encodeFloatBinary; FMADD-family -> encodeFloatTernary - FABS/FNEG/FSQRT/FRINT* -> floatUnaryOpcodes table The heterogeneous shift-immediate group (LSLI/LSRI/ASRI/RORI) is left as-is: each uses a distinct bitfield formula, so a single helper would only hide four different computations. Verified: GOOS=arm64 build+vet, plus the full encoder_test suite run on the host (its logic is arch-independent byte math) confirms identical output. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- asm/arm64/encoder.go | 670 +++++++++++++++++-------------------------- 1 file changed, 260 insertions(+), 410 deletions(-) diff --git a/asm/arm64/encoder.go b/asm/arm64/encoder.go index e3fcc5a..25ac948 100644 --- a/asm/arm64/encoder.go +++ b/asm/arm64/encoder.go @@ -52,6 +52,29 @@ var condCode = map[Op]uint32{ OpBLE: 0xD, // LE Z==1 || N!=V } +// extendOpcodes maps each sign/zero-extend opcode to its SBFM/UBFM base word +// and imms field (the source width minus one). +var extendOpcodes = map[Op]struct{ base, imms uint32 }{ + OpSXTB: {0x93400000, 7}, + OpSXTH: {0x93400000, 15}, + OpSXTW: {0x93400000, 31}, + OpUXTB: {0xD3400000, 7}, + OpUXTH: {0xD3400000, 15}, + OpUXTW: {0xD3400000, 31}, +} + +// floatUnaryOpcodes maps each scalar float unary opcode to its single- and +// double-precision base words. +var floatUnaryOpcodes = map[Op]struct{ single, double uint32 }{ + OpFABS: {0x1E20C000, 0x1E60C000}, + OpFNEG: {0x1E214000, 0x1E614000}, + OpFSQRT: {0x1E21C000, 0x1E61C000}, + OpFRINTN: {0x1E244000, 0x1E644000}, // round to nearest, ties to even + OpFRINTM: {0x1E254000, 0x1E654000}, // round toward minus infinity + OpFRINTP: {0x1E24C000, 0x1E64C000}, // round toward plus infinity + OpFRINTZ: {0x1E25C000, 0x1E65C000}, // round toward zero +} + func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { op := Op(inst.Op) switch op { @@ -61,35 +84,35 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpADD: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x8B000000, d, n, m) case OpADDS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xAB000000, d, n, m) case OpSUB: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xCB000000, d, n, m) case OpSUBS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xEB000000, d, n, m) case OpNEG: // NEG Xd, Xm → SUB Xd, XZR, Xm - d, m, err := decodeReg2(inst) + d, m, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -100,7 +123,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | 0x1F<<5 | reg(d)), nil case OpNEGS: // NEGS Xd, Xm → SUBS Xd, XZR, Xm - d, m, err := decodeReg2(inst) + d, m, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -111,70 +134,70 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | 0x1F<<5 | reg(d)), nil case OpMUL: // MUL → MADD Ra=XZR - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9B007C00, d, n, m) case OpMNEG: // MNEG → MSUB Ra=XZR - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9B00FC00, d, n, m) case OpMADD: - d, n, m, a, err := decodeReg4(inst) + d, n, m, a, err := e.decodeReg4(inst) if err != nil { return nil, err } return encR4(0x9B000000, d, n, m, a) case OpMSUB: - d, n, m, a, err := decodeReg4(inst) + d, n, m, a, err := e.decodeReg4(inst) if err != nil { return nil, err } return encR4(0x9B008000, d, n, m, a) case OpSDIV: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9AC00C00, d, n, m) case OpUDIV: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9AC00800, d, n, m) case OpADC: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9A000000, d, n, m) case OpADCS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xBA000000, d, n, m) case OpSBC: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xDA000000, d, n, m) case OpSBCS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } @@ -185,28 +208,28 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpADDI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } return encRImm12(0x91000000, d, n, imm) case OpADDSI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } return encRImm12(0xB1000000, d, n, imm) case OpSUBI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } return encRImm12(0xD1000000, d, n, imm) case OpSUBSI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } @@ -217,63 +240,63 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpAND: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x8A000000, d, n, m) case OpANDS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xEA000000, d, n, m) case OpORR: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xAA000000, d, n, m) case OpEOR: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xCA000000, d, n, m) case OpBIC: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x8A200000, d, n, m) case OpBICS: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xEA200000, d, n, m) case OpEON: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xCA200000, d, n, m) case OpORN: - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0xAA200000, d, n, m) case OpMVN: // MVN Xd, Xm → ORN Xd, XZR, Xm - d, m, err := decodeReg2(inst) + d, m, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -288,7 +311,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpANDI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } @@ -299,7 +322,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(word), nil case OpANDSI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } @@ -310,7 +333,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(word), nil case OpORRI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } @@ -321,7 +344,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(word), nil case OpEORI: - d, n, imm, err := decodeRegImm(inst) + d, n, imm, err := e.decodeRegImm(inst) if err != nil { return nil, err } @@ -336,28 +359,28 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpLSL: // LSLV - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9AC02000, d, n, m) case OpLSR: // LSRV - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9AC02400, d, n, m) case OpASR: // ASRV - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } return encR3(0x9AC02800, d, n, m) case OpROR: // RORV - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } @@ -368,7 +391,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpLSLI: - d, n, shift, err := decodeRegShift(inst) + d, n, shift, err := e.decodeRegShift(inst) if err != nil { return nil, err } @@ -380,7 +403,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | ((-s)&mask)<<16 | (mask-s)<<10 | reg(n)<<5 | reg(d)), nil case OpLSRI: - d, n, shift, err := decodeRegShift(inst) + d, n, shift, err := e.decodeRegShift(inst) if err != nil { return nil, err } @@ -392,7 +415,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | s<<16 | mask<<10 | reg(n)<<5 | reg(d)), nil case OpASRI: - d, n, shift, err := decodeRegShift(inst) + d, n, shift, err := e.decodeRegShift(inst) if err != nil { return nil, err } @@ -404,7 +427,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | s<<16 | mask<<10 | reg(n)<<5 | reg(d)), nil case OpRORI: - d, n, shift, err := decodeRegShift(inst) + d, n, shift, err := e.decodeRegShift(inst) if err != nil { return nil, err } @@ -420,21 +443,21 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpCLZ: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } return encR2(0xDAC01000, d, n) case OpRBIT: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } return encR2(0xDAC00000, d, n) case OpREV: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -448,82 +471,36 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(n)<<5 | reg(d)), nil case OpREV16: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } return encR2(0xDAC00400, d, n) case OpREV32: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } return encR2(0xDAC00800, d, n) - case OpSXTB: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - - return enc(0x93400000 | 0<<16 | 7<<10 | reg(n)<<5 | reg(d)), nil - - case OpSXTH: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - - return enc(0x93400000 | 0<<16 | 15<<10 | reg(n)<<5 | reg(d)), nil - - case OpSXTW: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - - return enc(0x93400000 | 0<<16 | 31<<10 | reg(n)<<5 | reg(d)), nil - - case OpUXTB: - d, n, err := decodeReg2(inst) + case OpSXTB, OpSXTH, OpSXTW, OpUXTB, OpUXTH, OpUXTW: + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } - - return enc(0xD3400000 | 0<<16 | 7<<10 | reg(n)<<5 | reg(d)), nil - - case OpUXTH: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return enc(0xD3400000 | 0<<16 | 15<<10 | reg(n)<<5 | reg(d)), nil - - case OpUXTW: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return enc(0xD3400000 | 0<<16 | 31<<10 | reg(n)<<5 | reg(d)), nil + ext := extendOpcodes[op] + return enc(ext.base | ext.imms<<10 | reg(n)<<5 | reg(d)), nil // ----------------------------------------------------------------------- // TST // ----------------------------------------------------------------------- case OpTST: // ANDS XZR, Xn, Xm - n, m, err := decodeCmp(inst) - if err != nil { - return nil, err - } - base, err := intBase(0xEA00001F, n, m) - if err != nil { - return nil, err - } - return enc(base | reg(m)<<16 | reg(n)<<5), nil + return e.encodeCompareReg(0xEA00001F, inst) case OpTSTI: - n, imm, err := decodeCmpImm(inst) + n, imm, err := e.decodeCmpImm(inst) if err != nil { return nil, err } @@ -538,51 +515,19 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpCMP: // SUBS XZR, Xn, Xm - n, m, err := decodeCmp(inst) - if err != nil { - return nil, err - } - base, err := intBase(0xEB00001F, n, m) - if err != nil { - return nil, err - } - return enc(base | reg(m)<<16 | reg(n)<<5), nil + return e.encodeCompareReg(0xEB00001F, inst) case OpCMPI: // SUBS XZR, Xn, #imm - n, imm, err := decodeCmpImm(inst) - if err != nil { - return nil, err - } - base, err := intBase(0xF100001F, n) - if err != nil { - return nil, err - } - return enc(base | (uint32(imm)&0xFFF)<<10 | reg(n)<<5), nil + return e.encodeCompareImm(0xF100001F, inst) case OpCMN: // ADDS XZR, Xn, Xm - n, m, err := decodeCmp(inst) - if err != nil { - return nil, err - } - base, err := intBase(0xAB00001F, n, m) - if err != nil { - return nil, err - } - return enc(base | reg(m)<<16 | reg(n)<<5), nil + return e.encodeCompareReg(0xAB00001F, inst) case OpCMNI: // ADDS XZR, Xn, #imm - n, imm, err := decodeCmpImm(inst) - if err != nil { - return nil, err - } - base, err := intBase(0xB100001F, n) - if err != nil { - return nil, err - } - return enc(base | (uint32(imm)&0xFFF)<<10 | reg(n)<<5), nil + return e.encodeCompareImm(0xB100001F, inst) case OpCCMP: - n, m, err := decodeCmp(inst) + n, m, err := e.decodeCmp(inst) if err != nil { return nil, err } @@ -597,7 +542,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | (uint32(flags.Value>>4)&0xF)<<12 | reg(n)<<5 | uint32(flags.Value)&0xF), nil case OpCCMPI: - n, imm, err := decodeCmpImm(inst) + n, imm, err := e.decodeCmpImm(inst) if err != nil { return nil, err } @@ -616,7 +561,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpMOV: // MOV Xd, Xn → ORR Xd, XZR, Xn - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -627,7 +572,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(n)<<16 | reg(d)), nil case OpMOVI: // pseudo: MOVZ + MOVK sequence; emit MOVZ for first 16 bits - d, imm64, err := decodeDstImm(inst) + d, imm64, err := e.decodeDstImm(inst) if err != nil { return nil, err } @@ -641,135 +586,56 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(0xD2800000 | (uint32(u)&0xFFFF)<<5 | reg(d)), nil case OpMOVZ: - d, imm, shift, err := decodeMovImm(inst) - if err != nil { - return nil, err - } - if err := validMoveImmediate(d, shift); err != nil { - return nil, err - } - hw := uint32(shift/16) & 3 - if d.Width() == asm.Width32 { - return enc(0x52800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil - } - return enc(0xD2800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil + return e.encodeMovImmediate(0x52800000, 0xD2800000, inst) case OpMOVK: - d, imm, shift, err := decodeMovImm(inst) - if err != nil { - return nil, err - } - if err := validMoveImmediate(d, shift); err != nil { - return nil, err - } - hw := uint32(shift/16) & 3 - if d.Width() == asm.Width32 { - return enc(0x72800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil - } - return enc(0xF2800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil + return e.encodeMovImmediate(0x72800000, 0xF2800000, inst) case OpMOVN: - d, imm, shift, err := decodeMovImm(inst) - if err != nil { - return nil, err - } - if err := validMoveImmediate(d, shift); err != nil { - return nil, err - } - hw := uint32(shift/16) & 3 - if d.Width() == asm.Width32 { - return enc(0x12800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil - } - return enc(0x92800000 | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil + return e.encodeMovImmediate(0x12800000, 0x92800000, inst) // ----------------------------------------------------------------------- // Load / Store 64-bit // ----------------------------------------------------------------------- - case OpLDR: - // LDR Xt, [Xn, #pimm*8] — unsigned offset - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/8) & 0xFFF - return enc(0xF9400000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + case OpLDR: // LDR Xt, [Xn, #pimm*8] — unsigned offset + return e.encodeLoad(0xF9400000, 8, inst) case OpSTR: - src, base, offset, err := decodeStrOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/8) & 0xFFF - return enc(0xF9000000 | pimm<<10 | reg(base)<<5 | reg(src)), nil + return e.encodeStore(0xF9000000, 8, inst) // ----------------------------------------------------------------------- // Load / Store 8-bit // ----------------------------------------------------------------------- case OpLDRB: - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset) & 0xFFF - return enc(0x39400000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + return e.encodeLoad(0x39400000, 1, inst) case OpLDRSB: // sign-extends to 64-bit - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset) & 0xFFF - return enc(0x39800000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + return e.encodeLoad(0x39800000, 1, inst) case OpSTRB: - src, base, offset, err := decodeStrOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset) & 0xFFF - return enc(0x39000000 | pimm<<10 | reg(base)<<5 | reg(src)), nil + return e.encodeStore(0x39000000, 1, inst) // ----------------------------------------------------------------------- // Load / Store 16-bit // ----------------------------------------------------------------------- case OpLDRH: - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/2) & 0xFFF - return enc(0x79400000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + return e.encodeLoad(0x79400000, 2, inst) case OpLDRSH: // sign-extends to 64-bit - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/2) & 0xFFF - return enc(0x79800000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + return e.encodeLoad(0x79800000, 2, inst) case OpSTRH: - src, base, offset, err := decodeStrOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/2) & 0xFFF - return enc(0x79000000 | pimm<<10 | reg(base)<<5 | reg(src)), nil + return e.encodeStore(0x79000000, 2, inst) // ----------------------------------------------------------------------- // Load / Store 32-bit sign-extended // ----------------------------------------------------------------------- case OpLDRSW: - d, base, offset, err := decodeMemOp(inst) - if err != nil { - return nil, err - } - pimm := uint32(offset/4) & 0xFFF - return enc(0xB9800000 | pimm<<10 | reg(base)<<5 | reg(d)), nil + return e.encodeLoad(0xB9800000, 4, inst) // ----------------------------------------------------------------------- // Load / Store register-offset [Xbase, Xoffset] @@ -777,7 +643,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpLDRR: // LDR Xt, [Xbase, Xm, LSL #3] — extended register - d, base, m, err := decodeReg3(inst) + d, base, m, err := e.decodeReg3(inst) if err != nil { return nil, err } @@ -786,7 +652,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpSTRR: // STR Xt, [Xbase, Xm, LSL #3] // inst encoding: Dst=base, Src1=src, Src2=offsetReg - d, n, m, err := decodeReg3(inst) + d, n, m, err := e.decodeReg3(inst) if err != nil { return nil, err } @@ -799,7 +665,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpLDP: // LDP Xt1, Xt2, [Xbase, #offset] // Encoding: Dst=Xt1, Src1=Mem(base,offset), Src2=Xt2 - d1, base, offset, err := decodeMemOp(inst) + d1, base, offset, err := e.decodeMemOp(inst) if err != nil { return nil, err } @@ -813,7 +679,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpSTP: // STP Xt1, Xt2, [Xbase, #offset] // Encoding: Dst=Mem(base,offset), Src1=Xt1, Src2=Xt2 - src1, base, offset, err := decodeStrOp(inst) + src1, base, offset, err := e.decodeStrOp(inst) if err != nil { return nil, err } @@ -829,7 +695,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpSCVTF: // SCVTF Dd, Xn (integer → double) - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -850,7 +716,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } case OpUCVTF: // UCVTF Dd, Xn (unsigned integer → double) - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -871,7 +737,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } case OpFCVTZS: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -892,7 +758,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } case OpFCVTZU: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -913,7 +779,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } case OpFCVT: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -933,168 +799,47 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpFADD: - d, n, m, err := decodeReg3(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1E202800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FADD Sd, Sn, Sm - } - return enc(0x1E602800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FADD Dd, Dn, Dm + return e.encodeFloatBinary(0x1E202800, 0x1E602800, inst) case OpFSUB: - d, n, m, err := decodeReg3(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1E203800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FSUB Sd, Sn, Sm - } - return enc(0x1E603800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FSUB Dd, Dn, Dm + return e.encodeFloatBinary(0x1E203800, 0x1E603800, inst) case OpFMUL: - d, n, m, err := decodeReg3(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1E200800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FMUL Sd, Sn, Sm - } - return enc(0x1E600800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FMUL Dd, Dn, Dm + return e.encodeFloatBinary(0x1E200800, 0x1E600800, inst) case OpFDIV: - d, n, m, err := decodeReg3(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1E201800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FDIV Sd, Sn, Sm - } - return enc(0x1E601800 | reg(m)<<16 | reg(n)<<5 | reg(d)), nil // FDIV Dd, Dn, Dm + return e.encodeFloatBinary(0x1E201800, 0x1E601800, inst) case OpFMADD: - d, n, m, a, err := decodeReg4(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m, a); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1F000000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FMADD Sd, Sn, Sm, Sa - } - return enc(0x1F400000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FMADD Dd, Dn, Dm, Da + return e.encodeFloatTernary(0x1F000000, 0x1F400000, inst) case OpFMSUB: - d, n, m, a, err := decodeReg4(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m, a); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1F008000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FMSUB Sd, Sn, Sm, Sa - } - return enc(0x1F408000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FMSUB Dd, Dn, Dm, Da + return e.encodeFloatTernary(0x1F008000, 0x1F408000, inst) case OpFNMADD: - d, n, m, a, err := decodeReg4(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m, a); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1F200000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FNMADD Sd, Sn, Sm, Sa - } - return enc(0x1F600000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FNMADD Dd, Dn, Dm, Da + return e.encodeFloatTernary(0x1F200000, 0x1F600000, inst) case OpFNMSUB: - d, n, m, a, err := decodeReg4(inst) - if err != nil { - return nil, err - } - if err := floatMatch(d, n, m, a); err != nil { - return nil, err - } - if d.Width() == asm.Width32 { - return enc(0x1F208000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FNMSUB Sd, Sn, Sm, Sa - } - return enc(0x1F608000 | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil // FNMSUB Dd, Dn, Dm, Da + return e.encodeFloatTernary(0x1F208000, 0x1F608000, inst) // ----------------------------------------------------------------------- // Float — unary // ----------------------------------------------------------------------- - case OpFABS: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E20C000, 0x1E60C000, d, n) - - case OpFNEG: - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E214000, 0x1E614000, d, n) - - case OpFSQRT: - d, n, err := decodeReg2(inst) + case OpFABS, OpFNEG, OpFSQRT, OpFRINTN, OpFRINTM, OpFRINTP, OpFRINTZ: + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } - return encodeFloatUnary(0x1E21C000, 0x1E61C000, d, n) - - case OpFRINTN: // round to nearest, ties to even - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E244000, 0x1E644000, d, n) - - case OpFRINTM: // round toward minus infinity - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E254000, 0x1E654000, d, n) - - case OpFRINTP: // round toward plus infinity - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E24C000, 0x1E64C000, d, n) - - case OpFRINTZ: // round toward zero - d, n, err := decodeReg2(inst) - if err != nil { - return nil, err - } - return encodeFloatUnary(0x1E25C000, 0x1E65C000, d, n) + fu := floatUnaryOpcodes[op] + return encodeFloatUnary(fu.single, fu.double, d, n) // ----------------------------------------------------------------------- // Float — move / compare // ----------------------------------------------------------------------- case OpFMOV: - d, n, err := decodeReg2(inst) + d, n, err := e.decodeReg2(inst) if err != nil { return nil, err } @@ -1134,7 +879,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } case OpFCMP: - n, m, err := decodeCmp(inst) + n, m, err := e.decodeCmp(inst) if err != nil { return nil, err } @@ -1147,7 +892,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(0x1E602000 | reg(m)<<16 | reg(n)<<5), nil // FCMP Dn, Dm case OpFCMPE: - n, m, err := decodeCmp(inst) + n, m, err := e.decodeCmp(inst) if err != nil { return nil, err } @@ -1164,7 +909,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpCSEL: // CSEL Xd, Xn, Xm, cond - d, n, m, cond, err := decodeSelect(inst) + d, n, m, cond, err := e.decodeSelect(inst) if err != nil { return nil, err } @@ -1175,7 +920,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | cond<<12 | reg(n)<<5 | reg(d)), nil case OpCSINC: // CSINC Xd, Xn, Xm, cond - d, n, m, cond, err := decodeSelect(inst) + d, n, m, cond, err := e.decodeSelect(inst) if err != nil { return nil, err } @@ -1186,7 +931,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | cond<<12 | reg(n)<<5 | reg(d)), nil case OpCSINV: - d, n, m, cond, err := decodeSelect(inst) + d, n, m, cond, err := e.decodeSelect(inst) if err != nil { return nil, err } @@ -1197,7 +942,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | cond<<12 | reg(n)<<5 | reg(d)), nil case OpCSNEG: - d, n, m, cond, err := decodeSelect(inst) + d, n, m, cond, err := e.decodeSelect(inst) if err != nil { return nil, err } @@ -1208,7 +953,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | reg(m)<<16 | cond<<12 | reg(n)<<5 | reg(d)), nil case OpCSET: // CSET Xd, cond → CSINC Xd, XZR, XZR, !cond - d, condImm, err := decodeDstImm(inst) + d, condImm, err := e.decodeDstImm(inst) if err != nil { return nil, err } @@ -1220,7 +965,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | cond<<12 | reg(d)), nil case OpCSETM: // CSETM Xd, cond → CSINV Xd, XZR, XZR, !cond - d, condImm, err := decodeDstImm(inst) + d, condImm, err := e.decodeDstImm(inst) if err != nil { return nil, err } @@ -1236,21 +981,21 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpB: - offset, err := decodeBranch(inst) + offset, err := e.decodeBranch(inst) if err != nil { return nil, err } return enc(0x14000000 | (uint32(offset/4) & 0x3FFFFFF)), nil case OpBL: - offset, err := decodeBranch(inst) + offset, err := e.decodeBranch(inst) if err != nil { return nil, err } return enc(0x94000000 | (uint32(offset/4) & 0x3FFFFFF)), nil case OpBR: - r, err := decodeRegOnly(inst) + r, err := e.decodeRegOnly(inst) if err != nil { return nil, err } @@ -1260,7 +1005,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(0xD61F0000 | reg(r)<<5), nil case OpBLR: - r, err := decodeRegOnly(inst) + r, err := e.decodeRegOnly(inst) if err != nil { return nil, err } @@ -1278,7 +1023,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpCBZ: - r, offset, err := decodeRegBranch(inst) + r, offset, err := e.decodeRegBranch(inst) if err != nil { return nil, err } @@ -1290,7 +1035,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(base | imm19 | reg(r)), nil case OpCBNZ: - r, offset, err := decodeRegBranch(inst) + r, offset, err := e.decodeRegBranch(inst) if err != nil { return nil, err } @@ -1306,7 +1051,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { // ----------------------------------------------------------------------- case OpTBZ: - r, bit, offset, err := decodeTestBranch(inst) + r, bit, offset, err := e.decodeTestBranch(inst) if err != nil { return nil, err } @@ -1319,7 +1064,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(0x36000000 | b5<<31 | b40<<19 | imm14 | reg(r)), nil case OpTBNZ: - r, bit, offset, err := decodeTestBranch(inst) + r, bit, offset, err := e.decodeTestBranch(inst) if err != nil { return nil, err } @@ -1337,7 +1082,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpBEQ, OpBNE, OpBCS, OpBCC, OpBMI, OpBPL, OpBVS, OpBVC, OpBHI, OpBLS, OpBGE, OpBLT, OpBGT, OpBLE: - offset, err := decodeBranch(inst) + offset, err := e.decodeBranch(inst) if err != nil { return nil, err } @@ -1356,14 +1101,14 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { return enc(0xD4400000), nil // HLT #0 case OpBRK: - imm, err := decodeBranch(inst) // reuse: imm16 in Src2 + imm, err := e.decodeBranch(inst) // reuse: imm16 in Src2 if err != nil { return nil, err } return enc(0xD4200000 | (uint32(imm)&0xFFFF)<<5), nil case OpSVC: - imm, err := decodeBranch(inst) + imm, err := e.decodeBranch(inst) if err != nil { return nil, err } @@ -1374,7 +1119,7 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { case OpMRS: // MRS Xt, sysreg — sysreg encoded in Src1 as ImmOperand - d, sysreg, err := decodeDstImm(inst) + d, sysreg, err := e.decodeDstImm(inst) if err != nil { return nil, err } @@ -1406,6 +1151,111 @@ func (e *Encoder) Encode(inst asm.Instruction) ([]byte, error) { } } +// --------------------------------------------------------------------------- +// Per-family encoders +// --------------------------------------------------------------------------- + +// encodeMovImmediate emits a MOVZ/MOVK/MOVN-style wide-immediate move, picking +// op32 or op64 by destination width. +func (e *Encoder) encodeMovImmediate(op32, op64 uint32, inst asm.Instruction) ([]byte, error) { + d, imm, shift, err := e.decodeMovImm(inst) + if err != nil { + return nil, err + } + if err := validMoveImmediate(d, shift); err != nil { + return nil, err + } + hw := uint32(shift/16) & 3 + base := op64 + if d.Width() == asm.Width32 { + base = op32 + } + return enc(base | hw<<21 | (uint32(imm)&0xFFFF)<<5 | reg(d)), nil +} + +// encodeCompareReg emits a register-form compare/test (CMP/CMN/TST → SUBS/ADDS/ +// ANDS with XZR destination). +func (e *Encoder) encodeCompareReg(base uint32, inst asm.Instruction) ([]byte, error) { + n, m, err := e.decodeCmp(inst) + if err != nil { + return nil, err + } + b, err := intBase(base, n, m) + if err != nil { + return nil, err + } + return enc(b | reg(m)<<16 | reg(n)<<5), nil +} + +// encodeCompareImm emits an immediate-form compare (CMPI/CMNI). +func (e *Encoder) encodeCompareImm(base uint32, inst asm.Instruction) ([]byte, error) { + n, imm, err := e.decodeCmpImm(inst) + if err != nil { + return nil, err + } + b, err := intBase(base, n) + if err != nil { + return nil, err + } + return enc(b | (uint32(imm)&0xFFF)<<10 | reg(n)<<5), nil +} + +// encodeLoad emits an unsigned-offset load, scaling the byte offset by the +// access size. +func (e *Encoder) encodeLoad(base uint32, scale int64, inst asm.Instruction) ([]byte, error) { + d, b, offset, err := e.decodeMemOp(inst) + if err != nil { + return nil, err + } + pimm := uint32(offset/scale) & 0xFFF + return enc(base | pimm<<10 | reg(b)<<5 | reg(d)), nil +} + +// encodeStore emits an unsigned-offset store, scaling the byte offset by the +// access size. +func (e *Encoder) encodeStore(base uint32, scale int64, inst asm.Instruction) ([]byte, error) { + src, b, offset, err := e.decodeStrOp(inst) + if err != nil { + return nil, err + } + pimm := uint32(offset/scale) & 0xFFF + return enc(base | pimm<<10 | reg(b)<<5 | reg(src)), nil +} + +// encodeFloatBinary emits a 3-register scalar float op (FADD/FSUB/FMUL/FDIV), +// picking op32 or op64 by destination width. +func (e *Encoder) encodeFloatBinary(op32, op64 uint32, inst asm.Instruction) ([]byte, error) { + d, n, m, err := e.decodeReg3(inst) + if err != nil { + return nil, err + } + if err := floatMatch(d, n, m); err != nil { + return nil, err + } + base := op64 + if d.Width() == asm.Width32 { + base = op32 + } + return enc(base | reg(m)<<16 | reg(n)<<5 | reg(d)), nil +} + +// encodeFloatTernary emits a 4-register scalar float op (FMADD-family), picking +// op32 or op64 by destination width. +func (e *Encoder) encodeFloatTernary(op32, op64 uint32, inst asm.Instruction) ([]byte, error) { + d, n, m, a, err := e.decodeReg4(inst) + if err != nil { + return nil, err + } + if err := floatMatch(d, n, m, a); err != nil { + return nil, err + } + base := op64 + if d.Width() == asm.Width32 { + base = op32 + } + return enc(base | reg(m)<<16 | reg(a)<<10 | reg(n)<<5 | reg(d)), nil +} + // --------------------------------------------------------------------------- // Operand decoders // --------------------------------------------------------------------------- @@ -1542,7 +1392,7 @@ func encodeFloatUnary(single, double uint32, dst, src asm.PReg) ([]byte, error) return enc(double | reg(src)<<5 | reg(dst)), nil } -func decodeReg4(inst asm.Instruction) (dst, src1, src2, src3 asm.PReg, err error) { +func (e *Encoder) decodeReg4(inst asm.Instruction) (dst, src1, src2, src3 asm.PReg, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { err = ErrMissingDestinationReg @@ -1558,7 +1408,7 @@ func decodeReg4(inst asm.Instruction) (dst, src1, src2, src3 asm.PReg, err error return dstOp.Reg, s1.Reg, s2.Reg, s3.Reg, nil } -func decodeReg3(inst asm.Instruction) (dst, src1, src2 asm.PReg, err error) { +func (e *Encoder) decodeReg3(inst asm.Instruction) (dst, src1, src2 asm.PReg, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { err = ErrMissingDestinationReg @@ -1573,7 +1423,7 @@ func decodeReg3(inst asm.Instruction) (dst, src1, src2 asm.PReg, err error) { return dstOp.Reg, s1.Reg, s2.Reg, nil } -func decodeReg2(inst asm.Instruction) (dst, src asm.PReg, err error) { +func (e *Encoder) decodeReg2(inst asm.Instruction) (dst, src asm.PReg, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, ErrMissingDestinationReg @@ -1585,7 +1435,7 @@ func decodeReg2(inst asm.Instruction) (dst, src asm.PReg, err error) { return dstOp.Reg, srcOp.Reg, nil } -func decodeRegImm(inst asm.Instruction) (dst, src asm.PReg, imm int64, err error) { +func (e *Encoder) decodeRegImm(inst asm.Instruction) (dst, src asm.PReg, imm int64, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, 0, ErrMissingDestinationReg @@ -1602,11 +1452,11 @@ func decodeRegImm(inst asm.Instruction) (dst, src asm.PReg, imm int64, err error } // decodeRegShift decodes (dst, src, shift_amount) for immediate-shift instructions. -func decodeRegShift(inst asm.Instruction) (dst, src asm.PReg, shift int64, err error) { - return decodeRegImm(inst) +func (e *Encoder) decodeRegShift(inst asm.Instruction) (dst, src asm.PReg, shift int64, err error) { + return e.decodeRegImm(inst) } -func decodeCmp(inst asm.Instruction) (src1, src2 asm.PReg, err error) { +func (e *Encoder) decodeCmp(inst asm.Instruction) (src1, src2 asm.PReg, err error) { s1, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, ErrMissingSourceReg @@ -1618,7 +1468,7 @@ func decodeCmp(inst asm.Instruction) (src1, src2 asm.PReg, err error) { return s1.Reg, s2.Reg, nil } -func decodeCmpImm(inst asm.Instruction) (src asm.PReg, imm int64, err error) { +func (e *Encoder) decodeCmpImm(inst asm.Instruction) (src asm.PReg, imm int64, err error) { srcOp, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, 0, ErrMissingSourceReg @@ -1630,7 +1480,7 @@ func decodeCmpImm(inst asm.Instruction) (src asm.PReg, imm int64, err error) { return srcOp.Reg, immOp.Value, nil } -func decodeSelect(inst asm.Instruction) (dst, src1, src2 asm.PReg, cond uint32, err error) { +func (e *Encoder) decodeSelect(inst asm.Instruction) (dst, src1, src2 asm.PReg, cond uint32, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, asm.PReg{}, 0, ErrMissingDestinationReg @@ -1650,7 +1500,7 @@ func decodeSelect(inst asm.Instruction) (dst, src1, src2 asm.PReg, cond uint32, return dstOp.Reg, s1.Reg, s2.Reg, uint32(condOp.Value) & 0xF, nil } -func decodeMovImm(inst asm.Instruction) (dst asm.PReg, imm, shift int64, err error) { +func (e *Encoder) decodeMovImm(inst asm.Instruction) (dst asm.PReg, imm, shift int64, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, 0, 0, ErrMissingDestinationReg @@ -1667,7 +1517,7 @@ func decodeMovImm(inst asm.Instruction) (dst asm.PReg, imm, shift int64, err err } // decodeDstImm decodes instructions where Dst is a register and Src1 is an immediate. -func decodeDstImm(inst asm.Instruction) (dst asm.PReg, imm int64, err error) { +func (e *Encoder) decodeDstImm(inst asm.Instruction) (dst asm.PReg, imm int64, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, 0, ErrMissingDestinationReg @@ -1679,7 +1529,7 @@ func decodeDstImm(inst asm.Instruction) (dst asm.PReg, imm int64, err error) { return dstOp.Reg, immOp.Value, nil } -func decodeMemOp(inst asm.Instruction) (dst, base asm.PReg, offset int64, err error) { +func (e *Encoder) decodeMemOp(inst asm.Instruction) (dst, base asm.PReg, offset int64, err error) { dstOp, ok := inst.Dst.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, 0, ErrMissingDestinationReg @@ -1695,7 +1545,7 @@ func decodeMemOp(inst asm.Instruction) (dst, base asm.PReg, offset int64, err er return dstOp.Reg, baseReg.Reg, memOp.Offset, nil } -func decodeStrOp(inst asm.Instruction) (src, base asm.PReg, offset int64, err error) { +func (e *Encoder) decodeStrOp(inst asm.Instruction) (src, base asm.PReg, offset int64, err error) { srcOp, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, asm.PReg{}, 0, ErrMissingSourceReg @@ -1711,7 +1561,7 @@ func decodeStrOp(inst asm.Instruction) (src, base asm.PReg, offset int64, err er return srcOp.Reg, baseReg.Reg, memOp.Offset, nil } -func decodeRegOnly(inst asm.Instruction) (asm.PReg, error) { +func (e *Encoder) decodeRegOnly(inst asm.Instruction) (asm.PReg, error) { op, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, ErrMissingRegisterOperand @@ -1719,7 +1569,7 @@ func decodeRegOnly(inst asm.Instruction) (asm.PReg, error) { return op.Reg, nil } -func decodeBranch(inst asm.Instruction) (int64, error) { +func (e *Encoder) decodeBranch(inst asm.Instruction) (int64, error) { immOp, ok := inst.Src2.(asm.ImmOperand) if !ok { return 0, ErrMissingBranchOffset @@ -1727,7 +1577,7 @@ func decodeBranch(inst asm.Instruction) (int64, error) { return immOp.Value, nil } -func decodeRegBranch(inst asm.Instruction) (r asm.PReg, offset int64, err error) { +func (e *Encoder) decodeRegBranch(inst asm.Instruction) (r asm.PReg, offset int64, err error) { rOp, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, 0, ErrMissingRegisterOperand @@ -1740,7 +1590,7 @@ func decodeRegBranch(inst asm.Instruction) (r asm.PReg, offset int64, err error) } // decodeTestBranch unpacks the packed (bit | offset<<8) encoding used by TBZ/TBNZ. -func decodeTestBranch(inst asm.Instruction) (r asm.PReg, bit uint8, offset int64, err error) { +func (e *Encoder) decodeTestBranch(inst asm.Instruction) (r asm.PReg, bit uint8, offset int64, err error) { rOp, ok := inst.Src1.(asm.PRegOperand) if !ok { return asm.PReg{}, 0, 0, ErrMissingRegisterOperand From 98a6974448c3ffe0faa4f7ad291d2bc5d90a24a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:44:00 +0000 Subject: [PATCH 15/18] refactor(interp): tidy arm64 JIT helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7c: - regOf -> (*jitSeg).regOf method (§1.5). - drop the compareI32 wrapper; route the I32 comparisons through the general compare() with (RegTypeInt, Width32, CMP, cond), matching how the I64 comparisons already work. - extract loadSlot/storeSlot for the boxed load/store emit pattern shared by LOCAL_GET/SET/TEE and GLOBAL_GET/SET/TEE; the handlers now read as decode -> validate -> emit. Emit order is preserved (Push is bookkeeping, not an emit), so generated code is unchanged. Note: jit_arm64.go is //go:build arm64 and generates native code, so this is verified by GOOS=arm64 build+vet only. Behavior must be confirmed on ARM64 CI/hardware before merge. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/jit_arm64.go | 80 +++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/interp/jit_arm64.go b/interp/jit_arm64.go index 1ce90a9..5b5c3fe 100644 --- a/interp/jit_arm64.go +++ b/interp/jit_arm64.go @@ -175,10 +175,7 @@ func init() { return false } s.ip += 3 - boxed := s.assembler.NewVReg(asm.RegTypeInt, asm.Width64) - s.assembler.Emit(arm64.LDR(boxed, s.scratch[rGlobals], offset)) - r0 := s.unbox64(boxed, kind) - s.Push(r0) + s.loadSlot(s.scratch[rGlobals], offset, kind) return true } jit[instr.GLOBAL_SET] = func(s *jitSeg) bool { @@ -197,9 +194,7 @@ func init() { } s.ip += 3 s.Pop() - boxed := s.box64(r0, kind, s.ip-3) - - s.assembler.Emit(arm64.STR(boxed, s.scratch[rGlobals], offset)) + s.storeSlot(r0, kind, s.scratch[rGlobals], offset, s.ip-3) s.facts[idx] = kind return true } @@ -218,8 +213,7 @@ func init() { return false } s.ip += 3 - boxed := s.box64(r0, kind, s.ip-3) - s.assembler.Emit(arm64.STR(boxed, s.scratch[rGlobals], offset)) + s.storeSlot(r0, kind, s.scratch[rGlobals], offset, s.ip-3) s.facts[idx] = kind return true } @@ -235,10 +229,7 @@ func init() { } s.ip += 2 - offset := int16(idx * 8) - boxed := s.assembler.NewVReg(asm.RegTypeInt, asm.Width64) - s.assembler.Emit(arm64.LDR(boxed, s.scratch[rStack], offset)) - s.Push(s.unbox64(boxed, typ.Kind())) + s.loadSlot(s.scratch[rStack], int16(idx*8), typ.Kind()) return true } jit[instr.LOCAL_SET] = func(s *jitSeg) bool { @@ -248,15 +239,13 @@ func init() { if !ok { return false } - reg, ok := regOf(typ.Kind()) + reg, ok := s.regOf(typ.Kind()) if !ok || !s.accepts(reg) { return false } s.ip += 2 - offset := int16(idx * 8) r0, _ := s.Take(reg.Type(), reg.Width()) - boxed := s.box64(r0, typ.Kind(), s.ip-2) - s.assembler.Emit(arm64.STR(boxed, s.scratch[rStack], offset)) + s.storeSlot(r0, typ.Kind(), s.scratch[rStack], int16(idx*8), s.ip-2) return true } jit[instr.LOCAL_TEE] = func(s *jitSeg) bool { @@ -266,16 +255,14 @@ func init() { if !ok { return false } - reg, ok := regOf(typ.Kind()) + reg, ok := s.regOf(typ.Kind()) if !ok || !s.accepts(reg) { return false } s.ip += 2 - offset := int16(idx * 8) r0, _ := s.Take(reg.Type(), reg.Width()) - boxed := s.box64(r0, typ.Kind(), s.ip-2) s.Push(r0) - s.assembler.Emit(arm64.STR(boxed, s.scratch[rStack], offset)) + s.storeSlot(r0, typ.Kind(), s.scratch[rStack], int16(idx*8), s.ip-2) return true } jit[instr.CONST_GET] = func(s *jitSeg) bool { @@ -384,34 +371,34 @@ func init() { return true } jit[instr.I32_EQ] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondEQ) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondEQ) } jit[instr.I32_NE] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondNE) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondNE) } jit[instr.I32_LT_S] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondLT) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondLT) } jit[instr.I32_LT_U] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondCC) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondCC) } jit[instr.I32_GT_S] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondGT) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondGT) } jit[instr.I32_GT_U] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondHI) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondHI) } jit[instr.I32_LE_S] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondLE) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondLE) } jit[instr.I32_LE_U] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondLS) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondLS) } jit[instr.I32_GE_S] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondGE) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondGE) } jit[instr.I32_GE_U] = func(s *jitSeg) bool { - return s.compareI32(arm64.CondCS) + return s.compare(asm.RegTypeInt, asm.Width32, arm64.CMP, arm64.CondCS) } jit[instr.I32_TO_I64_S] = func(s *jitSeg) bool { return s.convert(asm.RegTypeInt, asm.Width32, asm.RegTypeInt, asm.Width64, arm64.SXTW) @@ -676,20 +663,6 @@ func (s *jitSeg) remainder(width asm.RegWidth, divide func(dst, left, right asm. return true } -func (s *jitSeg) compareI32(cond uint8) bool { - reg := asm.NewPReg(0, asm.RegTypeInt, asm.Width32) - if !s.accepts(reg, reg) { - return false - } - s.ip++ - right, _ := s.Take(asm.RegTypeInt, asm.Width32) - left, _ := s.Take(asm.RegTypeInt, asm.Width32) - s.assembler.Emit(arm64.CMP(left, right)) - s.assembler.Emit(arm64.CSET(left, cond)) - s.Push(left) - return true -} - func (s *jitSeg) compare(typ asm.RegType, width asm.RegWidth, compare func(left, right asm.Reg) asm.Instruction, cond uint8) bool { reg := asm.NewPReg(0, typ, width) if !s.accepts(reg, reg) { @@ -832,7 +805,22 @@ func (s *jitSeg) ret(nextIP int) { s.assembler.Emit(arm64.RET()) } -func regOf(kind types.Kind) (asm.PReg, bool) { +// loadSlot emits a boxed 64-bit load from base+offset, unboxes it as kind, and +// pushes the result. Shared by LOCAL_GET and GLOBAL_GET. +func (s *jitSeg) loadSlot(base asm.PReg, offset int16, kind types.Kind) { + boxed := s.assembler.NewVReg(asm.RegTypeInt, asm.Width64) + s.assembler.Emit(arm64.LDR(boxed, base, offset)) + s.Push(s.unbox64(boxed, kind)) +} + +// storeSlot boxes r0 as kind and emits a 64-bit store to base+offset. Shared by +// LOCAL_SET/TEE and GLOBAL_SET/TEE. +func (s *jitSeg) storeSlot(r0 asm.VReg, kind types.Kind, base asm.PReg, offset int16, fallbackIP int) { + boxed := s.box64(r0, kind, fallbackIP) + s.assembler.Emit(arm64.STR(boxed, base, offset)) +} + +func (s *jitSeg) regOf(kind types.Kind) (asm.PReg, bool) { switch kind { case types.KindI32: return asm.NewPReg(0, asm.RegTypeInt, asm.Width32), true From c6eb4d5b65cfcf80f90038e732d6c56dc90efa07 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:56:16 +0000 Subject: [PATCH 16/18] refactor(types): standardize map/array naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give the generic value-keyed/primitive-element collections the short, standard names and qualify the boxed variants with the codebase's existing "Boxed" vocabulary, so naming is consistent across types, constructors, and tests: - TypedMap[K] -> Map[K] (NewTypedMap -> NewMap) - Map (boxed) -> BoxedMap (NewMap/NewMapWithCapacity -> NewBoxedMap/NewBoxedMapWithCapacity) - MapKey/MapEntry -> BoxedMapKey/BoxedMapEntry - TypedArray[T] -> Array[T] - Array (boxed) -> BoxedArray (NewArray -> NewBoxedArray) Shared descriptors (MapType, ArrayType, NewMapForType, TypeI32Array, …) keep their names. Test/benchmark names follow suit (TestMap_*/TestBoxedMap_*, TestArray_*/TestBoxedArray_*, BenchmarkBoxedMapStringGet_Interned). Pure rename across types/interp/transform; no behavior change. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- interp/interp_test.go | 84 ++++++++-------- interp/marshal.go | 62 ++++++------ interp/threaded.go | 220 +++++++++++++++++++++--------------------- transform/cf.go | 4 +- transform/cf_test.go | 4 +- types/array.go | 32 +++--- types/array_test.go | 52 +++++----- types/map.go | 88 ++++++++--------- types/map_test.go | 196 ++++++++++++++++++------------------- types/value_test.go | 2 +- 10 files changed, 372 insertions(+), 372 deletions(-) diff --git a/interp/interp_test.go b/interp/interp_test.go index 4a16791..fbe86df 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -1464,7 +1464,7 @@ var tests = []test{ }, program.WithConstants(types.String("foo")), ), - values: []types.Value{types.TypedArray[int32]("foo")}, + values: []types.Value{types.Array[int32]("foo")}, }, // --- array: ARRAY_NEW, ARRAY_NEW_DEFAULT, ARRAY_GET, ARRAY_SET, ARRAY_FILL --- { @@ -1476,7 +1476,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{types.TypedArray[int32]{1}}, + values: []types.Value{types.Array[int32]{1}}, }, { program: program.New( @@ -1487,7 +1487,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{types.TypedArray[int64]{1}}, + values: []types.Value{types.Array[int64]{1}}, }, { program: program.New( @@ -1498,7 +1498,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{types.TypedArray[float32]{42}}, + values: []types.Value{types.Array[float32]{42}}, }, { program: program.New( @@ -1509,7 +1509,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{types.TypedArray[float64]{42}}, + values: []types.Value{types.Array[float64]{42}}, }, { program: program.New( @@ -1520,7 +1520,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeRef)), ), - values: []types.Value{types.NewArray(types.NewArrayType(types.TypeRef), types.BoxI32(1))}, + values: []types.Value{types.NewBoxedArray(types.NewArrayType(types.TypeRef), types.BoxI32(1))}, }, { program: program.New( @@ -1530,7 +1530,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{make(types.TypedArray[int32], 1)}, + values: []types.Value{make(types.Array[int32], 1)}, }, { program: program.New( @@ -1540,7 +1540,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{make(types.TypedArray[int64], 1)}, + values: []types.Value{make(types.Array[int64], 1)}, }, { program: program.New( @@ -1550,7 +1550,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{make(types.TypedArray[float32], 1)}, + values: []types.Value{make(types.Array[float32], 1)}, }, { program: program.New( @@ -1560,7 +1560,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{make(types.TypedArray[float64], 1)}, + values: []types.Value{make(types.Array[float64], 1)}, }, { program: program.New( @@ -1570,7 +1570,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeRef)), ), - values: []types.Value{&types.Array{Typ: types.NewArrayType(types.TypeRef), Elems: make([]types.Boxed, 1)}}, + values: []types.Value{&types.BoxedArray{Typ: types.NewArrayType(types.TypeRef), Elems: make([]types.Boxed, 1)}}, }, { program: program.New( @@ -2479,7 +2479,7 @@ func TestInterpreter_Alloc(t *testing.T) { i := New(program.New(nil), WithHeap(2)) defer i.Close() - array := types.NewArray(types.NewArrayType(types.TypeRef)) + array := types.NewBoxedArray(types.NewArrayType(types.TypeRef)) addr, err := i.Alloc(array) require.NoError(t, err) array.Elems = append(array.Elems, types.BoxRef(addr)) @@ -2885,7 +2885,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.TypedArray[int32]{10, 20, 30}), + program.WithConstants(types.Array[int32]{10, 20, 30}), ), WithTick(1), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -2901,7 +2901,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.TypedArray[int32]{10, 20, 30}), + program.WithConstants(types.Array[int32]{10, 20, 30}), ), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -3817,19 +3817,19 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]int32{1, 2}) require.NoError(t, err) - require.Equal(t, types.TypedArray[int32]{1, 2}, got) + require.Equal(t, types.Array[int32]{1, 2}, got) got, err = i.Marshal([]uint32{math.MaxUint32}) require.NoError(t, err) - require.Equal(t, types.TypedArray[int32]{-1}, got) + require.Equal(t, types.Array[int32]{-1}, got) got, err = i.Marshal([]int{1, 2}) require.NoError(t, err) - require.Equal(t, types.TypedArray[int64]{1, 2}, got) + require.Equal(t, types.Array[int64]{1, 2}, got) got, err = i.Marshal([]uint64{math.MaxUint64}) require.NoError(t, err) - require.Equal(t, types.TypedArray[int64]{-1}, got) + require.Equal(t, types.Array[int64]{-1}, got) }) t.Run("reference slice", func(t *testing.T) { @@ -3839,7 +3839,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]string{"a", "b"}) require.NoError(t, err) - arr, ok := got.(*types.Array) + arr, ok := got.(*types.BoxedArray) require.True(t, ok) require.True(t, arr.Typ.Elem.Equals(types.TypeString)) require.Len(t, arr.Elems, 2) @@ -3856,7 +3856,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]string{"a", "b"}) require.NoError(t, err) - arr, ok := got.(*types.Array) + arr, ok := got.(*types.BoxedArray) require.True(t, ok) require.Len(t, arr.Elems, 2) @@ -3876,19 +3876,19 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]int32{"a": 1}) require.NoError(t, err) - m, ok := got.(*types.Map) + m, ok := got.(*types.BoxedMap) require.True(t, ok) require.True(t, m.Typ.Key.Equals(types.TypeString)) require.True(t, m.Typ.Elem.Equals(types.TypeI32)) keyRef := types.Boxed(0) - m.Range(func(_ types.MapKey, entry types.MapEntry) { + m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { keyRef = entry.Key }) require.Equal(t, types.KindRef, keyRef.Kind()) key, err := i.Load(keyRef.Ref()) require.NoError(t, err) require.Equal(t, types.String("a"), key) - entry, ok := m.Get(types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) + entry, ok := m.Get(types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) require.True(t, ok) require.Equal(t, types.BoxI32(1), entry.Value) }) @@ -3899,7 +3899,7 @@ func TestInterpreter_Marshal(t *testing.T) { i32, err := i.Marshal(map[int32]int32{1: 2}) require.NoError(t, err) - mI32, ok := i32.(*types.TypedMap[int32]) + mI32, ok := i32.(*types.Map[int32]) require.True(t, ok) gotI32, ok := mI32.Get(1) require.True(t, ok) @@ -3907,7 +3907,7 @@ func TestInterpreter_Marshal(t *testing.T) { i64, err := i.Marshal(map[int64]string{1: "a"}) require.NoError(t, err) - mI64, ok := i64.(*types.TypedMap[int64]) + mI64, ok := i64.(*types.Map[int64]) require.True(t, ok) gotI64, ok := mI64.Get(1) require.True(t, ok) @@ -3917,7 +3917,7 @@ func TestInterpreter_Marshal(t *testing.T) { f64, err := i.Marshal(map[float64]int32{math.Copysign(0, -1): 1}) require.NoError(t, err) - mF64, ok := f64.(*types.TypedMap[float64]) + mF64, ok := f64.(*types.Map[float64]) require.True(t, ok) gotF64, ok := mF64.Get(0) require.True(t, ok) @@ -3934,12 +3934,12 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[key]int32{{ID: 1}: 2}) require.NoError(t, err) - m, ok := got.(*types.Map) + m, ok := got.(*types.BoxedMap) require.True(t, ok) require.Equal(t, types.KindRef, m.Typ.KeyKind) - var entry types.MapEntry - m.Range(func(_ types.MapKey, e types.MapEntry) { + var entry types.BoxedMapEntry + m.Range(func(_ types.BoxedMapKey, e types.BoxedMapEntry) { entry = e }) require.Equal(t, types.KindRef, entry.Key.Kind()) @@ -3957,13 +3957,13 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]int{"a": 1}) require.NoError(t, err) - m, ok := got.(*types.Map) + m, ok := got.(*types.BoxedMap) require.True(t, ok) var keyRef types.Boxed - m.Range(func(_ types.MapKey, entry types.MapEntry) { + m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { keyRef = entry.Key }) - entry, ok := m.Get(types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) + entry, ok := m.Get(types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) require.True(t, ok) require.True(t, m.Typ.Elem.Equals(types.TypeI64)) require.Equal(t, types.KindI64, entry.Value.Kind()) @@ -4168,7 +4168,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]any{int32(1), "x", float64(2.5)}) require.NoError(t, err) - arr, ok := got.(*types.Array) + arr, ok := got.(*types.BoxedArray) require.True(t, ok) require.True(t, arr.Typ.Elem.Equals(types.TypeRef)) require.Len(t, arr.Elems, 3) @@ -4217,12 +4217,12 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]any{"a": int32(1)}) require.NoError(t, err) - m, ok := got.(*types.Map) + m, ok := got.(*types.BoxedMap) require.True(t, ok) require.True(t, m.Typ.Elem.Equals(types.TypeRef)) var value types.Boxed - m.Range(func(_ types.MapKey, entry types.MapEntry) { + m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { value = entry.Value }) require.Equal(t, types.KindRef, value.Kind()) @@ -4238,7 +4238,7 @@ func TestInterpreter_Marshal(t *testing.T) { var v any got, err := i.Marshal([]any{v}) require.NoError(t, err) - arr, ok := got.(*types.Array) + arr, ok := got.(*types.BoxedArray) require.True(t, ok) require.Equal(t, types.BoxedNull, arr.Elems[0]) }) @@ -4289,15 +4289,15 @@ func TestInterpreter_Unmarshal(t *testing.T) { defer i.Close() var out []int32 - require.NoError(t, i.Unmarshal(types.TypedArray[int32]{1, 2}, &out)) + require.NoError(t, i.Unmarshal(types.Array[int32]{1, 2}, &out)) require.Equal(t, []int32{1, 2}, out) var u32 []uint32 - require.NoError(t, i.Unmarshal(types.TypedArray[int32]{-1}, &u32)) + require.NoError(t, i.Unmarshal(types.Array[int32]{-1}, &u32)) require.Equal(t, []uint32{math.MaxUint32}, u32) var u64 []uint64 - require.NoError(t, i.Unmarshal(types.TypedArray[int64]{-1}, &u64)) + require.NoError(t, i.Unmarshal(types.Array[int64]{-1}, &u64)) require.Equal(t, []uint64{math.MaxUint64}, u64) }) @@ -4527,7 +4527,7 @@ func BenchmarkInterpreter_Alloc(b *testing.B) { var err error for n := 0; n < b.N; n++ { - array := types.NewArray(typ) + array := types.NewBoxedArray(typ) var addr int addr, err = i.Alloc(array) if err != nil { @@ -4599,7 +4599,7 @@ func BenchmarkInterpreter_Release(b *testing.B) { break } var addr int - addr, err = i.Alloc(types.NewArray(typ, types.BoxRef(child))) + addr, err = i.Alloc(types.NewBoxedArray(typ, types.BoxRef(child))) if err != nil { break } @@ -4656,7 +4656,7 @@ func BenchmarkInterpreter_Release(b *testing.B) { if err != nil { break } - m := types.NewTypedMap[int32](typ, 1) + m := types.NewMap[int32](typ, 1) m.Set(1, types.BoxRef(child)) var addr int addr, err = i.Alloc(m) diff --git a/interp/marshal.go b/interp/marshal.go index 8b5b5e6..f7d41a5 100644 --- a/interp/marshal.go +++ b/interp/marshal.go @@ -594,31 +594,31 @@ func (s *unmarshalState) value(val types.Value, dst reflect.Value) error { func (s *unmarshalState) elems(value types.Value) ([]types.Value, error) { switch v := value.(type) { - case types.TypedArray[int32]: + case types.Array[int32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I32(elem) } return out, nil - case types.TypedArray[int64]: + case types.Array[int64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I64(elem) } return out, nil - case types.TypedArray[float32]: + case types.Array[float32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F32(elem) } return out, nil - case types.TypedArray[float64]: + case types.Array[float64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F64(elem) } return out, nil - case *types.Array: + case *types.BoxedArray: out := make([]types.Value, len(v.Elems)) for idx, elem := range v.Elems { val, err := s.m.resolve(s.i, elem) @@ -736,37 +736,37 @@ func (m *codec) marshalArray(elem *marshalPlan) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { switch elemKind { case reflect.Int8, reflect.Int16, reflect.Int32: - out := make(types.TypedArray[int32], v.Len()) + out := make(types.Array[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Int()) } return out, nil case reflect.Uint8, reflect.Uint16, reflect.Uint32: - out := make(types.TypedArray[int32], v.Len()) + out := make(types.Array[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Uint()) } return out, nil case reflect.Int, reflect.Int64: - out := make(types.TypedArray[int64], v.Len()) + out := make(types.Array[int64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Int() } return out, nil case reflect.Uint, reflect.Uint64, reflect.Uintptr: - out := make(types.TypedArray[int64], v.Len()) + out := make(types.Array[int64], v.Len()) for idx := range out { out[idx] = int64(v.Index(idx).Uint()) } return out, nil case reflect.Float32: - out := make(types.TypedArray[float32], v.Len()) + out := make(types.Array[float32], v.Len()) for idx := range out { out[idx] = float32(v.Index(idx).Float()) } return out, nil case reflect.Float64: - out := make(types.TypedArray[float64], v.Len()) + out := make(types.Array[float64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Float() } @@ -780,7 +780,7 @@ func (m *codec) marshalArray(elem *marshalPlan) marshaler { } elems[idx] = boxed } - return types.NewArray(arrayType, elems...), nil + return types.NewBoxedArray(arrayType, elems...), nil } } @@ -788,7 +788,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { out := types.NewMapForType(mt, v.Len()) switch m := out.(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: iter := v.MapRange() for iter.Next() { key, err := s.boxAs(iter.Key(), mt.Key) @@ -801,7 +801,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(key.I32(), value) } - case *types.TypedMap[int64]: + case *types.Map[int64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -818,7 +818,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(keyInt, value) } - case *types.TypedMap[float32]: + case *types.Map[float32]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -835,7 +835,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(float32(keyFloat), value) } - case *types.TypedMap[float64]: + case *types.Map[float64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -852,18 +852,18 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(keyFloat, value) } - case *types.Map: + case *types.BoxedMap: iter := v.MapRange() for iter.Next() { keyValue, err := s.value(iter.Key()) if err != nil { return nil, fmt.Errorf("map key: %w", err) } - var mapKey types.MapKey + var mapKey types.BoxedMapKey var entryKey types.Boxed if mt.Key.Kind() == types.KindRef { entryKey = s.boxRef(keyValue) - mapKey = types.MapKey{Kind: types.KindRef, Bits: uint64(entryKey.Ref())} + mapKey = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(entryKey.Ref())} } else { return nil, fmt.Errorf("map key: %w: map key type=%s", ErrUnsupportedMarshalType, mt.Key) } @@ -871,7 +871,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { if err != nil { return nil, fmt.Errorf("map value: %w", err) } - m.Set(mapKey, types.MapEntry{Key: entryKey, Value: entryValue}) + m.Set(mapKey, types.BoxedMapEntry{Key: entryKey, Value: entryValue}) } } return out, nil @@ -1111,15 +1111,15 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { return func(s *unmarshalState, src types.Value, dst reflect.Value) error { size := 0 switch m := src.(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: size = m.Len() - case *types.TypedMap[int64]: + case *types.Map[int64]: size = m.Len() - case *types.TypedMap[float32]: + case *types.Map[float32]: size = m.Len() - case *types.TypedMap[float64]: + case *types.Map[float64]: size = m.Len() - case *types.Map: + case *types.BoxedMap: size = m.Len() default: return fmt.Errorf("%w: source=%T target=%s", ErrTypeMismatch, src, dst.Type()) @@ -1143,7 +1143,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { out.SetMapIndex(k, v) } switch m := src.(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: m.Range(func(key int32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1152,7 +1152,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I32(key), elemValue) }) - case *types.TypedMap[int64]: + case *types.Map[int64]: m.Range(func(key int64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1161,7 +1161,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I64(key), elemValue) }) - case *types.TypedMap[float32]: + case *types.Map[float32]: m.Range(func(key float32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1170,7 +1170,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.F32(key), elemValue) }) - case *types.TypedMap[float64]: + case *types.Map[float64]: m.Range(func(key float64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1179,8 +1179,8 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.F64(key), elemValue) }) - case *types.Map: - m.Range(func(mapKey types.MapKey, entry types.MapEntry) { + case *types.BoxedMap: + m.Range(func(mapKey types.BoxedMapKey, entry types.BoxedMapEntry) { var keyValue types.Value var err error switch mapKey.Kind { diff --git a/interp/threaded.go b/interp/threaded.go index 16f9a84..7d70040 100644 --- a/interp/threaded.go +++ b/interp/threaded.go @@ -1899,7 +1899,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp == 0 { panic(ErrStackUnderflow) } - val := unboxRef[types.TypedArray[int32]](i, i.stack[i.sp-1]) + val := unboxRef[types.Array[int32]](i, i.stack[i.sp-1]) i.stack[i.sp-1] = types.BoxRef(int(i.intern(string(types.String(val))))) i.fr.ip++ } @@ -2013,7 +2013,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } val := unboxRef[types.String](i, i.stack[i.sp-1]) - i.stack[i.sp-1] = types.BoxRef(i.alloc(types.TypedArray[int32](val))) + i.stack[i.sp-1] = types.BoxRef(i.alloc(types.Array[int32](val))) i.fr.ip++ } }, @@ -2041,7 +2041,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.TypedArray[int32], size) + val := make(types.Array[int32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].I32() } @@ -2058,7 +2058,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.TypedArray[int64], size) + val := make(types.Array[int64], size) for j := 0; j < size; j++ { val[j] = i.unboxI64(i.stack[i.sp-size-j-1]) } @@ -2075,7 +2075,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.TypedArray[float32], size) + val := make(types.Array[float32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F32() } @@ -2092,7 +2092,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.TypedArray[float64], size) + val := make(types.Array[float64], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F64() } @@ -2109,7 +2109,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := &types.Array{ + val := &types.BoxedArray{ Typ: typ, Elems: make([]types.Boxed, size), } @@ -2141,7 +2141,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.TypedArray[int32], size) + val := make(types.Array[int32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2151,7 +2151,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.TypedArray[int64], size) + val := make(types.Array[int64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2161,7 +2161,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.TypedArray[float32], size) + val := make(types.Array[float32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2171,7 +2171,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.TypedArray[float64], size) + val := make(types.Array[float64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2181,7 +2181,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := &types.Array{ + val := &types.BoxedArray{ Typ: typ, Elems: make([]types.Boxed, size), } @@ -2198,15 +2198,15 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } var n int32 switch arr := i.unbox(i.stack[i.sp-1]).(type) { - case types.TypedArray[int32]: + case types.Array[int32]: n = int32(len(arr)) - case types.TypedArray[int64]: + case types.Array[int64]: n = int32(len(arr)) - case types.TypedArray[float32]: + case types.Array[float32]: n = int32(len(arr)) - case types.TypedArray[float64]: + case types.Array[float64]: n = int32(len(arr)) - case *types.Array: + case *types.BoxedArray: n = int32(len(arr.Elems)) default: panic(ErrTypeMismatch) @@ -2229,27 +2229,27 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var val types.Boxed switch arr := i.heap[addr].(type) { - case types.TypedArray[int32]: + case types.Array[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxI32(int32(arr[idx])) - case types.TypedArray[int64]: + case types.Array[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = i.boxI64(int64(arr[idx])) - case types.TypedArray[float32]: + case types.Array[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxF32(float32(arr[idx])) - case types.TypedArray[float64]: + case types.Array[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxF64(float64(arr[idx])) - case *types.Array: + case *types.BoxedArray: if idx < 0 || idx >= len(arr.Elems) { panic(ErrIndexOutOfRange) } @@ -2279,27 +2279,27 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.TypedArray[int32]: + case types.Array[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.I32() - case types.TypedArray[int64]: + case types.Array[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = i.unboxI64(val) - case types.TypedArray[float32]: + case types.Array[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.F32() - case types.TypedArray[float64]: + case types.Array[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.F64() - case *types.Array: + case *types.BoxedArray: if idx < 0 || idx >= len(arr.Elems) { panic(ErrIndexOutOfRange) } @@ -2329,7 +2329,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.TypedArray[int32]: + case types.Array[int32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2337,7 +2337,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.TypedArray[int64]: + case types.Array[int64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2345,7 +2345,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.TypedArray[float32]: + case types.Array[float32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2353,7 +2353,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.TypedArray[float64]: + case types.Array[float64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2361,7 +2361,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case *types.Array: + case *types.BoxedArray: if idx < 0 || idx+size > len(arr.Elems) { panic(ErrIndexOutOfRange) } @@ -2396,27 +2396,27 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.TypedArray[int32]: + case types.Array[int32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.TypedArray[int64]: + case types.Array[int64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.TypedArray[float32]: + case types.Array[float32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.TypedArray[float64]: + case types.Array[float64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case *types.Array: + case *types.BoxedArray: if src < 0 || dst < 0 || src+size > len(arr.Elems) || dst+size > len(arr.Elems) { panic(ErrIndexOutOfRange) } @@ -2646,58 +2646,58 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ key := i.stack[base+j*2] value := i.stack[base+j*2+1] switch m := m.(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: old, ok := m.Set(key.I32(), value) if ok { i.releaseVal(old) } - case *types.TypedMap[int64]: + case *types.Map[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { i.releaseVal(old) } - case *types.TypedMap[float32]: + case *types.Map[float32]: old, ok := m.Set(key.F32(), value) if ok { i.releaseVal(old) } - case *types.TypedMap[float64]: + case *types.Map[float64]: old, ok := m.Set(key.F64(), value) if ok { i.releaseVal(old) } - case *types.Map: - var k types.MapKey - entry := types.MapEntry{Value: value} + case *types.BoxedMap: + var k types.BoxedMapKey + entry := types.BoxedMapEntry{Value: value} keyRef := 0 drop := false switch key.Kind() { case types.KindI32: bits := uint64(uint32(key.I32())) - k = types.MapKey{Kind: types.KindI32, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindI32, Bits: bits} entry.Key = types.BoxI32(int32(bits)) case types.KindI64: - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} entry.Key = types.BoxF32(math.Float32frombits(bits)) case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.MapKey{Kind: types.KindF64, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} entry.Key = types.BoxF64(math.Float64frombits(bits)) case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} entry.Key = key drop = true } @@ -2765,15 +2765,15 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() n := 0 switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: n = m.Len() - case *types.TypedMap[int64]: + case *types.Map[int64]: n = m.Len() - case *types.TypedMap[float32]: + case *types.Map[float32]: n = m.Len() - case *types.TypedMap[float64]: + case *types.Map[float64]: n = m.Len() - case *types.Map: + case *types.BoxedMap: n = m.Len() default: panic(ErrTypeMismatch) @@ -2797,61 +2797,61 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var result types.Boxed switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: value, ok := m.Get(key.I32()) if ok { result = value } else { result = m.Zero } - case *types.TypedMap[int64]: + case *types.Map[int64]: value, ok := m.Get(i.unboxI64(key)) if ok { result = value } else { result = m.Zero } - case *types.TypedMap[float32]: + case *types.Map[float32]: value, ok := m.Get(key.F32()) if ok { result = value } else { result = m.Zero } - case *types.TypedMap[float64]: + case *types.Map[float64]: value, ok := m.Get(key.F64()) if ok { result = value } else { result = m.Zero } - case *types.Map: - var k types.MapKey + case *types.BoxedMap: + var k types.BoxedMapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.MapKey{Kind: types.KindF64, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -2891,53 +2891,53 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ var result types.Boxed var found bool switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: result, found = m.Get(key.I32()) if !found { result = m.Zero } - case *types.TypedMap[int64]: + case *types.Map[int64]: result, found = m.Get(i.unboxI64(key)) if !found { result = m.Zero } - case *types.TypedMap[float32]: + case *types.Map[float32]: result, found = m.Get(key.F32()) if !found { result = m.Zero } - case *types.TypedMap[float64]: + case *types.Map[float64]: result, found = m.Get(key.F64()) if !found { result = m.Zero } - case *types.Map: - var k types.MapKey + case *types.BoxedMap: + var k types.BoxedMapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.MapKey{Kind: types.KindF64, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -2977,58 +2977,58 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: old, ok := m.Set(key.I32(), value) if ok { i.releaseVal(old) } - case *types.TypedMap[int64]: + case *types.Map[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { i.releaseVal(old) } - case *types.TypedMap[float32]: + case *types.Map[float32]: old, ok := m.Set(key.F32(), value) if ok { i.releaseVal(old) } - case *types.TypedMap[float64]: + case *types.Map[float64]: old, ok := m.Set(key.F64(), value) if ok { i.releaseVal(old) } - case *types.Map: - var k types.MapKey - entry := types.MapEntry{Value: value} + case *types.BoxedMap: + var k types.BoxedMapKey + entry := types.BoxedMapEntry{Value: value} keyRef := 0 drop := false switch key.Kind() { case types.KindI32: bits := uint64(uint32(key.I32())) - k = types.MapKey{Kind: types.KindI32, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindI32, Bits: bits} entry.Key = types.BoxI32(int32(bits)) case types.KindI64: - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} entry.Key = types.BoxF32(math.Float32frombits(bits)) case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.MapKey{Kind: types.KindF64, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} entry.Key = types.BoxF64(math.Float64frombits(bits)) case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} entry.Key = key drop = true } @@ -3063,53 +3063,53 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: old, ok := m.Delete(key.I32()) if ok { i.releaseVal(old) } - case *types.TypedMap[int64]: + case *types.Map[int64]: old, ok := m.Delete(i.unboxI64(key)) if ok { i.releaseVal(old) } - case *types.TypedMap[float32]: + case *types.Map[float32]: old, ok := m.Delete(key.F32()) if ok { i.releaseVal(old) } - case *types.TypedMap[float64]: + case *types.Map[float64]: old, ok := m.Delete(key.F64()) if ok { i.releaseVal(old) } - case *types.Map: - var k types.MapKey + case *types.BoxedMap: + var k types.BoxedMapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.MapKey{Kind: types.KindF64, Bits: bits} + k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -3143,24 +3143,24 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.TypedMap[int32]: + case *types.Map[int32]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.TypedMap[int64]: + case *types.Map[int64]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.TypedMap[float32]: + case *types.Map[float32]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.TypedMap[float64]: + case *types.Map[float64]: m.Clear(func(value types.Boxed) { i.releaseVal(value) }) - case *types.Map: - m.Clear(func(entry types.MapEntry) { + case *types.BoxedMap: + m.Clear(func(entry types.BoxedMapEntry) { i.releaseVal(entry.Key) i.releaseVal(entry.Value) }) diff --git a/transform/cf.go b/transform/cf.go index 9c7db72..95cb810 100644 --- a/transform/cf.go +++ b/transform/cf.go @@ -433,12 +433,12 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { default: } case instr.STRING_ENCODE_UTF32: - prog.Constants = append(prog.Constants, types.TypedArray[int32](v0)) + prog.Constants = append(prog.Constants, types.Array[int32](v0)) ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue default: } - case types.TypedArray[int32]: + case types.Array[int32]: switch i1.Opcode() { case instr.STRING_NEW_UTF32: prog.Constants = append(prog.Constants, types.String(v0)) diff --git a/transform/cf_test.go b/transform/cf_test.go index f6c60e0..425924d 100644 --- a/transform/cf_test.go +++ b/transform/cf_test.go @@ -1705,7 +1705,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 2), }, - program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo"), types.String("foo")), + program.WithConstants(types.String("foo"), types.Array[int32]("foo"), types.String("foo")), ), }, { @@ -1849,7 +1849,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 1), }, - program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo")), + program.WithConstants(types.String("foo"), types.Array[int32]("foo")), ), }, } diff --git a/types/array.go b/types/array.go index 244d1f9..4acfaf5 100644 --- a/types/array.go +++ b/types/array.go @@ -5,9 +5,9 @@ import ( "strings" ) -type TypedArray[T int32 | int64 | float32 | float64] []T +type Array[T int32 | int64 | float32 | float64] []T -type Array struct { +type BoxedArray struct { Typ *ArrayType Elems []Boxed } @@ -24,24 +24,24 @@ var ( TypeF64Array = NewArrayType(TypeF64) ) -var _ Value = TypedArray[int32](nil) -var _ Value = TypedArray[int64](nil) -var _ Value = TypedArray[float32](nil) -var _ Value = TypedArray[float64](nil) -var _ Traceable = (*Array)(nil) +var _ Value = Array[int32](nil) +var _ Value = Array[int64](nil) +var _ Value = Array[float32](nil) +var _ Value = Array[float64](nil) +var _ Traceable = (*BoxedArray)(nil) var _ Type = (*ArrayType)(nil) -func NewArray(typ *ArrayType, elems ...Boxed) *Array { - return &Array{Typ: typ, Elems: elems} +func NewBoxedArray(typ *ArrayType, elems ...Boxed) *BoxedArray { + return &BoxedArray{Typ: typ, Elems: elems} } func NewArrayType(elem Type) *ArrayType { return &ArrayType{Elem: elem, ElemKind: elem.Kind()} } -func (a TypedArray[T]) Kind() Kind { return KindRef } +func (a Array[T]) Kind() Kind { return KindRef } -func (a TypedArray[T]) Type() Type { +func (a Array[T]) Type() Type { var zero T switch any(zero).(type) { case int32: @@ -55,17 +55,17 @@ func (a TypedArray[T]) Type() Type { } } -func (a TypedArray[T]) String() string { +func (a Array[T]) String() string { return formatSlice(a.Type(), len(a), func(i int) string { return formatElem(a[i]) }) } -func (a *Array) Kind() Kind { return KindRef } -func (a *Array) Type() Type { return a.Typ } -func (a *Array) String() string { +func (a *BoxedArray) Kind() Kind { return KindRef } +func (a *BoxedArray) Type() Type { return a.Typ } +func (a *BoxedArray) String() string { return formatSlice(a.Type(), len(a.Elems), func(i int) string { return a.Elems[i].String() }) } -func (a *Array) Refs() []Ref { +func (a *BoxedArray) Refs() []Ref { var refs []Ref for _, e := range a.Elems { if e.Kind() == KindRef { diff --git a/types/array_test.go b/types/array_test.go index 5b02037..6e3284f 100644 --- a/types/array_test.go +++ b/types/array_test.go @@ -7,26 +7,26 @@ import ( "github.com/stretchr/testify/require" ) -func TestArray_Kind(t *testing.T) { - require.Equal(t, KindRef, NewArray(NewArrayType(TypeRef)).Kind()) +func TestBoxedArray_Kind(t *testing.T) { + require.Equal(t, KindRef, NewBoxedArray(NewArrayType(TypeRef)).Kind()) } -func TestArray_Type(t *testing.T) { +func TestBoxedArray_Type(t *testing.T) { typ := NewArrayType(TypeRef) - require.Equal(t, typ, NewArray(typ).Type()) + require.Equal(t, typ, NewBoxedArray(typ).Type()) } -func TestArray_String(t *testing.T) { - a := NewArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)) +func TestBoxedArray_String(t *testing.T) { + a := NewBoxedArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)) require.Equal(t, "[]ref{1, 2, 3}", a.String()) } -func TestTypedArray_Kind(t *testing.T) { +func TestArray_Kind(t *testing.T) { tests := []Value{ - TypedArray[int32]{}, - TypedArray[int64]{}, - TypedArray[float32]{}, - TypedArray[float64]{}, + Array[int32]{}, + Array[int64]{}, + Array[float32]{}, + Array[float64]{}, } for _, val := range tests { t.Run(fmt.Sprint(val), func(t *testing.T) { @@ -35,15 +35,15 @@ func TestTypedArray_Kind(t *testing.T) { } } -func TestTypedArray_Type(t *testing.T) { +func TestArray_Type(t *testing.T) { tests := []struct { val Value typ Type }{ - {val: TypedArray[int32]{}, typ: TypeI32Array}, - {val: TypedArray[int64]{}, typ: TypeI64Array}, - {val: TypedArray[float32]{}, typ: TypeF32Array}, - {val: TypedArray[float64]{}, typ: TypeF64Array}, + {val: Array[int32]{}, typ: TypeI32Array}, + {val: Array[int64]{}, typ: TypeI64Array}, + {val: Array[float32]{}, typ: TypeF32Array}, + {val: Array[float64]{}, typ: TypeF64Array}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { @@ -52,15 +52,15 @@ func TestTypedArray_Type(t *testing.T) { } } -func TestTypedArray_String(t *testing.T) { +func TestArray_String(t *testing.T) { tests := []struct { val Value str string }{ - {val: TypedArray[int32]{1, 2, 3}, str: "[]i32{1, 2, 3}"}, - {val: TypedArray[int64]{1, 2, 3}, str: "[]i64{1, 2, 3}"}, - {val: TypedArray[float32]{1, 2, 3}, str: "[]f32{1, 2, 3}"}, - {val: TypedArray[float64]{1, 2, 3}, str: "[]f64{1, 2, 3}"}, + {val: Array[int32]{1, 2, 3}, str: "[]i32{1, 2, 3}"}, + {val: Array[int64]{1, 2, 3}, str: "[]i64{1, 2, 3}"}, + {val: Array[float32]{1, 2, 3}, str: "[]f32{1, 2, 3}"}, + {val: Array[float64]{1, 2, 3}, str: "[]f64{1, 2, 3}"}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { @@ -69,9 +69,9 @@ func TestTypedArray_String(t *testing.T) { } } -func TestArray_Refs(t *testing.T) { +func TestBoxedArray_Refs(t *testing.T) { t.Run("primitive elements", func(t *testing.T) { - a := NewArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) + a := NewBoxedArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) require.Empty(t, a.Refs()) var refs []Ref @@ -83,7 +83,7 @@ func TestArray_Refs(t *testing.T) { }) t.Run("reference elements", func(t *testing.T) { - a := NewArray(NewArrayType(TypeRef), BoxRef(1), BoxI32(2), BoxRef(3)) + a := NewBoxedArray(NewArrayType(TypeRef), BoxRef(1), BoxI32(2), BoxRef(3)) require.Equal(t, []Ref{1, 3}, a.Refs()) }) @@ -91,7 +91,7 @@ func TestArray_Refs(t *testing.T) { func BenchmarkArray_Refs(b *testing.B) { b.Run("no refs", func(b *testing.B) { - a := NewArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) + a := NewBoxedArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) var refs []Ref b.ReportAllocs() @@ -104,7 +104,7 @@ func BenchmarkArray_Refs(b *testing.B) { }) b.Run("child refs", func(b *testing.B) { - a := NewArray(NewArrayType(TypeRef), BoxRef(1), BoxRef(2)) + a := NewBoxedArray(NewArrayType(TypeRef), BoxRef(1), BoxRef(2)) var refs []Ref b.ReportAllocs() diff --git a/types/map.go b/types/map.go index 7045ae8..cd2e1bd 100644 --- a/types/map.go +++ b/types/map.go @@ -7,24 +7,24 @@ import ( "strings" ) -type TypedMap[K comparable] struct { +type Map[K comparable] struct { Typ *MapType Zero Boxed entries map[K]Boxed } -type Map struct { +type BoxedMap struct { Typ *MapType Zero Boxed - entries map[MapKey]MapEntry + entries map[BoxedMapKey]BoxedMapEntry } -type MapKey struct { +type BoxedMapKey struct { Kind Kind Bits uint64 } -type MapEntry struct { +type BoxedMapEntry struct { Key Boxed Value Boxed } @@ -39,42 +39,42 @@ type MapType struct { } var ( - _ Traceable = (*Map)(nil) - _ Traceable = (*TypedMap[int32])(nil) - _ Traceable = (*TypedMap[int64])(nil) - _ Traceable = (*TypedMap[float32])(nil) - _ Traceable = (*TypedMap[float64])(nil) + _ Traceable = (*BoxedMap)(nil) + _ Traceable = (*Map[int32])(nil) + _ Traceable = (*Map[int64])(nil) + _ Traceable = (*Map[float32])(nil) + _ Traceable = (*Map[float64])(nil) _ Type = (*MapType)(nil) ) -func NewTypedMap[K comparable](typ *MapType, capacity int) *TypedMap[K] { - return &TypedMap[K]{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[K]Boxed, capacity)} +func NewMap[K comparable](typ *MapType, capacity int) *Map[K] { + return &Map[K]{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[K]Boxed, capacity)} } -func NewMap(typ *MapType) *Map { - return NewMapWithCapacity(typ, 0) +func NewBoxedMap(typ *MapType) *BoxedMap { + return NewBoxedMapWithCapacity(typ, 0) } -func NewMapWithCapacity(typ *MapType, capacity int) *Map { - return &Map{ +func NewBoxedMapWithCapacity(typ *MapType, capacity int) *BoxedMap { + return &BoxedMap{ Typ: typ, Zero: Zero(typ.ElemKind), - entries: make(map[MapKey]MapEntry, capacity), + entries: make(map[BoxedMapKey]BoxedMapEntry, capacity), } } func NewMapForType(typ *MapType, capacity int) Value { switch typ.KeyKind { case KindI32: - return NewTypedMap[int32](typ, capacity) + return NewMap[int32](typ, capacity) case KindI64: - return NewTypedMap[int64](typ, capacity) + return NewMap[int64](typ, capacity) case KindF32: - return NewTypedMap[float32](typ, capacity) + return NewMap[float32](typ, capacity) case KindF64: - return NewTypedMap[float64](typ, capacity) + return NewMap[float64](typ, capacity) default: - return NewMapWithCapacity(typ, capacity) + return NewBoxedMapWithCapacity(typ, capacity) } } @@ -89,24 +89,24 @@ func NewMapType(key Type, elem Type) *MapType { } } -func (m *TypedMap[K]) Kind() Kind { return KindRef } +func (m *Map[K]) Kind() Kind { return KindRef } -func (m *TypedMap[K]) Type() Type { return m.Typ } +func (m *Map[K]) Type() Type { return m.Typ } -func (m *TypedMap[K]) Len() int { return len(m.entries) } +func (m *Map[K]) Len() int { return len(m.entries) } -func (m *TypedMap[K]) Get(key K) (Boxed, bool) { +func (m *Map[K]) Get(key K) (Boxed, bool) { value, ok := m.entries[key] return value, ok } -func (m *TypedMap[K]) Set(key K, value Boxed) (Boxed, bool) { +func (m *Map[K]) Set(key K, value Boxed) (Boxed, bool) { old, ok := m.entries[key] m.entries[key] = value return old, ok } -func (m *TypedMap[K]) Delete(key K) (Boxed, bool) { +func (m *Map[K]) Delete(key K) (Boxed, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -114,20 +114,20 @@ func (m *TypedMap[K]) Delete(key K) (Boxed, bool) { return old, ok } -func (m *TypedMap[K]) Range(fn func(K, Boxed)) { +func (m *Map[K]) Range(fn func(K, Boxed)) { for key, value := range m.entries { fn(key, value) } } -func (m *TypedMap[K]) Clear(fn func(Boxed)) { +func (m *Map[K]) Clear(fn func(Boxed)) { for key, value := range m.entries { fn(value) delete(m.entries, key) } } -func (m *TypedMap[K]) String() string { +func (m *Map[K]) String() string { parts := make([]string, 0, m.Len()) m.Range(func(key K, value Boxed) { parts = append(parts, fmt.Sprintf("%s: %s", boxKey(any(key)), value.String())) @@ -136,7 +136,7 @@ func (m *TypedMap[K]) String() string { return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *TypedMap[K]) Refs() []Ref { +func (m *Map[K]) Refs() []Ref { if !m.Typ.TraceValues { return nil } @@ -152,24 +152,24 @@ func (m *TypedMap[K]) Refs() []Ref { return refs } -func (m *Map) Kind() Kind { return KindRef } +func (m *BoxedMap) Kind() Kind { return KindRef } -func (m *Map) Type() Type { return m.Typ } +func (m *BoxedMap) Type() Type { return m.Typ } -func (m *Map) Len() int { return len(m.entries) } +func (m *BoxedMap) Len() int { return len(m.entries) } -func (m *Map) Get(key MapKey) (MapEntry, bool) { +func (m *BoxedMap) Get(key BoxedMapKey) (BoxedMapEntry, bool) { entry, ok := m.entries[key] return entry, ok } -func (m *Map) Set(key MapKey, entry MapEntry) (MapEntry, bool) { +func (m *BoxedMap) Set(key BoxedMapKey, entry BoxedMapEntry) (BoxedMapEntry, bool) { old, ok := m.entries[key] m.entries[key] = entry return old, ok } -func (m *Map) Delete(key MapKey) (MapEntry, bool) { +func (m *BoxedMap) Delete(key BoxedMapKey) (BoxedMapEntry, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -177,29 +177,29 @@ func (m *Map) Delete(key MapKey) (MapEntry, bool) { return old, ok } -func (m *Map) Range(fn func(MapKey, MapEntry)) { +func (m *BoxedMap) Range(fn func(BoxedMapKey, BoxedMapEntry)) { for key, entry := range m.entries { fn(key, entry) } } -func (m *Map) Clear(fn func(MapEntry)) { +func (m *BoxedMap) Clear(fn func(BoxedMapEntry)) { for key, entry := range m.entries { fn(entry) delete(m.entries, key) } } -func (m *Map) String() string { +func (m *BoxedMap) String() string { parts := make([]string, 0, m.Len()) - m.Range(func(key MapKey, entry MapEntry) { + m.Range(func(key BoxedMapKey, entry BoxedMapEntry) { parts = append(parts, fmt.Sprintf("%s: %s", key.String(), entry.Value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *Map) Refs() []Ref { +func (m *BoxedMap) Refs() []Ref { traceKeys := m.Typ.TraceKeys traceValues := m.Typ.TraceValues if !traceKeys && !traceValues { @@ -223,7 +223,7 @@ func (m *Map) Refs() []Ref { return refs } -func (k MapKey) String() string { +func (k BoxedMapKey) String() string { switch k.Kind { case KindI32: return BoxI32(int32(k.Bits)).String() diff --git a/types/map_test.go b/types/map_test.go index f97f3b5..c89cf65 100644 --- a/types/map_test.go +++ b/types/map_test.go @@ -7,20 +7,20 @@ import ( "github.com/stretchr/testify/require" ) -func TestMap_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMap(NewMapType(TypeI32, TypeI32)).Kind()) +func TestBoxedMap_Kind(t *testing.T) { + require.Equal(t, KindRef, NewBoxedMap(NewMapType(TypeI32, TypeI32)).Kind()) } -func TestMap_Type(t *testing.T) { +func TestBoxedMap_Type(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - require.Equal(t, typ, NewMap(typ).Type()) + require.Equal(t, typ, NewBoxedMap(typ).Type()) } -func TestMap_String(t *testing.T) { +func TestBoxedMap_String(t *testing.T) { t.Run("i32 key", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - m := NewMap(typ) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{ + m := NewBoxedMap(typ) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{ Key: BoxI32(1), Value: BoxI32(2), }) @@ -29,8 +29,8 @@ func TestMap_String(t *testing.T) { t.Run("empty string key", func(t *testing.T) { typ := NewMapType(TypeString, TypeI32) - m := NewMap(typ) - m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{ + m := NewBoxedMap(typ) + m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{ Key: BoxRef(1), Value: BoxI32(2), }) @@ -39,89 +39,89 @@ func TestMap_String(t *testing.T) { t.Run("deterministic", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - m := NewMap(typ) - m.Set(MapKey{Kind: KindI32, Bits: 2}, MapEntry{Key: BoxI32(2), Value: BoxI32(20)}) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(10)}) + m := NewBoxedMap(typ) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 2}, BoxedMapEntry{Key: BoxI32(2), Value: BoxI32(20)}) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(10)}) require.Equal(t, "map[i32]i32{1: 10, 2: 20}", m.String()) }) } -func TestMap_Len(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI32)) +func TestBoxedMap_Len(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) require.Equal(t, 0, m.Len()) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) require.Equal(t, 1, m.Len()) } -func TestMap_Get(t *testing.T) { - m := NewMap(NewMapType(TypeString, TypeI32)) - m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxI32(2)}) +func TestBoxedMap_Get(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeString, TypeI32)) + m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxI32(2)}) - entry, ok := m.Get(MapKey{Kind: KindRef, Bits: 1}) + entry, ok := m.Get(BoxedMapKey{Kind: KindRef, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(2), entry.Value) - _, ok = m.Get(MapKey{Kind: KindRef, Bits: 2}) + _, ok = m.Get(BoxedMapKey{Kind: KindRef, Bits: 2}) require.False(t, ok) } -func TestMap_Set(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI32)) +func TestBoxedMap_Set(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) - old, ok := m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + old, ok := m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) require.False(t, ok) - require.Equal(t, MapEntry{}, old) + require.Equal(t, BoxedMapEntry{}, old) - old, ok = m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(3)}) + old, ok = m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(3)}) require.True(t, ok) require.Equal(t, BoxI32(2), old.Value) - entry, ok := m.Get(MapKey{Kind: KindI32, Bits: 1}) + entry, ok := m.Get(BoxedMapKey{Kind: KindI32, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(3), entry.Value) } -func TestMap_Delete(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI32)) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestBoxedMap_Delete(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - old, ok := m.Delete(MapKey{Kind: KindI32, Bits: 1}) + old, ok := m.Delete(BoxedMapKey{Kind: KindI32, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(2), old.Value) require.Equal(t, 0, m.Len()) - _, ok = m.Delete(MapKey{Kind: KindI32, Bits: 1}) + _, ok = m.Delete(BoxedMapKey{Kind: KindI32, Bits: 1}) require.False(t, ok) } -func TestMap_Range(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI32)) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestBoxedMap_Range(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - var keys []MapKey - m.Range(func(key MapKey, _ MapEntry) { + var keys []BoxedMapKey + m.Range(func(key BoxedMapKey, _ BoxedMapEntry) { keys = append(keys, key) }) - require.Equal(t, []MapKey{{Kind: KindI32, Bits: 1}}, keys) + require.Equal(t, []BoxedMapKey{{Kind: KindI32, Bits: 1}}, keys) } -func TestMap_Clear(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI32)) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestBoxedMap_Clear(t *testing.T) { + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - var entries []MapEntry - m.Clear(func(entry MapEntry) { + var entries []BoxedMapEntry + m.Clear(func(entry BoxedMapEntry) { entries = append(entries, entry) }) - require.Equal(t, []MapEntry{{Key: BoxI32(1), Value: BoxI32(2)}}, entries) + require.Equal(t, []BoxedMapEntry{{Key: BoxI32(1), Value: BoxI32(2)}}, entries) require.Equal(t, 0, m.Len()) } -func TestMap_Refs(t *testing.T) { +func TestBoxedMap_Refs(t *testing.T) { t.Run("inline i64 value", func(t *testing.T) { - m := NewMap(NewMapType(TypeI32, TypeI64)) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI64(2)}) + m := NewBoxedMap(NewMapType(TypeI32, TypeI64)) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI64(2)}) var refs []Ref allocs := testing.AllocsPerRun(100, func() { @@ -133,8 +133,8 @@ func TestMap_Refs(t *testing.T) { t.Run("ref key and value", func(t *testing.T) { typ := NewMapType(TypeRef, TypeRef) - m := NewMap(typ) - m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{ + m := NewBoxedMap(typ) + m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{ Key: BoxRef(1), Value: BoxRef(2), }) @@ -143,8 +143,8 @@ func TestMap_Refs(t *testing.T) { t.Run("spilled i64 value", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI64) - m := NewMap(typ) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{ + m := NewBoxedMap(typ) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{ Key: BoxI32(1), Value: BoxRef(2), }) @@ -159,13 +159,13 @@ func TestNewMapForType(t *testing.T) { typ *MapType want any }{ - {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*TypedMap[int32])(nil)}, - {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*TypedMap[int64])(nil)}, - {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*TypedMap[float32])(nil)}, - {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*TypedMap[float64])(nil)}, - {name: "ref", typ: NewMapType(TypeRef, TypeI32), want: (*Map)(nil)}, - {name: "string", typ: NewMapType(TypeString, TypeI32), want: (*Map)(nil)}, - {name: "struct", typ: NewMapType(structType, TypeI32), want: (*Map)(nil)}, + {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*Map[int32])(nil)}, + {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*Map[int64])(nil)}, + {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*Map[float32])(nil)}, + {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*Map[float64])(nil)}, + {name: "ref", typ: NewMapType(TypeRef, TypeI32), want: (*BoxedMap)(nil)}, + {name: "string", typ: NewMapType(TypeString, TypeI32), want: (*BoxedMap)(nil)}, + {name: "struct", typ: NewMapType(structType, TypeI32), want: (*BoxedMap)(nil)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -174,25 +174,25 @@ func TestNewMapForType(t *testing.T) { } } -func TestTypedMap_Kind(t *testing.T) { - require.Equal(t, KindRef, NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0).Kind()) +func TestMap_Kind(t *testing.T) { + require.Equal(t, KindRef, NewMap[int32](NewMapType(TypeI32, TypeI32), 0).Kind()) } -func TestTypedMap_Type(t *testing.T) { +func TestMap_Type(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - require.Equal(t, typ, NewTypedMap[int32](typ, 0).Type()) + require.Equal(t, typ, NewMap[int32](typ, 0).Type()) } -func TestTypedMap_Len(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestMap_Len(t *testing.T) { + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) require.Equal(t, 0, m.Len()) m.Set(1, BoxI32(2)) require.Equal(t, 1, m.Len()) } -func TestTypedMap_Get(t *testing.T) { +func TestMap_Get(t *testing.T) { t.Run("i32", func(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) got, ok := m.Get(1) require.True(t, ok) @@ -202,7 +202,7 @@ func TestTypedMap_Get(t *testing.T) { }) t.Run("i64 wide key", func(t *testing.T) { - m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) + m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1<<50, BoxI32(2)) got, ok := m.Get(1 << 50) require.True(t, ok) @@ -212,7 +212,7 @@ func TestTypedMap_Get(t *testing.T) { }) t.Run("f64", func(t *testing.T) { - m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(1.5, BoxI32(2)) got, ok := m.Get(1.5) require.True(t, ok) @@ -220,9 +220,9 @@ func TestTypedMap_Get(t *testing.T) { }) } -func TestTypedMap_Set(t *testing.T) { +func TestMap_Set(t *testing.T) { t.Run("overwrite returns old", func(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) old, ok := m.Set(1, BoxI32(2)) require.False(t, ok) require.Equal(t, Boxed(0), old) @@ -233,7 +233,7 @@ func TestTypedMap_Set(t *testing.T) { }) t.Run("f32 -0.0 collapses to +0.0", func(t *testing.T) { - m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) + m := NewMap[float32](NewMapType(TypeF32, TypeI32), 0) m.Set(float32(math.Copysign(0, -1)), BoxI32(1)) m.Set(0, BoxI32(2)) @@ -244,7 +244,7 @@ func TestTypedMap_Set(t *testing.T) { }) t.Run("f64 -0.0 collapses to +0.0", func(t *testing.T) { - m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(math.Copysign(0, -1), BoxI32(1)) m.Set(0, BoxI32(2)) @@ -255,15 +255,15 @@ func TestTypedMap_Set(t *testing.T) { }) t.Run("f64 NaN is not retrievable", func(t *testing.T) { - m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(math.NaN(), BoxI32(1)) _, ok := m.Get(math.NaN()) require.False(t, ok) }) } -func TestTypedMap_Delete(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestMap_Delete(t *testing.T) { + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) old, ok := m.Delete(1) @@ -275,8 +275,8 @@ func TestTypedMap_Delete(t *testing.T) { require.False(t, ok) } -func TestTypedMap_Range(t *testing.T) { - m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) +func TestMap_Range(t *testing.T) { + m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1, BoxI32(2)) var keys []int64 @@ -286,8 +286,8 @@ func TestTypedMap_Range(t *testing.T) { require.Equal(t, []int64{1}, keys) } -func TestTypedMap_Clear(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestMap_Clear(t *testing.T) { + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) var values []Boxed @@ -298,35 +298,35 @@ func TestTypedMap_Clear(t *testing.T) { require.Equal(t, 0, m.Len()) } -func TestTypedMap_String(t *testing.T) { +func TestMap_String(t *testing.T) { t.Run("i32", func(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[i32]i32{1: 2}", m.String()) }) t.Run("i64", func(t *testing.T) { - m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) + m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[i64]i32{1: 2}", m.String()) }) t.Run("f32", func(t *testing.T) { - m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) + m := NewMap[float32](NewMapType(TypeF32, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[f32]i32{1: 2}", m.String()) }) t.Run("f64", func(t *testing.T) { - m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[f64]i32{1: 2}", m.String()) }) } -func TestTypedMap_Refs(t *testing.T) { +func TestMap_Refs(t *testing.T) { t.Run("inline i64 value", func(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref @@ -338,7 +338,7 @@ func TestTypedMap_Refs(t *testing.T) { }) t.Run("ref value", func(t *testing.T) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeString), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeString), 0) m.Set(1, BoxRef(2)) require.Equal(t, []Ref{2}, m.Refs()) }) @@ -381,8 +381,8 @@ func TestMapType_Equals(t *testing.T) { require.False(t, NewMapType(TypeI32, TypeI32).Equals(NewMapType(TypeI32, TypeI64))) } -func BenchmarkTypedMap_Get(b *testing.B) { - m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) +func BenchmarkMap_Get(b *testing.B) { + m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(42, BoxI32(7)) b.ReportAllocs() @@ -394,10 +394,10 @@ func BenchmarkTypedMap_Get(b *testing.B) { require.True(b, ok) } -func BenchmarkMapStringGet_Interned(b *testing.B) { - m := NewMap(NewMapType(TypeString, TypeI32)) - m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxI32(7)}) - key := MapKey{Kind: KindRef, Bits: 1} +func BenchmarkBoxedMapStringGet_Interned(b *testing.B) { + m := NewBoxedMap(NewMapType(TypeString, TypeI32)) + m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxI32(7)}) + key := BoxedMapKey{Kind: KindRef, Bits: 1} b.ReportAllocs() var ok bool @@ -408,10 +408,10 @@ func BenchmarkMapStringGet_Interned(b *testing.B) { require.True(b, ok) } -func BenchmarkMap_Refs(b *testing.B) { +func BenchmarkBoxedMap_Refs(b *testing.B) { b.Run("no refs", func(b *testing.B) { - m := NewMap(NewMapType(TypeI32, TypeI32)) - m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) + m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) var refs []Ref b.ReportAllocs() @@ -424,7 +424,7 @@ func BenchmarkMap_Refs(b *testing.B) { }) b.Run("inline i64", func(b *testing.B) { - m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) + m := NewMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref @@ -438,8 +438,8 @@ func BenchmarkMap_Refs(b *testing.B) { }) b.Run("child refs", func(b *testing.B) { - m := NewMap(NewMapType(TypeRef, TypeRef)) - m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxRef(2)}) + m := NewBoxedMap(NewMapType(TypeRef, TypeRef)) + m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxRef(2)}) var refs []Ref b.ReportAllocs() diff --git a/types/value_test.go b/types/value_test.go index 361a4e9..8de3c14 100644 --- a/types/value_test.go +++ b/types/value_test.go @@ -37,7 +37,7 @@ func TestIsNull(t *testing.T) { {Ref(1), false}, {BoxRef(1), false}, {I32(0), false}, - {TypedArray[int32]{0}, false}, + {Array[int32]{0}, false}, {nil, false}, } for _, tt := range tests { From a2a71964c2b6ecd2cac71dbe9065241c08f4e8de Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 15:59:23 +0000 Subject: [PATCH 17/18] refactor: clearer names in map formatter and arm64 encode helpers - types: boxKey -> formatKey. It returns the key's formatted string (not a Boxed), and now reads parallel to formatElem/formatSlice. - asm/arm64: name the opcode-constant parameter `op` consistently across the single-opcode encode helpers (was `base`), and bind the memory base register to `base`/`dst`/`src` from the decoder instead of the cramped `b`. Pure renames; host run of encoder_test confirms identical output. https://claude.ai/code/session_01HaHURpzxVBJeKpvFFrfs32 --- asm/arm64/encoder.go | 24 ++++++++++++------------ types/map.go | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/asm/arm64/encoder.go b/asm/arm64/encoder.go index 25ac948..a1e7180 100644 --- a/asm/arm64/encoder.go +++ b/asm/arm64/encoder.go @@ -1175,51 +1175,51 @@ func (e *Encoder) encodeMovImmediate(op32, op64 uint32, inst asm.Instruction) ([ // encodeCompareReg emits a register-form compare/test (CMP/CMN/TST → SUBS/ADDS/ // ANDS with XZR destination). -func (e *Encoder) encodeCompareReg(base uint32, inst asm.Instruction) ([]byte, error) { +func (e *Encoder) encodeCompareReg(op uint32, inst asm.Instruction) ([]byte, error) { n, m, err := e.decodeCmp(inst) if err != nil { return nil, err } - b, err := intBase(base, n, m) + base, err := intBase(op, n, m) if err != nil { return nil, err } - return enc(b | reg(m)<<16 | reg(n)<<5), nil + return enc(base | reg(m)<<16 | reg(n)<<5), nil } // encodeCompareImm emits an immediate-form compare (CMPI/CMNI). -func (e *Encoder) encodeCompareImm(base uint32, inst asm.Instruction) ([]byte, error) { +func (e *Encoder) encodeCompareImm(op uint32, inst asm.Instruction) ([]byte, error) { n, imm, err := e.decodeCmpImm(inst) if err != nil { return nil, err } - b, err := intBase(base, n) + base, err := intBase(op, n) if err != nil { return nil, err } - return enc(b | (uint32(imm)&0xFFF)<<10 | reg(n)<<5), nil + return enc(base | (uint32(imm)&0xFFF)<<10 | reg(n)<<5), nil } // encodeLoad emits an unsigned-offset load, scaling the byte offset by the // access size. -func (e *Encoder) encodeLoad(base uint32, scale int64, inst asm.Instruction) ([]byte, error) { - d, b, offset, err := e.decodeMemOp(inst) +func (e *Encoder) encodeLoad(op uint32, scale int64, inst asm.Instruction) ([]byte, error) { + dst, base, offset, err := e.decodeMemOp(inst) if err != nil { return nil, err } pimm := uint32(offset/scale) & 0xFFF - return enc(base | pimm<<10 | reg(b)<<5 | reg(d)), nil + return enc(op | pimm<<10 | reg(base)<<5 | reg(dst)), nil } // encodeStore emits an unsigned-offset store, scaling the byte offset by the // access size. -func (e *Encoder) encodeStore(base uint32, scale int64, inst asm.Instruction) ([]byte, error) { - src, b, offset, err := e.decodeStrOp(inst) +func (e *Encoder) encodeStore(op uint32, scale int64, inst asm.Instruction) ([]byte, error) { + src, base, offset, err := e.decodeStrOp(inst) if err != nil { return nil, err } pimm := uint32(offset/scale) & 0xFFF - return enc(base | pimm<<10 | reg(b)<<5 | reg(src)), nil + return enc(op | pimm<<10 | reg(base)<<5 | reg(src)), nil } // encodeFloatBinary emits a 3-register scalar float op (FADD/FSUB/FMUL/FDIV), diff --git a/types/map.go b/types/map.go index cd2e1bd..caaef8e 100644 --- a/types/map.go +++ b/types/map.go @@ -130,7 +130,7 @@ func (m *Map[K]) Clear(fn func(Boxed)) { func (m *Map[K]) String() string { parts := make([]string, 0, m.Len()) m.Range(func(key K, value Boxed) { - parts = append(parts, fmt.Sprintf("%s: %s", boxKey(any(key)), value.String())) + parts = append(parts, fmt.Sprintf("%s: %s", formatKey(any(key)), value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) @@ -261,8 +261,8 @@ func (t *MapType) Equals(other Type) bool { return t.Key.Equals(o.Key) && t.Elem.Equals(o.Elem) } -// boxKey renders a native map key through its boxed value's String form. -func boxKey(k any) string { +// formatKey renders a native map key through its boxed value's String form. +func formatKey(k any) string { switch v := k.(type) { case int32: return I32(v).String() From 0febe2839a0651cfa11328769757a7cfaf9dd5c1 Mon Sep 17 00:00:00 2001 From: siyul-park Date: Sat, 30 May 2026 07:32:17 +0900 Subject: [PATCH 18/18] refactor: array and map types to use TypedArray and TypedMap - Renamed `Array` to `TypedArray` and updated its usage across the codebase. - Updated the `BoxedArray` type to a more generic `Array` type. - Refactored map types to use `TypedMap` instead of `Map`. - Adjusted related functions and tests to reflect the new type names and structures. - Ensured all references to the old types were replaced with the new ones in the code and tests. --- interp/interp.go | 6 +- interp/interp_test.go | 84 ++++++------ interp/marshal.go | 62 ++++----- interp/threaded.go | 308 +++++++++++++++++++++--------------------- transform/cf.go | 4 +- transform/cf_test.go | 4 +- types/array.go | 32 ++--- types/array_test.go | 54 ++++---- types/map.go | 88 ++++++------ types/map_test.go | 196 +++++++++++++-------------- types/value_test.go | 2 +- 11 files changed, 419 insertions(+), 421 deletions(-) diff --git a/interp/interp.go b/interp/interp.go index e41f9b7..fe0c7ca 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -702,15 +702,13 @@ func (i *Interpreter) keep(val types.Value) int { return i.alloc(val) } -// retainVal increments v's refcount when it holds a heap reference. -func (i *Interpreter) retainVal(v types.Boxed) { +func (i *Interpreter) retainBox(v types.Boxed) { if v.Kind() == types.KindRef { i.retain(v.Ref()) } } -// releaseVal decrements v's refcount when it holds a heap reference. -func (i *Interpreter) releaseVal(v types.Boxed) { +func (i *Interpreter) releaseBox(v types.Boxed) { if v.Kind() == types.KindRef { i.release(v.Ref()) } diff --git a/interp/interp_test.go b/interp/interp_test.go index fbe86df..4a16791 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -1464,7 +1464,7 @@ var tests = []test{ }, program.WithConstants(types.String("foo")), ), - values: []types.Value{types.Array[int32]("foo")}, + values: []types.Value{types.TypedArray[int32]("foo")}, }, // --- array: ARRAY_NEW, ARRAY_NEW_DEFAULT, ARRAY_GET, ARRAY_SET, ARRAY_FILL --- { @@ -1476,7 +1476,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{types.Array[int32]{1}}, + values: []types.Value{types.TypedArray[int32]{1}}, }, { program: program.New( @@ -1487,7 +1487,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{types.Array[int64]{1}}, + values: []types.Value{types.TypedArray[int64]{1}}, }, { program: program.New( @@ -1498,7 +1498,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{types.Array[float32]{42}}, + values: []types.Value{types.TypedArray[float32]{42}}, }, { program: program.New( @@ -1509,7 +1509,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{types.Array[float64]{42}}, + values: []types.Value{types.TypedArray[float64]{42}}, }, { program: program.New( @@ -1520,7 +1520,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeRef)), ), - values: []types.Value{types.NewBoxedArray(types.NewArrayType(types.TypeRef), types.BoxI32(1))}, + values: []types.Value{types.NewArray(types.NewArrayType(types.TypeRef), types.BoxI32(1))}, }, { program: program.New( @@ -1530,7 +1530,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI32)), ), - values: []types.Value{make(types.Array[int32], 1)}, + values: []types.Value{make(types.TypedArray[int32], 1)}, }, { program: program.New( @@ -1540,7 +1540,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeI64)), ), - values: []types.Value{make(types.Array[int64], 1)}, + values: []types.Value{make(types.TypedArray[int64], 1)}, }, { program: program.New( @@ -1550,7 +1550,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF32)), ), - values: []types.Value{make(types.Array[float32], 1)}, + values: []types.Value{make(types.TypedArray[float32], 1)}, }, { program: program.New( @@ -1560,7 +1560,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeF64)), ), - values: []types.Value{make(types.Array[float64], 1)}, + values: []types.Value{make(types.TypedArray[float64], 1)}, }, { program: program.New( @@ -1570,7 +1570,7 @@ var tests = []test{ }, program.WithTypes(types.NewArrayType(types.TypeRef)), ), - values: []types.Value{&types.BoxedArray{Typ: types.NewArrayType(types.TypeRef), Elems: make([]types.Boxed, 1)}}, + values: []types.Value{&types.Array{Typ: types.NewArrayType(types.TypeRef), Elems: make([]types.Boxed, 1)}}, }, { program: program.New( @@ -2479,7 +2479,7 @@ func TestInterpreter_Alloc(t *testing.T) { i := New(program.New(nil), WithHeap(2)) defer i.Close() - array := types.NewBoxedArray(types.NewArrayType(types.TypeRef)) + array := types.NewArray(types.NewArrayType(types.TypeRef)) addr, err := i.Alloc(array) require.NoError(t, err) array.Elems = append(array.Elems, types.BoxRef(addr)) @@ -2885,7 +2885,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.Array[int32]{10, 20, 30}), + program.WithConstants(types.TypedArray[int32]{10, 20, 30}), ), WithTick(1), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -2901,7 +2901,7 @@ func TestInterpreter_WithThreshold(t *testing.T) { instr.New(instr.I32_CONST, 1), instr.New(instr.ARRAY_GET), }, - program.WithConstants(types.Array[int32]{10, 20, 30}), + program.WithConstants(types.TypedArray[int32]{10, 20, 30}), ), WithThreshold(-1)) defer i.Close() require.NoError(t, i.Run(context.Background())) @@ -3817,19 +3817,19 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]int32{1, 2}) require.NoError(t, err) - require.Equal(t, types.Array[int32]{1, 2}, got) + require.Equal(t, types.TypedArray[int32]{1, 2}, got) got, err = i.Marshal([]uint32{math.MaxUint32}) require.NoError(t, err) - require.Equal(t, types.Array[int32]{-1}, got) + require.Equal(t, types.TypedArray[int32]{-1}, got) got, err = i.Marshal([]int{1, 2}) require.NoError(t, err) - require.Equal(t, types.Array[int64]{1, 2}, got) + require.Equal(t, types.TypedArray[int64]{1, 2}, got) got, err = i.Marshal([]uint64{math.MaxUint64}) require.NoError(t, err) - require.Equal(t, types.Array[int64]{-1}, got) + require.Equal(t, types.TypedArray[int64]{-1}, got) }) t.Run("reference slice", func(t *testing.T) { @@ -3839,7 +3839,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]string{"a", "b"}) require.NoError(t, err) - arr, ok := got.(*types.BoxedArray) + arr, ok := got.(*types.Array) require.True(t, ok) require.True(t, arr.Typ.Elem.Equals(types.TypeString)) require.Len(t, arr.Elems, 2) @@ -3856,7 +3856,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]string{"a", "b"}) require.NoError(t, err) - arr, ok := got.(*types.BoxedArray) + arr, ok := got.(*types.Array) require.True(t, ok) require.Len(t, arr.Elems, 2) @@ -3876,19 +3876,19 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]int32{"a": 1}) require.NoError(t, err) - m, ok := got.(*types.BoxedMap) + m, ok := got.(*types.Map) require.True(t, ok) require.True(t, m.Typ.Key.Equals(types.TypeString)) require.True(t, m.Typ.Elem.Equals(types.TypeI32)) keyRef := types.Boxed(0) - m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { + m.Range(func(_ types.MapKey, entry types.MapEntry) { keyRef = entry.Key }) require.Equal(t, types.KindRef, keyRef.Kind()) key, err := i.Load(keyRef.Ref()) require.NoError(t, err) require.Equal(t, types.String("a"), key) - entry, ok := m.Get(types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) + entry, ok := m.Get(types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) require.True(t, ok) require.Equal(t, types.BoxI32(1), entry.Value) }) @@ -3899,7 +3899,7 @@ func TestInterpreter_Marshal(t *testing.T) { i32, err := i.Marshal(map[int32]int32{1: 2}) require.NoError(t, err) - mI32, ok := i32.(*types.Map[int32]) + mI32, ok := i32.(*types.TypedMap[int32]) require.True(t, ok) gotI32, ok := mI32.Get(1) require.True(t, ok) @@ -3907,7 +3907,7 @@ func TestInterpreter_Marshal(t *testing.T) { i64, err := i.Marshal(map[int64]string{1: "a"}) require.NoError(t, err) - mI64, ok := i64.(*types.Map[int64]) + mI64, ok := i64.(*types.TypedMap[int64]) require.True(t, ok) gotI64, ok := mI64.Get(1) require.True(t, ok) @@ -3917,7 +3917,7 @@ func TestInterpreter_Marshal(t *testing.T) { f64, err := i.Marshal(map[float64]int32{math.Copysign(0, -1): 1}) require.NoError(t, err) - mF64, ok := f64.(*types.Map[float64]) + mF64, ok := f64.(*types.TypedMap[float64]) require.True(t, ok) gotF64, ok := mF64.Get(0) require.True(t, ok) @@ -3934,12 +3934,12 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[key]int32{{ID: 1}: 2}) require.NoError(t, err) - m, ok := got.(*types.BoxedMap) + m, ok := got.(*types.Map) require.True(t, ok) require.Equal(t, types.KindRef, m.Typ.KeyKind) - var entry types.BoxedMapEntry - m.Range(func(_ types.BoxedMapKey, e types.BoxedMapEntry) { + var entry types.MapEntry + m.Range(func(_ types.MapKey, e types.MapEntry) { entry = e }) require.Equal(t, types.KindRef, entry.Key.Kind()) @@ -3957,13 +3957,13 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]int{"a": 1}) require.NoError(t, err) - m, ok := got.(*types.BoxedMap) + m, ok := got.(*types.Map) require.True(t, ok) var keyRef types.Boxed - m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { + m.Range(func(_ types.MapKey, entry types.MapEntry) { keyRef = entry.Key }) - entry, ok := m.Get(types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) + entry, ok := m.Get(types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef.Ref())}) require.True(t, ok) require.True(t, m.Typ.Elem.Equals(types.TypeI64)) require.Equal(t, types.KindI64, entry.Value.Kind()) @@ -4168,7 +4168,7 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal([]any{int32(1), "x", float64(2.5)}) require.NoError(t, err) - arr, ok := got.(*types.BoxedArray) + arr, ok := got.(*types.Array) require.True(t, ok) require.True(t, arr.Typ.Elem.Equals(types.TypeRef)) require.Len(t, arr.Elems, 3) @@ -4217,12 +4217,12 @@ func TestInterpreter_Marshal(t *testing.T) { got, err := i.Marshal(map[string]any{"a": int32(1)}) require.NoError(t, err) - m, ok := got.(*types.BoxedMap) + m, ok := got.(*types.Map) require.True(t, ok) require.True(t, m.Typ.Elem.Equals(types.TypeRef)) var value types.Boxed - m.Range(func(_ types.BoxedMapKey, entry types.BoxedMapEntry) { + m.Range(func(_ types.MapKey, entry types.MapEntry) { value = entry.Value }) require.Equal(t, types.KindRef, value.Kind()) @@ -4238,7 +4238,7 @@ func TestInterpreter_Marshal(t *testing.T) { var v any got, err := i.Marshal([]any{v}) require.NoError(t, err) - arr, ok := got.(*types.BoxedArray) + arr, ok := got.(*types.Array) require.True(t, ok) require.Equal(t, types.BoxedNull, arr.Elems[0]) }) @@ -4289,15 +4289,15 @@ func TestInterpreter_Unmarshal(t *testing.T) { defer i.Close() var out []int32 - require.NoError(t, i.Unmarshal(types.Array[int32]{1, 2}, &out)) + require.NoError(t, i.Unmarshal(types.TypedArray[int32]{1, 2}, &out)) require.Equal(t, []int32{1, 2}, out) var u32 []uint32 - require.NoError(t, i.Unmarshal(types.Array[int32]{-1}, &u32)) + require.NoError(t, i.Unmarshal(types.TypedArray[int32]{-1}, &u32)) require.Equal(t, []uint32{math.MaxUint32}, u32) var u64 []uint64 - require.NoError(t, i.Unmarshal(types.Array[int64]{-1}, &u64)) + require.NoError(t, i.Unmarshal(types.TypedArray[int64]{-1}, &u64)) require.Equal(t, []uint64{math.MaxUint64}, u64) }) @@ -4527,7 +4527,7 @@ func BenchmarkInterpreter_Alloc(b *testing.B) { var err error for n := 0; n < b.N; n++ { - array := types.NewBoxedArray(typ) + array := types.NewArray(typ) var addr int addr, err = i.Alloc(array) if err != nil { @@ -4599,7 +4599,7 @@ func BenchmarkInterpreter_Release(b *testing.B) { break } var addr int - addr, err = i.Alloc(types.NewBoxedArray(typ, types.BoxRef(child))) + addr, err = i.Alloc(types.NewArray(typ, types.BoxRef(child))) if err != nil { break } @@ -4656,7 +4656,7 @@ func BenchmarkInterpreter_Release(b *testing.B) { if err != nil { break } - m := types.NewMap[int32](typ, 1) + m := types.NewTypedMap[int32](typ, 1) m.Set(1, types.BoxRef(child)) var addr int addr, err = i.Alloc(m) diff --git a/interp/marshal.go b/interp/marshal.go index f7d41a5..8b5b5e6 100644 --- a/interp/marshal.go +++ b/interp/marshal.go @@ -594,31 +594,31 @@ func (s *unmarshalState) value(val types.Value, dst reflect.Value) error { func (s *unmarshalState) elems(value types.Value) ([]types.Value, error) { switch v := value.(type) { - case types.Array[int32]: + case types.TypedArray[int32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I32(elem) } return out, nil - case types.Array[int64]: + case types.TypedArray[int64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.I64(elem) } return out, nil - case types.Array[float32]: + case types.TypedArray[float32]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F32(elem) } return out, nil - case types.Array[float64]: + case types.TypedArray[float64]: out := make([]types.Value, len(v)) for idx, elem := range v { out[idx] = types.F64(elem) } return out, nil - case *types.BoxedArray: + case *types.Array: out := make([]types.Value, len(v.Elems)) for idx, elem := range v.Elems { val, err := s.m.resolve(s.i, elem) @@ -736,37 +736,37 @@ func (m *codec) marshalArray(elem *marshalPlan) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { switch elemKind { case reflect.Int8, reflect.Int16, reflect.Int32: - out := make(types.Array[int32], v.Len()) + out := make(types.TypedArray[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Int()) } return out, nil case reflect.Uint8, reflect.Uint16, reflect.Uint32: - out := make(types.Array[int32], v.Len()) + out := make(types.TypedArray[int32], v.Len()) for idx := range out { out[idx] = int32(v.Index(idx).Uint()) } return out, nil case reflect.Int, reflect.Int64: - out := make(types.Array[int64], v.Len()) + out := make(types.TypedArray[int64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Int() } return out, nil case reflect.Uint, reflect.Uint64, reflect.Uintptr: - out := make(types.Array[int64], v.Len()) + out := make(types.TypedArray[int64], v.Len()) for idx := range out { out[idx] = int64(v.Index(idx).Uint()) } return out, nil case reflect.Float32: - out := make(types.Array[float32], v.Len()) + out := make(types.TypedArray[float32], v.Len()) for idx := range out { out[idx] = float32(v.Index(idx).Float()) } return out, nil case reflect.Float64: - out := make(types.Array[float64], v.Len()) + out := make(types.TypedArray[float64], v.Len()) for idx := range out { out[idx] = v.Index(idx).Float() } @@ -780,7 +780,7 @@ func (m *codec) marshalArray(elem *marshalPlan) marshaler { } elems[idx] = boxed } - return types.NewBoxedArray(arrayType, elems...), nil + return types.NewArray(arrayType, elems...), nil } } @@ -788,7 +788,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { return func(s *marshalState, v reflect.Value) (types.Value, error) { out := types.NewMapForType(mt, v.Len()) switch m := out.(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: iter := v.MapRange() for iter.Next() { key, err := s.boxAs(iter.Key(), mt.Key) @@ -801,7 +801,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(key.I32(), value) } - case *types.Map[int64]: + case *types.TypedMap[int64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -818,7 +818,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(keyInt, value) } - case *types.Map[float32]: + case *types.TypedMap[float32]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -835,7 +835,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(float32(keyFloat), value) } - case *types.Map[float64]: + case *types.TypedMap[float64]: iter := v.MapRange() for iter.Next() { key, err := s.value(iter.Key()) @@ -852,18 +852,18 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { } m.Set(keyFloat, value) } - case *types.BoxedMap: + case *types.Map: iter := v.MapRange() for iter.Next() { keyValue, err := s.value(iter.Key()) if err != nil { return nil, fmt.Errorf("map key: %w", err) } - var mapKey types.BoxedMapKey + var mapKey types.MapKey var entryKey types.Boxed if mt.Key.Kind() == types.KindRef { entryKey = s.boxRef(keyValue) - mapKey = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(entryKey.Ref())} + mapKey = types.MapKey{Kind: types.KindRef, Bits: uint64(entryKey.Ref())} } else { return nil, fmt.Errorf("map key: %w: map key type=%s", ErrUnsupportedMarshalType, mt.Key) } @@ -871,7 +871,7 @@ func (m *codec) marshalMap(mt *types.MapType) marshaler { if err != nil { return nil, fmt.Errorf("map value: %w", err) } - m.Set(mapKey, types.BoxedMapEntry{Key: entryKey, Value: entryValue}) + m.Set(mapKey, types.MapEntry{Key: entryKey, Value: entryValue}) } } return out, nil @@ -1111,15 +1111,15 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { return func(s *unmarshalState, src types.Value, dst reflect.Value) error { size := 0 switch m := src.(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: size = m.Len() - case *types.Map[int64]: + case *types.TypedMap[int64]: size = m.Len() - case *types.Map[float32]: + case *types.TypedMap[float32]: size = m.Len() - case *types.Map[float64]: + case *types.TypedMap[float64]: size = m.Len() - case *types.BoxedMap: + case *types.Map: size = m.Len() default: return fmt.Errorf("%w: source=%T target=%s", ErrTypeMismatch, src, dst.Type()) @@ -1143,7 +1143,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { out.SetMapIndex(k, v) } switch m := src.(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: m.Range(func(key int32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1152,7 +1152,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I32(key), elemValue) }) - case *types.Map[int64]: + case *types.TypedMap[int64]: m.Range(func(key int64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1161,7 +1161,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.I64(key), elemValue) }) - case *types.Map[float32]: + case *types.TypedMap[float32]: m.Range(func(key float32, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1170,7 +1170,7 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.F32(key), elemValue) }) - case *types.Map[float64]: + case *types.TypedMap[float64]: m.Range(func(key float64, value types.Boxed) { elemValue, err := s.m.resolve(s.i, value) if err != nil { @@ -1179,8 +1179,8 @@ func (m *codec) unmarshalMap(keyPlan, valPlan *marshalPlan) unmarshaler { } set(types.F64(key), elemValue) }) - case *types.BoxedMap: - m.Range(func(mapKey types.BoxedMapKey, entry types.BoxedMapEntry) { + case *types.Map: + m.Range(func(mapKey types.MapKey, entry types.MapEntry) { var keyValue types.Value var err error switch mapKey.Kind { diff --git a/interp/threaded.go b/interp/threaded.go index 7d70040..1fa79ff 100644 --- a/interp/threaded.go +++ b/interp/threaded.go @@ -46,7 +46,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } val := i.stack[i.sp-1] - i.releaseVal(val) + i.releaseBox(val) i.sp-- i.fr.ip++ } @@ -61,7 +61,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackOverflow) } val := i.stack[i.sp-1] - i.retainVal(val) + i.retainBox(val) i.stack[i.sp] = val i.sp++ i.fr.ip++ @@ -135,7 +135,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } else { result = v1 } - i.releaseVal(result) + i.releaseBox(result) i.stack[i.sp-3] = result i.sp -= 2 i.fr.ip++ @@ -280,7 +280,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.globals[idx] - i.retainVal(val) + i.retainBox(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 3 @@ -308,7 +308,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } old := i.globals[idx] if old != val { - i.releaseVal(old) + i.releaseBox(old) } i.globals[idx] = val i.sp-- @@ -337,7 +337,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } old := i.globals[idx] if old != val { - i.releaseVal(old) + i.releaseBox(old) } i.globals[idx] = val i.fr.ip += 3 @@ -402,7 +402,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.stack[addr] - i.retainVal(val) + i.retainBox(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 2 @@ -427,7 +427,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val := i.stack[i.sp-1] old := i.stack[addr] if old != val { - i.releaseVal(old) + i.releaseBox(old) } i.stack[addr] = val i.sp-- @@ -453,7 +453,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val := i.stack[i.sp-1] old := i.stack[addr] if old != val { - i.releaseVal(old) + i.releaseBox(old) } i.stack[addr] = val i.fr.ip += 2 @@ -531,7 +531,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrSegmentationFault) } val := i.fr.upvals[idx] - i.retainVal(val) + i.retainBox(val) i.stack[i.sp] = val i.sp++ i.fr.ip += 2 @@ -550,7 +550,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val := i.stack[i.sp-1] old := i.fr.upvals[idx] if old != val { - i.releaseVal(old) + i.releaseBox(old) } i.fr.upvals[idx] = val i.sp-- @@ -1899,7 +1899,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp == 0 { panic(ErrStackUnderflow) } - val := unboxRef[types.Array[int32]](i, i.stack[i.sp-1]) + val := unboxRef[types.TypedArray[int32]](i, i.stack[i.sp-1]) i.stack[i.sp-1] = types.BoxRef(int(i.intern(string(types.String(val))))) i.fr.ip++ } @@ -2013,7 +2013,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } val := unboxRef[types.String](i, i.stack[i.sp-1]) - i.stack[i.sp-1] = types.BoxRef(i.alloc(types.Array[int32](val))) + i.stack[i.sp-1] = types.BoxRef(i.alloc(types.TypedArray[int32](val))) i.fr.ip++ } }, @@ -2041,7 +2041,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.Array[int32], size) + val := make(types.TypedArray[int32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].I32() } @@ -2058,7 +2058,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.Array[int64], size) + val := make(types.TypedArray[int64], size) for j := 0; j < size; j++ { val[j] = i.unboxI64(i.stack[i.sp-size-j-1]) } @@ -2075,7 +2075,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.Array[float32], size) + val := make(types.TypedArray[float32], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F32() } @@ -2092,7 +2092,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := make(types.Array[float64], size) + val := make(types.TypedArray[float64], size) for j := 0; j < size; j++ { val[j] = i.stack[i.sp-size-j-1].F64() } @@ -2109,7 +2109,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if i.sp < size+1 { panic(ErrStackUnderflow) } - val := &types.BoxedArray{ + val := &types.Array{ Typ: typ, Elems: make([]types.Boxed, size), } @@ -2141,7 +2141,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.Array[int32], size) + val := make(types.TypedArray[int32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2151,7 +2151,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.Array[int64], size) + val := make(types.TypedArray[int64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2161,7 +2161,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.Array[float32], size) + val := make(types.TypedArray[float32], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2171,7 +2171,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := make(types.Array[float64], size) + val := make(types.TypedArray[float64], size) i.stack[i.sp-1] = types.BoxRef(i.alloc(val)) i.fr.ip += 3 } @@ -2181,7 +2181,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ panic(ErrStackUnderflow) } size := i.stack[i.sp-1].I32() - val := &types.BoxedArray{ + val := &types.Array{ Typ: typ, Elems: make([]types.Boxed, size), } @@ -2198,15 +2198,15 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } var n int32 switch arr := i.unbox(i.stack[i.sp-1]).(type) { - case types.Array[int32]: + case types.TypedArray[int32]: n = int32(len(arr)) - case types.Array[int64]: + case types.TypedArray[int64]: n = int32(len(arr)) - case types.Array[float32]: + case types.TypedArray[float32]: n = int32(len(arr)) - case types.Array[float64]: + case types.TypedArray[float64]: n = int32(len(arr)) - case *types.BoxedArray: + case *types.Array: n = int32(len(arr.Elems)) default: panic(ErrTypeMismatch) @@ -2229,32 +2229,32 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var val types.Boxed switch arr := i.heap[addr].(type) { - case types.Array[int32]: + case types.TypedArray[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxI32(int32(arr[idx])) - case types.Array[int64]: + case types.TypedArray[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = i.boxI64(int64(arr[idx])) - case types.Array[float32]: + case types.TypedArray[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxF32(float32(arr[idx])) - case types.Array[float64]: + case types.TypedArray[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } val = types.BoxF64(float64(arr[idx])) - case *types.BoxedArray: + case *types.Array: if idx < 0 || idx >= len(arr.Elems) { panic(ErrIndexOutOfRange) } elem := arr.Elems[idx] - i.retainVal(elem) + i.retainBox(elem) val = elem default: panic(ErrTypeMismatch) @@ -2279,33 +2279,33 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.Array[int32]: + case types.TypedArray[int32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.I32() - case types.Array[int64]: + case types.TypedArray[int64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = i.unboxI64(val) - case types.Array[float32]: + case types.TypedArray[float32]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.F32() - case types.Array[float64]: + case types.TypedArray[float64]: if idx < 0 || idx >= len(arr) { panic(ErrIndexOutOfRange) } arr[idx] = val.F64() - case *types.BoxedArray: + case *types.Array: if idx < 0 || idx >= len(arr.Elems) { panic(ErrIndexOutOfRange) } elem := arr.Elems[idx] arr.Elems[idx] = val - i.releaseVal(elem) + i.releaseBox(elem) default: panic(ErrTypeMismatch) } @@ -2329,7 +2329,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.Array[int32]: + case types.TypedArray[int32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2337,7 +2337,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.Array[int64]: + case types.TypedArray[int64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2345,7 +2345,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.Array[float32]: + case types.TypedArray[float32]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2353,7 +2353,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case types.Array[float64]: + case types.TypedArray[float64]: if idx < 0 || idx+size > len(arr) { panic(ErrIndexOutOfRange) } @@ -2361,7 +2361,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ for k := idx; k < idx+size; k++ { arr[k] = v } - case *types.BoxedArray: + case *types.Array: if idx < 0 || idx+size > len(arr.Elems) { panic(ErrIndexOutOfRange) } @@ -2372,7 +2372,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if val.Kind() == types.KindRef { i.retains(val.Ref(), size-1) } - i.releaseVal(elem) + i.releaseBox(elem) default: panic(ErrTypeMismatch) } @@ -2396,36 +2396,36 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch arr := i.heap[addr].(type) { - case types.Array[int32]: + case types.TypedArray[int32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.Array[int64]: + case types.TypedArray[int64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.Array[float32]: + case types.TypedArray[float32]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case types.Array[float64]: + case types.TypedArray[float64]: if src < 0 || dst < 0 || src+size > len(arr) || dst+size > len(arr) { panic(ErrIndexOutOfRange) } copy(arr[dst:dst+size], arr[src:src+size]) - case *types.BoxedArray: + case *types.Array: if src < 0 || dst < 0 || src+size > len(arr.Elems) || dst+size > len(arr.Elems) { panic(ErrIndexOutOfRange) } elems := arr.Elems for _, v := range elems[src : src+size] { - i.retainVal(v) + i.retainBox(v) } for _, v := range elems[dst : dst+size] { - i.releaseVal(v) + i.releaseBox(v) } copy(elems[dst:dst+size], elems[src:src+size]) default: @@ -2523,7 +2523,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val = types.BoxF64(math.Float64frombits(s.Data[idx])) case types.KindRef: val = types.Boxed(s.Data[idx]) - i.retainVal(val) + i.retainBox(val) default: panic(ErrTypeMismatch) } @@ -2540,7 +2540,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ val = i.boxI64(int64(s.Raw(idx))) case types.KindRef: val = types.Boxed(s.Raw(idx)) - i.retainVal(val) + i.retainBox(val) default: panic(ErrTypeMismatch) } @@ -2584,7 +2584,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ s.Data[idx] = math.Float64bits(val.F64()) case types.KindRef: old := types.Boxed(s.Data[idx]) - i.releaseVal(old) + i.releaseBox(old) s.Data[idx] = uint64(val) default: panic(ErrTypeMismatch) @@ -2602,7 +2602,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ s.SetRaw(idx, uint64(i.unboxI64(val))) case types.KindRef: old := types.Boxed(s.Raw(idx)) - i.releaseVal(old) + i.releaseBox(old) s.SetRaw(idx, uint64(val)) default: panic(ErrTypeMismatch) @@ -2646,58 +2646,58 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ key := i.stack[base+j*2] value := i.stack[base+j*2+1] switch m := m.(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: old, ok := m.Set(key.I32(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[int64]: + case *types.TypedMap[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float32]: + case *types.TypedMap[float32]: old, ok := m.Set(key.F32(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float64]: + case *types.TypedMap[float64]: old, ok := m.Set(key.F64(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.BoxedMap: - var k types.BoxedMapKey - entry := types.BoxedMapEntry{Value: value} + case *types.Map: + var k types.MapKey + entry := types.MapEntry{Value: value} keyRef := 0 drop := false switch key.Kind() { case types.KindI32: bits := uint64(uint32(key.I32())) - k = types.BoxedMapKey{Kind: types.KindI32, Bits: bits} + k = types.MapKey{Kind: types.KindI32, Bits: bits} entry.Key = types.BoxI32(int32(bits)) case types.KindI64: - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} entry.Key = types.BoxF32(math.Float32frombits(bits)) case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} + k = types.MapKey{Kind: types.KindF64, Bits: bits} entry.Key = types.BoxF64(math.Float64frombits(bits)) case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} entry.Key = key drop = true } @@ -2709,7 +2709,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if drop { i.release(keyRef) } - i.releaseVal(old.Value) + i.releaseBox(old.Value) } default: panic(ErrTypeMismatch) @@ -2765,15 +2765,15 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() n := 0 switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: n = m.Len() - case *types.Map[int64]: + case *types.TypedMap[int64]: n = m.Len() - case *types.Map[float32]: + case *types.TypedMap[float32]: n = m.Len() - case *types.Map[float64]: + case *types.TypedMap[float64]: n = m.Len() - case *types.BoxedMap: + case *types.Map: n = m.Len() default: panic(ErrTypeMismatch) @@ -2797,61 +2797,61 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ addr := ref.Ref() var result types.Boxed switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: value, ok := m.Get(key.I32()) if ok { result = value } else { result = m.Zero } - case *types.Map[int64]: + case *types.TypedMap[int64]: value, ok := m.Get(i.unboxI64(key)) if ok { result = value } else { result = m.Zero } - case *types.Map[float32]: + case *types.TypedMap[float32]: value, ok := m.Get(key.F32()) if ok { result = value } else { result = m.Zero } - case *types.Map[float64]: + case *types.TypedMap[float64]: value, ok := m.Get(key.F64()) if ok { result = value } else { result = m.Zero } - case *types.BoxedMap: - var k types.BoxedMapKey + case *types.Map: + var k types.MapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} + k = types.MapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -2869,7 +2869,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ default: panic(ErrTypeMismatch) } - i.retainVal(result) + i.retainBox(result) i.release(addr) i.sp-- i.stack[i.sp-1] = result @@ -2891,53 +2891,53 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ var result types.Boxed var found bool switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: result, found = m.Get(key.I32()) if !found { result = m.Zero } - case *types.Map[int64]: + case *types.TypedMap[int64]: result, found = m.Get(i.unboxI64(key)) if !found { result = m.Zero } - case *types.Map[float32]: + case *types.TypedMap[float32]: result, found = m.Get(key.F32()) if !found { result = m.Zero } - case *types.Map[float64]: + case *types.TypedMap[float64]: result, found = m.Get(key.F64()) if !found { result = m.Zero } - case *types.BoxedMap: - var k types.BoxedMapKey + case *types.Map: + var k types.MapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} + k = types.MapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -2956,7 +2956,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ default: panic(ErrTypeMismatch) } - i.retainVal(result) + i.retainBox(result) i.release(addr) i.stack[i.sp-2] = result i.stack[i.sp-1] = types.BoxBool(found) @@ -2977,58 +2977,58 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: old, ok := m.Set(key.I32(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[int64]: + case *types.TypedMap[int64]: old, ok := m.Set(i.unboxI64(key), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float32]: + case *types.TypedMap[float32]: old, ok := m.Set(key.F32(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float64]: + case *types.TypedMap[float64]: old, ok := m.Set(key.F64(), value) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.BoxedMap: - var k types.BoxedMapKey - entry := types.BoxedMapEntry{Value: value} + case *types.Map: + var k types.MapKey + entry := types.MapEntry{Value: value} keyRef := 0 drop := false switch key.Kind() { case types.KindI32: bits := uint64(uint32(key.I32())) - k = types.BoxedMapKey{Kind: types.KindI32, Bits: bits} + k = types.MapKey{Kind: types.KindI32, Bits: bits} entry.Key = types.BoxI32(int32(bits)) case types.KindI64: - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} entry.Key = types.BoxF32(math.Float32frombits(bits)) case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} + k = types.MapKey{Kind: types.KindF64, Bits: bits} entry.Key = types.BoxF64(math.Float64frombits(bits)) case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} entry.Key = key drop = true } @@ -3040,7 +3040,7 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ if drop { i.release(keyRef) } - i.releaseVal(old.Value) + i.releaseBox(old.Value) } default: panic(ErrTypeMismatch) @@ -3063,53 +3063,53 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: old, ok := m.Delete(key.I32()) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[int64]: + case *types.TypedMap[int64]: old, ok := m.Delete(i.unboxI64(key)) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float32]: + case *types.TypedMap[float32]: old, ok := m.Delete(key.F32()) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.Map[float64]: + case *types.TypedMap[float64]: old, ok := m.Delete(key.F64()) if ok { - i.releaseVal(old) + i.releaseBox(old) } - case *types.BoxedMap: - var k types.BoxedMapKey + case *types.Map: + var k types.MapKey keyRef := 0 drop := false switch key.Kind() { case types.KindI32: - k = types.BoxedMapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} + k = types.MapKey{Kind: types.KindI32, Bits: uint64(uint32(key.I32()))} case types.KindI64: - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} case types.KindF32: bits := math.Float32bits(key.F32()) if bits == 1<<31 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF32, Bits: uint64(bits)} + k = types.MapKey{Kind: types.KindF32, Bits: uint64(bits)} case types.KindF64: bits := math.Float64bits(key.F64()) if bits == 1<<63 { bits = 0 } - k = types.BoxedMapKey{Kind: types.KindF64, Bits: bits} + k = types.MapKey{Kind: types.KindF64, Bits: bits} case types.KindRef: keyRef = key.Ref() if _, ok := i.heap[keyRef].(types.I64); ok { - k = types.BoxedMapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} + k = types.MapKey{Kind: types.KindI64, Bits: uint64(i.unboxI64(key))} } else { - k = types.BoxedMapKey{Kind: types.KindRef, Bits: uint64(keyRef)} + k = types.MapKey{Kind: types.KindRef, Bits: uint64(keyRef)} drop = true } default: @@ -3117,8 +3117,8 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } old, ok := m.Delete(k) if ok { - i.releaseVal(old.Key) - i.releaseVal(old.Value) + i.releaseBox(old.Key) + i.releaseBox(old.Value) } if drop { i.release(keyRef) @@ -3143,26 +3143,26 @@ var threaded = [256]func(c *threadedCompiler) func(i *Interpreter){ } addr := ref.Ref() switch m := i.heap[addr].(type) { - case *types.Map[int32]: + case *types.TypedMap[int32]: m.Clear(func(value types.Boxed) { - i.releaseVal(value) + i.releaseBox(value) }) - case *types.Map[int64]: + case *types.TypedMap[int64]: m.Clear(func(value types.Boxed) { - i.releaseVal(value) + i.releaseBox(value) }) - case *types.Map[float32]: + case *types.TypedMap[float32]: m.Clear(func(value types.Boxed) { - i.releaseVal(value) + i.releaseBox(value) }) - case *types.Map[float64]: + case *types.TypedMap[float64]: m.Clear(func(value types.Boxed) { - i.releaseVal(value) + i.releaseBox(value) }) - case *types.BoxedMap: - m.Clear(func(entry types.BoxedMapEntry) { - i.releaseVal(entry.Key) - i.releaseVal(entry.Value) + case *types.Map: + m.Clear(func(entry types.MapEntry) { + i.releaseBox(entry.Key) + i.releaseBox(entry.Value) }) default: panic(ErrTypeMismatch) diff --git a/transform/cf.go b/transform/cf.go index 95cb810..9c7db72 100644 --- a/transform/cf.go +++ b/transform/cf.go @@ -433,12 +433,12 @@ func (p *ConstantFoldingPass) Run(m *pass.Manager) (*program.Program, error) { default: } case instr.STRING_ENCODE_UTF32: - prog.Constants = append(prog.Constants, types.Array[int32](v0)) + prog.Constants = append(prog.Constants, types.TypedArray[int32](v0)) ip = p.replace(fn.Code, ip, i0.Width()+i1.Width(), instr.New(instr.CONST_GET, uint64(len(prog.Constants)-1))) continue default: } - case types.Array[int32]: + case types.TypedArray[int32]: switch i1.Opcode() { case instr.STRING_NEW_UTF32: prog.Constants = append(prog.Constants, types.String(v0)) diff --git a/transform/cf_test.go b/transform/cf_test.go index 425924d..f6c60e0 100644 --- a/transform/cf_test.go +++ b/transform/cf_test.go @@ -1705,7 +1705,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 2), }, - program.WithConstants(types.String("foo"), types.Array[int32]("foo"), types.String("foo")), + program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo"), types.String("foo")), ), }, { @@ -1849,7 +1849,7 @@ func TestConstantFoldingPass_Run(t *testing.T) { instr.New(instr.NOP), instr.New(instr.CONST_GET, 1), }, - program.WithConstants(types.String("foo"), types.Array[int32]("foo")), + program.WithConstants(types.String("foo"), types.TypedArray[int32]("foo")), ), }, } diff --git a/types/array.go b/types/array.go index 4acfaf5..244d1f9 100644 --- a/types/array.go +++ b/types/array.go @@ -5,9 +5,9 @@ import ( "strings" ) -type Array[T int32 | int64 | float32 | float64] []T +type TypedArray[T int32 | int64 | float32 | float64] []T -type BoxedArray struct { +type Array struct { Typ *ArrayType Elems []Boxed } @@ -24,24 +24,24 @@ var ( TypeF64Array = NewArrayType(TypeF64) ) -var _ Value = Array[int32](nil) -var _ Value = Array[int64](nil) -var _ Value = Array[float32](nil) -var _ Value = Array[float64](nil) -var _ Traceable = (*BoxedArray)(nil) +var _ Value = TypedArray[int32](nil) +var _ Value = TypedArray[int64](nil) +var _ Value = TypedArray[float32](nil) +var _ Value = TypedArray[float64](nil) +var _ Traceable = (*Array)(nil) var _ Type = (*ArrayType)(nil) -func NewBoxedArray(typ *ArrayType, elems ...Boxed) *BoxedArray { - return &BoxedArray{Typ: typ, Elems: elems} +func NewArray(typ *ArrayType, elems ...Boxed) *Array { + return &Array{Typ: typ, Elems: elems} } func NewArrayType(elem Type) *ArrayType { return &ArrayType{Elem: elem, ElemKind: elem.Kind()} } -func (a Array[T]) Kind() Kind { return KindRef } +func (a TypedArray[T]) Kind() Kind { return KindRef } -func (a Array[T]) Type() Type { +func (a TypedArray[T]) Type() Type { var zero T switch any(zero).(type) { case int32: @@ -55,17 +55,17 @@ func (a Array[T]) Type() Type { } } -func (a Array[T]) String() string { +func (a TypedArray[T]) String() string { return formatSlice(a.Type(), len(a), func(i int) string { return formatElem(a[i]) }) } -func (a *BoxedArray) Kind() Kind { return KindRef } -func (a *BoxedArray) Type() Type { return a.Typ } -func (a *BoxedArray) String() string { +func (a *Array) Kind() Kind { return KindRef } +func (a *Array) Type() Type { return a.Typ } +func (a *Array) String() string { return formatSlice(a.Type(), len(a.Elems), func(i int) string { return a.Elems[i].String() }) } -func (a *BoxedArray) Refs() []Ref { +func (a *Array) Refs() []Ref { var refs []Ref for _, e := range a.Elems { if e.Kind() == KindRef { diff --git a/types/array_test.go b/types/array_test.go index 6e3284f..1910925 100644 --- a/types/array_test.go +++ b/types/array_test.go @@ -7,26 +7,26 @@ import ( "github.com/stretchr/testify/require" ) -func TestBoxedArray_Kind(t *testing.T) { - require.Equal(t, KindRef, NewBoxedArray(NewArrayType(TypeRef)).Kind()) +func TestArray_Kind(t *testing.T) { + require.Equal(t, KindRef, NewArray(NewArrayType(TypeRef)).Kind()) } -func TestBoxedArray_Type(t *testing.T) { +func TestArray_Type(t *testing.T) { typ := NewArrayType(TypeRef) - require.Equal(t, typ, NewBoxedArray(typ).Type()) + require.Equal(t, typ, NewArray(typ).Type()) } -func TestBoxedArray_String(t *testing.T) { - a := NewBoxedArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)) +func TestArray_String(t *testing.T) { + a := NewArray(NewArrayType(TypeRef), BoxI32(1), BoxI32(2), BoxI32(3)) require.Equal(t, "[]ref{1, 2, 3}", a.String()) } -func TestArray_Kind(t *testing.T) { +func TestTypedArray_Kind(t *testing.T) { tests := []Value{ - Array[int32]{}, - Array[int64]{}, - Array[float32]{}, - Array[float64]{}, + TypedArray[int32]{}, + TypedArray[int64]{}, + TypedArray[float32]{}, + TypedArray[float64]{}, } for _, val := range tests { t.Run(fmt.Sprint(val), func(t *testing.T) { @@ -35,15 +35,15 @@ func TestArray_Kind(t *testing.T) { } } -func TestArray_Type(t *testing.T) { +func TestTypedArray_Type(t *testing.T) { tests := []struct { val Value typ Type }{ - {val: Array[int32]{}, typ: TypeI32Array}, - {val: Array[int64]{}, typ: TypeI64Array}, - {val: Array[float32]{}, typ: TypeF32Array}, - {val: Array[float64]{}, typ: TypeF64Array}, + {val: TypedArray[int32]{}, typ: TypeI32Array}, + {val: TypedArray[int64]{}, typ: TypeI64Array}, + {val: TypedArray[float32]{}, typ: TypeF32Array}, + {val: TypedArray[float64]{}, typ: TypeF64Array}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { @@ -52,15 +52,15 @@ func TestArray_Type(t *testing.T) { } } -func TestArray_String(t *testing.T) { +func TestTypedArray_String(t *testing.T) { tests := []struct { val Value str string }{ - {val: Array[int32]{1, 2, 3}, str: "[]i32{1, 2, 3}"}, - {val: Array[int64]{1, 2, 3}, str: "[]i64{1, 2, 3}"}, - {val: Array[float32]{1, 2, 3}, str: "[]f32{1, 2, 3}"}, - {val: Array[float64]{1, 2, 3}, str: "[]f64{1, 2, 3}"}, + {val: TypedArray[int32]{1, 2, 3}, str: "[]i32{1, 2, 3}"}, + {val: TypedArray[int64]{1, 2, 3}, str: "[]i64{1, 2, 3}"}, + {val: TypedArray[float32]{1, 2, 3}, str: "[]f32{1, 2, 3}"}, + {val: TypedArray[float64]{1, 2, 3}, str: "[]f64{1, 2, 3}"}, } for _, tt := range tests { t.Run(fmt.Sprint(tt.val), func(t *testing.T) { @@ -69,9 +69,9 @@ func TestArray_String(t *testing.T) { } } -func TestBoxedArray_Refs(t *testing.T) { +func TestArray_Refs(t *testing.T) { t.Run("primitive elements", func(t *testing.T) { - a := NewBoxedArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) + a := NewArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) require.Empty(t, a.Refs()) var refs []Ref @@ -83,15 +83,15 @@ func TestBoxedArray_Refs(t *testing.T) { }) t.Run("reference elements", func(t *testing.T) { - a := NewBoxedArray(NewArrayType(TypeRef), BoxRef(1), BoxI32(2), BoxRef(3)) + a := NewArray(NewArrayType(TypeRef), BoxRef(1), BoxI32(2), BoxRef(3)) require.Equal(t, []Ref{1, 3}, a.Refs()) }) } -func BenchmarkArray_Refs(b *testing.B) { +func BenchmarkTypedArray_Refs(b *testing.B) { b.Run("no refs", func(b *testing.B) { - a := NewBoxedArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) + a := NewArray(NewArrayType(TypeI32), BoxI32(1), BoxI32(2)) var refs []Ref b.ReportAllocs() @@ -104,7 +104,7 @@ func BenchmarkArray_Refs(b *testing.B) { }) b.Run("child refs", func(b *testing.B) { - a := NewBoxedArray(NewArrayType(TypeRef), BoxRef(1), BoxRef(2)) + a := NewArray(NewArrayType(TypeRef), BoxRef(1), BoxRef(2)) var refs []Ref b.ReportAllocs() diff --git a/types/map.go b/types/map.go index caaef8e..747f916 100644 --- a/types/map.go +++ b/types/map.go @@ -7,24 +7,24 @@ import ( "strings" ) -type Map[K comparable] struct { +type TypedMap[K comparable] struct { Typ *MapType Zero Boxed entries map[K]Boxed } -type BoxedMap struct { +type Map struct { Typ *MapType Zero Boxed - entries map[BoxedMapKey]BoxedMapEntry + entries map[MapKey]MapEntry } -type BoxedMapKey struct { +type MapKey struct { Kind Kind Bits uint64 } -type BoxedMapEntry struct { +type MapEntry struct { Key Boxed Value Boxed } @@ -39,42 +39,42 @@ type MapType struct { } var ( - _ Traceable = (*BoxedMap)(nil) - _ Traceable = (*Map[int32])(nil) - _ Traceable = (*Map[int64])(nil) - _ Traceable = (*Map[float32])(nil) - _ Traceable = (*Map[float64])(nil) + _ Traceable = (*Map)(nil) + _ Traceable = (*TypedMap[int32])(nil) + _ Traceable = (*TypedMap[int64])(nil) + _ Traceable = (*TypedMap[float32])(nil) + _ Traceable = (*TypedMap[float64])(nil) _ Type = (*MapType)(nil) ) -func NewMap[K comparable](typ *MapType, capacity int) *Map[K] { - return &Map[K]{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[K]Boxed, capacity)} +func NewTypedMap[K comparable](typ *MapType, capacity int) *TypedMap[K] { + return &TypedMap[K]{Typ: typ, Zero: Zero(typ.ElemKind), entries: make(map[K]Boxed, capacity)} } -func NewBoxedMap(typ *MapType) *BoxedMap { - return NewBoxedMapWithCapacity(typ, 0) +func NewMap(typ *MapType) *Map { + return NewMapWithCapacity(typ, 0) } -func NewBoxedMapWithCapacity(typ *MapType, capacity int) *BoxedMap { - return &BoxedMap{ +func NewMapWithCapacity(typ *MapType, capacity int) *Map { + return &Map{ Typ: typ, Zero: Zero(typ.ElemKind), - entries: make(map[BoxedMapKey]BoxedMapEntry, capacity), + entries: make(map[MapKey]MapEntry, capacity), } } func NewMapForType(typ *MapType, capacity int) Value { switch typ.KeyKind { case KindI32: - return NewMap[int32](typ, capacity) + return NewTypedMap[int32](typ, capacity) case KindI64: - return NewMap[int64](typ, capacity) + return NewTypedMap[int64](typ, capacity) case KindF32: - return NewMap[float32](typ, capacity) + return NewTypedMap[float32](typ, capacity) case KindF64: - return NewMap[float64](typ, capacity) + return NewTypedMap[float64](typ, capacity) default: - return NewBoxedMapWithCapacity(typ, capacity) + return NewMapWithCapacity(typ, capacity) } } @@ -89,24 +89,24 @@ func NewMapType(key Type, elem Type) *MapType { } } -func (m *Map[K]) Kind() Kind { return KindRef } +func (m *TypedMap[K]) Kind() Kind { return KindRef } -func (m *Map[K]) Type() Type { return m.Typ } +func (m *TypedMap[K]) Type() Type { return m.Typ } -func (m *Map[K]) Len() int { return len(m.entries) } +func (m *TypedMap[K]) Len() int { return len(m.entries) } -func (m *Map[K]) Get(key K) (Boxed, bool) { +func (m *TypedMap[K]) Get(key K) (Boxed, bool) { value, ok := m.entries[key] return value, ok } -func (m *Map[K]) Set(key K, value Boxed) (Boxed, bool) { +func (m *TypedMap[K]) Set(key K, value Boxed) (Boxed, bool) { old, ok := m.entries[key] m.entries[key] = value return old, ok } -func (m *Map[K]) Delete(key K) (Boxed, bool) { +func (m *TypedMap[K]) Delete(key K) (Boxed, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -114,20 +114,20 @@ func (m *Map[K]) Delete(key K) (Boxed, bool) { return old, ok } -func (m *Map[K]) Range(fn func(K, Boxed)) { +func (m *TypedMap[K]) Range(fn func(K, Boxed)) { for key, value := range m.entries { fn(key, value) } } -func (m *Map[K]) Clear(fn func(Boxed)) { +func (m *TypedMap[K]) Clear(fn func(Boxed)) { for key, value := range m.entries { fn(value) delete(m.entries, key) } } -func (m *Map[K]) String() string { +func (m *TypedMap[K]) String() string { parts := make([]string, 0, m.Len()) m.Range(func(key K, value Boxed) { parts = append(parts, fmt.Sprintf("%s: %s", formatKey(any(key)), value.String())) @@ -136,7 +136,7 @@ func (m *Map[K]) String() string { return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *Map[K]) Refs() []Ref { +func (m *TypedMap[K]) Refs() []Ref { if !m.Typ.TraceValues { return nil } @@ -152,24 +152,24 @@ func (m *Map[K]) Refs() []Ref { return refs } -func (m *BoxedMap) Kind() Kind { return KindRef } +func (m *Map) Kind() Kind { return KindRef } -func (m *BoxedMap) Type() Type { return m.Typ } +func (m *Map) Type() Type { return m.Typ } -func (m *BoxedMap) Len() int { return len(m.entries) } +func (m *Map) Len() int { return len(m.entries) } -func (m *BoxedMap) Get(key BoxedMapKey) (BoxedMapEntry, bool) { +func (m *Map) Get(key MapKey) (MapEntry, bool) { entry, ok := m.entries[key] return entry, ok } -func (m *BoxedMap) Set(key BoxedMapKey, entry BoxedMapEntry) (BoxedMapEntry, bool) { +func (m *Map) Set(key MapKey, entry MapEntry) (MapEntry, bool) { old, ok := m.entries[key] m.entries[key] = entry return old, ok } -func (m *BoxedMap) Delete(key BoxedMapKey) (BoxedMapEntry, bool) { +func (m *Map) Delete(key MapKey) (MapEntry, bool) { old, ok := m.entries[key] if ok { delete(m.entries, key) @@ -177,29 +177,29 @@ func (m *BoxedMap) Delete(key BoxedMapKey) (BoxedMapEntry, bool) { return old, ok } -func (m *BoxedMap) Range(fn func(BoxedMapKey, BoxedMapEntry)) { +func (m *Map) Range(fn func(MapKey, MapEntry)) { for key, entry := range m.entries { fn(key, entry) } } -func (m *BoxedMap) Clear(fn func(BoxedMapEntry)) { +func (m *Map) Clear(fn func(MapEntry)) { for key, entry := range m.entries { fn(entry) delete(m.entries, key) } } -func (m *BoxedMap) String() string { +func (m *Map) String() string { parts := make([]string, 0, m.Len()) - m.Range(func(key BoxedMapKey, entry BoxedMapEntry) { + m.Range(func(key MapKey, entry MapEntry) { parts = append(parts, fmt.Sprintf("%s: %s", key.String(), entry.Value.String())) }) sort.Strings(parts) return fmt.Sprintf("%s{%s}", m.Typ, strings.Join(parts, ", ")) } -func (m *BoxedMap) Refs() []Ref { +func (m *Map) Refs() []Ref { traceKeys := m.Typ.TraceKeys traceValues := m.Typ.TraceValues if !traceKeys && !traceValues { @@ -223,7 +223,7 @@ func (m *BoxedMap) Refs() []Ref { return refs } -func (k BoxedMapKey) String() string { +func (k MapKey) String() string { switch k.Kind { case KindI32: return BoxI32(int32(k.Bits)).String() diff --git a/types/map_test.go b/types/map_test.go index c89cf65..f97f3b5 100644 --- a/types/map_test.go +++ b/types/map_test.go @@ -7,20 +7,20 @@ import ( "github.com/stretchr/testify/require" ) -func TestBoxedMap_Kind(t *testing.T) { - require.Equal(t, KindRef, NewBoxedMap(NewMapType(TypeI32, TypeI32)).Kind()) +func TestMap_Kind(t *testing.T) { + require.Equal(t, KindRef, NewMap(NewMapType(TypeI32, TypeI32)).Kind()) } -func TestBoxedMap_Type(t *testing.T) { +func TestMap_Type(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - require.Equal(t, typ, NewBoxedMap(typ).Type()) + require.Equal(t, typ, NewMap(typ).Type()) } -func TestBoxedMap_String(t *testing.T) { +func TestMap_String(t *testing.T) { t.Run("i32 key", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - m := NewBoxedMap(typ) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{ + m := NewMap(typ) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{ Key: BoxI32(1), Value: BoxI32(2), }) @@ -29,8 +29,8 @@ func TestBoxedMap_String(t *testing.T) { t.Run("empty string key", func(t *testing.T) { typ := NewMapType(TypeString, TypeI32) - m := NewBoxedMap(typ) - m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{ + m := NewMap(typ) + m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{ Key: BoxRef(1), Value: BoxI32(2), }) @@ -39,89 +39,89 @@ func TestBoxedMap_String(t *testing.T) { t.Run("deterministic", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - m := NewBoxedMap(typ) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 2}, BoxedMapEntry{Key: BoxI32(2), Value: BoxI32(20)}) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(10)}) + m := NewMap(typ) + m.Set(MapKey{Kind: KindI32, Bits: 2}, MapEntry{Key: BoxI32(2), Value: BoxI32(20)}) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(10)}) require.Equal(t, "map[i32]i32{1: 10, 2: 20}", m.String()) }) } -func TestBoxedMap_Len(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) +func TestMap_Len(t *testing.T) { + m := NewMap(NewMapType(TypeI32, TypeI32)) require.Equal(t, 0, m.Len()) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) require.Equal(t, 1, m.Len()) } -func TestBoxedMap_Get(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeString, TypeI32)) - m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxI32(2)}) +func TestMap_Get(t *testing.T) { + m := NewMap(NewMapType(TypeString, TypeI32)) + m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxI32(2)}) - entry, ok := m.Get(BoxedMapKey{Kind: KindRef, Bits: 1}) + entry, ok := m.Get(MapKey{Kind: KindRef, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(2), entry.Value) - _, ok = m.Get(BoxedMapKey{Kind: KindRef, Bits: 2}) + _, ok = m.Get(MapKey{Kind: KindRef, Bits: 2}) require.False(t, ok) } -func TestBoxedMap_Set(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) +func TestMap_Set(t *testing.T) { + m := NewMap(NewMapType(TypeI32, TypeI32)) - old, ok := m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + old, ok := m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) require.False(t, ok) - require.Equal(t, BoxedMapEntry{}, old) + require.Equal(t, MapEntry{}, old) - old, ok = m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(3)}) + old, ok = m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(3)}) require.True(t, ok) require.Equal(t, BoxI32(2), old.Value) - entry, ok := m.Get(BoxedMapKey{Kind: KindI32, Bits: 1}) + entry, ok := m.Get(MapKey{Kind: KindI32, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(3), entry.Value) } -func TestBoxedMap_Delete(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestMap_Delete(t *testing.T) { + m := NewMap(NewMapType(TypeI32, TypeI32)) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - old, ok := m.Delete(BoxedMapKey{Kind: KindI32, Bits: 1}) + old, ok := m.Delete(MapKey{Kind: KindI32, Bits: 1}) require.True(t, ok) require.Equal(t, BoxI32(2), old.Value) require.Equal(t, 0, m.Len()) - _, ok = m.Delete(BoxedMapKey{Kind: KindI32, Bits: 1}) + _, ok = m.Delete(MapKey{Kind: KindI32, Bits: 1}) require.False(t, ok) } -func TestBoxedMap_Range(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestMap_Range(t *testing.T) { + m := NewMap(NewMapType(TypeI32, TypeI32)) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - var keys []BoxedMapKey - m.Range(func(key BoxedMapKey, _ BoxedMapEntry) { + var keys []MapKey + m.Range(func(key MapKey, _ MapEntry) { keys = append(keys, key) }) - require.Equal(t, []BoxedMapKey{{Kind: KindI32, Bits: 1}}, keys) + require.Equal(t, []MapKey{{Kind: KindI32, Bits: 1}}, keys) } -func TestBoxedMap_Clear(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) +func TestMap_Clear(t *testing.T) { + m := NewMap(NewMapType(TypeI32, TypeI32)) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) - var entries []BoxedMapEntry - m.Clear(func(entry BoxedMapEntry) { + var entries []MapEntry + m.Clear(func(entry MapEntry) { entries = append(entries, entry) }) - require.Equal(t, []BoxedMapEntry{{Key: BoxI32(1), Value: BoxI32(2)}}, entries) + require.Equal(t, []MapEntry{{Key: BoxI32(1), Value: BoxI32(2)}}, entries) require.Equal(t, 0, m.Len()) } -func TestBoxedMap_Refs(t *testing.T) { +func TestMap_Refs(t *testing.T) { t.Run("inline i64 value", func(t *testing.T) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI64)) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI64(2)}) + m := NewMap(NewMapType(TypeI32, TypeI64)) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI64(2)}) var refs []Ref allocs := testing.AllocsPerRun(100, func() { @@ -133,8 +133,8 @@ func TestBoxedMap_Refs(t *testing.T) { t.Run("ref key and value", func(t *testing.T) { typ := NewMapType(TypeRef, TypeRef) - m := NewBoxedMap(typ) - m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{ + m := NewMap(typ) + m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{ Key: BoxRef(1), Value: BoxRef(2), }) @@ -143,8 +143,8 @@ func TestBoxedMap_Refs(t *testing.T) { t.Run("spilled i64 value", func(t *testing.T) { typ := NewMapType(TypeI32, TypeI64) - m := NewBoxedMap(typ) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{ + m := NewMap(typ) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{ Key: BoxI32(1), Value: BoxRef(2), }) @@ -159,13 +159,13 @@ func TestNewMapForType(t *testing.T) { typ *MapType want any }{ - {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*Map[int32])(nil)}, - {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*Map[int64])(nil)}, - {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*Map[float32])(nil)}, - {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*Map[float64])(nil)}, - {name: "ref", typ: NewMapType(TypeRef, TypeI32), want: (*BoxedMap)(nil)}, - {name: "string", typ: NewMapType(TypeString, TypeI32), want: (*BoxedMap)(nil)}, - {name: "struct", typ: NewMapType(structType, TypeI32), want: (*BoxedMap)(nil)}, + {name: "i32", typ: NewMapType(TypeI32, TypeI32), want: (*TypedMap[int32])(nil)}, + {name: "i64", typ: NewMapType(TypeI64, TypeI32), want: (*TypedMap[int64])(nil)}, + {name: "f32", typ: NewMapType(TypeF32, TypeI32), want: (*TypedMap[float32])(nil)}, + {name: "f64", typ: NewMapType(TypeF64, TypeI32), want: (*TypedMap[float64])(nil)}, + {name: "ref", typ: NewMapType(TypeRef, TypeI32), want: (*Map)(nil)}, + {name: "string", typ: NewMapType(TypeString, TypeI32), want: (*Map)(nil)}, + {name: "struct", typ: NewMapType(structType, TypeI32), want: (*Map)(nil)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -174,25 +174,25 @@ func TestNewMapForType(t *testing.T) { } } -func TestMap_Kind(t *testing.T) { - require.Equal(t, KindRef, NewMap[int32](NewMapType(TypeI32, TypeI32), 0).Kind()) +func TestTypedMap_Kind(t *testing.T) { + require.Equal(t, KindRef, NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0).Kind()) } -func TestMap_Type(t *testing.T) { +func TestTypedMap_Type(t *testing.T) { typ := NewMapType(TypeI32, TypeI32) - require.Equal(t, typ, NewMap[int32](typ, 0).Type()) + require.Equal(t, typ, NewTypedMap[int32](typ, 0).Type()) } -func TestMap_Len(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestTypedMap_Len(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) require.Equal(t, 0, m.Len()) m.Set(1, BoxI32(2)) require.Equal(t, 1, m.Len()) } -func TestMap_Get(t *testing.T) { +func TestTypedMap_Get(t *testing.T) { t.Run("i32", func(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) got, ok := m.Get(1) require.True(t, ok) @@ -202,7 +202,7 @@ func TestMap_Get(t *testing.T) { }) t.Run("i64 wide key", func(t *testing.T) { - m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1<<50, BoxI32(2)) got, ok := m.Get(1 << 50) require.True(t, ok) @@ -212,7 +212,7 @@ func TestMap_Get(t *testing.T) { }) t.Run("f64", func(t *testing.T) { - m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(1.5, BoxI32(2)) got, ok := m.Get(1.5) require.True(t, ok) @@ -220,9 +220,9 @@ func TestMap_Get(t *testing.T) { }) } -func TestMap_Set(t *testing.T) { +func TestTypedMap_Set(t *testing.T) { t.Run("overwrite returns old", func(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) old, ok := m.Set(1, BoxI32(2)) require.False(t, ok) require.Equal(t, Boxed(0), old) @@ -233,7 +233,7 @@ func TestMap_Set(t *testing.T) { }) t.Run("f32 -0.0 collapses to +0.0", func(t *testing.T) { - m := NewMap[float32](NewMapType(TypeF32, TypeI32), 0) + m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) m.Set(float32(math.Copysign(0, -1)), BoxI32(1)) m.Set(0, BoxI32(2)) @@ -244,7 +244,7 @@ func TestMap_Set(t *testing.T) { }) t.Run("f64 -0.0 collapses to +0.0", func(t *testing.T) { - m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(math.Copysign(0, -1), BoxI32(1)) m.Set(0, BoxI32(2)) @@ -255,15 +255,15 @@ func TestMap_Set(t *testing.T) { }) t.Run("f64 NaN is not retrievable", func(t *testing.T) { - m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(math.NaN(), BoxI32(1)) _, ok := m.Get(math.NaN()) require.False(t, ok) }) } -func TestMap_Delete(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestTypedMap_Delete(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) old, ok := m.Delete(1) @@ -275,8 +275,8 @@ func TestMap_Delete(t *testing.T) { require.False(t, ok) } -func TestMap_Range(t *testing.T) { - m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) +func TestTypedMap_Range(t *testing.T) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1, BoxI32(2)) var keys []int64 @@ -286,8 +286,8 @@ func TestMap_Range(t *testing.T) { require.Equal(t, []int64{1}, keys) } -func TestMap_Clear(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) +func TestTypedMap_Clear(t *testing.T) { + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) var values []Boxed @@ -298,35 +298,35 @@ func TestMap_Clear(t *testing.T) { require.Equal(t, 0, m.Len()) } -func TestMap_String(t *testing.T) { +func TestTypedMap_String(t *testing.T) { t.Run("i32", func(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI32), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[i32]i32{1: 2}", m.String()) }) t.Run("i64", func(t *testing.T) { - m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[i64]i32{1: 2}", m.String()) }) t.Run("f32", func(t *testing.T) { - m := NewMap[float32](NewMapType(TypeF32, TypeI32), 0) + m := NewTypedMap[float32](NewMapType(TypeF32, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[f32]i32{1: 2}", m.String()) }) t.Run("f64", func(t *testing.T) { - m := NewMap[float64](NewMapType(TypeF64, TypeI32), 0) + m := NewTypedMap[float64](NewMapType(TypeF64, TypeI32), 0) m.Set(1, BoxI32(2)) require.Equal(t, "map[f64]i32{1: 2}", m.String()) }) } -func TestMap_Refs(t *testing.T) { +func TestTypedMap_Refs(t *testing.T) { t.Run("inline i64 value", func(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeI64), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref @@ -338,7 +338,7 @@ func TestMap_Refs(t *testing.T) { }) t.Run("ref value", func(t *testing.T) { - m := NewMap[int32](NewMapType(TypeI32, TypeString), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeString), 0) m.Set(1, BoxRef(2)) require.Equal(t, []Ref{2}, m.Refs()) }) @@ -381,8 +381,8 @@ func TestMapType_Equals(t *testing.T) { require.False(t, NewMapType(TypeI32, TypeI32).Equals(NewMapType(TypeI32, TypeI64))) } -func BenchmarkMap_Get(b *testing.B) { - m := NewMap[int64](NewMapType(TypeI64, TypeI32), 0) +func BenchmarkTypedMap_Get(b *testing.B) { + m := NewTypedMap[int64](NewMapType(TypeI64, TypeI32), 0) m.Set(42, BoxI32(7)) b.ReportAllocs() @@ -394,10 +394,10 @@ func BenchmarkMap_Get(b *testing.B) { require.True(b, ok) } -func BenchmarkBoxedMapStringGet_Interned(b *testing.B) { - m := NewBoxedMap(NewMapType(TypeString, TypeI32)) - m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxI32(7)}) - key := BoxedMapKey{Kind: KindRef, Bits: 1} +func BenchmarkMapStringGet_Interned(b *testing.B) { + m := NewMap(NewMapType(TypeString, TypeI32)) + m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxI32(7)}) + key := MapKey{Kind: KindRef, Bits: 1} b.ReportAllocs() var ok bool @@ -408,10 +408,10 @@ func BenchmarkBoxedMapStringGet_Interned(b *testing.B) { require.True(b, ok) } -func BenchmarkBoxedMap_Refs(b *testing.B) { +func BenchmarkMap_Refs(b *testing.B) { b.Run("no refs", func(b *testing.B) { - m := NewBoxedMap(NewMapType(TypeI32, TypeI32)) - m.Set(BoxedMapKey{Kind: KindI32, Bits: 1}, BoxedMapEntry{Key: BoxI32(1), Value: BoxI32(2)}) + m := NewMap(NewMapType(TypeI32, TypeI32)) + m.Set(MapKey{Kind: KindI32, Bits: 1}, MapEntry{Key: BoxI32(1), Value: BoxI32(2)}) var refs []Ref b.ReportAllocs() @@ -424,7 +424,7 @@ func BenchmarkBoxedMap_Refs(b *testing.B) { }) b.Run("inline i64", func(b *testing.B) { - m := NewMap[int32](NewMapType(TypeI32, TypeI64), 0) + m := NewTypedMap[int32](NewMapType(TypeI32, TypeI64), 0) m.Set(1, BoxI64(2)) var refs []Ref @@ -438,8 +438,8 @@ func BenchmarkBoxedMap_Refs(b *testing.B) { }) b.Run("child refs", func(b *testing.B) { - m := NewBoxedMap(NewMapType(TypeRef, TypeRef)) - m.Set(BoxedMapKey{Kind: KindRef, Bits: 1}, BoxedMapEntry{Key: BoxRef(1), Value: BoxRef(2)}) + m := NewMap(NewMapType(TypeRef, TypeRef)) + m.Set(MapKey{Kind: KindRef, Bits: 1}, MapEntry{Key: BoxRef(1), Value: BoxRef(2)}) var refs []Ref b.ReportAllocs() diff --git a/types/value_test.go b/types/value_test.go index 8de3c14..361a4e9 100644 --- a/types/value_test.go +++ b/types/value_test.go @@ -37,7 +37,7 @@ func TestIsNull(t *testing.T) { {Ref(1), false}, {BoxRef(1), false}, {I32(0), false}, - {Array[int32]{0}, false}, + {TypedArray[int32]{0}, false}, {nil, false}, } for _, tt := range tests {