Skip to content

Commit 171e706

Browse files
committed
Merge elem_size and byte_size into size
1 parent 5679ffb commit 171e706

File tree

3 files changed

+59
-71
lines changed

3 files changed

+59
-71
lines changed

design/mvp/CanonicalABI.md

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -141,23 +141,10 @@ def alignment_flags(labels):
141141

142142
### Size
143143

144-
Each interface type is assigned two slightly-different measures of "size":
145-
* its "byte size", which is the smallest number of bytes covering all its
146-
fields when stored at an aligned address in linear memory; and
147-
* its "element size", which is the size of the type when stored as an element
148-
of a list, which may include additional padding at the end to ensure the
149-
alignment of the next element.
150-
151-
These two measures are defined by the following functions, which build on
152-
the preceding alignment functions:
144+
Each interface type is also assigned a `size`, measured in bytes, which
145+
corresponds the `sizeof` operator in C:
153146
```python
154-
def elem_size(t):
155-
return align_to(byte_size(t), alignment(t))
156-
157-
def align_to(ptr, alignment):
158-
return math.ceil(ptr / alignment) * alignment
159-
160-
def byte_size(t):
147+
def size(t):
161148
match despecialize(t):
162149
case Bool() : return 1
163150
case S8() | U8() : return 1
@@ -168,26 +155,30 @@ def byte_size(t):
168155
case Float64() : return 8
169156
case Char() : return 4
170157
case String() | List(_) : return 8
171-
case Record(fields) : return byte_size_record(fields)
172-
case Variant(cases) : return byte_size_variant(cases)
173-
case Flags(labels) : return byte_size_flags(labels)
158+
case Record(fields) : return size_record(fields)
159+
case Variant(cases) : return size_variant(cases)
160+
case Flags(labels) : return size_flags(labels)
174161

175-
def byte_size_record(fields):
162+
def size_record(fields):
176163
s = 0
177164
for f in fields:
178165
s = align_to(s, alignment(f.t))
179-
s += byte_size(f.t)
180-
return s
166+
s += size(f.t)
167+
return align_to(s, alignment(Record(fields)))
168+
169+
def align_to(ptr, alignment):
170+
return math.ceil(ptr / alignment) * alignment
181171

182-
def byte_size_variant(cases):
183-
s = byte_size(discriminant_type(cases))
172+
def size_variant(cases):
173+
s = size(discriminant_type(cases))
184174
s = align_to(s, max_alignment(types_of(cases)))
185175
cs = 0
186176
for c in cases:
187-
cs = max(cs, byte_size(c.t))
188-
return s + cs
177+
cs = max(cs, size(c.t))
178+
s += cs
179+
return align_to(s, alignment(Variant(cases)))
189180

190-
def byte_size_flags(labels):
181+
def size_flags(labels):
191182
n = len(labels)
192183
if n <= 8: return 1
193184
if n <= 16: return 2
@@ -316,8 +307,7 @@ def load_string_from_range(opts, ptr, tagged_code_units):
316307
return (s, opts.string_encoding, tagged_code_units)
317308
```
318309

