@@ -1313,281 +1313,6 @@ the AOT compiler as requiring an intermediate copy to implement the above
1313
1313
` lift ` -then-` lower ` semantics.
1314
1314
1315
1315
1316
- ## Canonical ABI
1317
-
1318
- The above ` canon ` definitions are parameterized, giving each component a small
1319
- space of ABI options for interfacing with its contained core modules. Moreover,
1320
- each component can choose its ABI options independently of each other component,
1321
- with compiled adapter trampolines handling any conversions at cross-component
1322
- call boundaries. However, in some contexts, it is useful to fix a ** single** ,
1323
- "** canonical** " ABI that is fully determined by a given component type (which
1324
- itself is fully determined by a set of [ ` wit ` ] ( WIT.md ) files). For example,
1325
- this allows existing Core WebAssembly toolchains to continue targeting [ WASI]
1326
- by importing and exporting fixed Core Module functions signatures, without
1327
- having to add any new component-model concepts.
1328
-
1329
- To support these use cases, the following section defines two new mappings:
1330
- 1 . ` canonical-module-type : componenttype -> core:moduletype `
1331
- 2 . ` lift-canonical-module : core:module -> component `
1332
-
1333
- The ` canonical-module-type ` mapping defines the collection of core function
1334
- signatures that a core module must import and export to implement the given
1335
- component type via the Canonical ABI.
1336
-
1337
- The ` lift-canonical-module ` mapping defines the runtime behavior of a core
1338
- module that has successfully implemented ` canonical-module-type ` by fixing
1339
- a canonical set of ABI options that are passed to the above-defined ` canon `
1340
- definitions.
1341
-
1342
- Together, these definitions are intended to satisfy the invariant:
1343
- ```
1344
- for all m : core:module and ct : componenttype:
1345
- module-type(m) = canonical-module-type(ct) implies ct = type-of(lift-canonical-module(m))
1346
- ```
1347
- One consequence of this is that the canonical ` core:moduletype ` must encode
1348
- enough high-level type information for ` lift-canonical-module ` to be able to
1349
- reconstruct a working component. This is achieved using [ name mangling] . Unlike
1350
- traditional C-family name mangling, which have a limited character set imposed
1351
- by linkers and aim to be space-efficient enough to support millions of
1352
- * internal* names, the Canonical ABI can use any valid UTF-8 string and only
1353
- needs to mangle * external* names, of which there will only be a handful.
1354
- Therefore, squeezing out every byte is a lower concern and so, for simplicity
1355
- and readability, type information is mangled using a subset of the
1356
- [ ` wit ` ] ( WIT.md ) syntax.
1357
-
1358
- One final point of note is that ` lift-canonical-module ` is only able to produce
1359
- a * subset* of all possible components (e.g., not covering nesting and
1360
- virtualization scenarios); to express the full variety of components, a
1361
- toolchain needs to emit proper components directly. Thus, the Canonical ABI
1362
- serves as an incremental adoption path to the full component model, allowing
1363
- existing Core WebAssembly toolchains to produce simple components simply by
1364
- emitting module imports and exports with the appropriate mangled names (e.g.,
1365
- in LLVM using the [ ` import_name ` ] and [ ` export_name ` ] attributes).
1366
-
1367
-
1368
- ### Canonical Module Type
1369
-
1370
- For the same reason that core module and component [ binaries] ( Binary.md )
1371
- include a version number (that is intended to never change after it reaches
1372
- 1.0), the Canonical ABI defines its own version that is explicitly declared by
1373
- a core module. Before reaching stable 1.0, the Canonical ABI is explicitly
1374
- allowed to make breaking changes, so this version also serves the purpose of
1375
- coordinating breaking changes in pre-1.0 tools and runtimes.
1376
- ``` python
1377
- CABI_VERSION = ' 0.1'
1378
- ```
1379
- Working top-down, a canonical module type is defined by the following mapping:
1380
- ``` python
1381
- def canonical_module_type (ct : ComponentType) -> ModuleType:
1382
- start_params, import_funcs = mangle_instances(ct.imports)
1383
- start_results, export_funcs = mangle_instances(ct.exports)
1384
-
1385
- imports = []
1386
- for name,ft in import_funcs:
1387
- flat_ft = flatten_functype(ft, ' lower' )
1388
- imports.append(CoreImportDecl(' ' , mangle_funcname(name, ft), flat_ft))
1389
-
1390
- exports = []
1391
- exports.append(CoreExportDecl(' cabi_memory' , CoreMemoryType(initial = 0 , maximum = None )))
1392
- exports.append(CoreExportDecl(' cabi_realloc' , CoreFuncType([' i32' ,' i32' ,' i32' ,' i32' ], [' i32' ])))
1393
-
1394
- start_ft = FuncType(start_params, start_results)
1395
- start_name = mangle_funcname(' cabi_start{cabi=' + CABI_VERSION + ' }' , start_ft)
1396
- exports.append(CoreExportDecl(start_name, flatten_functype(start_ft, ' lift' )))
1397
-
1398
- for name,ft in export_funcs:
1399
- flat_ft = flatten_functype(ft, ' lift' )
1400
- exports.append(CoreExportDecl(mangle_funcname(name, ft), flat_ft))
1401
- if any (contains_dynamic_allocation(t) for t in ft.results):
1402
- exports.append(CoreExportDecl(' cabi_post_' + name, CoreFuncType(flat_ft.results, [])))
1403
-
1404
- return ModuleType(imports, exports)
1405
-
1406
- def contains_dynamic_allocation (t ):
1407
- match despecialize(t):
1408
- case String() : return True
1409
- case List(t) : return True
1410
- case Record(fields) : return any (contains_dynamic_allocation(f.t) for f in fields)
1411
- case Variant(cases) : return any (contains_dynamic_allocation(c.t) for c in cases)
1412
- case _ : return False
1413
- ```
1414
- This definition starts by mangling all nested instances into the names of the
1415
- leaf fields, so that instances can be subsequently ignored. Next, each
1416
- component-level function import/export is mapped to corresponding core function
1417
- import/export with the function type mangled into the name. Additionally, each
1418
- export whose return type implies possible dynamic allocation is given a
1419
- ` post-return ` function so that it can deallocate after the caller reads the
1420
- return value. Lastly, all value imports and exports are concatenated into a
1421
- synthetic ` cabi_start ` function that is called immediately after instantiation.
1422
-
1423
- For imports (which in Core WebAssembly are [ two-level] ), the first-level name
1424
- is set to be a zero-length string so that the entire rest of the first-level
1425
- string space is available for [ shared-everything dynamic linking] .
1426
-
1427
- For imports and exports, the Canonical ABI assumes that ` _ ` is not a valid
1428
- character in a component-level import/export (as is currently the case in ` wit `
1429
- [ identifiers] ( WIT.md#identifiers ) ) and thus can safely be used to prefix
1430
- auxiliary Canonical ABI-induced imports/exports.
1431
-
1432
- #### Instance type mangling
1433
-
1434
- Instance-type mangling recursively builds a dotted path string (of instance names)
1435
- that is included in the mangled core import/export name:
1436
- ``` python
1437
- def mangle_instances (xs , path = ' ' ):
1438
- values = []
1439
- funcs = []
1440
- for x in xs:
1441
- name = path + x.name
1442
- match x.t:
1443
- case ValueType(t):
1444
- values.append( (name, t) )
1445
- case FuncType(params,results):
1446
- funcs.append( (name, x.t) )
1447
- case InstanceType(exports):
1448
- vs,fs = mangle_instances(exports, name + ' .' )
1449
- values += vs
1450
- funcs += fs
1451
- case TypeType(bounds):
1452
- assert (False ) # TODO : resource types
1453
- case ComponentType(imports, exports):
1454
- assert (False ) # TODO : `canon instantiate`
1455
- case ModuleType(imports, exports):
1456
- assert (False ) # TODO : canonical shared-everything linking
1457
- return (values, funcs)
1458
- ```
1459
- The three ` TODO ` cases are intended to be filled in by future PRs extending
1460
- the Canonical ABI.
1461
-
1462
- #### Function type mangling
1463
-
1464
- Function types are mangled into [ ` wit ` ] ( WIT.md ) -compatible syntax:
1465
- ``` python
1466
- def mangle_funcname (name , ft ):
1467
- params = mangle_named_types(ft.params)
1468
- if len (ft.results) == 1 and isinstance (ft.results[0 ], ValType):
1469
- results = mangle_valtype(ft.results[0 ])
1470
- else :
1471
- results = mangle_named_types(ft.results)
1472
- return f ' { name} : func { params} -> { results} '
1473
-
1474
- def mangle_named_types (nts ):
1475
- assert (all (type (nt) == tuple and len (nt) == 2 for nt in nts))
1476
- mangled_elems = (nt[0 ] + ' : ' + mangle_valtype(nt[1 ]) for nt in nts)
1477
- return ' (' + ' , ' .join(mangled_elems) + ' )'
1478
- ```
1479
-
1480
- #### Value type mangling
1481
-
1482
- Value types are similarly mangled into [ ` wit ` ] ( WIT.md ) -compatible syntax,
1483
- recursively:
1484
-
1485
- ```
1486
- def mangle_valtype(t):
1487
- match t:
1488
- case Bool() : return 'bool'
1489
- case S8() : return 's8'
1490
- case U8() : return 'u8'
1491
- case S16() : return 's16'
1492
- case U16() : return 'u16'
1493
- case S32() : return 's32'
1494
- case U32() : return 'u32'
1495
- case S64() : return 's64'
1496
- case U64() : return 'u64'
1497
- case Float32() : return 'float32'
1498
- case Float64() : return 'float64'
1499
- case Char() : return 'char'
1500
- case String() : return 'string'
1501
- case List(t) : return 'list<' + mangle_valtype(t) + '>'
1502
- case Record(fields) : return mangle_recordtype(fields)
1503
- case Tuple(ts) : return mangle_tupletype(ts)
1504
- case Flags(labels) : return mangle_flags(labels)
1505
- case Variant(cases) : return mangle_varianttype(cases)
1506
- case Enum(labels) : return mangle_enumtype(labels)
1507
- case Union(ts) : return mangle_uniontype(ts)
1508
- case Option(t) : return mangle_optiontype(t)
1509
- case Result(ok,error) : return mangle_resulttype(ok,error)
1510
-
1511
- def mangle_recordtype(fields):
1512
- mangled_fields = (f.label + ': ' + mangle_valtype(f.t) for f in fields)
1513
- return 'record { ' + ', '.join(mangled_fields) + ' }'
1514
-
1515
- def mangle_tupletype(ts):
1516
- return 'tuple<' + ', '.join(mangle_valtype(t) for t in ts) + '>'
1517
-
1518
- def mangle_flags(labels):
1519
- return 'flags { ' + ', '.join(labels) + ' }'
1520
-
1521
- def mangle_varianttype(cases):
1522
- mangled_cases = ('{label}{payload}'.format(
1523
- label = c.label,
1524
- payload = '' if c.t is None else '(' + mangle_valtype(c.t) + ')')
1525
- for c in cases)
1526
- return 'variant { ' + ', '.join(mangled_cases) + ' }'
1527
-
1528
- def mangle_enumtype(labels):
1529
- return 'enum { ' + ', '.join(labels) + ' }'
1530
-
1531
- def mangle_uniontype(ts):
1532
- return 'union { ' + ', '.join(mangle_valtype(t) for t in ts) + ' }'
1533
-
1534
- def mangle_optiontype(t):
1535
- return 'option<' + mangle_valtype(t) + '>'
1536
-
1537
- def mangle_resulttype(ok, error):
1538
- match (ok, error):
1539
- case (None, None) : return 'result'
1540
- case (None, _) : return 'result<_, ' + mangle_valtype(error) + '>'
1541
- case (_, None) : return 'result<' + mangle_valtype(ok) + '>'
1542
- case (_, _) : return 'result<' + mangle_valtype(ok) + ', ' + mangle_valtype(error) + '>'
1543
- ```
1544
- As an example, given a component type:
1545
- ``` wasm
1546
- (component
1547
- (import "foo" (func))
1548
- (import "a" (instance
1549
- (export "bar" (func (param "x" u32) (param "y" u32) (result u32)))
1550
- ))
1551
- (import "v1" (value string))
1552
- (export "baz" (func (param "s" string) (result string)))
1553
- (export "v2" (value list<list<string>>))
1554
- )
1555
- ```
1556
- the ` canonical_module_type ` would be:
1557
- ``` wasm
1558
- (module
1559
- (import "" "foo: func() -> ()" (func))
1560
- (import "" "a.bar: func(x: u32, y: u32) -> u32" (func param i32 i32) (result i32))
1561
- (export "cabi_memory" (memory 0))
1562
- (export "cabi_realloc" (func (param i32 i32 i32 i32) (result i32)))
1563
- (export "cabi_start{cabi=0.1}: func(v1: string) -> (v2: list<list<string>>)" (func (param i32 i32) (result i32)))
1564
- (export "baz: func(s: string) -> string" (func (param i32 i32) (result i32)))
1565
- (export "cabi_post_baz" (func (param i32)))
1566
- )
1567
- ```
1568
-
1569
- ### Lifting Canonical Modules
1570
-
1571
- TODO
1572
-
1573
- ``` python
1574
- class Module :
1575
- t: ModuleType
1576
- instantiate: Callable[typing.List[typing.Tuple[str ,str ,Value]], typing.List[typing.Tuple[str ,Value]]]
1577
-
1578
- class Component :
1579
- t: ComponentType
1580
- instantiate: Callable[typing.List[typing.Tuple[str ,any ]], typing.List[typing.Tuple[str ,any ]]]
1581
-
1582
- def lift_canonical_module (module : Module) -> Component:
1583
- # TODO : define component.instantiate by:
1584
- # 1. creating canonical import adapters
1585
- # 2. creating a core module instance that imports (1)
1586
- # 3. creating canonical export adapters from the exports of (2)
1587
- pass
1588
- ```
1589
-
1590
-
1591
1316
1592
1317
[ Canonical Definitions ] : Explainer.md#canonical-definitions
1593
1318
[ `canonopt` ] : Explainer.md#canonical-definitions
0 commit comments