Skip to content

Commit 3e5939a

Browse files
committed
Clarify memory equality check in task.return
Resolves #464
1 parent e819a61 commit 3e5939a

File tree

3 files changed

+49
-40
lines changed

3 files changed

+49
-40
lines changed

design/mvp/CanonicalABI.md

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -128,35 +128,36 @@ known to not contain `borrow`. The `CanonicalOptions`, `ComponentInstance`,
128128

129129
### Canonical ABI Options
130130

131-
The following two classes list the various Canonical ABI options ([`canonopt`])
132-
that can be set on various Canonical ABI definitions. The default values of the
133-
Python fields are the default values when the associated `canonopt` is not
134-
present in the binary or text format definition.
131+
The following classes list the various Canonical ABI options ([`canonopt`])
132+
that can be set on various Canonical ABI definitions. The default values of
133+
the Python fields are the default values when the associated `canonopt` is
134+
not present in the binary or text format definition.
135135

136-
The `LiftLowerContext` class contains the subset of [`canonopt`] which are
137-
used to lift and lower the individual parameters and results of function
138-
calls:
136+
The `LiftOptions` class contains the subset of [`canonopt`] which are needed
137+
when lifting individual parameters and results:
139138
```python
140139
@dataclass
141-
class LiftLowerOptions:
140+
class LiftOptions:
142141
string_encoding: str = 'utf8'
143142
memory: Optional[bytearray] = None
144-
realloc: Optional[Callable] = None
145143

146-
def __eq__(self, other):
147-
return self.string_encoding == other.string_encoding and \
148-
self.memory is other.memory and \
149-
self.realloc is other.realloc
144+
def equal(lhs, rhs):
145+
return lhs.string_encoding == rhs.string_encoding and \
146+
lhs.memory is rhs.memory
147+
```
148+
The `equal` static method is used by `task.return` below to dynamically
149+
compare equality of just this subset of `canonopt`.
150150

151-
def copy(opts):
152-
return LiftLowerOptions(opts.string_encoding, opts.memory, opts.realloc)
151+
The `LiftLowerOptions` class contains the subset of [`canonopt`] which are
152+
needed when lifting *or* lowering individual parameters and results:
153+
```python
154+
@dataclass
155+
class LiftLowerOptions(LiftOptions):
156+
realloc: Optional[Callable] = None
153157
```
154-
The `__eq__` override specifies that equality of `LiftLowerOptions` (as used
155-
by, e.g., `canon_task_return` below) is defined in terms of the identity of
156-
the memory and `realloc`-function instances.
157158

158-
The `CanonicalOptions` class contains the rest of the [`canonopt`] options
159-
that affect how an overall function is lifted/lowered:
159+
The `CanonicalOptions` class contains the rest of the [`canonopt`]
160+
options that affect how an overall function is lifted/lowered:
160161
```python
161162
@dataclass
162163
class CanonicalOptions(LiftLowerOptions):
@@ -3207,7 +3208,7 @@ For a canonical definition:
32073208
```
32083209
validation specifies:
32093210
* `$f` is given type `flatten_functype($opts, (func (param $t)?), 'lower')`
3210-
* `$opts` may only contain `memory`, `string-encoding` and `realloc`
3211+
* `$opts` may only contain `memory` and `string-encoding`
32113212

