1
0
mirror of https://github.com/golang/go synced 2024-11-14 13:20:30 -07:00

runtime: (re)use unused linear memory on Wasm

CL 476717 adopted the memory management mechanism on Plan 9 to
manage Wasm's linear memory. But the Plan 9 code uses global
variable bloc and blocMax to keep track of the runtime's and the
OS's sense of break, whereas the Wasm sbrk function doesn't use
those global variables, and directly goes to grow the linear
memory instead. This causes that if there is any unused portion at
the end of the linear memory, the runtime doesn't use it. This CL
fixes it, adopts the same mechanism as the Plan 9 code.

In particular, the runtime is not aware of any unused initial
memory at startup. Therefore, (most of) the extra initial memory
set by the linker are not actually used. This CL fixes this as
well.

For #69018.

Change-Id: I2ea6a138310627eda5f19a1c76b1e1327362e5f2
Reviewed-on: https://go-review.googlesource.com/c/go/+/621635
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Cherry Mui 2024-10-21 13:01:56 -04:00
parent 6a49f81edc
commit 4510586f93
4 changed files with 42 additions and 8 deletions

View File

@ -10,8 +10,28 @@ import "unsafe"
const memDebug = false const memDebug = false
var bloc uintptr // Memory management on sbrk systems (including the linear memory
var blocMax uintptr // on Wasm).
// bloc is the runtime's sense of the break, which can go up or
// down. blocMax is the system's break, also the high water mark
// of bloc. The runtime uses memory up to bloc. The memory
// between bloc and blocMax is allocated by the OS but not used
// by the runtime.
//
// When the runtime needs to grow the heap address range, it
// increases bloc. When it needs to grow beyond blocMax, it calls
// the system sbrk to allocate more memory (and therefore
// increase blocMax).
//
// When the runtime frees memory at the end of the address space,
// it decreases bloc, but does not reduces the system break (as
// the OS doesn't support it). When the runtime frees memory in
// the middle of the address space, the memory goes to a free
// list.
var bloc uintptr // The runtime's sense of break. Can go up or down.
var blocMax uintptr // The break of the OS. Only increase.
var memlock mutex var memlock mutex
type memHdr struct { type memHdr struct {

View File

@ -7,14 +7,21 @@ package runtime
import "unsafe" import "unsafe"
func sbrk(n uintptr) unsafe.Pointer { func sbrk(n uintptr) unsafe.Pointer {
grow := divRoundUp(n, physPageSize) bl := bloc
n = memRound(n)
if bl+n > blocMax {
grow := (bl + n - blocMax) / physPageSize
size := growMemory(int32(grow)) size := growMemory(int32(grow))
if size < 0 { if size < 0 {
return nil return nil
} }
resetMemoryDataView() resetMemoryDataView()
return unsafe.Pointer(uintptr(size) * physPageSize) blocMax = bl + n
}
bloc += n
return unsafe.Pointer(bl)
} }
// Implemented in src/runtime/sys_wasm.s // Implemented in src/runtime/sys_wasm.s
func growMemory(pages int32) int32 func growMemory(pages int32) int32
func currentMemory() int32

View File

@ -13,6 +13,7 @@ func osinit() {
// https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
physPageSize = 64 * 1024 physPageSize = 64 * 1024
initBloc() initBloc()
blocMax = uintptr(currentMemory()) * physPageSize // record the initial linear memory size
ncpu = 1 ncpu = 1
getg().m.procid = 2 getg().m.procid = 2
} }

View File

@ -86,6 +86,12 @@ TEXT runtime·exitThread(SB), NOSPLIT, $0-0
TEXT runtime·osyield(SB), NOSPLIT, $0-0 TEXT runtime·osyield(SB), NOSPLIT, $0-0
UNDEF UNDEF
TEXT runtime·currentMemory(SB), NOSPLIT, $0
Get SP
CurrentMemory
I32Store ret+0(FP)
RET
TEXT runtime·growMemory(SB), NOSPLIT, $0 TEXT runtime·growMemory(SB), NOSPLIT, $0
Get SP Get SP
I32Load pages+0(FP) I32Load pages+0(FP)