Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Add bulk array creation instructions #278

Merged
merged 3 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions proposals/gc/MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,13 @@ This can compile to machine code that (1) reads the RTT from `$x`, (2) checks th
* `struct.new_with_rtt <typeidx>` allocates a structure with RTT information determining its [runtime type](#values) and initialises its fields with given values
- `struct.new_with_rtt $t : [t'* (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = struct (mut t')*`
- this is a *constant instruction*

* `struct.new_default_with_rtt <typeidx>` allocates a structure of type `$t` and initialises its fields with default values
- `struct.new_default_with_rtt $t : [(rtt $t)] -> [(ref $t)]`
- iff `expand($t) = struct (mut t')*`
- and all `t'*` are defaultable
- this is a *constant instruction*

* `struct.get_<sx>? <typeidx> <fieldidx>` reads field `i` from a structure
- `struct.get_<sx>? $t i : [(ref null $t)] -> [t]`
Expand All @@ -460,12 +462,39 @@ This can compile to machine code that (1) reads the RTT from `$x`, (2) checks th

* `array.new_with_rtt <typeidx>` allocates an array with RTT information determining its [runtime type](#values)
- `array.new_with_rtt $t : [t' i32 (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = array (var t')`
- iff `expand($t) = array (mut t')`
- this is a *constant instruction*

* `array.new_default_with_rtt <typeidx>` allocates an array and initialises its fields with the default value
- `array.new_default_with_rtt $t : [i32 (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = array (var t')`
- iff `expand($t) = array (mut t')`
- and `t'` is defaultable
- this is a *constant instruction*

* `array.new_fixed <typeidx> <N>` allocates an array of fixed size and initialises it from operands
- `array.new_fixed $t N : [t^N (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = array (mut t')`
- this is a *constant instruction*

* `array.new_data <typeidx> <dataidx>` allocates an array and initialises it from a data segment
- `array.new_data $t $d : [i32 i32 (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = array (mut t')`
- and `t'` is numeric or packed numeric
- and `$d` is a defined data segment
- the 1st operand is the `offset` into the segment
- the 2nd operand is the `size` of the array
- traps if `offset + |t'|*size > len($d)`
- this is a *constant instruction*

* `array.new_elem <typeidx> <elemidx>` allocates an array and initialises it from an element segment
- `array.new_elem $t $e : [i32 i32 (rtt $t)] -> [(ref $t)]`
- iff `expand($t) = array (mut t')`
- and `t'` is a reference type
- and `$e` is a defined element segment
- the 1st operand is the `offset` into the segment
- the 2nd operand is the `size` of the array
- traps if `offset + size > len($e)`
- note: for now, this is _not_ a constant instruction, in order to side-step issues of recursion between binary sections; this restriction will be lifted later

* `array.get_<sx>? <typeidx>` reads an element from an array
- `array.get_<sx>? $t : [(ref null $t) i32] -> [t]`
Expand Down Expand Up @@ -642,6 +671,10 @@ Note: These instructions allow an operand of unrelated reference type, even thou
In order to allow RTTs to be initialised as globals, the following extensions are made to the definition of *constant expressions*:

* `rtt.canon` is a constant instruction
* `i31.new` is a constant instruction
* `struct.new` and `struct.new_default` are constant instructions
* `array.new`, `array.new_default`, `array.new_fixed`, and `array.new_data` are constant instructions
- Note: `array.new_elem` is not for the time being, see above
* `global.get` is a constant instruction and can access preceding (immutable) global definitions, not just imports as in the MVP


Expand Down Expand Up @@ -737,6 +770,9 @@ The opcode for heap types is encoded as an `s33`.
| 0xfb15 | `array.get_u $t` | `$t : typeidx` |
| 0xfb16 | `array.set $t` | `$t : typeidx` |
| 0xfb17 | `array.len` | `_ : u32` (TODO: remove, was typeidx) |
| 0xfb19 | `array.new_fixed $t N` | `$t : typeidx`, `N : u32` |
| 0xfb1b | `array.new_data $t $d` | `$t : typeidx`, `$d : dataidx` |
| 0xfb1c | `array.new_elem $t $e` | `$t : typeidx`, `$e : elemidx` |
| 0xfb20 | `i31.new` | |
| 0xfb21 | `i31.get_s` | |
| 0xfb22 | `i31.get_u` | |
Expand Down Expand Up @@ -772,8 +808,6 @@ See [GC JS API document](MVP-JS.md) .

* Make rtt operands nullable?

* Make `i31.new` a constant instruction. Others too?

* Enable `i31` as a type definition.

* Should reference types be generalised to *unions*, e.g., of the form `(ref null? i31? data? func? extern? $t?)`? Perhaps even allowing multiple concrete types?
Expand Down
51 changes: 42 additions & 9 deletions proposals/gc/Post-MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,50 @@ To that end, _bulk copying_ instructions could be added, similar to the [bulk in
- `struct.copy $d : [(ref $d) (ref $s)] -> []` where both `$d` and `$s` are struct types, `$d` has only mutable fields, and `$s <: $d` modulo mutability

* An instruction for bulk copying an array range:
- `array.copy $d : [(ref $d) i32 (ref $s) i32 i32] -> []`
- iff both `$d` and `$s` are array types
- and `$d` has mutable element type
- and `$s <: $d` modulo mutability
- the remaining operands are destination and source offset and length of the range
- `array.copy $d $s : [(ref null $d) i32 (ref null $s) i32 i32] -> []`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should move array.copy to the MVP. J2CL has found it very useful in improving performance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be opposed to that, but it's a bit harder to argue that it is "MVP" material, given that it can be compiled out easily. Therefore, I would keep that decision separate from this PR, which focusses on an actual new ability, namely ways to create (useful) immutable arrays.

(Also, I'm not very keen on writing the test cases for such an instruction, so somebody else would have to own it as a PR. ;) )

- iff `expand($d) = array (var t1)`
- and `expand($s) = array (mut t2)`
- and `t2 <: t1`
- the 1st i32 operand is the `destination` offset in the first array
- the 2nd i32 operand is the `source` offset in the second array
- the 3rd i32 operand is the `length` of the array subrange
- traps if either array is null
- traps if `destination + length > len(array1)`
- traps if `source + length > len(array2)`

* An instruction for bulk setting an array range:
- `array.fill $d : [(ref $d) i32 t i32] -> []`
- iff `$d = (array (mut st))`
- and `t = unpacked(st)`
- the remaining operands are destination offset, initialisation value, and length of the range
- `array.fill $t : [(ref null $t) i32 t i32] -> []`
- iff `expand($t) = array (var t')`
- and `t = unpacked(t')`
- the 1st operand is the `offset` in the array
- the 2nd operand is the `length` of the array subrange
- traps if the array is null
- traps if `offset + length > len(array)`

* An instruction to (re)initialise an array range from a data segment
- `array.init_data $t $d : [(ref null $t) i32 i32 i32] -> []`
- iff `expand($t) = array (var t')`
- and `t'` is numeric or packed numeric
- and `$d` is a defined data segment
- the 1st operand is the `destination` offset in the array
- the 2nd operand is the `source` offset in the segment
- the 3rd operand is the `length` of the array subrange
- traps if the array is null
- traps if `destination + length > len(array)`
- traps if `source + |t'|*length > len($d)`

* An instruction to (re)initialise an array range from an element segment
- `array.init_elem $t $e : [(ref null $t) i32 i32 i32] -> []`
- iff `expand($t) = array (var t')`
- and `t'` is a reference type
- and `$e` is a defined element segment
- the 1st operand is the `destination` offset in the array
- the 2nd operand is the `source` offset in the segment
- the 3rd operand is the `length` of the array subrange
- traps if the array is null
- traps if `destination + length > len(array)`
- traps if `source + length > len($e)`



## Array with Fields
Expand Down