Skip to content

Commit

Permalink
WIP: 新版 malloc 补充测试
Browse files Browse the repository at this point in the history
  • Loading branch information
chai2010 committed Jan 14, 2025
1 parent 929af7c commit e05560c
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 12 deletions.
27 changes: 27 additions & 0 deletions internal/waroot/malloc/freelist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 版权 @2025 凹语言 作者。保留所有权利。

package malloc

// 空闲链表
type HeapFreeList struct {
Fixed bool
Header HeapBlock

heap *Heap
}

func (p *HeapFreeList) Len() int32 {
return 0
}

func (p *HeapFreeList) Cap() int32 {
return 0
}

func (p *HeapFreeList) Next(iter *HeapBlock) *HeapBlock {
return &HeapBlock{}
}

func (p *HeapFreeList) String() string {
return ""
}
98 changes: 96 additions & 2 deletions internal/waroot/malloc/malloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
package malloc

import (
"bytes"
"context"
_ "embed"
"fmt"
"html/template"
"sync"

"wa-lang.org/wa/internal/3rdparty/wazero"
"wa-lang.org/wa/internal/3rdparty/wazero/api"
"wa-lang.org/wa/internal/wat/watutil"
)

//go:embed malloc.wat
Expand All @@ -19,6 +28,12 @@ const (
DefaultHeapLFixedCap int32 = 100 // 固定大小的空闲链表最大长度
)

// 内部常量
const (
kFreeListHeadSize = 5 * 8 // 全部空闲链表头大小
kBlockHeadSize = 2 * 8 // 块头大小
)

