component-model icon indicating copy to clipboard operation
component-model copied to clipboard

Proposal: simple instance lowering?

Open peterhuene opened this issue 3 years ago • 4 comments

Overview of current problem

Assume there is an interface named example with this definition (in wit):

func1: function(x: string) -> string
func2: function(y: list<s32>)
func3: function(z: tuple<s8, s64>)

And a component imports the example interface as an instance:

(component
  (module (;0;) ...) ;; inner core implementation module
  (type (;0;) (func (param "x" string) (result string)))
  (type (;1;) (list s32))
  (type (;2;) (func (param "y" (type 1))))
  (type (;3;) (tuple s8 s64))
  (type (;4;) (func (param "z" (type 3))))
  (type (;5;) 
    (instance
      (alias outer 1 (type (;0;) 0))
      (export "func1" (type 0))
      (alias outer 1 (type (;1;) 2))
      (export "func2" (type 1))
      (alias outer 1 (type (;2;) 4))
      (export "func3" (type 2))
    )
  )
  (import "example" (instance (;0;) (type 5)))
  (module (;1;) ...) ;; shim module for use with `into` option for import lowering
  (instance (;1;) (instantiate (module 1)))
  ;; begin lowering boilerplate
  (alias export (instance 0) "func1" (func (;0;)))
  (alias export (instance 0) "func2" (func (;1;)))
  (alias export (instance 0) "func3" (func (;2;)))
  (func (;3;) (canon.lower utf8 (into (instance 1)) (func 0)))
  (func (;4;) (canon.lower utf8 (into (instance 1)) (func 1)))
  (func (;5;) (canon.lower utf8 (into (instance 1)) (func 2)))
  (instance (;2;) core (export "func1" (func 3)) (export "func2" (func 4)) (export "func3" (func 5)))
  ;; end lowering boilerplate
  ;; we now have instance 2 (a lowered form of instance 0) that the core implementation module may import
  (instance (;3;) (instantiate (module 0) (with "example" (instance 2))))
  ...
)

To be able to pass the instance import through to the inner core module, we must iterate all of the functions exported on the instance, alias them into the component's function index space, lower each individual function, create a new core instance with the lowered functions, and then finally pass the instance through as an instantiation argument.

This is potentially a lot of boilerplate needed to lower each imported instance to the inner core module; additionally both the aliased functions and their lowered forms don't really need to exist in the component's function index space as they only serve as something temporary to synthesize the instance being imported by the core implementation module.

Proposal

Add a way to declare lowered instances to both the text and binary formats. A lowered instance is a new instance with a core instance type that exports functions of the same names as the component instance type, but with lowered function types as if each exported function were explicitly lowered.

Binary format change

instanceexpr ::=
               ...
               | 0x03 opt*:<canonopt>* i:<instanceidx> => (instance lower i))

Text format change

instanceexpr ::=
               ...
               lower <canonopt>* (instance <instanceidx>)

Validation

  • i:instanceidx is a component instance type that only exports functions.
  • The canonical options are validated according to the same rules as lowering functions; applies to all functions exported by the instance.

Production

Produces an instance of core instance type where all exports are functions of the lowered function types.

Example

Lowering an instance of type:

(type
  (instance
    (type (;0;) (func (param "x" string) (result string)))
    (type (;1;) (list s32))
    (type (;2;) (func (param "y" (type 1))))
    (type (;3;) (tuple s8 s64))
    (type (;4;) (func (param "z" (type 3))))
    (export "func1" (type 0))
    (export "func2" (type 2))
    (export "func3" (type 4))
  )
)

Produces an instance of (hypothetical) core type:

(type
  (instance core
    (type (func (param i32 i32 i32)))
    (type (func (param i32 i32)))
    (type (func (param i32 i64)))
    (export "func1" (type 0))
    (export "func2" (type 1))
    (export "func3" (type 2))
  )
)

peterhuene avatar Mar 22 '22 22:03 peterhuene

Conversely, it would be nice to have a "lifting" sugar for core instances: given a core instance and a component instance type, lift function exports from the core instance that match (by name) the exports in the component instance type and produce a new instance that exports the lifted component functions.

This would be potentially useful for exporting implementations of "interfaces" as instances from components.

peterhuene avatar Mar 23 '22 07:03 peterhuene

I think this could makes sense as a size optimization (I don't think there's any expressivity gain?), but I think it'd be good to have some size data to motivate the optimization first.

lukewagner avatar Mar 23 '22 23:03 lukewagner

There shouldn't be any expressivity gain; this would solely be for a encoding/decoding optimization as there's potentially a lot of repeated strings to lower/lift entire instances: one needs to alias the items from the instance, then lower/lift individually, and then usually create a new instance based off the lowered/lifted items.

A shortened example from above:

(alias export (instance 0) "foo" (func (;0;)))
(alias export (instance 0) "bar" (func (;1;)))
(alias export (instance 0) "baz" (func (;2;)))
(func (;3;) (canon.lower (func 0)))
(func (;4;) (canon.lower (func 1)))
(func (;5;) (canon.lower (func 2)))
(instance (;1;) core (export "foo" (func 3)) (export "bar" (func 4)) (export "baz" (func 5)))

It'd be nice to not have to repeat every export name in the instance both for aliasing and for the creation of the "lowered" instance. A shortcut form such as (instance lower (instance 0)) requires no repeated export names at all.

peterhuene avatar Mar 29 '22 02:03 peterhuene

Yeah, that makes sense. If this was a programming language, the convenience would definitely be worth it. As an assembly language, given that I think this additional feature doesn't subsume or obviate any existing features, it's just a question of motivating the additional feature with some sort of quantifiable benefit (e.g., % binary size reduction).

lukewagner avatar Mar 29 '22 14:03 lukewagner