319-
Lists and records are loaded by recursively loading their elements/fields.
320-
Note that lists use `elem_size` while records use `byte_size`.
310+
Lists and records are loaded by recursively loading their elements/fields:
321311
```python
322312
def load_list(opts, ptr, elem_type):
323313
begin = load_int(opts, ptr, 4)
@@ -326,18 +316,18 @@ def load_list(opts, ptr, elem_type):
326316

327317
def load_list_from_range(opts, ptr, length, elem_type):
328318
trap_if(ptr != align_to(ptr, alignment(elem_type)))
329-
trap_if(ptr + length * elem_size(elem_type) > len(opts.memory))
319+
trap_if(ptr + length * size(elem_type) > len(opts.memory))
330320
a = []
331321
for i in range(length):
332-
a.append(load(opts, ptr + i * elem_size(elem_type), elem_type))
322+
a.append(load(opts, ptr + i * size(elem_type), elem_type))
333323
return a
334324

335325
def load_record(opts, ptr, fields):
336326
record = {}
337327
for field in fields:
338328
ptr = align_to(ptr, alignment(field.t))
339329
record[field.label] = load(opts, ptr, field.t)
340-
ptr += byte_size(field.t)
330+
ptr += size(field.t)
341331
return record
342332
```
343333
As a technical detail: the `align_to` in the loop in `load_record` is
@@ -354,7 +344,7 @@ tables at compile-time so that variant-passing is always O(1) and not involving
354344
string operations.
355345
```python
356346
def load_variant(opts, ptr, cases):
357-
disc_size = byte_size(discriminant_type(cases))
347+
disc_size = size(discriminant_type(cases))
358348
disc = load_int(opts, ptr, disc_size)
359349
ptr += disc_size
360350
trap_if(disc >= len(cases))
@@ -382,7 +372,7 @@ derived from the ordered labels of the `flags` type. The code here takes
382372
advantage of Python's support for integers of arbitrary width.
383373
```python
384374
def load_flags(opts, ptr, labels):
385-
i = load_int(opts, ptr, byte_size_flags(labels))
375+
i = load_int(opts, ptr, size_flags(labels))
386376
return unpack_flags_from_int(i, labels)
387377

388378
def unpack_flags_from_int(i, labels):
@@ -647,20 +637,20 @@ def store_list(opts, v, ptr, elem_type):
647637
store_int(opts, length, ptr + 4, 4)
648638

649639
def store_list_into_range(opts, v, elem_type):
650-
byte_length = len(v) * elem_size(elem_type)
640+
byte_length = len(v) * size(elem_type)
651641
trap_if(byte_length >= (1 << 32))
652642
ptr = opts.realloc(0, 0, alignment(elem_type), byte_length)
653643
trap_if(ptr != align_to(ptr, alignment(elem_type)))
654644
trap_if(ptr + byte_length > len(opts.memory))
655645
for i,e in enumerate(v):
656-
store(opts, e, elem_type, ptr + i * elem_size(elem_type))
646+
store(opts, e, elem_type, ptr + i * size(elem_type))
657647
return (ptr, len(v))
658648

