Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ssa): Treat globals as constant in a function's DFG #7040

Open
wants to merge 39 commits into
base: master
Choose a base branch
from

Conversation

vezenovm
Copy link
Contributor

@vezenovm vezenovm commented Jan 13, 2025

Description

Problem*

Resolves TODO leftover from #6985

Also mentioned in this comment #7021 (comment).

Summary*

In order to re-use our existing instruction simplification we need to be able to access globals inside of the DFG. In order to do this I have created a new GlobalsGraph type that now exists as an Arc<GlobalsGraph> in the DataFlowGraph. Arc is used so that we can share the GlobalsGraph across all functions. However, we will have to eat a cloning cost as we now place the globals information in all functions rather than simply placing it in the main Ssa object.

Additional Context

In #6985 we only added a Value::Global, but globals support both numeric constants and array constants. This means that we can have overlapping instruction ids in the function graph and the globals graph. To avoid having to add placeholder instructions like we do for values, when checking for a constant array instruction we also check whether the value tied to that instruction is specified as a global. If the instruction is specified as global we look for the instruction in the globals graph.

Documentation*

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist*

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Compilation Memory Report

Program Peak Memory
keccak256 77.63M
workspace 123.55M
regression_4709 424.14M
ram_blowup_regression 1.58G
rollup-root 204.23M
rollup-merge 204.23M
rollup-block-root-single-tx 204.23M
rollup-block-root-empty 204.23M
rollup-block-root 204.23M
rollup-block-merge 204.23M
rollup-base-public 204.23M
rollup-base-private 204.23M
private-kernel-tail 168.36M
private-kernel-reset 168.39M
private-kernel-inner 168.36M

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Execution Memory Report

Program Peak Memory
keccak256 74.69M
workspace 123.53M
regression_4709 316.01M
ram_blowup_regression 512.61M
rollup-root 204.23M
rollup-merge 204.23M
rollup-block-root 204.23M
rollup-block-merge 204.23M
rollup-base-public 204.23M
rollup-base-private 204.23M
private-kernel-tail 168.36M
private-kernel-reset 168.39M
private-kernel-inner 168.36M

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Changes to Brillig bytecode sizes

Generated at commit: 56adeee7c43641e5418a69af6818c66fdb69b1df, compared to commit: 14a7e37d7f18f1d6e9148ecc77059999d5f9b14b

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
conditional_regression_short_circuit_inliner_zero -78 ✅ -8.77%
6_inliner_zero -78 ✅ -9.68%
regression_4449_inliner_zero -83 ✅ -9.76%

Full diff report 👇
Program Brillig opcodes (+/-) %
bigint_inliner_zero 2,238 (+18) +0.81%
bigint_inliner_min 2,464 (+18) +0.74%
bigint_inliner_max 2,114 (+12) +0.57%
ram_blowup_regression_inliner_zero 943 (+3) +0.32%
sha256_var_padding_regression_inliner_zero 2,963 (+9) +0.30%
sha256_inliner_zero 1,261 (+2) +0.16%
sha256_var_witness_const_regression_inliner_zero 802 (+1) +0.12%
sha256_regression_inliner_zero 4,869 (-23) -0.47%
array_dynamic_blackbox_input_inliner_zero 1,022 (-18) -1.73%
sha2_byte_inliner_zero 2,763 (-78) -2.75%
conditional_1_inliner_zero 1,124 (-79) -6.57%
ecdsa_secp256k1_inliner_zero 911 (-77) -7.79%
array_dynamic_nested_blackbox_input_inliner_zero 888 (-82) -8.45%
conditional_regression_short_circuit_inliner_zero 811 (-78) -8.77%
6_inliner_zero 728 (-78) -9.68%
regression_4449_inliner_zero 767 (-83) -9.76%

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Execution Report

Program Execution Time %
sha256_regression 0.052s 0%
regression_4709 0.001s 0%
ram_blowup_regression 0.598s -2%
rollup-root 0.105s 0%
rollup-merge 0.006s -15%
rollup-block-root 36.900s -1%
rollup-block-merge 0.104s -1%
rollup-base-public 1.222s 0%
rollup-base-private 0.454s -1%
private-kernel-tail 0.019s 0%
private-kernel-reset 0.314s 0%
private-kernel-inner 0.068s -7%

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Compilation Report

Program Compilation Time %
sha256_regression 1.020s -11%
regression_4709 0.805s -6%
ram_blowup_regression 16.200s -5%
rollup-root 3.412s -1%
rollup-merge 1.910s -1%
rollup-block-root-single-tx 147.000s -3%
rollup-block-root-empty 1.816s -2%
rollup-block-root 143.000s -3%
rollup-block-merge 3.470s 2%
rollup-base-public 30.260s 1%
rollup-base-private 11.220s -2%
private-kernel-tail 0.997s 3%
private-kernel-reset 6.282s 2%
private-kernel-inner 2.084s 4%

@vezenovm
Copy link
Contributor Author

