@@ -134,6 +134,18 @@ def_instruction! {
134134 /// of a single huge function.
135135 LiftNamedFromMemory { ty: TypeId , offset: ArchitectureSize } : [ 1 ] => [ 1 ] ,
136136
137+ /// Pops a value to lower and then a base pointer from the stack, calls a
138+ /// pre-generated, shared per-type lower helper function to write the
139+ /// named aggregate type `ty` at the constant `offset` from that
140+ /// pointer, producing no result.
141+ ///
142+ /// This is the lower-side counterpart of `LiftNamedFromMemory`: it
143+ /// outlines the canonical-ABI "write to memory" of large named
144+ /// record/variant types into shared helper functions instead of
145+ /// inlining the full recursive lower at every use site. The value
146+ /// operand is `operands[0]` and the base pointer is `operands[1]`.
147+ LowerNamedToMemory { ty: TypeId , offset: ArchitectureSize } : [ 2 ] => [ 0 ] ,
148+
137149 /// Pops a pointer from the stack and then an `i32` value.
138150 /// Stores the value in little-endian at the pointer specified plus the
139151 /// constant `offset`.
@@ -816,6 +828,19 @@ pub trait Bindgen {
816828 let _ = ( resolve, id) ;
817829 None
818830 }
831+
832+ /// Returns the name of a pre-generated, shared lower helper function for
833+ /// the named aggregate type `id`, if one exists.
834+ ///
835+ /// When this returns `Some`, the canonical-ABI lower of that type (in
836+ /// `write_to_memory`) is "outlined" into a call to the named helper
837+ /// (emitted as `Instruction::LowerNamedToMemory`) instead of being inlined
838+ /// recursively. Generators that do not implement helper outlining should
839+ /// return `None` (the default).
840+ fn lower_helper_name ( & self , resolve : & Resolve , id : TypeId ) -> Option < String > {
841+ let _ = ( resolve, id) ;
842+ None
843+ }
819844}
820845
821846/// Generates an abstract sequence of instructions which represents this
@@ -914,6 +939,38 @@ pub fn lift_from_memory_root<B: Bindgen>(
914939 generator. stack . pop ( ) . unwrap ( )
915940}
916941
942+ /// Like [`lower_to_memory`], but used to generate the *body* of an outlined
943+ /// lower helper for the named type `skip_outline_root`.
944+ ///
945+ /// The root type itself is lowered inline (one level deep) while nested named
946+ /// aggregate types are outlined into calls to their own shared helpers (see
947+ /// [`Bindgen::lower_helper_name`] and [`Instruction::LowerNamedToMemory`]).
948+ ///
949+ /// `skip_outline_root` must be the id of the named aggregate (record/variant)
950+ /// being lowered, and `ty` must be exactly `Type::Id(skip_outline_root)` (not
951+ /// an alias to it) so that the single-shot inline of the root in
952+ /// `write_to_memory` lands on the intended type.
953+ pub fn lower_to_memory_root < B : Bindgen > (
954+ resolve : & Resolve ,
955+ bindgen : & mut B ,
956+ address : B :: Operand ,
957+ value : B :: Operand ,
958+ ty : & Type ,
959+ skip_outline_root : TypeId ,
960+ ) {
961+ assert ! (
962+ matches!( ty, Type :: Id ( id) if * id == skip_outline_root) ,
963+ "lower_to_memory_root requires `ty` to be `Type::Id(skip_outline_root)`, \
964+ not an alias or other type"
965+ ) ;
966+ let mut generator = Generator :: new ( resolve, bindgen) ;
967+ generator. realloc = Some ( Realloc :: Export ( "cabi_realloc" ) ) ;
968+ generator. lower_outline_root = Some ( skip_outline_root) ;
969+ generator. stack . push ( value) ;
970+ generator. write_to_memory ( ty, address, Default :: default ( ) ) ;
971+ debug_assert ! ( generator. stack. is_empty( ) ) ;
972+ }
973+
917974/// Used in a similar manner as the `Interface::call` function except is
918975/// used to generate the `post-return` callback for `func`.
919976///
@@ -1060,6 +1117,11 @@ struct Generator<'a, B: Bindgen> {
10601117 /// level) while nested named aggregates are still outlined into their own
10611118 /// helpers. `None` everywhere else (so all eligible named types outline).
10621119 lift_outline_root : Option < TypeId > ,
1120+ /// Like `lift_outline_root`, but for the lower side: when generating the
1121+ /// body of an outlined lower helper for a named type, this holds that
1122+ /// type's id so its own top-level lower is inlined (one level) while
1123+ /// nested named aggregates are still outlined. `None` everywhere else.
1124+ lower_outline_root : Option < TypeId > ,
10631125}
10641126
10651127const MAX_FLAT_PARAMS : usize = 16 ;
@@ -1076,6 +1138,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
10761138 return_pointer : None ,
10771139 realloc : None ,
10781140 lift_outline_root : None ,
1141+ lower_outline_root : None ,
10791142 }
10801143 }
10811144
@@ -2029,7 +2092,21 @@ impl<'a, B: Bindgen> Generator<'a, B> {
20292092 Type :: String => self . write_list_to_memory ( ty, addr, offset) ,
20302093 Type :: ErrorContext => self . lower_and_emit ( ty, addr, & I32Store { offset } ) ,
20312094
2032- Type :: Id ( id) => match & self . resolve . types [ id] . kind {
2095+ Type :: Id ( id) => {
2096+ // Outline the lower of large named aggregate types into shared
2097+ // helper functions instead of inlining the full recursive
2098+ // lower here. `lower_outline_root` is `Some(id)` only while
2099+ // generating that type's own helper body, in which case its
2100+ // top level is inlined (one level) and nested types still
2101+ // outline.
2102+ let is_outline_root = self . lower_outline_root == Some ( id) ;
2103+ self . lower_outline_root = None ;
2104+ if !is_outline_root && self . bindgen . lower_helper_name ( self . resolve , id) . is_some ( ) {
2105+ self . stack . push ( addr) ;
2106+ self . emit ( & Instruction :: LowerNamedToMemory { ty : id, offset } ) ;
2107+ return ;
2108+ }
2109+ match & self . resolve . types [ id] . kind {
20332110 TypeDefKind :: Type ( t) => self . write_to_memory ( t, addr, offset) ,
20342111 TypeDefKind :: List ( _) => self . write_list_to_memory ( ty, addr, offset) ,
20352112 // Maps have the same linear memory layout as list<tuple<K, V>>.
@@ -2143,6 +2220,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
21432220 id,
21442221 } ) ;
21452222 }
2223+ }
21462224 } ,
21472225 }
21482226 }
0 commit comments