659649
def store_record(opts, v, ptr, fields):
660650
for f in fields:
661651
ptr = align_to(ptr, alignment(f.t))
662652
store(opts, v[f.label], f.t, ptr)
663-
ptr += byte_size(f.t)
653+
ptr += size(f.t)
664654
```
665655

666656
Variants are stored using the `|`-separated list of `defaults-to` cases built
@@ -672,7 +662,7 @@ case indices to the consumer's case indices.
672662
```python
673663
def store_variant(opts, v, ptr, cases):
674664
case_index, case_value = match_case(v, cases)
675-
disc_size = byte_size(discriminant_type(cases))
665+
disc_size = size(discriminant_type(cases))
676666
store_int(opts, case_index, ptr, disc_size)
677667
ptr += disc_size
678668
ptr = align_to(ptr, max_alignment(types_of(cases)))
@@ -697,7 +687,7 @@ to variants.
697687
```python
698688
def store_flags(opts, v, ptr, labels):
699689
i = pack_flags_into_int(v, labels)
700-
store_int(opts, i, ptr, byte_size_flags(labels))
690+
store_int(opts, i, ptr, size_flags(labels))
701691

702692
def pack_flags_into_int(v, labels):
703693
i = 0
@@ -1067,7 +1057,7 @@ def lower(opts, max_flat, vs, ts, out_param = None):
10671057
tuple_type = Tuple(functype.params)
10681058
tuple_value = {str(i): v for i,v in enumerate(vs)}
10691059
if out_param is None:
1070-
ptr = opts.realloc(0, 0, alignment(tuple_type), byte_size(tuple_type))
1060+
ptr = opts.realloc(0, 0, alignment(tuple_type), size(tuple_type))
10711061
else:
10721062
ptr = out_param.next('i32')
10731063
trap_if(ptr != align_to(ptr, alignment(tuple_type)))

design/mvp/canonical-abi/definitions.py

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,7 @@ def alignment_flags(labels):
147147

148148
### Size
149149

150-
def elem_size(t):
151-
return align_to(byte_size(t), alignment(t))
152-
153-
def align_to(ptr, alignment):
154-
return math.ceil(ptr / alignment) * alignment
155-
156-
def byte_size(t):
150+
def size(t):
157151
match despecialize(t):
158152
case Bool() : return 1
159153
case S8() | U8() : return 1
@@ -164,26 +158,30 @@ def byte_size(t):
164158
case Float64() : return 8
165159
case Char() : return 4
166160
case String() | List(_) : return 8
167-
case Record(fields) : return byte_size_record(fields)
168-
case Variant(cases) : return byte_size_variant(cases)
169-
case Flags(labels) : return byte_size_flags(labels)
161+
case Record(fields) : return size_record(fields)
162+
case Variant(cases) : return size_variant(cases)
163+
case Flags(labels) : return size_flags(labels)
170164

171-
def byte_size_record(fields):
165+
def size_record(fields):
172166
s = 0
173167
for f in fields:
174168
s = align_to(s, alignment(f.t))
175-
s += byte_size(f.t)
176-
return s
169+
s += size(f.t)
170+
return align_to(s, alignment(Record(fields)))
171+
172+
def align_to(ptr, alignment):
173+
return math.ceil(ptr / alignment) * alignment
177174

178-
def byte_size_variant(cases):
179-
s = byte_size(discriminant_type(cases))
175+
def size_variant(cases):
176+
s = size(discriminant_type(cases))
180177
s = align_to(s, max_alignment(types_of(cases)))
181178
cs = 0
182179
for c in cases:
183-
cs = max(cs, byte_size(c.t))
184-
return s + cs
180+
cs = max(cs, size(c.t))
181+
s += cs
182+
return align_to(s, alignment(Variant(cases)))
185183

186-
def byte_size_flags(labels):
184+
def size_flags(labels):
187185
n = len(labels)
188186
if n <= 8: return 1
189187
if n <= 16: return 2
@@ -294,24 +292,24 @@ def load_list(opts, ptr, elem_type):
294292

295293
def load_list_from_range(opts, ptr, length, elem_type):
296294
trap_if(ptr != align_to(ptr, alignment(elem_type)))
297-
trap_if(ptr + length * elem_size(elem_type) > len(opts.memory))
295+
trap_if(ptr + length * size(elem_type) > len(opts.memory))
298296
a = []
299297
for i in range(length):
300-
a.append(load(opts, ptr + i * elem_size(elem_type), elem_type))
298+
a.append(load(opts, ptr + i * size(elem_type), elem_type))
301299
return a
302300

303301
def load_record(opts, ptr, fields):
304302
record = {}
305303
for field in fields:
306304
ptr = align_to(ptr, alignment(field.t))
307305
record[field.label] = load(opts, ptr, field.t)
308-
ptr += byte_size(field.t)
306+
ptr += size(field.t)
309307
return record
310308

311309
#
312310

313311
def load_variant(opts, ptr, cases):
314-
disc_size = byte_size(discriminant_type(cases))
312+
disc_size = size(discriminant_type(cases))
315313
disc = load_int(opts, ptr, disc_size)
316314
ptr += disc_size
317315
trap_if(disc >= len(cases))
@@ -336,7 +334,7 @@ def find_case(label, cases):
336334
#
337335

338336
def load_flags(opts, ptr, labels):
339-
i = load_int(opts, ptr, byte_size_flags(labels))
337+
i = load_int(opts, ptr, size_flags(labels))
340338
return unpack_flags_from_int(i, labels)
341339

342340
def unpack_flags_from_int(i, labels):
@@ -534,26 +532,26 @@ def store_list(opts, v, ptr, elem_type):
534532
store_int(opts, length, ptr + 4, 4)
535533

536534
def store_list_into_range(opts, v, elem_type):
537-
byte_length = len(v) * elem_size(elem_type)
535+
byte_length = len(v) * size(elem_type)
538536
trap_if(byte_length >= (1 << 32))
539537
ptr = opts.realloc(0, 0, alignment(elem_type), byte_length)
540538
trap_if(ptr != align_to(ptr, alignment(elem_type)))
541539
trap_if(ptr + byte_length > len(opts.memory))
542540
for i,e in enumerate(v):
543-
store(opts, e, elem_type, ptr + i * elem_size(elem_type))
541+
store(opts, e, elem_type, ptr + i * size(elem_type))
544542
return (ptr, len(v))
545543

546544
def store_record(opts, v, ptr, fields):
547545
for f in fields:
548546
ptr = align_to(ptr, alignment(f.t))
549547
store(opts, v[f.label], f.t, ptr)
550-
ptr += byte_size(f.t)
548+
ptr += size(f.t)
551549

552550
#
553551

554552
def store_variant(opts, v, ptr, cases):
555553
case_index, case_value = match_case(v, cases)
556-
disc_size = byte_size(discriminant_type(cases))
554+
disc_size = size(discriminant_type(cases))
557555
store_int(opts, case_index, ptr, disc_size)
558556
ptr += disc_size
559557
ptr = align_to(ptr, max_alignment(types_of(cases)))
@@ -572,7 +570,7 @@ def match_case(v, cases):
572570

573571
def store_flags(opts, v, ptr, labels):
574572
i = pack_flags_into_int(v, labels)
575-
store_int(opts, i, ptr, byte_size_flags(labels))
573+
store_int(opts, i, ptr, size_flags(labels))
576574

577575
def pack_flags_into_int(v, labels):
578576
i = 0
@@ -848,7 +846,7 @@ def lower(opts, max_flat, vs, ts, out_param = None):
848846
tuple_type = Tuple(functype.params)
849847
tuple_value = {str(i): v for i,v in enumerate(vs)}
850848
if out_param is None:
851-
ptr = opts.realloc(0, 0, alignment(tuple_type), byte_size(tuple_type))
849+
ptr = opts.realloc(0, 0, alignment(tuple_type), size(tuple_type))
852850
else:
853851
ptr = out_param.next('i32')
854852
trap_if(ptr != align_to(ptr, alignment(tuple_type)))

design/mvp/canonical-abi/run_tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,14 @@ def test_heap(t, expect, args, byte_array):
239239
test_heap(List(Tuple([U16(),U8()])), [mk_tup(6,7),mk_tup(8,9)], [0,2],
240240
[6,0, 7, 0x0ff, 8,0, 9, 0xff])
241241
test_heap(List(Tuple([Tuple([U16(),U8()]),U8()])), [mk_tup([4,5],6),mk_tup([7,8],9)], [0,2],
242-
[4,0, 5, 6, 7,0, 8, 9])
242+
[4,0, 5,0xff, 6,0xff, 7,0, 8,0xff, 9,0xff])
243243
test_heap(List(Union([Unit(),U8(),Tuple([U8(),U16()])])), [{'0':{}}, {'1':42}, {'2':mk_tup(6,7)}], [0,3],
244244
[0,0xff,0xff,0xff,0xff,0xff, 1,0xff,42,0xff,0xff,0xff, 2,0xff,6,0xff,7,0])
245245
test_heap(List(Union([U32(),U8()])), [{'0':256}, {'1':42}], [0,2],
246246
[0,0xff,0xff,0xff,0,1,0,0, 1,0xff,0xff,0xff,42,0xff,0xff,0xff])
247247
test_heap(List(Tuple([Union([U8(),Tuple([U16(),U8()])]),U8()])),
248248
[mk_tup({'1':mk_tup(5,6)},7),mk_tup({'0':8},9)], [0,2],
249-
[1,0xff,5,0,6,7, 0,0xff,8,0xff,0xff,9])
249+
[1,0xff,5,0,6,0xff,7,0xff, 0,0xff,8,0xff,0xff,0xff,9,0xff])
250250
test_heap(List(Union([U8()])), [{'0':6},{'0':7},{'0':8}], [0,3],
251251
[0,6, 0,7, 0,8])
252252
t = List(Flags(['a','b']))

0 commit comments

Comments
 (0)