@@ -11,6 +11,8 @@ use std::fmt::Write;
1111use std:: slice:: Iter ;
1212use std:: sync:: Arc ;
1313
14+ const DEFAULT_MATCH_ARM_BODY_CLOSURE_THRESHOLD : usize = 256 ;
15+
1416/// Options for code generation.
1517#[ derive( Clone , Debug , Default ) ]
1618pub struct CodegenOptions {
@@ -28,6 +30,17 @@ pub struct CodegenOptions {
2830 /// In Cranelift this is typically controlled by a cargo feature on the
2931 /// crate that includes the generated code (e.g. `cranelift-codegen`).
3032 pub emit_logging : bool ,
33+
34+ /// Split large match arms into local closures when generating iterator terms.
35+ ///
36+ /// In Cranelift this is typically controlled by a cargo feature on the
37+ /// crate that includes the generated code (e.g. `cranelift-codegen`).
38+ pub split_match_arms : bool ,
39+
40+ /// Threshold for splitting match arms into local closures.
41+ ///
42+ /// If `None`, a default threshold is used.
43+ pub match_arm_split_threshold : Option < usize > ,
3144}
3245
3346/// A path prefix which should be replaced when printing file names.
@@ -72,10 +85,29 @@ struct BodyContext<'a, W> {
7285 is_bound : StableSet < BindingId > ,
7386 term_name : & ' a str ,
7487 emit_logging : bool ,
88+ split_match_arms : bool ,
89+ match_arm_split_threshold : Option < usize > ,
90+
91+ // Extra fields for iterator-returning terms.
92+ // These fields are used to generate optimized Rust code for iterator-returning terms.
93+ /// The number of match splits that have been generated.
94+ /// This is used to generate unique names for the match splits.
95+ match_split : usize ,
96+
97+ /// The action to take when the iterator overflows.
98+ iter_overflow_action : & ' static str ,
7599}
76100
77101impl < ' a , W : Write > BodyContext < ' a , W > {
78- fn new ( out : & ' a mut W , ruleset : & ' a RuleSet , term_name : & ' a str , emit_logging : bool ) -> Self {
102+ fn new (
103+ out : & ' a mut W ,
104+ ruleset : & ' a RuleSet ,
105+ term_name : & ' a str ,
106+ emit_logging : bool ,
107+ split_match_arms : bool ,
108+ match_arm_split_threshold : Option < usize > ,
109+ iter_overflow_action : & ' static str ,
110+ ) -> Self {
79111 Self {
80112 out,
81113 ruleset,
@@ -84,6 +116,10 @@ impl<'a, W: Write> BodyContext<'a, W> {
84116 is_bound : Default :: default ( ) ,
85117 term_name,
86118 emit_logging,
119+ split_match_arms,
120+ match_arm_split_threshold,
121+ match_split : Default :: default ( ) ,
122+ iter_overflow_action,
87123 }
88124 }
89125
@@ -426,7 +462,19 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
426462
427463 let termdata = & self . termenv . terms [ termid. index ( ) ] ;
428464 let term_name = & self . typeenv . syms [ termdata. name . index ( ) ] ;
429- let mut ctx = BodyContext :: new ( code, ruleset, term_name, options. emit_logging ) ;
465+
466+ // Split a match if the term returns an iterator.
467+ let mut ctx = BodyContext :: new (
468+ code,
469+ ruleset,
470+ term_name,
471+ options. emit_logging ,
472+ options. split_match_arms ,
473+ options. match_arm_split_threshold ,
474+ "return;" , // At top level, we just return.
475+ ) ;
476+
477+ // Generate the function signature.
430478 writeln ! ( ctx. out) ?;
431479 writeln ! (
432480 ctx. out,
@@ -470,6 +518,7 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
470518 ReturnKind :: Option => write ! ( ctx. out, "Option<{ret}>" ) ?,
471519 ReturnKind :: Plain => write ! ( ctx. out, "{ret}" ) ?,
472520 } ;
521+ // Generating the function signature is done.
473522
474523 let last_expr = if let Some ( EvalStep {
475524 check : ControlFlow :: Return { .. } ,
@@ -530,6 +579,21 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
530579 Nested :: Cases ( block. steps . iter ( ) )
531580 }
532581
582+ fn block_weight ( block : & Block ) -> usize {
583+ fn cf_weight ( cf : & ControlFlow ) -> usize {
584+ match cf {
585+ ControlFlow :: Match { arms, .. } => {
586+ arms. iter ( ) . map ( |a| Codegen :: block_weight ( & a. body ) ) . sum ( )
587+ }
588+ ControlFlow :: Equal { body, .. } => Codegen :: block_weight ( body) ,
589+ ControlFlow :: Loop { body, .. } => Codegen :: block_weight ( body) ,
590+ ControlFlow :: Return { .. } => 0 ,
591+ }
592+ }
593+
594+ block. steps . iter ( ) . map ( |s| 1 + cf_weight ( & s. check ) ) . sum ( )
595+ }
596+
533597 fn emit_block < W : Write > (
534598 & self ,
535599 ctx : & mut BodyContext < W > ,
@@ -538,8 +602,19 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
538602 last_expr : & str ,
539603 scope : StableSet < BindingId > ,
540604 ) -> std:: fmt:: Result {
541- let mut stack = Vec :: new ( ) ;
542605 ctx. begin_block ( ) ?;
606+ self . emit_block_contents ( ctx, block, ret_kind, last_expr, scope)
607+ }
608+
609+ fn emit_block_contents < W : Write > (
610+ & self ,
611+ ctx : & mut BodyContext < W > ,
612+ block : & Block ,
613+ ret_kind : ReturnKind ,
614+ last_expr : & str ,
615+ scope : StableSet < BindingId > ,
616+ ) -> std:: fmt:: Result {
617+ let mut stack = Vec :: new ( ) ;
543618 stack. push ( ( Self :: validate_block ( ret_kind, block) , last_expr, scope) ) ;
544619
545620 while let Some ( ( mut nested, last_line, scope) ) = stack. pop ( ) {
@@ -706,8 +781,8 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
706781 writeln ! ( ctx. out, "));" ) ?;
707782 writeln ! (
708783 ctx. out,
709- "{}if returns.len() >= MAX_ISLE_RETURNS {{ return; }}" ,
710- ctx. indent
784+ "{}if returns.len() >= MAX_ISLE_RETURNS {{ {} }}" ,
785+ ctx. indent, ctx . iter_overflow_action
711786 ) ?;
712787 }
713788 }
@@ -729,7 +804,42 @@ impl<L: Length, C> Length for ContextIterWrapper<L, C> {{
729804 self . emit_constraint ( ctx, source, arm) ?;
730805 write ! ( ctx. out, " =>" ) ?;
731806 ctx. begin_block ( ) ?;
732- stack. push ( ( Self :: validate_block ( ret_kind, & arm. body ) , "" , scope) ) ;
807+
808+ // Compile-time optimization: huge function bodies (often from very large match arms
809+ // of constructor bodies)cause rustc to spend a lot of time in analysis passes.
810+ // Wrap such bodies in a local closure to move the bulk of the work into a separate body
811+ // without needing to know the types of captured locals.
812+ let match_arm_body_closure_threshold = ctx
813+ . match_arm_split_threshold
814+ . unwrap_or ( DEFAULT_MATCH_ARM_BODY_CLOSURE_THRESHOLD ) ;
815+ if ctx. split_match_arms
816+ && ret_kind == ReturnKind :: Iterator
817+ && Codegen :: block_weight ( & arm. body ) > match_arm_body_closure_threshold
818+ {
819+ let closure_id = ctx. match_split ;
820+ ctx. match_split += 1 ;
821+
822+ write ! ( ctx. out, "{}if (|| -> bool" , & ctx. indent) ?;
823+ ctx. begin_block ( ) ?;
824+
825+ let old_overflow_action = ctx. iter_overflow_action ;
826+ ctx. iter_overflow_action = "return true;" ;
827+ let closure_scope = ctx. enter_scope ( ) ;
828+ self . emit_block_contents ( ctx, & arm. body , ret_kind, "false" , closure_scope) ?;
829+ ctx. iter_overflow_action = old_overflow_action;
830+
831+ // Close `if (|| -> bool { ... })()` and stop the outer function on
832+ // iterator-overflow.
833+ writeln ! (
834+ ctx. out,
835+ "{})() {{ {} }} // __isle_arm_{}" ,
836+ & ctx. indent, ctx. iter_overflow_action, closure_id
837+ ) ?;
838+
839+ ctx. end_block ( "" , scope) ?;
840+ } else {
841+ stack. push ( ( Self :: validate_block ( ret_kind, & arm. body ) , "" , scope) ) ;
842+ }
733843 }
734844 }
735845 }
0 commit comments