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

[mlir][spirv] Modernize op assembly formats and syntax docs #73359

Open
kuhar opened this issue Nov 24, 2023 · 16 comments
Open

[mlir][spirv] Modernize op assembly formats and syntax docs #73359

kuhar opened this issue Nov 24, 2023 · 16 comments
Assignees
Labels
code-cleanup good first issue https://github.com/llvm/llvm-project/contribute mlir:spirv

Comments

@kuhar
Copy link
Member

kuhar commented Nov 24, 2023

The SPIR-V dialect predates the assemblyFormat definitions in TableGen and some of the SPRI-V ops needlessly use handwritten parsers. We should go over the ops in the dialect and update to assemblyFormat wherever possible.

With assemblyFormat in place, we can also drop the handwritten syntax docs, since the latter is redundant in the presence of assemblyFormat. This way the two can't possibly diverge and become confusing. This is similar to the cleanup done in #73343.

@kuhar kuhar added good first issue https://github.com/llvm/llvm-project/contribute code-cleanup mlir:spirv labels Nov 24, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2023

@llvm/issue-subscribers-mlir-spirv

Author: Jakub Kuderski (kuhar)

The SPIR-V dialect predates the `assemblyFormat` definitions in TableGen and some of the SPRI-V ops needlessly use handwritten parsers. We should go over the ops in the dialect and update to `assemblyFormat` wherever possible.

With assemblyFormat in place, we can also drop the handwritten syntax docs, since the latter is redundant in the presence of assemblyFormat. This way the two can't possibly diverge and become confusing. This is similar to the cleanup done in #73343.

@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2023

Hi!

This issue may be a good introductory issue for people new to working on LLVM. If you would like to work on this issue, your first steps are:

  1. Assign the issue to you.
  2. Fix the issue locally.
  3. Run the test suite locally.
    3.1) Remember that the subdirectories under test/ create fine-grained testing targets, so you can
    e.g. use make check-clang-ast to only run Clang's AST tests.
  4. Create a git commit
  5. Run git clang-format HEAD~1 to format your changes.
  6. Submit the patch to Phabricator.
    6.1) Detailed instructions can be found here

For more instructions on how to submit a patch to LLVM, see our documentation.

If you have any further questions about this issue, don't hesitate to ask via a comment on this Github issue.

@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2023

@llvm/issue-subscribers-good-first-issue

Author: Jakub Kuderski (kuhar)

The SPIR-V dialect predates the `assemblyFormat` definitions in TableGen and some of the SPRI-V ops needlessly use handwritten parsers. We should go over the ops in the dialect and update to `assemblyFormat` wherever possible.

With assemblyFormat in place, we can also drop the handwritten syntax docs, since the latter is redundant in the presence of assemblyFormat. This way the two can't possibly diverge and become confusing. This is similar to the cleanup done in #73343.

