Skip to content

Commit 4efc080

Browse files
committed
Editorial: move the context/runtime sections to the top of CanonicalABI.md
1 parent 5a91c06 commit 4efc080

File tree

2 files changed

+292
-290
lines changed

2 files changed

+292
-290
lines changed

design/mvp/CanonicalABI.md

Lines changed: 174 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ walkthrough of the static structure of a component and the
88
being specified here.
99

1010
* [Supporting definitions](#supporting-definitions)
11-
* [Despecialization](#despecialization)
12-
* [Alignment](#alignment)
13-
* [Element Size](#element-size)
1411
* [Context](#context)
1512
* [Canonical ABI Options](#canonical-abi-options)
1613
* [Runtime State](#runtime-state)
14+
* [Despecialization](#despecialization)
15+
* [Alignment](#alignment)
16+
* [Element Size](#element-size)
1717
* [Loading](#loading)
1818
* [Storing](#storing)
1919
* [Flattening](#flattening)
@@ -71,177 +71,9 @@ intentionally propagate OOM into the appropriate explicit return value of the
7171
function's declared return type.
7272

7373

74-
### Despecialization
75-
76-
[In the explainer][Type Definitions], component value types are classified as
77-
either *fundamental* or *specialized*, where the specialized value types are
78-
defined by expansion into fundamental value types. In most cases, the canonical
79-
ABI of a specialized value type is the same as its expansion so, to avoid
80-
repetition, the other definitions below use the following `despecialize`
81-
function to replace specialized value types with their expansion:
82-
```python
83-
def despecialize(t):
84-
match t:
85-
case Tuple(ts) : return Record([ Field(str(i), t) for i,t in enumerate(ts) ])
86-
case Enum(labels) : return Variant([ Case(l, None) for l in labels ])
87-
case Option(t) : return Variant([ Case("none", None), Case("some", t) ])
88-
case Result(ok, error) : return Variant([ Case("ok", ok), Case("error", error) ])
89-
case _ : return t
90-
```
91-
The specialized value types `string` and `flags` are missing from this list
92-
because they are given specialized canonical ABI representations distinct from
93-
their respective expansions.
94-
95-
96-
### Alignment
97-
98-
Each value type is assigned an [alignment] which is used by subsequent
99-
Canonical ABI definitions. Presenting the definition of `alignment` piecewise,
100-
we start with the top-level case analysis:
101-
```python
102-
def alignment(t):
103-
match despecialize(t):
104-
case Bool() : return 1
105-
case S8() | U8() : return 1
106-
case S16() | U16() : return 2
107-
case S32() | U32() : return 4
108-
case S64() | U64() : return 8
109-
case F32() : return 4
110-
case F64() : return 8
111-
case Char() : return 4
112-
case String() : return 4
113-
case List(t, l) : return alignment_list(t, l)
114-
case Record(fields) : return alignment_record(fields)
115-
case Variant(cases) : return alignment_variant(cases)
116-
case Flags(labels) : return alignment_flags(labels)
117-
case Own(_) | Borrow(_) : return 4
118-
```
119-
120-
List alignment is the same as tuple alignment when the length is fixed and
121-
otherwise uses the alignment of pointers.
122-
```python
123-
def alignment_list(elem_type, maybe_length):
124-
if maybe_length is not None:
125-
return alignment(elem_type)
126-
return 4
127-
```
128-
129-
Record alignment is tuple alignment, with the definitions split for reuse below:
130-
```python
131-
def alignment_record(fields):
132-
a = 1
133-
for f in fields:
134-
a = max(a, alignment(f.t))
135-
return a
136-
```
137-
138-
As an optimization, `variant` discriminants are represented by the smallest integer
139-
covering the number of cases in the variant (with cases numbered in order from
140-
`0` to `len(cases)-1`). Depending on the payload type, this can allow more
141-
compact representations of variants in memory. This smallest integer type is
142-
selected by the following function, used above and below:
143-
```python
144-
def alignment_variant(cases):
145-
return max(alignment(discriminant_type(cases)), max_case_alignment(cases))
146-
147-
def discriminant_type(cases):
148-
n = len(cases)
149-
assert(0 < n < (1 << 32))
150-
match math.ceil(math.log2(n)/8):
151-
case 0: return U8()
152-
case 1: return U8()
153-
case 2: return U16()
154-
case 3: return U32()
155-
156-
def max_case_alignment(cases):
157-
a = 1
158-
for c in cases:
159-
if c.t is not None:
160-
a = max(a, alignment(c.t))
161-
return a
162-
```
163-
164-
As an optimization, `flags` are represented as packed bit-vectors. Like variant
165-
discriminants, `flags` use the smallest integer that fits all the bits, falling
166-
back to sequences of `i32`s when there are more than 32 flags.
167-
```python
168-
def alignment_flags(labels):
169-
n = len(labels)
170-
assert(0 < n <= 32)
171-
if n <= 8: return 1
172-
if n <= 16: return 2
173-
return 4
174-
```
175-
176-
Handle types are passed as `i32` indices into the `Table[HandleElem]`
177-
introduced below.
178-
179-
180-
### Element Size
181-
182-
Each value type is also assigned an `elem_size` which is the number of bytes
183-
used when values of the type are stored as elements of a `list`. Having this
184-
byte size be a static property of the type instead of attempting to use a
185-
variable-length element-encoding scheme both simplifies the implementation and
186-
maps well to languages which represent `list`s as random-access arrays. Empty
187-
types, such as records with no fields, are not permitted, to avoid
188-
complications in source languages.
189-
```python
190-
def elem_size(t):
191-
match despecialize(t):
192-
case Bool() : return 1
193-
case S8() | U8() : return 1
194-
case S16() | U16() : return 2
195-
case S32() | U32() : return 4
196-
case S64() | U64() : return 8
197-
case F32() : return 4
198-
case F64() : return 8
199-
case Char() : return 4
200-
case String() : return 8
201-
case List(t, l) : return elem_size_list(t, l)
202-
case Record(fields) : return elem_size_record(fields)
203-
case Variant(cases) : return elem_size_variant(cases)
204-
case Flags(labels) : return elem_size_flags(labels)
205-
case Own(_) | Borrow(_) : return 4
206-
207-
def elem_size_list(elem_type, maybe_length):
208-
if maybe_length is not None:
209-
return maybe_length * elem_size(elem_type)
210-
return 8
211-
212-
def elem_size_record(fields):
213-
s = 0
214-
for f in fields:
215-
s = align_to(s, alignment(f.t))
216-
s += elem_size(f.t)
217-
assert(s > 0)
218-
return align_to(s, alignment_record(fields))
219-
220-
def align_to(ptr, alignment):
221-
return math.ceil(ptr / alignment) * alignment
222-
223-
def elem_size_variant(cases):
224-
s = elem_size(discriminant_type(cases))
225-
s = align_to(s, max_case_alignment(cases))
226-
cs = 0
227-
for c in cases:
228-
if c.t is not None:
229-
cs = max(cs, elem_size(c.t))
230-
s += cs
231-
return align_to(s, alignment_variant(cases))
232-
233-
def elem_size_flags(labels):
234-
n = len(labels)
235-
assert(0 < n <= 32)
236-
if n <= 8: return 1
237-
if n <= 16: return 2
238-
return 4
239-
```
240-
24174
### Context
24275

243-
The subsequent definitions of lifting and lowering depend on three kinds of
244-
ambient information:
76+
The subsequent definitions depend on three kinds of ambient information:
24577
* static ABI options supplied via [`canonopt`]
24678
* dynamic state in the containing component instance
24779
* dynamic state in the [current task]
@@ -263,6 +95,7 @@ The `cx` parameter in functions below refers to the ambient `Context`. The
26395
`Task` and `Subtask` classes derive `Context` and thus having a `task` or
26496
`subtask` also establishes the ambient `Context`.
26597

98+
26699
### Canonical ABI Options
267100

268101
The `opts` field of `Context` contains all the possible [`canonopt`]
@@ -282,6 +115,7 @@ class CanonicalOptions:
282115
reason that `async` is a keyword and most branches below want to start with the
283116
`sync = True` case.)
284117

118+
285119
### Runtime State
286120

287121
The `inst` field of `Context` points to the component instance which the
@@ -886,6 +720,174 @@ stack frame.
886720
return self.flat_results
887721
```
888722

723+
724+
### Despecialization
725+
726+
[In the explainer][Type Definitions], component value types are classified as
727+
either *fundamental* or *specialized*, where the specialized value types are
728+
defined by expansion into fundamental value types. In most cases, the canonical
729+
ABI of a specialized value type is the same as its expansion so, to avoid
730+
repetition, the other definitions below use the following `despecialize`
731+
function to replace specialized value types with their expansion:
732+
```python
733+
def despecialize(t):
734+
match t:
735+
case Tuple(ts) : return Record([ Field(str(i), t) for i,t in enumerate(ts) ])
736+
case Enum(labels) : return Variant([ Case(l, None) for l in labels ])
737+
case Option(t) : return Variant([ Case("none", None), Case("some", t) ])
738+
case Result(ok, error) : return Variant([ Case("ok", ok), Case("error", error) ])
739+
case _ : return t
740+
```
741+
The specialized value types `string` and `flags` are missing from this list
742+
because they are given specialized canonical ABI representations distinct from
743+
their respective expansions.
744+
745+
746+
### Alignment
747+
748+
Each value type is assigned an [alignment] which is used by subsequent
749+
Canonical ABI definitions. Presenting the definition of `alignment` piecewise,
750+
we start with the top-level case analysis:
751+
```python
752+
def alignment(t):
753+
match despecialize(t):
754+
case Bool() : return 1
755+
case S8() | U8() : return 1
756+
case S16() | U16() : return 2
757+
case S32() | U32() : return 4
758+
case S64() | U64() : return 8
759+
case F32() : return 4
760+
case F64() : return 8
761+
case Char() : return 4
762+
case String() : return 4
763+
case List(t, l) : return alignment_list(t, l)
764+
case Record(fields) : return alignment_record(fields)
765+
case Variant(cases) : return alignment_variant(cases)
766+
case Flags(labels) : return alignment_flags(labels)
767+
case Own(_) | Borrow(_) : return 4
768+
```
769+
770+
List alignment is the same as tuple alignment when the length is fixed and
771+
otherwise uses the alignment of pointers.
772+
```python
773+
def alignment_list(elem_type, maybe_length):
774+
if maybe_length is not None:
775+
return alignment(elem_type)
776+
return 4
777+
```
778+
779+
Record alignment is tuple alignment, with the definitions split for reuse below:
780+
```python
781+
def alignment_record(fields):
782+
a = 1
783+
for f in fields:
784+
a = max(a, alignment(f.t))
785+
return a
786+
```
787+
788+
As an optimization, `variant` discriminants are represented by the smallest integer
789+
covering the number of cases in the variant (with cases numbered in order from
790+
`0` to `len(cases)-1`). Depending on the payload type, this can allow more
791+
compact representations of variants in memory. This smallest integer type is
792+
selected by the following function, used above and below:
793+
```python
794+
def alignment_variant(cases):
795+
return max(alignment(discriminant_type(cases)), max_case_alignment(cases))
796+
797+
def discriminant_type(cases):
798+
n = len(cases)
799+
assert(0 < n < (1 << 32))
800+
match math.ceil(math.log2(n)/8):
801+
case 0: return U8()
802+
case 1: return U8()
803+
case 2: return U16()
804+
case 3: return U32()
805+
806+
def max_case_alignment(cases):
807+
a = 1
808+
for c in cases:
809+
if c.t is not None:
810+
a = max(a, alignment(c.t))
811+
return a
812+
```
813+
814+
As an optimization, `flags` are represented as packed bit-vectors. Like variant
815+
discriminants, `flags` use the smallest integer that fits all the bits, falling
816+
back to sequences of `i32`s when there are more than 32 flags.
817+
```python
818+
def alignment_flags(labels):
819+
n = len(labels)
820+
assert(0 < n <= 32)
821+
if n <= 8: return 1
822+
if n <= 16: return 2
823+
return 4
824+
```
825+
826+
Handle types are passed as `i32` indices into the `Table[HandleElem]`
827+
introduced below.
828+
829+
830+
### Element Size
831+
832+
Each value type is also assigned an `elem_size` which is the number of bytes
833+
used when values of the type are stored as elements of a `list`. Having this
834+
byte size be a static property of the type instead of attempting to use a
835+
variable-length element-encoding scheme both simplifies the implementation and
836+
maps well to languages which represent `list`s as random-access arrays. Empty
837+
types, such as records with no fields, are not permitted, to avoid
838+
complications in source languages.
839+
```python
840+
def elem_size(t):
841+
match despecialize(t):
842+
case Bool() : return 1
843+
case S8() | U8() : return 1
844+
case S16() | U16() : return 2
845+
case S32() | U32() : return 4
846+
case S64() | U64() : return 8
847+
case F32() : return 4
848+
case F64() : return 8
849+
case Char() : return 4
850+
case String() : return 8
851+
case List(t, l) : return elem_size_list(t, l)
852+
case Record(fields) : return elem_size_record(fields)
853+
case Variant(cases) : return elem_size_variant(cases)
854+
case Flags(labels) : return elem_size_flags(labels)
855+
case Own(_) | Borrow(_) : return 4
856+
857+
def elem_size_list(elem_type, maybe_length):
858+
if maybe_length is not None:
859+
return maybe_length * elem_size(elem_type)
860+
return 8
861+
862+
def elem_size_record(fields):
863+
s = 0
864+
for f in fields:
865+
s = align_to(s, alignment(f.t))
866+
s += elem_size(f.t)
867+
assert(s > 0)
868+
return align_to(s, alignment_record(fields))
869+
870+
def align_to(ptr, alignment):
871+
return math.ceil(ptr / alignment) * alignment
872+
873+
def elem_size_variant(cases):
874+
s = elem_size(discriminant_type(cases))
875+
s = align_to(s, max_case_alignment(cases))
876+
cs = 0
877+
for c in cases:
878+
if c.t is not None:
879+
cs = max(cs, elem_size(c.t))
880+
s += cs
881+
return align_to(s, alignment_variant(cases))
882+
883+
def elem_size_flags(labels):
884+
n = len(labels)
885+
assert(0 < n <= 32)
886+
if n <= 8: return 1
887+
if n <= 16: return 2
888+
return 4
889+
```
890+
889891
### Loading
890892

891893
The `load` function defines how to read a value of a given value type `t`

0 commit comments

Comments
 (0)