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

Add a monomorphization cache to the declaration engine. #2637

Closed
wants to merge 1 commit into from

Conversation

tritao
Copy link
Contributor

@tritao tritao commented Aug 29, 2022

This introduces a cache to the declaration engine that keeps track of functions/structs that have been instantiated from a specific set of type parameters.

The approach here is relatively simple and relies on a linear search. I originally thought about having more maps to speed it up by keying on the type arguments, but since it adds a bit of complexity I opted for this for now. If it proves to be a bottleneck in the future we can always speed it up.

This was prototyped in tritao/declaration-engine-and-collection-context-demo@c3aa5e7, where these APIs where wired to the function application and struct expressions instantiation code.

However given the declaration engine is not hooked up yet here, this PR is just adding the APIs, and more proper testing will be added as we wire things up.

Closes #2636.

@tritao tritao self-assigned this Aug 29, 2022
@tritao tritao added the compiler: frontend Everything to do with type checking, control flow analysis, and everything between parsing and IRgen label Aug 29, 2022
@tritao tritao marked this pull request as ready for review August 29, 2022 13:51
@tritao tritao requested a review from a team August 29, 2022 14:36
vaivaswatha
vaivaswatha previously approved these changes Aug 29, 2022
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
vaivaswatha
vaivaswatha previously approved these changes Aug 29, 2022
Copy link
Contributor

@emilyaherbert emilyaherbert left a comment

Choose a reason for hiding this comment

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

