@@ -500,6 +500,48 @@ impl<'a> Module<'a> {
500500 live_iter ( & self . live_tables , self . tables . iter ( ) )
501501 }
502502
503+ /// Returns whether the module appears to have been generated by TinyGo. Absent
504+ /// any evidence, returns false.
505+ fn is_from_tiny_go ( & self ) -> bool {
506+ self . producers . as_ref ( ) . map_or ( false , |producers| {
507+ producers. iter ( ) . any ( |( field, values) | {
508+ field == "processed-by"
509+ && values
510+ . iter ( )
511+ . any ( |( processor, _version) | processor == "TinyGo" )
512+ } )
513+ } )
514+ }
515+
516+ /// Returns a new Wasm function body which implements `cabi_realloc` to call a
517+ /// `malloc` that's exported from the main module.
518+ fn realloc_via_malloc ( & self ) -> Result < wasm_encoder:: Function > {
519+ // (func $cabi_realloc (;someIndex;) (type someType) (param i32 i32 i32 i32) (result i32)
520+ // local.get 3
521+ // call $malloc
522+ // )
523+
524+ // Find `malloc`.
525+ let malloc_index = match self . exports . get ( "malloc" ) {
526+ None => bail ! (
527+ "found no exported `malloc` function to use to allocate the adapter's state in a TinyGo-derived module"
528+ ) ,
529+ Some ( export) => {
530+ if export. kind != ExternalKind :: Func {
531+ bail ! ( "found a `malloc` export, but it was not a function" )
532+ }
533+ export. index
534+ }
535+ } ;
536+
537+ let mut func = wasm_encoder:: Function :: new ( [ ] ) ;
538+
539+ func. instructions ( ) . local_get ( 3 ) ; // desired new size
540+ func. instructions ( ) . call ( malloc_index) ;
541+
542+ Ok ( func)
543+ }
544+
503545 /// Encodes this `Module` to a new wasm module which is gc'd and only
504546 /// contains the items that are live as calculated by the `liveness` pass.
505547 fn encode ( & mut self , main_module_realloc : Option < & str > ) -> Result < Vec < u8 > > {
@@ -733,10 +775,20 @@ impl<'a> Module<'a> {
733775
734776 if sp. is_some ( ) && ( realloc_index. is_none ( ) || allocation_state. is_none ( ) ) {
735777 // Either the main module does _not_ export a realloc function, or it is not safe to use for stack
736- // allocation because we have no way to short-circuit reentrance, so we'll use `memory.grow` instead.
778+ // allocation because we have no way to short-circuit reentrance, so we'll provide our own realloc
779+ // function instead.
737780 realloc_index = Some ( num_func_imports + funcs. len ( ) ) ;
738781 funcs. function ( add_realloc_type ( & mut types) ) ;
739- code. function ( & realloc_via_memory_grow ( ) ) ;
782+
783+ // If it appears the module was emitted by TinyGo, we delegate to its `malloc()` function. (TinyGo
784+ // assumes its GC has reign over the whole memory and quickly overwrites the adapter's State struct
785+ // unless we inform its GC of the memory we use.)
786+ if self . is_from_tiny_go ( ) {
787+ code. function ( & self . realloc_via_malloc ( ) ?) ;
788+ } else {
789+ // If it's not TinyGo, use `memory.grow` instead.
790+ code. function ( & realloc_via_memory_grow ( ) ) ;
791+ }
740792 }
741793
742794 // Inject a start function to initialize the stack pointer which will be local to this module. This only
0 commit comments