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

General Interface-level helper functions #65

Open
jgravelle-google opened this issue Sep 5, 2019 · 2 comments
Open

General Interface-level helper functions #65

jgravelle-google opened this issue Sep 5, 2019 · 2 comments

Comments

@jgravelle-google
Copy link
Contributor

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.)
@lukewagner
Copy link
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).

@fitzgen
Copy link
Contributor

fitzgen commented Sep 16, 2019

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants