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

Specialize generic types and methods #604

Merged
merged 1 commit into from
Oct 13, 2023
Merged

Specialize generic types and methods #604

merged 1 commit into from
Oct 13, 2023

Conversation

yorickpeterse
Copy link
Collaborator

@yorickpeterse yorickpeterse commented Aug 31, 2023

This implements specialization of generics as outlined in #525. See the commit(s) for more details.

This fixes #525

@yorickpeterse yorickpeterse added compiler Changes related to the compiler runtime Changes related to the Rust-based runtime library labels Aug 31, 2023
@yorickpeterse yorickpeterse added this to the 0.13.0 milestone Aug 31, 2023
@yorickpeterse yorickpeterse self-assigned this Aug 31, 2023
@yorickpeterse
Copy link
Collaborator Author

Note to self: disallow trait types anywhere but in type parameter requirements, meaning any method using traits in arguments must be turned into a generic method. This makes things more consistent, and solves the age old discussion of "Should I use generics, or force dynamic dispatch using trait types?". This should also solve some outstanding covariance soundness issues (e.g. #502).

@yorickpeterse yorickpeterse force-pushed the generics branch 9 times, most recently from 4693392 to 0d4d064 Compare September 7, 2023 16:16
@yorickpeterse yorickpeterse force-pushed the generics branch 7 times, most recently from dcd5aaa to bc8fa2b Compare September 16, 2023 21:39
@yorickpeterse yorickpeterse force-pushed the generics branch 8 times, most recently from 4c902a9 to 284a9bb Compare September 22, 2023 17:08
This commit implements type and method specialization. This removes the
need for runtime pointer tagging and allows unboxed Int and Float
values. In addition, the use of specialization may allow for more
optimizations in the future.

The implementation is a little different from other languages. Instead
of specializing generics over every type assigned to a generic type
parameter, types are grouped into "shapes", and we specialize over
shapes. Int, Float, Bool, Nil, String, and atomic types (channels and
processes) get their own shapes, so we can generate slightly different
code for each.

Other owned values are all grouped into the "owned" shape, while
immutable and mutable references go in the "ref" and "mut" shapes
respectively. This is done because such values are always addressed
through pointers, and thus specializing every type/method over such
types results in many types/methods with virtually identical code.

The nice thing about this setup is that we can provide a better balance
between fast compile times and good runtime performance. In addition, a
future version of the compiler could be extended to dynamically create
shapes for types where this makes sense (e.g. if we at some point decide
to stack allocate certain types).

For generics that are assigned the "owned", "ref" or "mut" shape we use
dynamic dispatch, while for Int, Float, etc we use static dispatch. Our
dynamic dispatch strategy is generally fast enough to permit this, and
it means you don't have to decide between using generics and explicit
dynamic dispatch; instead you use generics most of the time and leave it
up to the compiler to compile the code efficiently.

This does require some restrictions to be put in place. Most notably,
Int, Float and String can no longer be cast to traits, and types are no
longer implicitly compatible with traits and instead require explicit
casts. Int and Float can't be used in dynamic dispatch contexts because
they are unboxed values, and thus there's no header/class we can use for
dynamic dispatch. String isn't allowed because of its use of atomic
reference counting, and supporting that would incur an additional cost
for every dynamic dispatch call site. In practise none of this should be
an issue, as these types are rarely used in dynamic dispatch contexts.

This commit also includes various smaller tweaks to the standard and
runtime library, such as the removal of `std.channel.wait` (because it's
a performance trap), and the ability to use generics with raw pointers
(i.e. `Pointer[T]` and `Pointer[String]` are now valid). These changes
are less interesting though, and were needed in response to/to
facilitate the specialization changes.

This fixes #502.

Changelog: changed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler Changes related to the compiler runtime Changes related to the Rust-based runtime library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Specialise generic functions and types over kinds/shapes
1 participant