// Heap配置
type Config struct {
MemoryPages int32 // 内存页数
Expand All @@ -31,6 +46,23 @@ type Config struct {
// 封装的Heap, 便于测试
type Heap struct {
cfg *Config

wazeroOnce sync.Once
wazeroCtx context.Context
wazeroConf wazero.ModuleConfig
wazeroRuntime wazero.Runtime
wazeroCompileModule wazero.CompiledModule
wazeroModule api.Module
wazeroInitErr error

fnMalloc api.Function
fnFree api.Function
}

// 内存块
type HeapBlock struct {
Size int32
Next int32
}

// 构造新的Heap
Expand All @@ -47,17 +79,79 @@ func NewHeap(cfg *Config) *Heap {
if cfg != nil {
*p.cfg = *cfg
}
p.init()
return p
}

func (p *Heap) init() {
// 1. 获取 wat 文本
var buf bytes.Buffer
buf.WriteString("(module $malloc\n")
if err := template.Must(template.New("wat").Parse(malloc_wat)).Execute(&buf, p.cfg); err != nil {
panic(err)
}
buf.WriteString("\n)")

// 2. wat 转为 wasm 字节数组
wasmBytes, err := watutil.Wat2Wasm("malloc.wat", buf.Bytes())
if err != nil {
panic(err)
}

// 3. 初始化 wazero 运行时
p.wazeroCtx = context.Background()
p.wazeroConf = wazero.NewModuleConfig().WithName("malloc.wat")

p.wazeroRuntime = wazero.NewRuntime(p.wazeroCtx)
p.wazeroCompileModule, err = p.wazeroRuntime.CompileModule(p.wazeroCtx, wasmBytes)
if err != nil {
p.wazeroInitErr = err
panic(err)
}

p.wazeroModule, p.wazeroInitErr = p.wazeroRuntime.InstantiateModule(
p.wazeroCtx, p.wazeroCompileModule, p.wazeroConf,
)

// 4. 导出函数
p.fnMalloc = p.wazeroModule.ExportedFunction("wa_malloc")
if p.fnMalloc == nil {
err = fmt.Errorf("wazero: func wa_malloc not found")
return
}
p.fnFree = p.wazeroModule.ExportedFunction("wa_free")
if p.fnFree == nil {
err = fmt.Errorf("wazero: func wa_free not found")
return
}
}

// 初始化获取空闲链表
func (p *Heap) FreeList(size int32) HeapBlock {
return HeapBlock{}
}

// 分配 size 字节的内存, 返回地址 8 字节对齐
func (p *Heap) Malloc(size int32) int32 {
return 0
results, err := p.fnMalloc.Call(p.wazeroCtx, uint64(size))
if err != nil {
panic(err)
}
if len(results) != 1 {
panic("unreachable")
}
return int32(results[0])
}

// 释放内存
func (p *Heap) Free(ptr int32) {
return
results, err := p.fnFree.Call(p.wazeroCtx, uint64(ptr))
if err != nil {
panic(err)
}
if len(results) != 0 {
panic("unreachable")
}
}

// 打印统计信息
Expand Down
16 changes: 7 additions & 9 deletions internal/waroot/malloc/malloc.wat
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
;; Copyright 2025 The Wa Authors. All rights reserved.

(module $malloc
(memory $memory 1024)

(export "memory" (memory $memory))

(memory $memory {{.MemoryPages}} {{.MemoryPagesMax}})

;; 栈/静态数据/堆的内存布局
;;
;; 0 __stack_ptr __heap_base __heap_ptr __heap_top
Expand Down Expand Up @@ -46,12 +45,12 @@
;; | size:i32 | next:i32 | data[0] |
;; +----------+----------+---------+
;;
(global $__stack_ptr (mut i32) (i32.const 1024)) ;; index=0, 用户可配置
(global $__heap_base i32 (i32.const 10000)) ;; index=1, 编译器生成, 8字节对齐, 只读
(global $__stack_ptr (mut i32) (i32.const {{.StackPtr}})) ;; index=0, 用户可配置
(global $__heap_base i32 (i32.const {{.HeapBase}})) ;; index=1, 编译器生成, 8字节对齐, 只读
(global $__heap_ptr (mut i32) (i32.const 0)) ;; heap 当前位置指针
(global $__heap_top (mut i32) (i32.const 0)) ;; heap 最大位置指针(超过时要 grow 内存)
(global $__heap_l128_freep (mut i32) (i32.const 0)) ;; l128 是循环链表, 记录当前的迭代位置
(global $__heap_lfixed_cap (mut i32) (i32.const 100)) ;; 固定尺寸空闲链表最大长度, 满时回收
(global $__heap_lfixed_cap (mut i32) (i32.const {{.HeapLFixedCap}})) ;; 固定尺寸空闲链表最大长度, 满时回收

;; 判断是否为固定大小内存
(func $heap_is_fixed_size (param $size i32) (result i32)
Expand Down Expand Up @@ -298,7 +297,7 @@

;; func wa_malloc(size: i32) => i32
;; 在堆上分配内存并返回地址, 内存不超过 2GB, 失败返回 0
(func $wa_malloc (param $size i32) (result i32)
(func $wa_malloc (export "wa_malloc") (param $size i32) (result i32)
(local $free_list i32) ;; *heap_block_t, 空闲链表头
(local $b i32) ;; *heap_block_t

Expand Down Expand Up @@ -595,7 +594,7 @@
)

;; func wa_free(ptr: i32) => i32
(func $wa_free (param $ptr i32)
(func $wa_free (export "wa_free") (param $ptr i32)
(local $size i32) ;; *heap_block_t
(local $block i32) ;; *heap_block_t
(local $freep i32) ;; 空闲链表指针
Expand Down Expand Up @@ -851,4 +850,3 @@
(func $_start (export "_start")
call $wa_malloc_init
)
)
8 changes: 7 additions & 1 deletion internal/waroot/malloc/malloc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@ package malloc
import "testing"

func TestHeap(t *testing.T) {
// TODO
h := NewHeap(nil)
got := h.Malloc(1)
defer h.Free(got)
expect := DefaultHeapBase + kFreeListHeadSize + kBlockHeadSize
if got != expect {
t.Fatalf("invalid ptr: expect = %d, got = %d", expect, got)
}
}

0 comments on commit e05560c

Please sign in to comment.