Skip to content

Commit 8ab246a

Browse files
mixed context types behavior
1 parent f834bbb commit 8ab246a

File tree

4 files changed

+41
-10
lines changed

4 files changed

+41
-10
lines changed

design/mvp/CanonicalABI.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3752,12 +3752,17 @@ validation specifies:
37523752
* `$f` is given type `(func (result $t))`
37533753

37543754
Calling `$f` invokes the following function, which reads the [thread-local
3755-
storage] of the [current thread]:
3755+
storage] of the [current thread] (taking only the low 32-bits if `$t` is `i32`):
37563756
```python
37573757
def canon_context_get(t, i, thread):
3758+
MASK_32BIT = 1 << 32 - 1
3759+
37583760
assert(t == 'i32' or t == 'i64')
37593761
assert(i < Thread.CONTEXT_LENGTH)
3760-
return [thread.context[i]]
3762+
result = thread.context[i]
3763+
if t == 'i32':
3764+
result &= MASK_32BIT
3765+
return [result]
37613766
```
37623767

37633768

design/mvp/Concurrency.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,24 @@ current thread's thread-local storage can be read and written from core wasm
414414
code by calling the [`context.get`] and [`context.set`] built-ins.
415415

416416
The thread-local storage array's length is currently fixed to contain exactly 2
417-
`i32`s or `i64`s with the goal of allowing this array to be stored inline in
418-
whatever existing runtime data structure is already efficiently reachable from
419-
ambient compiled wasm code. Because module instantiation is declarative in the
420-
Component Model, the imported `context.{get,set}` built-ins can be inlined by
421-
the core wasm compiler as-if they were instructions, allowing the generated
422-
machine code to be a single load or store. This makes thread-local storage a
423-
natural place to store:
417+
`i64`s with the goal of allowing this array to be stored inline in whatever
418+
existing runtime data structure is already efficiently reachable from ambient
419+
compiled wasm code. Because module instantiation is declarative in the Component
420+
Model, the imported `context.{get,set}` built-ins can be inlined by the core
421+
wasm compiler as-if they were instructions, allowing the generated machine code
422+
to be a single load or store. This makes thread-local storage a natural place to
423+
store:
424424
1. a pointer to the linear-memory "shadow stack" pointer
425425
2. a pointer to a struct used by the runtime to implement the language's
426426
thread-local features
427427

428+
Both of `context.{get,set}` take an immediate argument of `i32` or `i64` to
429+
indicate the return or argument type. `context.set i32` will zero the high
430+
bits of the stored value and `context.get i32` will only read the low bits of
431+
the stored value. Generally it is expected that 32-bit components always use
432+
the `i32` immediate and 64-bit components always use the `i64` immediate, but
433+
mixing these calls is still valid.
434+
428435
When threads are created explicitly by `thread.new-indirect`, the lifetime of
429436
the thread-local storage array ends when the function passed to
430437
`thread.new-indirect` returns and thus any linear-memory allocations associated

design/mvp/Explainer.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,13 @@ The `context.get` built-in returns the `i`th element of the [current thread]'s
15641564
than 2 and `T` to be `i32` or `i64`, but these restrictions may be relaxed in
15651565
the future.
15661566

1567+
Mixing `i32` and `i64` results in truncating or unsigned extending the
1568+
stored values:
1569+
* If `context.get i32 i` is called after `context.set i64 i v`,
1570+
only the low 32-bits are read (returning `i32.wrap_i64 v`).
1571+
* If `context.get i64 i` is called after `context.set i32 i v`,
1572+
only the upper 32-bits will be zeroed (returning `i64.extend_i32_u v`).
1573+
15671574
For details, see [Thread-Local Storage] in the concurrency explainer and
15681575
[`canon_context_get`] in the Canonical ABI explainer.
15691576

@@ -1579,6 +1586,13 @@ The `context.set` built-in sets the `i`th element of the [current thread]'s
15791586
`i` to be less than 2 and `T` to be `i32` or `i64`, but these restrictions may
15801587
be relaxed in the future.
15811588

1589+
Mixing `i32` and `i64` results in truncating or unsigned extending the
1590+
stored values:
1591+
* If `context.get i32 i` is called after `context.set i64 i v`,
1592+
only the low 32-bits are read (returning `i32.wrap_i64 v`).
1593+
* If `context.get i64 i` is called after `context.set i32 i v`,
1594+
only the upper 32-bits will be zeroed (returning `i64.extend_i32_u v`).
1595+
15821596
For details, see [Thread-Local Storage] in the concurrency explainer and
15831597
[`canon_context_set`] in the Canonical ABI explainer.
15841598

design/mvp/canonical-abi/definitions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2195,9 +2195,14 @@ def canon_resource_rep(rt, thread, i):
21952195
### 🔀 `canon context.get`
21962196

21972197
def canon_context_get(t, i, thread):
2198+
MASK_32BIT = 1 << 32 - 1
2199+
21982200
assert(t == 'i32' or t == 'i64')
21992201
assert(i < Thread.CONTEXT_LENGTH)
2200-
return [thread.context[i]]
2202+
result = thread.context[i]
2203+
if t == 'i32':
2204+
result &= MASK_32BIT
2205+
return [result]
22012206

22022207
### 🔀 `canon context.set`
22032208

0 commit comments

Comments
 (0)