Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-24.04 ]
go: [ '1.25' ]
go: [ '1.26.2' ]
runs-on: ${{ matrix.os }}
permissions:
contents: write
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-24.04 ]
go: [ '1.25' ]
go: [ '1.26.2' ]
name: Check ${{ matrix.os }} @ Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -30,8 +30,8 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-${{ runner.go }}-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-${{ runner.go }}-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}

- name: Initialize Project
run: make init
Expand All @@ -55,7 +55,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-24.04 ]
go: [ '1.25' ]
go: [ '1.26.2' ]
name: Benchmark Comparison ${{ matrix.os }} @ Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
continue-on-error: true
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ minivm gives your Go service a lightweight programmable runtime: assemble byteco
go get github.com/siyul-park/minivm
```

> Requires Go 1.25+. The VM core uses only the Go standard library; the CLI and tests use small Go module dependencies.
> Requires Go 1.26.2+. The VM core uses only the Go standard library; the CLI and tests use small Go module dependencies.

---

Expand Down
2 changes: 1 addition & 1 deletion README_kr.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ minivm은 Go 서비스에 가벼운 실행 엔진을 내장합니다. 바이트
go get github.com/siyul-park/minivm
```

> Go 1.25 이상. VM 코어는 Go 표준 라이브러리만 사용하며, CLI와 테스트에는 작은 Go 모듈 의존성이 있습니다.
> Go 1.26.2 이상. VM 코어는 Go 표준 라이브러리만 사용하며, CLI와 테스트에는 작은 Go 모듈 의존성이 있습니다.

---

Expand Down
15 changes: 15 additions & 0 deletions docs/instruction-set.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,18 @@ Offsets are signed 16-bit values encoded little-endian. `BR 5` skips 5 bytes pas
| `STRUCT_NEW_DEFAULT` | `{2}` | `→ struct` | ⬜ | Create zero-initialized struct. |
| `STRUCT_GET` | `{}` | `struct index → value` | ⬜ | Load struct field. |
| `STRUCT_SET` | `{}` | `struct index value →` | ⬜ | Store struct field. |

## Map Operations

Map keys must be comparable: `i32`, `i64`, `f32`, `f64`, `string`, or `ref` identity. Missing keys read as the element zero value. `MAP_LOOKUP` also returns `I32(1)` for present and `I32(0)` for missing.

| Opcode | Widths | Stack | JIT | Description |
|---|---|---|---|---|
| `MAP_NEW` | `{2}` | `k1 v1 ... kn vn count → map` | ⬜ | Create typed map from key/value pairs; later duplicate keys overwrite earlier values. |
| `MAP_NEW_DEFAULT` | `{2}` | `capacity → map` | ⬜ | Create empty typed map with capacity hint. |
| `MAP_LEN` | `{}` | `map → i32` | ⬜ | Push entry count. |
| `MAP_GET` | `{}` | `map key → value` | ⬜ | Load value or element zero value when key is missing. |
| `MAP_LOOKUP` | `{}` | `map key → value ok` | ⬜ | Load value plus presence flag. |
| `MAP_SET` | `{}` | `map key value →` | ⬜ | Insert or replace entry. |
| `MAP_DELETE` | `{}` | `map key →` | ⬜ | Delete entry; missing key is a no-op. |
| `MAP_CLEAR` | `{}` | `map →` | ⬜ | Delete all entries. |
1 change: 1 addition & 0 deletions docs/memory-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ RC is manually handled in every threaded closure touching refs.
| `DUP` ref | `retain(addr)` |
| `DROP` ref | `release(addr)` |
| store ref to local/global | `retain(new)`, `release(old)` |
| map insert/replace/delete/clear | transfer or release map-owned ref keys/values |

`retain(addr)` increments `rc[addr]`.

