@@ -110,13 +110,23 @@ def alignment(t):
110
110
case F32() : return 4
111
111
case F64() : return 8
112
112
case Char() : return 4
113
- case String() | List(_) : return 4
113
+ case String() : return 4
114
+ case List(t, l) : return alignment_list(t, l)
114
115
case Record(fields) : return alignment_record(fields)
115
116
case Variant(cases) : return alignment_variant(cases)
116
117
case Flags(labels) : return alignment_flags(labels)
117
118
case Own(_) | Borrow(_) : return 4
118
119
```
119
120
121
+ List alignment is the same as tuple alignment when the length is fixed and
122
+ otherwise uses the alignment of pointers.
123
+ ``` python
124
+ def alignment_list (elem_type , maybe_length ):
125
+ if maybe_length is not None :
126
+ return alignment(elem_type)
127
+ return 4
128
+ ```
129
+
120
130
Record alignment is tuple alignment, with the definitions split for reuse below:
121
131
``` python
122
132
def alignment_record (fields ):
@@ -188,12 +198,18 @@ def elem_size(t):
188
198
case F32() : return 4
189
199
case F64() : return 8
190
200
case Char() : return 4
191
- case String() | List(_) : return 8
201
+ case String() : return 8
202
+ case List(t, l) : return elem_size_list(t, l)
192
203
case Record(fields) : return elem_size_record(fields)
193
204
case Variant(cases) : return elem_size_variant(cases)
194
205
case Flags(labels) : return elem_size_flags(labels)
195
206
case Own(_) | Borrow(_) : return 4
196
207
208
+ def elem_size_list (elem_type , maybe_length ):
209
+ if maybe_length is not None :
210
+ return maybe_length * size(elem_type)
211
+ return 8
212
+
197
213
def elem_size_record (fields ):
198
214
s = 0
199
215
for f in fields:
@@ -872,7 +888,7 @@ def load(cx, ptr, t):
872
888
case F64() : return decode_i64_as_float(load_int(cx, ptr, 8 ))
873
889
case Char() : return convert_i32_to_char(cx, load_int(cx, ptr, 4 ))
874
890
case String() : return load_string(cx, ptr)
875
- case List(t) : return load_list(cx, ptr, t)
891
+ case List(t, l ) : return load_list(cx, ptr, t, l )
876
892
case Record(fields) : return load_record(cx, ptr, fields)
877
893
case Variant(cases) : return load_variant(cx, ptr, cases)
878
894
case Flags(labels) : return load_flags(cx, ptr, labels)
@@ -992,14 +1008,19 @@ def load_string_from_range(cx, ptr, tagged_code_units):
992
1008
993
1009
Lists and records are loaded by recursively loading their elements/fields:
994
1010
``` python
995
- def load_list (cx , ptr , elem_type ):
1011
+ def load_list (cx , ptr , elem_type , maybe_length ):
1012
+ if maybe_length is not None :
1013
+ return load_list_from_valid_range(cx, ptr, maybe_length, elem_type)
996
1014
begin = load_int(cx, ptr, 4 )
997
1015
length = load_int(cx, ptr + 4 , 4 )
998
1016
return load_list_from_range(cx, begin, length, elem_type)
999
1017
1000
1018
def load_list_from_range (cx , ptr , length , elem_type ):
1001
1019
trap_if(ptr != align_to(ptr, alignment(elem_type)))
1002
1020
trap_if(ptr + length * elem_size(elem_type) > len (cx.opts.memory))
1021
+ return load_list_from_valid_range(cx, ptr, length, elem_type)
1022
+
1023
+ def load_list_from_valid_range (cx , ptr , length , elem_type ):
1003
1024
a = []
1004
1025
for i in range (length):
1005
1026
a.append(load(cx, ptr + i * elem_size(elem_type), elem_type))
@@ -1131,7 +1152,7 @@ def store(cx, v, t, ptr):
1131
1152
case F64() : store_int(cx, encode_float_as_i64(v), ptr, 8 )
1132
1153
case Char() : store_int(cx, char_to_i32(v), ptr, 4 )
1133
1154
case String() : store_string(cx, v, ptr)
1134
- case List(t) : store_list(cx, v, ptr, t)
1155
+ case List(t, l ) : store_list(cx, v, ptr, t, l )
1135
1156
case Record(fields) : store_record(cx, v, ptr, fields)
1136
1157
case Variant(cases) : store_variant(cx, v, ptr, cases)
1137
1158
case Flags(labels) : store_flags(cx, v, ptr, labels)
@@ -1420,7 +1441,11 @@ are symmetric to the loading functions. Unlike strings, lists can
1420
1441
simply allocate based on the up-front knowledge of length and static
1421
1442
element size.
1422
1443
``` python
1423
- def store_list (cx , v , ptr , elem_type ):
1444
+ def store_list (cx , v , ptr , elem_type , maybe_length ):
1445
+ if maybe_length is not None :
1446
+ assert (maybe_length == len (v))
1447
+ store_list_into_valid_range(cx, v, ptr, elem_type)
1448
+ return
1424
1449
begin, length = store_list_into_range(cx, v, elem_type)
1425
1450
store_int(cx, begin, ptr, 4 )
1426
1451
store_int(cx, length, ptr + 4 , 4 )
@@ -1431,9 +1456,12 @@ def store_list_into_range(cx, v, elem_type):
1431
1456
ptr = cx.opts.realloc(0 , 0 , alignment(elem_type), byte_length)
1432
1457
trap_if(ptr != align_to(ptr, alignment(elem_type)))
1433
1458
trap_if(ptr + byte_length > len (cx.opts.memory))
1459
+ store_list_into_valid_range(cx, v, ptr, elem_type)
1460
+ return (ptr, len (v))
1461
+
1462
+ def store_list_into_valid_range (cx , v , ptr , elem_type ):
1434
1463
for i,e in enumerate (v):
1435
1464
store(cx, e, elem_type, ptr + i * elem_size(elem_type))
1436
- return (ptr, len (v))
1437
1465
1438
1466
def store_record (cx , v , ptr , fields ):
1439
1467
for f in fields:
@@ -1587,13 +1615,23 @@ def flatten_type(t):
1587
1615
case F32() : return [' f32' ]
1588
1616
case F64() : return [' f64' ]
1589
1617
case Char() : return [' i32' ]
1590
- case String() | List(_) : return [' i32' , ' i32' ]
1618
+ case String() : return [' i32' , ' i32' ]
1619
+ case List(t, l) : return flatten_list(t, l)
1591
1620
case Record(fields) : return flatten_record(fields)
1592
1621
case Variant(cases) : return flatten_variant(cases)
1593
1622
case Flags(labels) : return [' i32' ]
1594
1623
case Own(_) | Borrow(_) : return [' i32' ]
1595
1624
```
1596
1625
1626
+ List flattening of a fixed-length list uses the same flattening as a tuple
1627
+ (via ` flatten_record ` below).
1628
+ ``` python
1629
+ def flatten_list (elem_type , maybe_length ):
1630
+ if maybe_length is not None :
1631
+ return flatten_type(elem_type) * maybe_length
1632
+ return [' i32' , ' i32' ]
1633
+ ```
1634
+
1597
1635
Record flattening simply flattens each field in sequence.
1598
1636
``` python
1599
1637
def flatten_record (fields ):
@@ -1671,7 +1709,7 @@ def lift_flat(cx, vi, t):
1671
1709
case F64() : return canonicalize_nan64(vi.next(' f64' ))
1672
1710
case Char() : return convert_i32_to_char(cx, vi.next(' i32' ))
1673
1711
case String() : return lift_flat_string(cx, vi)
1674
- case List(t) : return lift_flat_list(cx, vi, t)
1712
+ case List(t, l ) : return lift_flat_list(cx, vi, t, l )
1675
1713
case Record(fields) : return lift_flat_record(cx, vi, fields)
1676
1714
case Variant(cases) : return lift_flat_variant(cx, vi, cases)
1677
1715
case Flags(labels) : return lift_flat_flags(vi, labels)
@@ -1700,17 +1738,23 @@ def lift_flat_signed(vi, core_width, t_width):
1700
1738
return i
1701
1739
```
1702
1740
1703
- The contents of strings and lists are always stored in memory so lifting these
1704
- types is essentially the same as loading them from memory; the only difference
1705
- is that the pointer and length come from ` i32 ` values instead of from linear
1706
- memory:
1741
+ The contents of strings and variable-length lists are stored in memory so
1742
+ lifting these types is essentially the same as loading them from memory; the
1743
+ only difference is that the pointer and length come from ` i32 ` values instead
1744
+ of from linear memory. Fixed-length lists are lifted the same way as a
1745
+ tuple (via ` lift_flat_record ` below).
1707
1746
``` python
1708
1747
def lift_flat_string (cx , vi ):
1709
1748
ptr = vi.next(' i32' )
1710
1749
packed_length = vi.next(' i32' )
1711
1750
return load_string_from_range(cx, ptr, packed_length)
1712
1751
1713
- def lift_flat_list (cx , vi , elem_type ):
1752
+ def lift_flat_list (cx , vi , elem_type , maybe_length ):
1753
+ if maybe_length is not None :
1754
+ a = []
1755
+ for i in range (maybe_length):
1756
+ a.append(lift_flat(cx, vi, elem_type))
1757
+ return a
1714
1758
ptr = vi.next(' i32' )
1715
1759
length = vi.next(' i32' )
1716
1760
return load_list_from_range(cx, ptr, length, elem_type)
@@ -1791,7 +1835,7 @@ def lower_flat(cx, v, t):
1791
1835
case F64() : return [maybe_scramble_nan64(v)]
1792
1836
case Char() : return [char_to_i32(v)]
1793
1837
case String() : return lower_flat_string(cx, v)
1794
- case List(t) : return lower_flat_list(cx, v, t)
1838
+ case List(t, l ) : return lower_flat_list(cx, v, t, l )
1795
1839
case Record(fields) : return lower_flat_record(cx, v, fields)
1796
1840
case Variant(cases) : return lower_flat_variant(cx, v, cases)
1797
1841
case Flags(labels) : return lower_flat_flags(v, labels)
@@ -1811,15 +1855,23 @@ def lower_flat_signed(i, core_bits):
1811
1855
return [i]
1812
1856
```
1813
1857
1814
- Since strings and lists are stored in linear memory, lifting can reuse the
1815
- previous definitions; only the resulting pointers are returned differently
1816
- (as ` i32 ` values instead of as a pair in linear memory):
1858
+ Since strings and variable-length lists are stored in linear memory, lifting
1859
+ can reuse the previous definitions; only the resulting pointers are returned
1860
+ differently (as ` i32 ` values instead of as a pair in linear memory).
1861
+ Fixed-length lists are lowered the same way as tuples (via ` lower_flat_record `
1862
+ below).
1817
1863
``` python
1818
1864
def lower_flat_string (cx , v ):
1819
1865
ptr, packed_length = store_string_into_range(cx, v)
1820
1866
return [ptr, packed_length]
1821
1867
1822
- def lower_flat_list (cx , v , elem_type ):
1868
+ def lower_flat_list (cx , v , elem_type , maybe_length ):
1869
+ if maybe_length is not None :
1870
+ assert (maybe_length == len (v))
1871
+ flat = []
1872
+ for e in v:
1873
+ flat += lower_flat(cx, e, elem_type)
1874
+ return flat
1823
1875
(ptr, length) = store_list_into_range(cx, v, elem_type)
1824
1876
return [ptr, length]
1825
1877
```
0 commit comments