11use crate :: ScopeVec ;
2+ use crate :: component:: dfg:: AbstractInstantiations ;
23use crate :: component:: * ;
34use crate :: prelude:: * ;
45use crate :: {
@@ -8,6 +9,8 @@ use crate::{
89} ;
910use anyhow:: anyhow;
1011use anyhow:: { Result , bail} ;
12+ use cranelift_entity:: SecondaryMap ;
13+ use cranelift_entity:: packed_option:: PackedOption ;
1114use indexmap:: IndexMap ;
1215use std:: collections:: HashMap ;
1316use std:: mem;
@@ -492,12 +495,133 @@ impl<'a, 'data> Translator<'a, 'data> {
492495 & self . static_modules ,
493496 & self . static_components ,
494497 ) ?;
498+
495499 self . partition_adapter_modules ( & mut component) ;
500+
496501 let translation =
497502 component. finish ( self . types . types_mut_for_inlining ( ) , self . result . types_ref ( ) ) ?;
503+
504+ self . analyze_function_imports ( & translation) ;
505+
498506 Ok ( ( translation, self . static_modules ) )
499507 }
500508
509+ fn analyze_function_imports ( & mut self , translation : & ComponentTranslation ) {
510+ // First, abstract interpret the initializers to create a map from each
511+ // static module to its abstract set of instantiations.
512+ let mut instantiations = SecondaryMap :: < StaticModuleIndex , AbstractInstantiations > :: new ( ) ;
513+ let mut instance_to_module =
514+ PrimaryMap :: < RuntimeInstanceIndex , PackedOption < StaticModuleIndex > > :: new ( ) ;
515+ for init in & translation. component . initializers {
516+ match init {
517+ GlobalInitializer :: InstantiateModule ( instantiation) => match instantiation {
518+ InstantiateModule :: Static ( module, args) => {
519+ instantiations[ * module] . join ( AbstractInstantiations :: One ( & * args) ) ;
520+ instance_to_module. push ( Some ( * module) . into ( ) ) ;
521+ }
522+ _ => {
523+ instance_to_module. push ( None . into ( ) ) ;
524+ }
525+ } ,
526+ _ => continue ,
527+ }
528+ }
529+
530+ // Second, make sure to mark exported modules as instantiated many
531+ // times, since they could be linked with who-knows-what at runtime.
532+ for item in translation. component . export_items . values ( ) {
533+ if let Export :: ModuleStatic { index, .. } = item {
534+ instantiations[ * index] . join ( AbstractInstantiations :: Many )
535+ }
536+ }
537+
538+ // Finally, iterate over our instantiations and record statically-known
539+ // function imports so that they can get translated into direct calls
540+ // (and eventually get inlined) rather than indirect calls through the
541+ // imports table.
542+ for ( module, instantiations) in instantiations. iter ( ) {
543+ let args = match instantiations {
544+ dfg:: AbstractInstantiations :: Many | dfg:: AbstractInstantiations :: None => continue ,
545+ dfg:: AbstractInstantiations :: One ( args) => args,
546+ } ;
547+
548+ let mut imported_func_counter = 0_u32 ;
549+ for ( i, arg) in args. iter ( ) . enumerate ( ) {
550+ // Only consider function imports.
551+ let ( _, _, crate :: types:: EntityType :: Function ( _) ) =
552+ self . static_modules [ module] . module . import ( i) . unwrap ( )
553+ else {
554+ continue ;
555+ } ;
556+
557+ let imported_func = FuncIndex :: from_u32 ( imported_func_counter) ;
558+ imported_func_counter += 1 ;
559+ debug_assert ! (
560+ self . static_modules[ module]
561+ . module
562+ . defined_func_index( imported_func)
563+ . is_none( )
564+ ) ;
565+
566+ match arg {
567+ CoreDef :: InstanceFlags ( _) => unreachable ! ( "instance flags are not a function" ) ,
568+
569+ // We could in theory inline these trampolines, so it could
570+ // potentially make sense to record that we know this
571+ // imported function is this particular trampoline. However,
572+ // everything else is based around (module,
573+ // defined-function) pairs and these trampolines don't fit
574+ // that paradigm. Also, inlining trampolines gets really
575+ // tricky when we consider the stack pointer, frame pointer,
576+ // and return address note-taking that they do for the
577+ // purposes of stack walking. We could, with enough effort,
578+ // turn them into direct calls even though we probably
579+ // wouldn't ever inline them, but it just doesn't seem worth
580+ // the effort.
581+ CoreDef :: Trampoline ( _) => continue ,
582+
583+ // This imported function is an export from another
584+ // instance, a perfect candidate for becoming an inlinable
585+ // direct call!
586+ CoreDef :: Export ( export) => {
587+ let Some ( arg_module) = & instance_to_module[ export. instance ] . expand ( ) else {
588+ // Instance of a dynamic module that is not part of
589+ // this component, not a statically-known module
590+ // inside this component. We have to do an indirect
591+ // call.
592+ continue ;
593+ } ;
594+
595+ let ExportItem :: Index ( EntityIndex :: Function ( arg_func) ) = & export. item
596+ else {
597+ unreachable ! ( "function imports must be functions" )
598+ } ;
599+
600+ let Some ( arg_module_def_func) = self . static_modules [ * arg_module]
601+ . module
602+ . defined_func_index ( * arg_func)
603+ else {
604+ // TODO: we should ideally follow re-export chains
605+ // to bottom out the instantiation argument in
606+ // either a definition or an import at the root
607+ // component boundary. In practice, this pattern is
608+ // rare, so following these chains is left for the
609+ // Future.
610+ continue ;
611+ } ;
612+
613+ assert ! (
614+ self . static_modules[ module] . known_imported_functions[ imported_func]
615+ . is_none( )
616+ ) ;
617+ self . static_modules [ module] . known_imported_functions [ imported_func] =
618+ Some ( ( * arg_module, arg_module_def_func) ) ;
619+ }
620+ }
621+ }
622+ }
623+ }
624+
501625 fn translate_payload (
502626 & mut self ,
503627 payload : Payload < ' data > ,
0 commit comments