32123213
Calling `$f` invokes the following function which uses `Task.return_` to lift
32133214
and pass the results to the caller:
@@ -3216,7 +3217,7 @@ async def canon_task_return(task, result_type, opts: LiftLowerOptions, flat_args
32163217
trap_if(not task.inst.may_leave)
32173218
trap_if(task.opts.sync and not task.opts.always_task_return)
32183219
trap_if(result_type != task.ft.results)
3219-
trap_if(opts != LiftLowerOptions.copy(task.opts))
3220+
trap_if(not LiftOptions.equal(opts, task.opts))
32203221
task.return_(flat_args)
32213222
return []
32223223
```
@@ -3225,13 +3226,18 @@ component with multiple exported functions of different types, `task.return` is
32253226
not called with a mismatched result type (which, due to indirect control flow,
32263227
can in general only be caught dynamically).
32273228

3228-
The `trap_if(opts != LiftLowerOptions.copy(task.opts))` guard ensures that
3229-
the return value is lifted the same way as the `canon lift` from which this
3229+
The `trap_if(not LiftOptions.equal(opts, task.opts))` guard ensures that the
3230+
return value is lifted the same way as the `canon lift` from which this
32303231
`task.return` is returning. This ensures that AOT fusion of `canon lift` and
32313232
`canon lower` can generate a thunk that is indirectly called by `task.return`
3232-
after these guards. The `LiftLowerOptions.copy` method is used to select just
3233-
the `LiftLowerOptions` subset of `CanonicalOptions` (since fields like
3234-
`async` and `callback` aren't relevant to `task.return`).
3233+
after these guards. Inside `LiftOptions.equal`, `opts.memory` is compared with
3234+
`task.opts.memory` via object identity of the mutable memory instance. Since
3235+
`memory` refers to a mutable *instance* of memory, this comparison is not
3236+
concerned with the static memory indices (in `canon lift` and `canon
3237+
task.return`), only the identity of the memories created
3238+
at instantiation-/ run-time. In Core WebAssembly spec terms, the test is on the
3239+
equality of the [`memaddr`] values stored in the instance's [`memaddrs` table]
3240+
which is indexed by the static [`memidx`].
32353241

32363242

32373243
### 🔀 `canon yield`
@@ -3919,6 +3925,9 @@ def canon_thread_available_parallelism():
39193925
[WASI]: https://github.com/webassembly/wasi
39203926
[Deterministic Profile]: https://github.com/WebAssembly/profiles/blob/main/proposals/profiles/Overview.md
39213927
[stack-switching]: https://github.com/WebAssembly/stack-switching
3928+
[`memaddr`]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-memaddr
3929+
[`memaddrs` table]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-moduleinst
3930+
[`memidx`]: https://webassembly.github.io/spec/core/syntax/modules.html#syntax-memidx
39223931

39233932
[Alignment]: https://en.wikipedia.org/wiki/Data_structure_alignment
39243933
[UTF-8]: https://en.wikipedia.org/wiki/UTF-8

design/mvp/Explainer.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,9 +1590,10 @@ The `task.return` built-in takes as parameters the result values of the
15901590
currently-executing task. This built-in must be called exactly once per export
15911591
activation. The `canon task.return` definition takes component-level return
15921592
type and the list of `canonopt` to be used to lift the return value. When
1593-
called, the declared return type and `canonopt`s are checked to exactly match
1594-
those of the current task. (See also "[Returning]" in the async explainer and
1595-
[`canon_task_return`] in the Canonical ABI explainer.)
1593+
called, the declared return type and the `string-encoding` and `memory`
1594+
`canonopt`s are checked to exactly match those of the current task. (See also
1595+
"[Returning]" in the async explainer and [`canon_task_return`] in the Canonical
1596+
ABI explainer.)
15961597

15971598
###### 🔀 `yield`
15981599

design/mvp/canonical-abi/definitions.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,17 @@ def __init__(self, opts, inst, borrow_scope = None):
192192
### Canonical ABI Options
193193

194194
@dataclass
195-
class LiftLowerOptions:
195+
class LiftOptions:
196196
string_encoding: str = 'utf8'
197197
memory: Optional[bytearray] = None
198-
realloc: Optional[Callable] = None
199198

200-
def __eq__(self, other):
201-
return self.string_encoding == other.string_encoding and \
202-
self.memory is other.memory and \
203-
self.realloc is other.realloc
199+
def equal(lhs, rhs):
200+
return lhs.string_encoding == rhs.string_encoding and \
201+
lhs.memory is rhs.memory
204202

205-
def copy(opts):
206-
return LiftLowerOptions(opts.string_encoding, opts.memory, opts.realloc)
203+
@dataclass
204+
class LiftLowerOptions(LiftOptions):
205+
realloc: Optional[Callable] = None
207206

208207
@dataclass
209208
class CanonicalOptions(LiftLowerOptions):
@@ -1931,11 +1930,11 @@ async def canon_backpressure_set(task, flat_args):
19311930

19321931
### 🔀 `canon task.return`
19331932

1934-
async def canon_task_return(task, result_type, opts: LiftLowerOptions, flat_args):
1933+
async def canon_task_return(task, result_type, opts: LiftOptions, flat_args):
19351934
trap_if(not task.inst.may_leave)
19361935
trap_if(task.opts.sync and not task.opts.always_task_return)
19371936
trap_if(result_type != task.ft.results)
1938-
trap_if(opts != LiftLowerOptions.copy(task.opts))
1937+
trap_if(not LiftOptions.equal(opts, task.opts))
19391938
task.return_(flat_args)
19401939
return []
19411940

0 commit comments

Comments
 (0)