Expand Down
1 change: 1 addition & 0 deletions docs/value-representation.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Heap objects implement `types.Value`.
| `types.String` | `KindRef` | `TypeString` | `Value` |
| `*types.Array` | `KindRef` | `*ArrayType` | `Value`, `Traceable` |
| `*types.Struct` | `KindRef` | `*StructType` | `Value`, `Traceable` |
| `*types.Map` | `KindRef` | `*MapType` | `Value`, `Traceable` |
| `*types.Function` | `KindRef` | `*FunctionType` | `Value` |
| `*interp.HostFunction` | `KindRef` | `*FunctionType` | `Value` |

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/siyul-park/minivm

go 1.26.3
go 1.26.2

require (
github.com/spf13/cobra v1.10.2
Expand Down
10 changes: 10 additions & 0 deletions instr/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,14 @@ const (

STRUCT_GET
STRUCT_SET

MAP_NEW
MAP_NEW_DEFAULT

MAP_LEN
MAP_GET
MAP_LOOKUP
MAP_SET
MAP_DELETE
MAP_CLEAR
)
10 changes: 10 additions & 0 deletions instr/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ var types = map[Opcode]Type{

STRUCT_GET: {"struct.get", []int{}},
STRUCT_SET: {"struct.set", []int{}},

MAP_NEW: {"map.new", []int{2}},
MAP_NEW_DEFAULT: {"map.new_default", []int{2}},

MAP_LEN: {"map.len", []int{}},
MAP_GET: {"map.get", []int{}},
MAP_LOOKUP: {"map.lookup", []int{}},
MAP_SET: {"map.set", []int{}},
MAP_DELETE: {"map.delete", []int{}},
MAP_CLEAR: {"map.clear", []int{}},
}

func TypeOf(op Opcode) Type {
Expand Down
10 changes: 10 additions & 0 deletions instr/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,16 @@ func TestTypeOf(t *testing.T) {

{opcode: STRUCT_GET},
{opcode: STRUCT_SET},

{opcode: MAP_NEW},
{opcode: MAP_NEW_DEFAULT},

{opcode: MAP_LEN},
{opcode: MAP_GET},
{opcode: MAP_LOOKUP},
{opcode: MAP_SET},
{opcode: MAP_DELETE},
{opcode: MAP_CLEAR},
}

for _, tt := range tests {
Expand Down
127 changes: 126 additions & 1 deletion interp/interp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,132 @@ var tests = []test{
),
values: []types.Value{types.I32(4)},
},
// --- map: MAP_NEW, MAP_NEW_DEFAULT, MAP_LEN, MAP_GET, MAP_LOOKUP, MAP_SET, MAP_DELETE, MAP_CLEAR ---
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.MAP_LEN),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(1)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 4),
instr.New(instr.MAP_NEW_DEFAULT, 0),
instr.New(instr.MAP_LEN),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(0)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_GET),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(42)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.CONST_GET, 0),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.CONST_GET, 1),
instr.New(instr.MAP_GET),
},
program.WithConstants(types.String("key"), types.String("key")),
program.WithTypes(types.NewMapType(types.TypeString, types.TypeI32)),
),
values: []types.Value{types.I32(42)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_LOOKUP),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(1), types.I32(42)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.I32_CONST, 2),
instr.New(instr.MAP_LOOKUP),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(0), types.I32(0)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 0),
instr.New(instr.MAP_NEW_DEFAULT, 0),
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.MAP_SET),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: nil,
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.DUP),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_DELETE),
instr.New(instr.MAP_LEN),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(0)},
},
{
program: program.New(
[]instr.Instruction{
instr.New(instr.I32_CONST, 1),
instr.New(instr.I32_CONST, 42),
instr.New(instr.I32_CONST, 1),
instr.New(instr.MAP_NEW, 0),
instr.New(instr.DUP),
instr.New(instr.MAP_CLEAR),
instr.New(instr.MAP_LEN),
},
program.WithTypes(types.NewMapType(types.TypeI32, types.TypeI32)),
),
values: []types.Value{types.I32(0)},
},
// --- recursive: fibonacci (i32), factorial (i64) ---
{
program: program.New(
Expand Down Expand Up @@ -3134,5 +3260,4 @@ func BenchmarkInterpreter_Run(b *testing.B) {
})
}
})

}
Loading
Loading