rikhuijzer added a commit that referenced this issue Nov 27, 2023
Some operations defined their syntax both in the documentation and via
`assemblyFormat`. This leads to two syntax descriptions in the
documentation for SPIR-V, see for example the documentation for
[`spirv.mlir.yield`](https://mlir.llvm.org/docs/Dialects/SPIR-V/#spirvmliryield-spirvyieldop).
Since the `assemblyFormat` is used to generate the actual parsers and
printer implementations, this PR removes the manual syntax descriptions.
(Similar to #73343.)

The strategy that I used to find the duplicates was pretty
uncomplicated. I scrolled through the [SPIR-V
Dialect](https://mlir.llvm.org/docs/Dialects/SPIR-V) to find all
duplicates and then remove the duplicate text from the `td` file.

Note that the `Syntax:` block in the docs is a good proxy for whether
`assemblyFormat` is defined because it will only be generated if the op
has defined `assemblyFormat` (`op.hasAssemblyFormat()`):


https://github.com/llvm/llvm-project/blob/e970652776bd07dbe42be557bf98722749230653/mlir/tools/mlir-tblgen/OpDocGen.cpp#L108-L124


https://github.com/llvm/llvm-project/blob/e970652776bd07dbe42be557bf98722749230653/mlir/tools/mlir-tblgen/OpDocGen.cpp#L197-L199

Related issue #73359.
@alexbeloi
Copy link
Contributor

Hi, I'd like to work on this as my first issue

@kuhar
Copy link
Member Author

kuhar commented Dec 19, 2023

Sure @alexbeloi. Feel free to ping me if you need some pointers. Also happy to have a chat on discord or a call.

@alexbeloi
Copy link
Contributor

I don't see any duplicate Syntax: in the SPIRV dialect documentation, but there are a bunch in Math/IR/MathOps.td like atan and others, so I will fix those.

@kuhar
Copy link
Member Author

kuhar commented Dec 20, 2023

I don't see any duplicate Syntax: in the SPIRV dialect documentation, but there are a bunch in Math/IR/MathOps.td like atan and others, so I will fix those.

@alexbeloi These duplicate ones have already been removed from the SPIR-V dialect: #73386. The remaining part of the task is to use let assemblyFormat = tablegen syntax definition where we currently rely on hand-written parsers.

You can find these by running something like grep -rn 'Op::print(' mlir/lib/Dialect/SPIRV/IR. Some of these are complicated enough to justify the hand-written parsers and printers, but we also have a bunch of old ops that have simple syntax and should be upgraded to assemblyFormat (even if it requires minor syntax changes like using the <attribute> syntax instead of "attribute").

kuhar pushed a commit that referenced this issue Dec 20, 2023
…lyFormat` (#76002)

See #73359

Types using `assemblyFormat` to define parsing don't need an additional
handwritten parser. So we should remove the handwritten parsers where
one
provided by an `assemblyFormat` already exists to avoid confusion and
de-syncing.
@alexbeloi
Copy link
Contributor

alexbeloi commented Dec 21, 2023

Hey Jakub, I'm updating some of the SPIRV parsing to use assemblyFormat and have a couple questions.

It seems like the change may require more than just a small "attribute" -> <attribute> kind of change. I wanted to check if this would be ok or too much or I should just ignore these cases

It seems that assemblyFormat has some general requirements (AFAIU) to generate the builders

  1. attr-dict is required
  2. operands, results (either individually or with the builtin operands and results keywords) and (sometimes?) their types are required

For example syntax:

%0 = spirv.AtomicAnd "Device" "None" %ptr, %value : !spirv.ptr<i32, StorageBuffer>

Just replacing the quotes with angle brackets doesn't work, it seems to require something like

let assemblyFormat = [{
    `<` $memory_scope `>` `<` $semantics `>` operands attr-dict `:`
    functional-type(operands, results)
}];

This would be added here, to replace the print/parse here.

A bunch of handwritten parsers don't use $result or its type for printing, but the compiler/table-gen complains if it's not included in the assemblyFormat with this error

error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred

@kuhar
Copy link
Member Author

kuhar commented Dec 21, 2023

@alexbeloi

It seems like the change may require more than just a small "attribute" -> <attribute> kind of change. I wanted to check if this would be ok or too much or I should just ignore these cases

There are a few options here. In general, we should be fine with updating op syntax as long as the new syntax is not too different and we converge towards uniformity within the dialect.

It seems that assemblyFormat has some general requirements (AFAIU) to generate the builders

  1. attr-dict is required
  2. operands, results (either individually or with the builtin operands and results keywords) and (sometimes?) their types are required

The result type can often be inferred from ODS type constraints, e.g., AllTypesMatch<[...]>, SameOperandsAndResultTypes -- you can grep for these in mlir/include/mlir/Dialect/SPIRV/IR.

For example syntax:

%0 = spirv.AtomicAnd "Device" "None" %ptr, %value : !spirv.ptr<i32, StorageBuffer>

Just replacing the quotes with angle brackets doesn't work, it seems to require something like

let assemblyFormat = [{
    `<` $memory_scope `>` `<` $semantics `>` operands attr-dict `:`
    functional-type(operands, results)
}];

We have some examples of ops with attributes that use assemblyFormat, one recent one that comes to mind is:

class SPIRV_IntegerDotProductBinaryOp<string mnemonic,
                                      list<Trait> traits = []> :
      SPIRV_IntegerDotProductOp<mnemonic,
        !listconcat(traits, [AllTypesMatch<["vector1", "vector2"]>])> {
  let arguments = (ins
    SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$vector1,
    SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$vector2,
    OptionalAttr<SPIRV_PackedVectorFormatAttr>:$format
  );

  let assemblyFormat = [{
    $vector1 `,` $vector2 ( `,` $format^ )? attr-dict `:`
      type($vector1) `->` type($result)
  }];
}

Here the format is optional and uses the <attr-kind> syntax. There's also a way to tell tablegen that your operand uses custom syntax, you can grep other dialects for custom<.+> and hasCustomAssemblyFormat, but I'd rather avoid that if we can.

Also, I'm personally not a fan of the parentheses introduced by functional-type(operands, results), I find that it's more readable to just do:

type($my-operand) `->` type($my-result)

I'd argue that in the specific case of spirv.AtomicAnd it'd be better to spell out the type of the value operand (same as the return type):

%0 = spirv.AtomicAnd <Device> <None> %ptr, %value : !spirv.ptr<i32, StorageBuffer> -> i32

@kuhar
Copy link
Member Author

kuhar commented Dec 21, 2023

gpu.subgroup_reduce is an example of an op that puts its attribute first and uses assembly format:

def GPU_SubgroupReduceOp : GPU_Op<"subgroup_reduce", [SameOperandsAndResultType]> {
let summary = "Reduce values among subgroup.";
let description = [{
The `subgroup_reduce` op reduces the value of every work item across a
subgroup. The result is equal for all work items of a subgroup.
Example:
```mlir
%1 = gpu.subgroup_reduce add %0 : (f32) -> (f32)
```
If `uniform` flag is set either none or all work items of a subgroup
need to execute this op in convergence. The reduction operation must be one
of:
* Integer types: `add`, `mul`, `minui`, `minsi`, `maxui`, `maxsi`, `and`,
`or`, `xor`
* Floating point types: `add`, `mul`, `minnumf`, `maxnumf`, `minimumf`,
`maximumf`
}];
let arguments = (ins
AnyIntegerOrFloat:$value,
GPU_AllReduceOperationAttr:$op,
UnitAttr:$uniform
);
let results = (outs AnyIntegerOrFloat:$result);
let assemblyFormat = [{ custom<AllReduceOperation>($op) $value
(`uniform` $uniform^)? attr-dict
`:` functional-type(operands, results) }];

antiagainst pushed a commit that referenced this issue Jan 7, 2024
see #73359

Declarative assemblyFormat ODS is more concise and requires less
boilerplate than filling out CPP interfaces.

Changes:
* updates the Ops defined in `SPIRVAtomicOps.td` to use assemblyFormat.
* Removes print/parse from`AtomcOps.cpp` which is now generated by
assemblyFormat
* Adds `Trait` to verify that a pointer operand `foo`'s pointee type
matches operand `bar`'s type
* * Updates error message expected in tests from new Trait
* Updates tests to updated format (largely using <operand> in place of
"operand")
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this issue Jan 28, 2024
…6323)

see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less
boilerplate than filling out CPP interfaces.

Changes:
* updates the Ops defined in `SPIRVAtomicOps.td` to use assemblyFormat.
* Removes print/parse from`AtomcOps.cpp` which is now generated by
assemblyFormat
* Adds `Trait` to verify that a pointer operand `foo`'s pointee type
matches operand `bar`'s type
* * Updates error message expected in tests from new Trait
* Updates tests to updated format (largely using <operand> in place of
"operand")
@hahacyd
Copy link
Contributor

hahacyd commented Oct 28, 2024

I'm currently looking for a good first issue to work on and noticed this one. It seems like the issue has already been resolved.
If everything is indeed addressed, could you please consider closing the issue? This will help newcomers like me find open issues to contribute to.

@alexbeloi
Copy link
Contributor

@hahacyd I think there are still a lot of places that need updating. You can take this issue over if you'd like.

@hahacyd
Copy link
Contributor

hahacyd commented Oct 29, 2024

@alexbeloi Thank you for your response! I apologize for mistakenly thinking the issue was resolved because I noticed it was unassigned. I still need to gain a deeper understanding of this issue, so I won't be taking it over for now. However, as I learn more, I'll be sure to offer help where I can. Thanks again for your guidance!

@kuhar
Copy link
Member Author

kuhar commented Oct 29, 2024

@hahacyd yes, this is far from being done. Feel free to ask questions if you are not sure about something along the way.

@hahacyd
Copy link
Contributor

hahacyd commented Nov 6, 2024

Hi, I found that most operations in SPIRVGroupOps.td both have assemblyFormat and hand-writen parser&printer(in the corresponding cxx files: GroupOps.cpp), I think as for such operations, I can just remove the redundant hand-writen parser & printer (on the basis of that they both have same output).
ps: I'd be happy to chat on discord.

@kuhar
Copy link
Member Author

kuhar commented Nov 6, 2024

SGTM, @hahacyd. Also feel free to ping me on discord if you have questions.

hahacyd added a commit to hahacyd/llvm-project that referenced this issue Nov 10, 2024
see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less boilerplate than filling out CPP interfaces.

Changes:
* updates the Ops defined in `SPIRVNonUniformOps.td and SPIRVGroupOps.td` to use assemblyFormat.
* Removes print/parse from `GroupOps.cpp` which is now generated by assemblyFormat
* Updates tests to updated format (largely using <operand> in place of "operand" and complementing type information)
hahacyd added a commit to hahacyd/llvm-project that referenced this issue Nov 10, 2024
see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less boilerplate than filling out CPP interfaces.

Changes:
* updates the Ops defined in `SPIRVNonUniformOps.td and SPIRVGroupOps.td` to use assemblyFormat.
* Removes print/parse from `GroupOps.cpp` which is now generated by assemblyFormat
* Updates tests to updated format (largely using <operand> in place of "operand" and complementing type information)
kuhar pushed a commit that referenced this issue Nov 13, 2024
#115662)

Declarative assemblyFormat ODS is more concise and requires less
boilerplate than filling out CPP interfaces.

Changes:
* updates the Ops defined in `SPIRVNonUniformOps.td and
SPIRVGroupOps.td` to use assemblyFormat.
* Removes print/parse from `GroupOps.cpp` which is now generated by
assemblyFormat
* Updates tests to updated format (largely using <operand> in place of
"operand" and complementing type information)

Issue: #73359
hahacyd added a commit to hahacyd/llvm-project that referenced this issue Nov 17, 2024
see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less boilerplate than filling out cpp interfaces.

Changes:
updates the AccessChainOp defined in SPIRVMemoryOps.td to use assemblyFormat.
Removes part print/parse from MemoryOps.cpp which is now generated by assemblyFormat
Updates tests to updated format
hahacyd added a commit to hahacyd/llvm-project that referenced this issue Nov 17, 2024
see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less boilerplate than filling out cpp interfaces.

Changes:
updates the AccessChainOp defined in SPIRVMemoryOps.td to use assemblyFormat.
Removes part print/parse from MemoryOps.cpp which is now generated by assemblyFormat
Updates tests to updated format
kuhar pushed a commit that referenced this issue Nov 19, 2024
…16545)

see #73359

Declarative assemblyFormat ODS is more concise and requires less
boilerplate than filling out cpp interfaces.

Changes:
- updates the AccessChainOp defined in SPIRVMemoryOps.td to use
assemblyFormat.
- Removes part print/parse from MemoryOps.cpp which is now generated by
assemblyFormat
- Updates tests to updated format
hahacyd added a commit to hahacyd/llvm-project that referenced this issue Nov 20, 2024
…ndPtrAccessChainOp assembly

see llvm#73359

Declarative assemblyFormat ODS is more concise and requires less boilerplate than filling out cpp interfaces.

Changes:
updates the PtrAccessChainOp and InBoundPtrAccessChainOp defined in SPIRVMemoryOps.td to use assemblyFormat.
Removes part print/parse from MemoryOps.cpp which is now generated by assemblyFormat
Updates tests to updated format
kuhar pushed a commit that referenced this issue Nov 25, 2024
…assembly (#116943)

Declarative assemblyFormat ODS is more concise and requires less
boilerplate than filling out cpp interfaces.

Changes:
updates the PtrAccessChainOp and InBoundPtrAccessChainOp defined in
SPIRVMemoryOps.td to use assemblyFormat. Removes part print/parse from
MemoryOps.cpp which is now generated by assemblyFormat
Updates tests to updated format

Issue: #73359
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
code-cleanup good first issue https://github.com/llvm/llvm-project/contribute mlir:spirv
Projects
None yet
Development

No branches or pull requests

4 participants