vezenovm commented Jan 13, 2025

Program Brillig opcodes (+/-) %
bigint +12 ❌ +0.57%

For some reason it seems inlining global constants immediately causes this test to regress. We saw the same improvement as this regression in #6985 (comment).

@vezenovm vezenovm marked this pull request as ready for review January 13, 2025 19:20
@vezenovm vezenovm requested a review from a team January 13, 2025 19:20
@vezenovm vezenovm marked this pull request as draft January 13, 2025 19:40
@vezenovm vezenovm marked this pull request as ready for review January 13, 2025 20:07
Copy link
Collaborator

@asterite asterite left a comment

Choose a reason for hiding this comment

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

Looks good!

Comment on lines 650 to 652
if self.is_global(argument) {
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bit odd when self.is_global just matches on the value for a Value::Global, I think it'd be clearer to just have the Global match case below return true instead of unreachable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I chose to separately check as I would need to special case Value::Instruction. I can switch to checking inside the match cases though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have switched it

Comment on lines 681 to 682
pub(crate) fn get_instruction(&self, value: ValueId) -> Option<&Instruction> {
match &self[value] {
Copy link
Contributor

@jfecher jfecher Jan 14, 2025

Choose a reason for hiding this comment

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

I think this method needs some clarification in both a doc comment and making the name more verbose to highlight that the returned instruction may not be in this DFG (!) since using the valueids directly from it could result in different underlying values from this DFG. As-is I can see someone using this as a general purpose way to get an instruction in an optimization pass and run into that bug without knowing - and it'd be hard to track down.

Edit: I realize this is mostly mitigated by the Value indexing change below. So we'd only encounter this if we try to retrieve a value by accessing self.values[value] directly. It's probably fine then but something to keep in mind now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a safety comment either way.

How does the name get_local_or_global_instruction sound?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the rename to get_local_or_global_instruction with an assertion for MakeArray when we have a global instruction. I also included a small comment stating that this may come from a separate DFG.

Comment on lines +712 to +716
let value = &self.values[id];
if matches!(value, Value::Global(_)) {
return &self.globals[id];
}
value
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, if we're checking here then my last comment shouldn't be as much of an issue - but I'll leave it anyway since I think it's an important point to highlight.

For arrays of integers, all those integers should be Value::Globals as well, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For arrays of integers, all those integers should be Value::Globals as well, right?

Yes for a global array of integers. Right now the globals we declare are only dictated by user code, however, in the future we may want to hoist constants into the global space.

array_id = *array; // recur
} else {
return SimplifyResult::None;
if let Some(instruction) = dfg.get_instruction(array_id) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we expect array_set to be used on global arrays at all? I'd expect/hope this wouldn't be needed here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is needed for the MakeArray case not the ArraySet case. I could assert inside of the DFG method that the instruction must be MakeArray for safety.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added an insertion inside of the dfg method.

let mut function_builder = FunctionBuilder::new("apply".to_string(), id);
function_builder.set_globals(Arc::new(GlobalsGraph::from_dfg(globals_dfg.clone())));
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why we do the whole std::mem::take thing with the globals dfg here. Shouldn't we have a Arc<GlobalsGraph> somewhere? If Ssa doesn't already store a Arc<GlobalsGraph> then it probably should - we should avoid cloning entire DFGs if possible - even small ones like globals is likely expected to be.

Copy link
Contributor Author

@vezenovm vezenovm Jan 14, 2025

Choose a reason for hiding this comment

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

It was originally due to add_fn mutably borrowing ssa. I switched to using function_ids to access a function's globals graph. This is safe due to assert!(!function_ids.is_empty());.

Copy link
Contributor

Changes to number of Brillig opcodes executed

Generated at commit: 56adeee7c43641e5418a69af6818c66fdb69b1df, compared to commit: 14a7e37d7f18f1d6e9148ecc77059999d5f9b14b

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
6_inliner_zero -110 ✅ -1.40%
regression_4449_inliner_zero -3,849 ✅ -1.67%

Full diff report 👇
Program Brillig opcodes (+/-) %
ram_blowup_regression_inliner_zero 814,524 (+3,179) +0.39%
sha256_var_padding_regression_inliner_zero 265,695 (+788) +0.30%
sha256_regression_inliner_zero 155,443 (+388) +0.25%
sha2_byte_inliner_zero 72,372 (-55) -0.08%
array_dynamic_blackbox_input_inliner_zero 21,268 (-19) -0.09%
ecdsa_secp256k1_inliner_zero 7,610 (-54) -0.70%
conditional_1_inliner_zero 5,514 (-55) -0.99%
array_dynamic_nested_blackbox_input_inliner_zero 4,491 (-59) -1.30%
conditional_regression_short_circuit_inliner_zero 7,820 (-110) -1.39%
6_inliner_zero 7,740 (-110) -1.40%
regression_4449_inliner_zero 227,252 (-3,849) -1.67%

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.

4 participants