Skip to content

Optimize parser reduction stack popping logic#57

Merged
ehwan merged 1 commit into
mainfrom
emit_data_extract_order
Jun 19, 2026
Merged

Optimize parser reduction stack popping logic#57
ehwan merged 1 commit into
mainfrom
emit_data_extract_order

Conversation

@ehwan

@ehwan ehwan commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Description

This PR optimizes the generated parser's stack extraction logic during production rule reductions.
Currently, during a reduction step, the parser pops from the __location_stack and __data_stack alternately for each token in the rule. In addition, it issues individual .pop().unwrap() calls for every discarded value, leading to extra branching and larger binary sizes in the generated parser.

Key Changes

  1. Sequential Extraction: Refactored emit_data_stack in emit.rs to extract all required __location_stack elements first, followed by all __data_stack elements.
  2. Consecutive Pop Optimization (Vec::truncate): Introduced an optimization that detects consecutive runs of discarded/unneeded elements from the stacks:
    • Consecutive runs of size $\ge 2$ are collapsed into a single Vec::truncate call utilizing unsuffixed integer literals (e.g., __location_stack.truncate(__location_stack.len() - 2)).
    • Single unneeded elements are now popped via a simple .pop() rather than .pop().unwrap(), avoiding unnecessary panic-checking code branches.
  3. Rust Type Inference: Utilized syn::Index::from for numerical constants so they compile cleanly as unsuffixed integer literals, allowing Rust to infer the type as usize directly.

Impact

  • Reduced Generated Binary Size: Eliminating redundant .pop().unwrap() calls reduces compiled code size and compile times.
  • Improved Performance: Sequential execution and bulk-truncation of unused stack values avoid repetitive bounds checks and length updates.

@ehwan ehwan self-assigned this Jun 19, 2026
@ehwan ehwan merged commit ea38a3f into main Jun 19, 2026
1 check passed
@ehwan ehwan deleted the emit_data_extract_order branch June 19, 2026 06:08

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request optimizes the generated parser code by batching consecutive unused stack pops into a single truncate operation for both the location and data stacks, reducing generated code size and overhead. The reviewer suggested refactoring the generator logic in rusty_lr_parser/src/emit.rs to extract the repetitive pop and truncate generation code into a local helper closure, which would eliminate duplication and improve maintainability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1190 to 1276
// 1. Generate location pops/truncates
{
let mut consecutive_unneeded = 0;
for loc_mapto in &location_maptos {
if let Some(loc_mapto) = loc_mapto {
if consecutive_unneeded > 0 {
if consecutive_unneeded == 1 {
extract_data_stream.extend(quote! {
__location_stack.pop();
});
} else {
let consecutive_unneeded =
syn::Index::from(consecutive_unneeded);
extract_data_stream.extend(quote! {
__location_stack.truncate(__location_stack.len() - #consecutive_unneeded);
});
}
consecutive_unneeded = 0;
}
extract_data_stream.extend(quote! {
let mut #loc_mapto = __location_stack.pop().unwrap();
});
} else {
quote! { val }
};
extract_data_stream.extend(quote! {
let mut #data_mapto = match __data_stack.__stack.pop().unwrap() {
#data_enum_typename::#variant_name(val) => #val_extracted,
_ => unreachable!(),
consecutive_unneeded += 1;
}
}
if consecutive_unneeded > 0 {
if consecutive_unneeded == 1 {
extract_data_stream.extend(quote! {
__location_stack.pop();
});
} else {
let consecutive_unneeded = syn::Index::from(consecutive_unneeded);
extract_data_stream.extend(quote! {
__location_stack.truncate(__location_stack.len() - #consecutive_unneeded);
});
}
}
}

// 2. Generate data stack pops/truncates
{
let mut consecutive_unneeded = 0;
for data_info in &data_maptos {
if let Some((variant_name, data_mapto, is_boxed)) = data_info {
if consecutive_unneeded > 0 {
if consecutive_unneeded == 1 {
extract_data_stream.extend(quote! {
__data_stack.__stack.pop();
});
} else {
let consecutive_unneeded =
syn::Index::from(consecutive_unneeded);
extract_data_stream.extend(quote! {
__data_stack.__stack.truncate(__data_stack.__stack.len() - #consecutive_unneeded);
});
}
consecutive_unneeded = 0;
}
let val_extracted = if *is_boxed {
quote! { *val }
} else {
quote! { val }
};
});
} else {
extract_data_stream.extend(quote! {
__data_stack.__stack.pop().unwrap();
});
extract_data_stream.extend(quote! {
let mut #data_mapto = match __data_stack.__stack.pop().unwrap() {
#data_enum_typename::#variant_name(val) => #val_extracted,
_ => unreachable!(),
};
});
} else {
consecutive_unneeded += 1;
}
}
if consecutive_unneeded > 0 {
if consecutive_unneeded == 1 {
extract_data_stream.extend(quote! {
__data_stack.__stack.pop();
});
} else {
let consecutive_unneeded = syn::Index::from(consecutive_unneeded);
extract_data_stream.extend(quote! {
__data_stack.__stack.truncate(__data_stack.__stack.len() - #consecutive_unneeded);
});
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for generating pop and truncate operations for both __location_stack and __data_stack is highly repetitive. We can extract this into a local helper closure to eliminate code duplication, improve maintainability, and make the code much cleaner.

                    let mut emit_unneeded = |stack: TokenStream, count: usize, stream: &mut TokenStream| {
                        if count == 1 {
                            stream.extend(quote! {
                                #stack.pop();
                            });
                        } else if count > 1 {
                            let count_idx = syn::Index::from(count);
                            stream.extend(quote! {
                                #stack.truncate(#stack.len() - #count_idx);
                            });
                        }
                    };

                    // 1. Generate location pops/truncates
                    {
                        let mut consecutive_unneeded = 0;
                        for loc_mapto in &location_maptos {
                            if let Some(loc_mapto) = loc_mapto {
                                emit_unneeded(quote! { __location_stack }, consecutive_unneeded, &mut extract_data_stream);
                                consecutive_unneeded = 0;
                                extract_data_stream.extend(quote! {
                                    let mut #loc_mapto = __location_stack.pop().unwrap();
                                });
                            } else {
                                consecutive_unneeded += 1;
                            }
                        }
                        emit_unneeded(quote! { __location_stack }, consecutive_unneeded, &mut extract_data_stream);
                    }

                    // 2. Generate data stack pops/truncates
                    {
                        let mut consecutive_unneeded = 0;
                        for data_info in &data_maptos {
                            if let Some((variant_name, data_mapto, is_boxed)) = data_info {
                                emit_unneeded(quote! { __data_stack.__stack }, consecutive_unneeded, &mut extract_data_stream);
                                consecutive_unneeded = 0;
                                let val_extracted = if *is_boxed {
                                    quote! { *val }
                                } else {
                                    quote! { val }
                                };
                                extract_data_stream.extend(quote! {
                                    let mut #data_mapto = match __data_stack.__stack.pop().unwrap() {
                                        #data_enum_typename::#variant_name(val) => #val_extracted,
                                        _ => unreachable!(),
                                    };
                                });
                            } else {
                                consecutive_unneeded += 1;
                            }
                        }
                        emit_unneeded(quote! { __data_stack.__stack }, consecutive_unneeded, &mut extract_data_stream);
                    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant