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

dialects: (csl) builtin math lib for DSDs #2686

Merged
merged 23 commits into from
Jun 6, 2024
Merged

dialects: (csl) builtin math lib for DSDs #2686

merged 23 commits into from
Jun 6, 2024

Conversation

n-io
Copy link
Collaborator

@n-io n-io commented Jun 3, 2024

DSD builtin operations typically come with a variety of overloads. This PR proposes a base class handling ops and verification. The subclasses correspond to CSL builtin ops. Their implementation only needs to specify op name and the valid function signatures. The goal is to keep everything really simple.

@n-io n-io requested a review from math-fehr June 4, 2024 09:08
Copy link

codecov bot commented Jun 4, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.74%. Comparing base (a83b98b) to head (34983d3).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2686      +/-   ##
==========================================
+ Coverage   89.70%   89.74%   +0.04%     
==========================================
  Files         366      367       +1     
  Lines       47071    47285     +214     
  Branches     7140     7191      +51     
==========================================
+ Hits        42223    42437     +214     
  Misses       3745     3745              
  Partials     1103     1103              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@n-io n-io changed the title Dialects: (csl) Adding builtin ops for DSDs core: allow uppercase fields on ops to enable... dialects: (csl) builtin math lib for DSDs Jun 4, 2024
@n-io n-io requested a review from AntonLydike June 4, 2024 09:14
@n-io n-io marked this pull request as ready for review June 4, 2024 09:14
@n-io n-io changed the title core: allow uppercase fields on ops to enable... dialects: (csl) builtin math lib for DSDs dialects: (csl) builtin math lib for DSDs Jun 4, 2024
@n-io n-io requested review from dk949 and removed request for math-fehr June 4, 2024 09:32
@n-io n-io marked this pull request as draft June 4, 2024 10:10
Copy link
Collaborator

@dk949 dk949 left a comment

Choose a reason for hiding this comment

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

I am still not sure about implementing a class for every version of each
builtin. I agree that this is the simpler solution to initially implement, I
feel like it will be harder to maintain and a bit more difficult to transform.

I think it should be possible to roll any number of builtins into the same
class. The two ideas I had yesterday were to:

  1. Provide the suffixes as part of the signature and a public function for the
    printer to call, performing a match and returning the associated suffix. This
    method would be quite easy to implement, but it adds printer specific
    functionality to the dialect, which is undesirable.
  2. Derive the suffix from the signature(s). There appears to be some sort of a
    system for naming these builtins, however, some functions are
    indistinguishable by signature alone, as they use untyped DSDs.
  3. (This is the 3rd of the two ideas). Disregard concerns in 1st paragraph and
    implement each builtin as its own class 🤷

def verify_(self) -> None:
def typcheck(
op_typ: Attribute,
sig_typ: AttrConstraint | Attribute | type[Attribute] | TypeVar,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure we need AttrConstraint here, since we don't really use them anywhere else in the dialect (not directly any way). If you do think we should keep it, maybe consider using attr_constr_coercion from irdl, to convert everything to an AttrConstraint then using the verify method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's the type signature copied over from operand_def

@@ -883,6 +892,50 @@ def verify_(self) -> None:
raise VerifyException(f"{self.name} can only operate on mem1d_dsd type")


FunctionSignatures = list[
tuple[AttrConstraint | Attribute | type[Attribute] | TypeVar, ...]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto for use of AttrConstraint.

@n-io
Copy link
Collaborator Author

n-io commented Jun 4, 2024

I am still not sure about implementing a class for every version of each builtin. I agree that this is the simpler solution to initially implement, I feel like it will be harder to maintain and a bit more difficult to transform.

I agree, but regrettably haven't really gotten to that part yet. Happy to include it in this PR if you like.

I think it should be possible to roll any number of builtins into the same class. The two ideas I had yesterday were to:

  1. Provide the suffixes as part of the signature and a public function for the
    printer to call, performing a match and returning the associated suffix. This
    method would be quite easy to implement, but it adds printer specific
    functionality to the dialect, which is undesirable.

This may not really work with the current design, since the signature is based off of the var args, and we can't pass the precision as an operand. However, what we could do is pass the precision as an operand. Then in the get_signatures method we could check the precision property value and return the correct set of signatures. Do you think we want to provide a method for the name as well or do we figure it out in the printer?

  1. Derive the suffix from the signature(s). There appears to be some sort of a
    system for naming these builtins, however, some functions are
    indistinguishable by signature alone, as they use untyped DSDs.

This would be really really good - however, I do see practical problems. For instance, if we get (DsdType, DsdType, DsdType) as the function signature, this would match both faddh, faddhs, and fadds, since we don't carry around element type information on the DSDs (anymore).

  1. (This is the 3rd of the two ideas). Disregard concerns in 1st paragraph and
    implement each builtin as its own class 🤷

Yes, I suppose it's between this and option 1.

@AntonLydike
Copy link
Collaborator

AntonLydike commented Jun 5, 2024

So I guess to summarise previous discussion:

  • having one generic csl.dsd_builtin(%a, %b, %c) <{name = "faddh", precision = ...}> : (...) -> ... would be painful and complex to verify?
  • having one class per "name" and precision might be excessive
  • having one class per op "name" with precision as a property seems to be the sweet spot?

I would favour having one op per function name emitted in the resulting CSL, except if there's a good reason not to.

@n-io n-io marked this pull request as ready for review June 5, 2024 13:39
Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

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

image

🥹 Can we split this into two or three PRs? This is quite a lot to review all at once

Comment on lines 1292 to 1293
case BuiltinOpPrecisionKind.mixed:
return []
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a way of saying "I support no mixed-precision modes", right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It will cause it to fail the verification performed by the superclass

@dk949
Copy link
Collaborator

dk949 commented Jun 5, 2024

@AntonLydike

I would favour having one op per function name emitted in the resulting CSL, except if there's a good reason not to.

This is one reason...

I wasn't proposing one single op to rule them all. But maybe all additions together, all macs together etc..

@dk949
Copy link
Collaborator

dk949 commented Jun 5, 2024

@n-io i really appreciate you adding all the possible DSD operations, but we will realistically never use 90% of these...

I don't think I've ever used a clz in regular code, let alone CSL 😅

@n-io
Copy link
Collaborator Author

n-io commented Jun 5, 2024

@n-io i really appreciate you adding all the possible DSD operations, but we will realistically never use 90% of these...

I don't think I've ever used a clz in regular code, let alone CSL 😅

Easy to do, but dito for control tasks 😂

@n-io
Copy link
Collaborator Author

n-io commented Jun 5, 2024

If we want one op per function name emitted, that's done quickly and I'm happy to do it. It may actually not be a bad approach going forward, I suppose. We had a similar discussion regarding tasks, where in the end we decided to combine all types of tasks into one parameterised class. I'm happy either way, to be honest.

One op to rule them all might actually make op rewriting rather complicated, also making it generally a lot harder to easily access common functionalities that xdsl provides us with.

@n-io n-io marked this pull request as draft June 5, 2024 16:09
@n-io n-io marked this pull request as ready for review June 6, 2024 11:54
@n-io n-io requested review from AntonLydike and dk949 June 6, 2024 11:55
@math-fehr math-fehr added the dialects Changes on the dialects label Jun 6, 2024
Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

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

Yeah, lgtm

@AntonLydike AntonLydike self-assigned this Jun 6, 2024
Comment on lines 921 to 935
def verify_(self) -> None:
def typcheck(
op_typ: Attribute,
sig_typ: AttrConstraint | Attribute | type[Attribute] | TypeVar,
) -> bool:
if isinstance(sig_typ, type):
return isinstance(op_typ, sig_typ)
else:
return op_typ == sig_typ

for sig in self.SIGNATURES:
if len(self.ops) == len(sig):
if all(typcheck(op.type, sig_t) for (op, sig_t) in zip(self.ops, sig)):
return
raise VerifyException("Cannot find matching type signature")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a unit test for this (just in python, not filecheck). I think this would increase confidence here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a python test as suggested.

Copy link
Collaborator

@dk949 dk949 left a comment

Choose a reason for hiding this comment

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

It's 👍 from me as well, except the AttrConstraint thing in the type checking function

@n-io
Copy link
Collaborator Author

n-io commented Jun 6, 2024

It's 👍 from me as well, except the AttrConstraint thing in the type checking function

It was the full union definition used by operand_def which I thought might be what we wanted. But since we're not actually exposing anything here, you're completely right that we don't technically need it. Removed as suggested.

@n-io n-io merged commit 6095319 into main Jun 6, 2024
10 checks passed
@n-io n-io deleted the nicolai/dsd-math-ops branch June 6, 2024 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dialects Changes on the dialects
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants