Skip to content

Commit d8f775e

Browse files
authored
Merge mutable global proposal into spec (#814)
See the mutable-globals proposal repo here: https://github.com/WebAssembly/mutable-global
1 parent ca5ec41 commit d8f775e

File tree

9 files changed

+429
-54
lines changed

9 files changed

+429
-54
lines changed

document/core/syntax/modules.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,6 @@ Each export is labeled by a unique :ref:`name <syntax-name>`.
311311
Exportable definitions are :ref:`functions <syntax-func>`, :ref:`tables <syntax-table>`, :ref:`memories <syntax-mem>`, and :ref:`globals <syntax-global>`,
312312
which are referenced through a respective descriptor.
313313

314-
.. note::
315-
In the current version of WebAssembly, only *immutable* globals may be exported.
316-
317314

318315
Conventions
319316
...........
@@ -362,8 +359,6 @@ Every import defines an index in the respective :ref:`index space <syntax-index>
362359
In each index space, the indices of imports go before the first index of any definition contained in the module itself.
363360

364361
.. note::
365-
In the current version of WebAssembly, only *immutable* globals may be imported.
366-
367362
Unlike export names, import names are not necessarily unique.
368363
It is possible to import the same |IMODULE|/|INAME| pair multiple times;
369364
such imports may even have different type descriptions, including different kinds of entities.

document/core/valid/modules.rst

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -321,17 +321,13 @@ Exports :math:`\export` and export descriptions :math:`\exportdesc` are classifi
321321

322322
* The global :math:`C.\CGLOBALS[x]` must be defined in the context.
323323

324-
* Let :math:`\mut~t` be the :ref:`global type <syntax-globaltype>` :math:`C.\CGLOBALS[x]`.
325-
326-
* The mutability :math:`\mut` must be |MCONST|.
327-
328324
* Then the export description is valid with :ref:`external type <syntax-externtype>` :math:`\ETGLOBAL~C.\CGLOBALS[x]`.
329325

330326
.. math::
331327
\frac{
332-
C.\CGLOBALS[x] = \MCONST~t
328+
C.\CGLOBALS[x] = \globaltype
333329
}{
334-
C \vdashexportdesc \EDGLOBAL~x : \ETGLOBAL~(\MCONST~t)
330+
C \vdashexportdesc \EDGLOBAL~x : \ETGLOBAL~\globaltype
335331
}
336332
337333
.. note::
@@ -417,15 +413,13 @@ Imports :math:`\import` and import descriptions :math:`\importdesc` are classifi
417413

418414
* The global type :math:`\globaltype` must be :ref:`valid <valid-globaltype>`.
419415

420-
* The mutability of :math:`\globaltype` must be |MCONST|.
421-
422416
* Then the import description is valid with type :math:`\ETGLOBAL~\globaltype`.
423417

424418
.. math::
425419
\frac{
426-
\vdashglobaltype \MCONST~t \ok
420+
\vdashglobaltype \globaltype \ok
427421
}{
428-
C \vdashimportdesc \IDGLOBAL~\MCONST~t : \ETGLOBAL~\MCONST~t
422+
C \vdashimportdesc \IDGLOBAL~\globaltype : \ETGLOBAL~\globaltype
429423
}
430424
431425
.. note::

document/js-api/index.bs

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
132132
text: table address; url: exec/runtime.html#syntax-tableaddr
133133
text: function address; url: exec/runtime.html#syntax-funcaddr
134134
text: memory address; url: exec/runtime.html#syntax-memaddr
135+
text: global address; url: exec/runtime.html#syntax-globaladdr
135136
url: syntax/types.html#syntax-valtype
136137
text: 𝗂𝟥𝟤
137138
text: 𝗂𝟨𝟦
@@ -153,6 +154,9 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
153154
text: 𝗆𝖾𝗆
154155
text: 𝗀𝗅𝗈𝖻𝖺𝗅
155156
text: global type; url: syntax/types.html#syntax-globaltype
157+
url: syntax/types.html#syntax-mut
158+
text: var
159+
text: const
156160
text: address; url: exec/runtime.html#addresses
157161
text: signed_32; url: exec/numerics.html#aux-signed
158162
text: grow_memory; url: exec/instructions.html#exec-grow-memory
@@ -225,6 +229,7 @@ Each [=agent=] is associated with the following [=ordered map=]s:
225229
* The <dfn>Memory object cache</dfn>, mapping [=memory address=]es to {{Memory}} objects.
226230
* The <dfn>Table object cache</dfn>, mapping [=table address=]es to {{Table}} objects.
227231
* The <dfn>Exported Function cache</dfn>, mapping [=function address=]es to [=Exported Function=] objects.
232+
* The <dfn>Global object cache</dfn>, mapping [=global address=]es to {{Global}} objects.
228233

229234
<h2 id="webassembly-namespace">The WebAssembly Namespace</h2>
230235

@@ -326,13 +331,17 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
326331
1. Let |index| be the number of external functions in |imports|. This value |index| is known as the <dfn>index of the host function</dfn> |funcaddr|.
327332
1. Let |externfunc| be the [=external value=] [=external value|𝖿𝗎𝗇𝖼=] |funcaddr|.
328333
1. [=Append=] |externfunc| to |imports|.
329-
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |globaltype|,
330-
1. If |globaltype| is [=𝗂𝟨𝟦=] or [=Type=](|v|) is not [=Number=], throw a {{LinkError}} exception.
331-
1. Let |value| be [=ToWebAssemblyValue=](|v|, |globaltype|.<em>[=global type|valtype=]</em>)
332-
1. Assert: |globaltype|.<em>[=global type|mut=]</em> is [=global type|𝖼𝗈𝗇𝗌𝗍=], as verified by WebAssembly validation.
333-
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
334-
1. Let (|store|, |globaladdr|) be [=alloc_global=](|store|, |globaltype|, |value|).
335-
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
334+
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |mut| |valtype|,
335+
1. If [=Type=](|v|) is [=Number=],
336+
1. If |valtype| is [=𝗂𝟨𝟦=], throw a {{LinkError}} exception.
337+
1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|)
338+
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
339+
1. Let (|store|, |globaladdr|) be [=alloc_global=](|store|, [=const=] |valtype|, |value|).
340+
1. Set the [[surrounding agent=]'s [=associated store=] to |store|.
341+
1. If |v| is a {{Global}} instance,
342+
1. Let |globaladdr| be |v|.\[[Global]]
343+
1. Otherwise,
344+
1. Throw a {{LinkError}} exception.
336345
1. Let |externglobal| be [=external value|𝗀𝗅𝗈𝖻𝖺𝗅=] |globaladdr|.
337346
1. [=Append=] |externglobal| to |imports|.
338347
1. If |externtype| is of the form [=𝗆𝖾𝗆=] |memtype|,
@@ -365,11 +374,10 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
365374
1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|.
366375
1. Let |value| be |func|.
367376
1. If |externtype| is of the form [=𝗀𝗅𝗈𝖻𝖺𝗅=] |globaltype|,
368-
1. If |globaltype|.<em>[=global type|valtype=]</em> is [=𝗂𝟨𝟦=], throw a {{LinkError}} exception.
369-
1. Assert: |globaltype|.<em>[=global type|mut=]</em> is [=global type|𝖼𝗈𝗇𝗌𝗍=], as verified by WebAssembly validation.
370377
1. Assert: |externval| is of the form [=external value|𝗀𝗅𝗈𝖻𝖺𝗅=] |globaladdr|.
371378
1. Let [=external value|𝗀𝗅𝗈𝖻𝖺𝗅=] |globaladdr| be |externval|.
372-
1. Let |value| be [=ToJSValue=]([=read_global=](|store|, |globaladdr|)).
379+
1. Let |global| be [=create a global object|a new Global object=] created from |globaladdr|.
380+
1. Let |value| be |global|.
373381
1. If |externtype| is of the form [=𝗆𝖾𝗆=] |memtype|,
374382
1. Assert: |externval| is of the form [=external value|𝗆𝖾𝗆=] |memaddr|.
375383
1. Let [=external value|𝗆𝖾𝗆=] |memaddr| be |externval|.
@@ -704,6 +712,93 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
704712
1. Return undefined.
705713
</div>
706714

715+
<h3 id="globals">Globals</h3>
716+
717+
<pre class="idl">
718+
dictionary GlobalDescriptor {
719+
required USVString value;
720+
boolean mutable = false;
721+
};
722+
723+
[LegacyNamespace=WebAssembly, Constructor(GlobalDescriptor descriptor, unrestricted double value = 0), Exposed=(Window,Worker,Worklet)]
724+
interface Global {
725+
unrestricted double valueOf();
726+
attribute unrestricted double value;
727+
};
728+
</pre>
729+
730+
<div>
731+
A {{Global}} object represents a single [=global instance=]
732+
which can be simultaneously referenced by multiple {{Instance}} objects. Each
733+
{{Global}} object has one internal slot:
734+
735+
* \[[Global]] : a [=global address=]
736+
</div>
737+
738+
<div algorithm>
739+
To <dfn>create a global object</dfn> from a [=global address=] |globaladdr|, perform the following steps:
740+
1. Let |map| be the current [=agent=]'s associated [=Global object cache=].
741+
1. If |map|[|globaladdr|] [=map/exists=],
742+
1. Return |map|[|globaladdr|].
743+
1. Let |global| be a new {{Global}} instance with \[[Global]] set to |globaladdr|.
744+
1. [=map/Set=] |map|[|globaladdr|] to |global|.
745+
1. Return |global|.
746+
</div>
747+
748+
<div algorithm>
749+
The algorithm <dfn>ToValueType</dfn>(|s|) performs the following steps:
750+
1. If |s| equals "i32", return [=𝗂𝟥𝟤=].
751+
1. If |s| equals "f32", return [=𝖿𝟥𝟤=].
752+
1. If |s| equals "f64", return [=𝖿𝟨𝟦=].
753+
1. Otherwise, throw a {{TypeError}} exception.
754+
</div>
755+
756+
<div algorithm>
757+
The <dfn constructor for="Global">Global(descriptor, v)</dfn> constructor, when invoked, performs the following steps:
758+
1. Let |mutable| be |descriptor|["mutable"].
759+
1. Let |valuetype| be [=ToValueType=](|descriptor|["value"]).
760+
1. Let |value| be [=ToWebAssemblyValue=](|v|, |valuetype|).
761+
1. If |mutable| is true, let |globaltype| be [=var=] |valuetype|; otherwise, let |globaltype| be [=const=] |valuetype|.
762+
1. Let |store| be the current agent's [=associated store=].
763+
1. Let (|store|, |globaladdr|) be [=alloc_global=](|store|, |globaltype|, |value|). <!-- TODO(littledan): Report allocation failure https://github.com/WebAssembly/spec/issues/584 -->
764+
1. Set the current agent's [=associated store=] to |store|.
765+
1. [=Create a global object=] from the global address |globaladdr| and return the result.
766+
</div>
767+
768+
<div algorithm>
769+
The algorithm <dfn>GetGlobalValue</dfn>({{Global}} |global|) performs the following steps:
770+
1. Let |store| be the current agent's [=associated store=].
771+
1. Let |globaladdr| be |global|.\[[Global]].
772+
1. Let |globaltype| be [=type_global=](|store|, |globaladdr|).
773+
1. If |globaltype| is of the form |mut| [=𝗂𝟨𝟦=], throw a {{TypeError}}.
774+
1. Let |value| be [=read_global=](|store|, |globaladdr|).
775+
1. Return [=ToJSValue=](|value|).
776+
</div>
777+
778+
<div algorithm>
779+
The getter of the <dfn attribute for="Global">value</dfn> attribute of {{Global}}, when invoked, performs the following steps:
780+
1. Let |global| be the {{Global}} instance.
781+
1. Return [=GetGlobalValue=](|global|).
782+
783+
The setter of the value attribute of {{Global}}, when invoked with a value |v|, performs the following steps:
784+
1. Let |global| be the {{Global}} instance.
785+
1. Let |store| be the current agent's [=associated store=].
786+
1. Let |globaladdr| be |global|.\[[Global]].
787+
1. Let |globaltype| be [=type_global=](|store|, |globaladdr|), where |globaltype| is of the form |mut| |valuetype|.
788+
1. If |mut| is [=const=], throw a {{TypeError}.
789+
1. If |valuetype| is [=𝗂𝟨𝟦=], throw a {{TypeError}}.
790+
1. Let |value| be [=ToWebAssemblyValue=](|v|, |valuetype|).
791+
1. Let |store| be [=write_global=](|store|, |globaladdr|, |value|).
792+
1. If |store| is [=error=], throw a {{RangeError}} exception.
793+
1. Set the current agent's [=associated store=] to |store|.
794+
</div>
795+
796+
<div algorithm>
797+
The <dfn method for="Global">valueOf()</dfn> method, when invoked, performs the following steps:
798+
1. Let |global| be the {{Global}} instance.
799+
1. Return [=GetGlobalValue=](|global|).
800+
</div>
801+
707802
<h3 id="exported-function-exotic-objects">Exported Functions</h3>
708803

709804
A WebAssembly function is made available in JavaScript as an <dfn>Exported Function</dfn>.

interpreter/script/js.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ let harness =
6161
"}\n" ^
6262
"\n" ^
6363
"function get(instance, name) {\n" ^
64-
" return instance.exports[name];\n" ^
64+
" let v = instance.exports[name];\n" ^
65+
" return (v instanceof WebAssembly.Global) ? v.value : v;\n" ^
6566
"}\n" ^
6667
"\n" ^
6768
"function exports(name, instance) {\n" ^

interpreter/valid/valid.ml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,6 @@ let check_import (im : import) (c : context) : context =
428428
{c with memories = mt :: c.memories}
429429
| GlobalImport gt ->
430430
check_global_type gt idesc.at;
431-
let GlobalType (_, mut) = gt in
432-
require (mut = Immutable) idesc.at
433-
"mutable globals cannot be imported (yet)";
434431
{c with globals = gt :: c.globals}
435432

436433
module NameSet = Set.Make(struct type t = Ast.name let compare = compare end)
@@ -441,10 +438,7 @@ let check_export (c : context) (set : NameSet.t) (ex : export) : NameSet.t =
441438
| FuncExport x -> ignore (func c x)
442439
| TableExport x -> ignore (table c x)
443440
| MemoryExport x -> ignore (memory c x)
444-
| GlobalExport x ->
445-
let GlobalType (_, mut) = global c x in
446-
require (mut = Immutable) edesc.at
447-
"mutable globals cannot be exported (yet)"
441+
| GlobalExport x -> ignore (global c x)
448442
);
449443
require (not (NameSet.mem name set)) ex.at "duplicate export name";
450444
NameSet.add name set

test/core/globals.wast

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,9 @@
5151
"global is immutable"
5252
)
5353

54-
(assert_invalid
55-
(module (import "m" "a" (global (mut i32))))
56-
"mutable globals cannot be imported"
57-
)
58-
59-
(assert_invalid
60-
(module (global (import "m" "a") (mut i32)))
61-
"mutable globals cannot be imported"
62-
)
63-
64-
(assert_invalid
65-
(module (global (mut f32) (f32.const 0)) (export "a" (global 0)))
66-
"mutable globals cannot be exported"
67-
)
68-
69-
(assert_invalid
70-
(module (global (export "a") (mut f32) (f32.const 0)))
71-
"mutable globals cannot be exported"
72-
)
54+
;; mutable globals can be exported
55+
(module (global (mut f32) (f32.const 0)) (export "a" (global 0)))
56+
(module (global (export "a") (mut f32) (f32.const 0)))
7357

7458
(assert_invalid
7559
(module (global f32 (f32.neg (f32.const 0))))

test/core/linking.wast

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,29 @@
3939
(module $Mg
4040
(global $glob (export "glob") i32 (i32.const 42))
4141
(func (export "get") (result i32) (get_global $glob))
42+
43+
;; export mutable globals
44+
(global $mut_glob (export "mut_glob") (mut i32) (i32.const 142))
45+
(func (export "get_mut") (result i32) (get_global $mut_glob))
46+
(func (export "set_mut") (param i32) (set_global $mut_glob (get_local 0)))
4247
)
4348
(register "Mg" $Mg)
4449

4550
(module $Ng
4651
(global $x (import "Mg" "glob") i32)
52+
(global $mut_glob (import "Mg" "mut_glob") (mut i32))
4753
(func $f (import "Mg" "get") (result i32))
54+
(func $get_mut (import "Mg" "get_mut") (result i32))
55+
(func $set_mut (import "Mg" "set_mut") (param i32))
56+
4857
(export "Mg.glob" (global $x))
4958
(export "Mg.get" (func $f))
5059
(global $glob (export "glob") i32 (i32.const 43))
5160
(func (export "get") (result i32) (get_global $glob))
61+
62+
(export "Mg.mut_glob" (global $mut_glob))
63+
(export "Mg.get_mut" (func $get_mut))
64+
(export "Mg.set_mut" (func $set_mut))
5265
)
5366

5467
(assert_return (get $Mg "glob") (i32.const 42))
@@ -58,6 +71,26 @@
5871
(assert_return (invoke $Ng "Mg.get") (i32.const 42))
5972
(assert_return (invoke $Ng "get") (i32.const 43))
6073

74+
(assert_return (get $Mg "mut_glob") (i32.const 142))
75+
(assert_return (get $Ng "Mg.mut_glob") (i32.const 142))
76+
(assert_return (invoke $Mg "get_mut") (i32.const 142))
77+
(assert_return (invoke $Ng "Mg.get_mut") (i32.const 142))
78+
79+
(assert_return (invoke $Mg "set_mut" (i32.const 241)))
80+
(assert_return (get $Mg "mut_glob") (i32.const 241))
81+
(assert_return (get $Ng "Mg.mut_glob") (i32.const 241))
82+
(assert_return (invoke $Mg "get_mut") (i32.const 241))
83+
(assert_return (invoke $Ng "Mg.get_mut") (i32.const 241))
84+
85+
86+
(assert_unlinkable
87+
(module (import "Mg" "mut_glob" (global i32)))
88+
"incompatible import type"
89+
)
90+
(assert_unlinkable
91+
(module (import "Mg" "glob" (global (mut i32))))
92+
"incompatible import type"
93+
)
6194

6295
;; Tables
6396

test/harness/wasm-module-builder.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ class WasmModuleBuilder {
225225
return this.num_imported_funcs++;
226226
}
227227

228-
addImportedGlobal(module = "", name, type) {
228+
addImportedGlobal(module = "", name, type, mutable = false) {
229229
let o = {module: module, name: name, kind: kExternalGlobal, type: type,
230-
mutable: false}
230+
mutable: mutable}
231231
this.imports.push(o);
232232
return this.num_imported_globals++;
233233
}

0 commit comments

Comments
 (0)