// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build wasm.stackswitch // Continuation table for WebAssembly stack switching (GOWASM=stackswitch). // // With stack switching, a g's WebAssembly execution stack is a continuation, // a reference of type (ref null $cont). Reference values are opaque and // cannot be stored in linear memory, so they cannot live in a g. Instead the // linker declares a continuation table, wasm table wasmContTable, and every // g owns one slot of it, recorded in g.wasmCont.index. A slot holds the g's // suspended continuation, or null if the g has not started yet. // // m0.g0 is allocated statically rather than by malg, so its index is the zero // value: slot wasmContG0Slot belongs to it. malg gives every other g a slot // of its own, starting at 1, which the g and its continuation keep for their // existence, including while the g is on a free list. Like the g's // themselves, slots are never freed, so the table grows to the number of g's // ever allocated. // // wasm runs a single thread, so the allocation state below needs no locking. package runtime const ( // wasmContTable is the index of the continuation table among the // module's wasm tables. It must match cmd/link/internal/wasm's // writeTableSec, which declares it with an initial size of // contTableInitialSize. Assembly refers to it as $const_wasmContTable. wasmContTable = 1 // wasmContG0Slot is m0.g0's slot in the continuation table. // Assembly refers to it as $const_wasmContG0Slot. wasmContG0Slot = 0 ) // wasmContPerG is the per-g state of WebAssembly stack switching. type wasmContPerG struct { // index is the g's slot in the continuation table: wasmContG0Slot for // m0.g0, and a slot of its own, assigned by malg, for every other g. index int32 } var ( wasmContLast int32 // the highest slot assigned by wasmContAlloc so far wasmContSize int32 // the continuation table's size, as last read or grown ) // wasmContAlloc gives gp, which malg just allocated, the next unused slot of // the continuation table, growing the table if the slot does not fit. func wasmContAlloc(gp *g) { slot := wasmContLast + 1 if slot >= wasmContSize { wasmContGrow(slot) } wasmContLast = slot gp.wasmCont.index = slot } // wasmContGrow grows the continuation table so that it has slot n. It at // least doubles the table and does not depend on the table's initial // size, which the linker sets. func wasmContGrow(n int32) { size := wasmContTableSize() if n >= size { delta := max(size, n+1-size) if wasmContTableGrow(delta) < 0 { throw("runtime: cannot grow WebAssembly continuation table") } size += delta } wasmContSize = size } // Implemented in stackswitch_wasm.s. // wasmContTableSize returns the size of the continuation table (table.size). func wasmContTableSize() int32 // wasmContTableGrow appends delta null slots to the continuation table and // returns its previous size, or -1 if it cannot grow (table.grow). func wasmContTableGrow(delta int32) int32