@@ -1132,9 +1132,12 @@ the outside world through an export.
1132
1132
1133
1133
Given the above closure arguments, ` canon_lift ` is defined:
1134
1134
``` python
1135
- def canon_lift (callee_opts , callee_instance , callee , functype , args ):
1136
- trap_if(not callee_instance.may_enter)
1137
- callee_instance.may_enter = False
1135
+ def canon_lift (callee_opts , callee_instance , callee , functype , args , called_as_export ):
1136
+ if called_as_export:
1137
+ trap_if(not callee_instance.may_enter)
1138
+ callee_instance.may_enter = False
1139
+ else :
1140
+ assert (not callee_instance.may_enter)
1138
1141
1139
1142
assert (callee_instance.may_leave)
1140
1143
callee_instance.may_leave = False
@@ -1150,7 +1153,8 @@ def canon_lift(callee_opts, callee_instance, callee, functype, args):
1150
1153
def post_return ():
1151
1154
if callee_opts.post_return is not None :
1152
1155
callee_opts.post_return(flat_results)
1153
- callee_instance.may_enter = True
1156
+ if called_as_export:
1157
+ callee_instance.may_enter = True
1154
1158
1155
1159
return (result, post_return)
1156
1160
```
@@ -1161,29 +1165,20 @@ boundaries. Thus, if a component wishes to signal an error, it must use some
1161
1165
sort of explicit type such as ` expected ` (whose ` error ` case particular
1162
1166
language bindings may choose to map to and from exceptions).
1163
1167
1164
- The clearing of ` may_enter ` for the entire duration of ` canon_lift ` and the
1165
- fact that ` canon_lift ` brackets all calls into a component ensure that
1166
- components cannot be reentered, which is a [ component invariant] . Furthermore,
1167
- because ` may_enter ` is not cleared on the exceptional exit path taken by
1168
- ` trap() ` , if there is a trap during Core WebAssembly execution or
1169
- lifting/lowering, the component is left permanently un-enterable, ensuring the
1170
- lockdown-after-trap [ component invariant] .
1168
+ The ` called_as_export ` parameter indicates whether ` canon_lift ` is being called
1169
+ as part of a component export or whether this ` canon_lift ` is being called
1170
+ internally (for example, by a child component instance). By clearing
1171
+ ` may_enter ` for the duration of ` canon_lift ` when called as an export, the
1172
+ dynamic traps ensure that components cannot be reentered, which is a [ component
1173
+ invariant] . Furthermore, because ` may_enter ` is not cleared on the exceptional
1174
+ exit path taken by ` trap() ` , if there is a trap during Core WebAssembly
1175
+ execution or lifting/lowering, the component is left permanently un-enterable,
1176
+ ensuring the lockdown-after-trap [ component invariant] .
1171
1177
1172
1178
The contract assumed by ` canon_lift ` (and ensured by ` canon_lower ` below) is
1173
1179
that the caller of ` canon_lift ` * must* call ` post_return ` right after lowering
1174
- ` result ` . This ordering ensures that the engine can reliably copy directly from
1175
- the callee's linear memory (read by ` lift ` ) into the caller's linear memory
1176
- (written by ` lower ` ). If ` post_return ` were called earlier (e.g., before
1177
- ` canon_lift ` returned), the callee's linear memory would have already been
1178
- freed and so the engine would need to eagerly make an intermediate copy in
1179
- ` lift ` .
1180
-
1181
- The ` may_leave ` guard wrapping the lowering of parameters conservatively
1182
- ensures that ` realloc ` calls during lowering do not call imports that
1183
- indirectly re-enter the instance that lifted the same parameters. While the
1184
- ` may_enter ` guards of * those* component instances would also prevent this
1185
- re-entrance, it would be an error that only manifested in certain component
1186
- linking configurations, hence the eager error helps ensure compositionality.
1180
+ ` result ` . This ensures that ` post_return ` can be used to perform cleanup
1181
+ actions after the lowering is complete.
1187
1182
1188
1183
1189
1184
### ` lower `
@@ -1230,19 +1225,26 @@ lifting and lowering), with a few exceptions:
1230
1225
the caller pass in a pointer to caller-allocated memory as a final
1231
1226
` i32 ` parameter.
1232
1227
1233
- A useful consequence of the above rules for ` may_enter ` and ` may_leave ` is that
1234
- attempting to ` canon lower ` to a ` callee ` in the same instance is a guaranteed,
1235
- immediate trap which a link-time compiler can eagerly compile to an
1236
- ` unreachable ` . This avoids what would otherwise be a surprising form of memory
1237
- aliasing that could introduce obscure bugs.
1238
-
1239
- The net effect here is that any cross-component call necessarily
1240
- transits through a composed ` canon_lower ` /` canon_lift ` pair, allowing a link-time
1241
- compiler to fuse the lifting/lowering steps of these two definitions into a
1242
- single, efficient trampoline. This fusion model allows efficient compilation of
1243
- the permissive [ subtyping] ( Subtyping.md ) allowed between components (including
1244
- the elimination of string operations on the labels of records and variants) as
1245
- well as post-MVP [ adapter functions] .
1228
+ Since any cross-component call necessarily transits through a statically-known
1229
+ ` canon_lower ` +` canon_lift ` call pair, an AOT compiler can fuse ` canon_lift ` and
1230
+ ` canon_lower ` into a single, efficient trampoline. This allows efficient
1231
+ compilation of the permissive [ subtyping] ( Subtyping.md ) allowed between
1232
+ components (including the elimination of string operations on the labels of
1233
+ records and variants) as well as post-MVP [ adapter functions] .
1234
+
1235
+ The ` may_leave ` flag set during lowering in ` canon_lift ` and ` canon_lower `
1236
+ ensures that the relative ordering of the side effects of ` lift ` and ` lower `
1237
+ cannot be observed via import calls and thus an implementation may reliably
1238
+ interleave ` lift ` and ` lower ` whenever making a cross-component call to avoid
1239
+ the intermediate copy performed by ` lift ` . This unobservability of interleaving
1240
+ depends on the shared-nothing property of components which guarantees that all
1241
+ the low-level state touched by ` lift ` and ` lower ` are disjoint. Though it
1242
+ should be rare, same-component-instance ` canon_lift ` +` canon_lower ` call pairs
1243
+ are technically allowed by the above rules (and may arise unintentionally in
1244
+ component reexport scenarios). Such cases can be statically distinguished by
1245
+ the AOT compiler as requiring an intermediate copy to implement the above
1246
+ ` lift ` -then-` lower ` semantics.
1247
+
1246
1248
1247
1249
1248
1250
[ Canonical Definitions ] : Explainer.md#canonical-definitions
0 commit comments