My recommendation is that we leave this PR as a draft until we have at least one feature already in master in that will allow us to test it (either #2631 or #2635)

@tritao
Copy link
Contributor Author

tritao commented Aug 29, 2022

My recommendation is that we leave this PR as a draft until we have at least one feature already in master in that will allow us to test it (either #2631 or #2635)

I would rather get this now because it introduces new cache-aware APIs, the following PRs for those issues will depend on these APIs anyway and will build on top of this PR. I think your concerns around the stability of this makes sense though, so I've disabled the cache by default and we can enable it by default at a later stage.

@emilyaherbert emilyaherbert requested review from emilyaherbert and a team August 29, 2022 22:44
@emilyaherbert
Copy link
Contributor

so I've disabled the cache by default and we can enable it by default at a later stage

Good plan 👍

Copy link
Contributor

@emilyaherbert emilyaherbert left a comment

Choose a reason for hiding this comment

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

We need to think a little bit longer about how to achieve optimal results with the cache I think, could you iterate on the design another time?

sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
.cloned()
{
let monomorphized_fn_decl = copy.expect_function(&Span::dummy()).unwrap();
if &monomorphized_fn_decl.type_parameters == type_arguments {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that we may need to think more deeply about the logic of this line. In general, most generic functions are called without type arguments, so most of the time this check will fail, missing valid cache opportunities.

sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
sway-core/src/declaration_engine/declaration_engine.rs Outdated Show resolved Hide resolved
@emilyaherbert emilyaherbert requested a review from a team September 1, 2022 17:36
@tritao tritao force-pushed the de-monomorph-cache branch 2 times, most recently from 2a59131 to e8b9e2a Compare September 5, 2022 12:02
@@ -7,7 +7,7 @@ mod resolved_type;
mod trait_constraint;
mod type_argument;
mod type_binding;
mod type_engine;
pub(crate) mod type_engine;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@emilyaherbert This is needed for the following error:

error[E0603]: module `type_engine` is private
  --> sway-core/src/declaration_engine/declaration_engine.rs:14:19
   |
14 |     type_system::{type_engine::monomorphize, EnforceTypeArguments},
   |                   ^^^^^^^^^^^ private module
   |
note: the module `type_engine` is defined here
  --> sway-core/src/type_system/mod.rs:10:1
   |
10 | mod type_engine;
   | ^^^^^^^^^^^^^^^

Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of changing the visibility of the module, change the import statement to match this example:

insert_type, monomorphize, unify_with_self, CopyTypes, EnforceTypeArguments,

This introduces a cache to the declaration engine that keeps track of
functions/structs that have been instantiated from a specific set
of type parameters.

This was originally prototyped in
tritao/declaration-engine-and-collection-context-demo@c3aa5e7,
where these APIs where wired
to the function application and struct expressions instantiation code.

However given the declaration engine is not hooked up yet here, this
PR is just adding the APIs, and more proper testing will be added as we
wire things up.

Closes FuelLabs#2636.
@tritao
Copy link
Contributor Author

tritao commented Sep 5, 2022

Updated the PR with a more generic design, threaded some more error results throughout, added enum declarations to DeclarationWrapper as well for enum support.

@emilyaherbert I'm leaving the future improvements (caching generics called without type parameters) to a future issue (opening one), as right now this is blocking 2 other PRs and that can only be tested properly once the declarations are hooked to the engine anyway.

Copy link
Contributor

@emilyaherbert emilyaherbert left a comment

Choose a reason for hiding this comment

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

Adding the monomorphization cache is not a blocker for adding the declaration engine, and the cache-aware API is similar enough to the non-cache-aware API that switching API functions would only be a small change. Considering we are not able to test the code in the cache until the declaration engine goes in, I recommend that we merge #2631 and #2635 in before we add the cache. Would you close this PR? I recommend keeping this branch alive though so that we can bring it back when we are ready to add the cache again.

.iter()
.cloned()
{
if monomorphized_decl.type_parameters() == type_arguments {
Copy link
Contributor

Choose a reason for hiding this comment

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

This line will cause the cache to fail in some cases (e.g. nested generic function calls).

For example this:

fn foo<T, F>(a: T, b: F) -> F {
    b
}

fn bar<T, F>(a: T, b: F) -> F {
    foo::<T, F>(a, b)
}

would generate a false cache hit due to the implementation of PartialEq on TypeInfo::UnknownGeneric:

(Self::UnknownGeneric { name: l }, Self::UnknownGeneric { name: r }) => l == r,

}

fn de_get_monomorphized_struct_copies(
pub(crate) fn de_get_or_create_monomorphized_decl<T>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the pub(crate)

@@ -17,6 +17,7 @@ use crate::{
pub(crate) enum DeclarationWrapper {
// no-op variant to fulfill the default trait
Unknown,
Enum(TypedEnumDeclaration),
Copy link
Contributor

Choose a reason for hiding this comment

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

There is already a PR for adding enum support: #2713. Please remove these additions on this PR.

| DeclarationWrapper::TraitImpl(_)
| DeclarationWrapper::Storage(_)
| DeclarationWrapper::Unknown => {
panic!("declaration type does not support type parameters")
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, we should not use panic! (unwrap!, etc), unless absolutely necessary. The compiler is designed so that if there is an error when type checking one AST nodes, the other AST nodes are relatively unaffected and can continue to be type checked, providing the best end-user experience. panic! generates an early exit, preventing the compiler from offering this feature to users. This is why we have the heafty CompileResult struct. In this particular case, remove this panic and I recommend that you return an empty vec instead.

}

fn to_wrapper(&self) -> DeclarationWrapper {
panic!("not expected to be called for DeclarationWrapper")
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the panic!

monomorphized_copies
.entry(*original_id)
.and_modify(|f| f.push(new_id.clone()))
.or_insert_with(|| vec![new_id.clone()]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggests that smallvec might be an improvement here... i.e. there tends to be a single monomorphized copy? Hard to know without a benchmark of course...

@tritao tritao marked this pull request as draft September 7, 2022 13:25
@tritao
Copy link
Contributor Author

tritao commented Oct 27, 2022

Closing this for now

@tritao tritao closed this Oct 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler: frontend Everything to do with type checking, control flow analysis, and everything between parsing and IRgen
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce a monomorphization cache
4 participants