Skip to content
This repository was archived by the owner on Aug 17, 2022. It is now read-only.
This repository was archived by the owner on Aug 17, 2022. It is now read-only.

General Interface-level helper functions #65

Description

@jgravelle-google

We started discussing this in the last video call. I believe it will be useful in a variety of contexts to have helper functions in the interface. I propose allowing something like:

(@interface func $foo
  ;; can take mixed wasm + interface type signature
  (param $ptr i32) (param $str string)
  (result string f32)
  ;; adapter instructions
)

Similar to declarations of imports+exports, but with a body.

Use cases + examples:

  1. Rather than specifying locals in adapter functions or stack-modifying instructions (dup, pick, et al), we can reuse function arguments as a natural scoping mechanism. We need to do something here to avoid duplicating function calls to exports when we want to reference a value twice.
  (@interface func $read-cstr (param $ptr i32) (result string)
    ;; Helper function to read a C char* from a single pointer
    arg.get $ptr
    arg.get $ptr
    call-export "strlen"
    read-utf8 "mem"
  )
  ;; C function: char* getFoo(void);
  (@interface func (export "getFoo") (result string)
    ;; Need some way to deduplicate the result pointer here, we can't
    ;; duplicate the call to getFoo
    call-export "getFoo"
    call $read-cstr
  )
  1. For call-defer-export, we could use helper function to defer arbitrary adapter instructions rather than only wasm exports.
  (@interface func $fooExport (; signature of foo ;)
    ;; can still call exports via helper functions
    call-export "foo"
  )
  (@interface func $doSomethingComplex
    ;; ...
  )
  (@interface func (export "doThing")
    ;; ...
    defer $fooExport
    ;; ...
    defer $doSomethingComplex
    ;; ...
  )
  1. For code compression, toolchains can encode sequences of instructions as a single call to a helper function (e.g. for structs with many fields)
  (@interface func $ptrToFoo (param $ptr i32) (result $Foo)
    (make-record $Foo
      (call-export "getFieldA" (arg.get $ptr))
      (call-export "getFieldB" (arg.get $ptr))
      (call-export "getFieldC" (arg.get $ptr))
      (call-export "getFieldD" (arg.get $ptr))
      ;; ...
    )
  )
  1. For dealing with sequences, higher-order functions like map, fold, read-until, etc. are likely to be useful for doing things more complex than copying buffers of primitive values. (This depends on the design of sequences.)

Activity

  1. lukewagner commented on Sep 6, 2019

    @lukewagner
    Member

    Thanks for the writeup and agreed that this seems useful and even, for item (4) in your list, necessary. One important detail is that these helper functions should by design be inlineable into the calling adapter function, so that no actual dynamic call stack is needed. In particular, this rules out mutual recursion (one trivial way to implement/specify this is by saying that an adapter function with index i can only call adapter functions with index i-1).

  2. fitzgen commented on Sep 16, 2019

    @fitzgen
    Contributor

    Great proposal and write up, thanks for laying out the motivation so clearly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions