Skip to content

[mlir][spirv] Fix int type declaration duplication when serializing #143108

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

Conversation

davidegrohmann
Copy link
Contributor

@davidegrohmann davidegrohmann commented Jun 6, 2025

At the MLIR level unsigned integer and signless integers are different types. Indeed when looking up the two types in type definition cache they do not match.

Hence when translating a SPIR-V module which contains both usign and signless integers will contain the same type declaration twice (something like OpTypeInt 32 0) which is not permitted in SPIR-V and such generated modules fail validation.

This patch solves the problem by mapping unisgned integer types to singless integer types before looking up in the type definition cache.

At the MLIR level unsigned integer and signless integers are different
types.  Indeed when looking up the two types in type definition cache
they do not match.

Hence when translating a SPIR-V module which contains both usign and
signless integers will contain the same type declaration twice
(something like OpTypeInt 32 0) which is not permitted in SPIR-V and
such generated modules fail validation.

This patch solves the problem by mapping unisgned integer types to
singless integer types before looking up in the type definition cache.

Signed-off-by: Davide Grohmann <[email protected]>
Change-Id: I9b5ecc72049d2642308b70acc335b6dc6bf6b5be
Copy link

github-actions bot commented Jun 6, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jun 6, 2025

@llvm/pr-subscribers-mlir-spirv

@llvm/pr-subscribers-mlir

Author: Davide Grohmann (davidegrohmann)

Changes

At the MLIR level unsigned integer and signless integers are different types. Indeed when looking up the two types in type definition cache they do not match.

Hence when translating a SPIR-V module which contains both usign and signless integers will contain the same type declaration twice (something like OpTypeInt 32 0) which is not permitted in SPIR-V and such generated modules fail validation.

This patch solves the problem by mapping unisgned integer types to singless integer types before looking up in the type definition cache.

Change-Id: I9b5ecc72049d2642308b70acc335b6dc6bf6b5be


Full diff: https://github.com/llvm/llvm-project/pull/143108.diff

1 Files Affected:

  • (modified) mlir/lib/Target/SPIRV/Serialization/Serializer.cpp (+13)
diff --git a/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp b/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
index 15e06616f4492..c9b2a01091460 100644
--- a/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
+++ b/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
@@ -446,6 +446,19 @@ LogicalResult Serializer::processType(Location loc, Type type,
 LogicalResult
 Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
                             SetVector<StringRef> &serializationCtx) {
+
+  // Map unisgned integer types to singless integer types
+  // This is needed otherwise the generated spirv assembly will contain
+  // twice a type declaration (like OpTypeInt 32 0) which is no permitted and
+  // such module could no pass validation. Indeed at MLIR level the two types
+  // are different and lookup in the cache below fails.
+  // Note: This convertion needs to happen here before the type is looked up in
+  // the cache
+  if (type.isUnsignedInteger()) {
+    type = IntegerType::get(loc->getContext(), type.getIntOrFloatBitWidth(),
+                            IntegerType::SignednessSemantics::Signless);
+  }
+
   typeID = getTypeID(type);
   if (typeID)
     return success();

Copy link
Member

@kuhar kuhar left a comment

Choose a reason for hiding this comment

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

Would it be possible to add a test for this?

Also add capabilities and entry point to allow the spirv assembly
generated from the constants.mlir test to pass validation

Signed-off-by: Davide Grohmann <[email protected]>
Change-Id: I22df9051de66a3dcb11d70bb95d06801aea62942
@davidegrohmann
Copy link
Contributor Author

Would it be possible to add a test for this?

There is already a test checking that a mix of unsigned/signless ints in the same SPIRV module can be serialized/deserialized to/from MLIR and SPIRV (see mlir/test/Target/SPIRV/constant.mlir). The test pass since the deserializer does not fail on the invalid SPIRV code, validating the spriv assembly from that test fails with the following without the code fix in this patch:

error: line 49: Duplicate non-aggregate type declarations are not allowed. Opcode: TypeInt id: 29
  %uint_1 = OpTypeInt 32 0

The only way to test this properly is to add a validation step on the produced SPIRV assembly, but that cannot be achieve easily at the moment.

SPIR-V assembly from constants.mlir before the change (marked double declaration by hand):

; SPIR-V
; Version: 1.0
; Generator: LLVM MLIR SPIR-V Serializer; 21
; Bound: 209
; Schema: 0
               OpCapability Shader
               OpCapability Int64
               OpCapability Int16
               OpCapability Int8
               OpCapability Float64
               OpCapability Float16
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %bool_const "bool_const"
               OpName %bool_const "bool_const"
               OpName %i32_const "i32_const"
               OpName %si32_const "si32_const"
               OpName %ui32_const "ui32_const"
               OpName %i64_const "i64_const"
               OpName %i16_const "i16_const"
               OpName %i8_const "i8_const"
               OpName %float_const "float_const"
               OpName %double_const "double_const"
               OpName %half_const "half_const"
               OpName %bool_vector_const "bool_vector_const"
               OpName %int_vector_const "int_vector_const"
               OpName %fp_vector_const "fp_vector_const"
               OpName %ui64_array_const "ui64_array_const"
               OpName %si32_array_const "si32_array_const"
               OpName %float_array_const "float_array_const"
               OpName %ignore_not_used_const "ignore_not_used_const"
               OpName %materialize_const_at_each_use "materialize_const_at_each_use"
               OpName %const_variable "const_variable"
               OpName %multi_dimensions_const "multi_dimensions_const"
               OpName %multi_dimensions_splat_const "multi_dimensions_splat_const"
               OpName %signless_int_const_bit_extension "signless_int_const_bit_extension"
               OpName %signed_int_const_bit_extension "signed_int_const_bit_extension"
               OpDecorate %_arr_uint_uint_3 ArrayStride 4
               OpDecorate %_arr__arr_uint_uint_3_uint_2 ArrayStride 12
               OpDecorate %_arr__arr__arr_uint_uint_3_uint_2_uint_2 ArrayStride 24
       %void = OpTypeVoid
          %1 = OpTypeFunction %void
       %bool = OpTypeBool
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
%_ptr_Function_bool = OpTypePointer Function %bool
       %uint = OpTypeInt 32 0                       <=================
     %uint_0 = OpConstant %uint 0
    %uint_10 = OpConstant %uint 10
%uint_4294967291 = OpConstant %uint 4294967291
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
     %int_10 = OpConstant %int 10
     %int_n5 = OpConstant %int -5
     %uint_1 = OpTypeInt 32 0                     <=================
   %uint_1_0 = OpConstant %uint_1 0
  %uint_1_10 = OpConstant %uint_1 10
  [...]

After the patch:

; SPIR-V
; Version: 1.0
; Generator: LLVM MLIR SPIR-V Serializer; 21
; Bound: 206
; Schema: 0
               OpCapability Shader
               OpCapability Int64
               OpCapability Int16
               OpCapability Int8
               OpCapability Float64
               OpCapability Float16
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %bool_const "bool_const"
               OpName %bool_const "bool_const"
               OpName %i32_const "i32_const"
               OpName %si32_const "si32_const"
               OpName %ui32_const "ui32_const"
               OpName %i64_const "i64_const"
               OpName %i16_const "i16_const"
               OpName %i8_const "i8_const"
               OpName %float_const "float_const"
               OpName %double_const "double_const"
               OpName %half_const "half_const"
               OpName %bool_vector_const "bool_vector_const"
               OpName %int_vector_const "int_vector_const"
               OpName %fp_vector_const "fp_vector_const"
               OpName %ui64_array_const "ui64_array_const"
               OpName %si32_array_const "si32_array_const"
               OpName %float_array_const "float_array_const"
               OpName %ignore_not_used_const "ignore_not_used_const"
               OpName %materialize_const_at_each_use "materialize_const_at_each_use"
               OpName %const_variable "const_variable"
               OpName %multi_dimensions_const "multi_dimensions_const"
               OpName %multi_dimensions_splat_const "multi_dimensions_splat_const"
               OpName %signless_int_const_bit_extension "signless_int_const_bit_extension"
               OpName %signed_int_const_bit_extension "signed_int_const_bit_extension"
               OpDecorate %_arr_uint_uint_3 ArrayStride 4
               OpDecorate %_arr__arr_uint_uint_3_uint_2 ArrayStride 12
               OpDecorate %_arr__arr__arr_uint_uint_3_uint_2_uint_2 ArrayStride 24
       %void = OpTypeVoid
          %1 = OpTypeFunction %void
       %bool = OpTypeBool
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
%_ptr_Function_bool = OpTypePointer Function %bool
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
    %uint_10 = OpConstant %uint 10
%uint_4294967291 = OpConstant %uint 4294967291
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
     %int_10 = OpConstant %int 10
     %int_n5 = OpConstant %int -5
   %uint_0_0 = OpConstant %uint 0
  %uint_10_0 = OpConstant %uint 10
%uint_4294967291_0 = OpConstant %uint 4294967291
      %ulong = OpTypeInt 64 0
[...]

@kuhar
Copy link
Member

kuhar commented Jun 9, 2025

The only way to test this properly is to add a validation step on the produced SPIRV assembly, but that cannot be achieve easily at the moment.

I thought we have some tests that use spir-v when present:

; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

Could we do something similar in mlir spirv serialization?

If this is not straightforward, validating by hand is also fine IMO.

SPIR-V validation is run if spirv-tools are available otherwise
skipped.

Signed-off-by: Davide Grohmann <[email protected]>
Change-Id: Id0af7c983c4bd6596a4837812f22ced3c7616177
@davidegrohmann
Copy link
Contributor Author

I thought we have some tests that use spir-v when present:

; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

Could we do something similar in mlir spirv serialization?

If this is not straightforward, validating by hand is also fine IMO.

Thanks for the suggestion I have added that to the test. I have also validated manually the SPIR-V module produced by that test and it passes locally for me.

@kuhar
Copy link
Member

kuhar commented Jun 10, 2025

Thanks for the suggestion I have added that to the test. I have also validated manually the SPIR-V module produced by that test and it passes locally for me.

Have you checked that the spirv tools invocation works as expected and fails to validate without your fix? I haven't tried it myself, so I'm not sure if it has to be enabled somewhere in cmake etc.

@davidegrohmann
Copy link
Contributor Author

davidegrohmann commented Jun 11, 2025

Have you checked that the spirv tools invocation works as expected and fails to validate without your fix? I haven't tried it myself, so I'm not sure if it has to be enabled somewhere in cmake etc.

I was hoping that to work out of the box since there are also other tests in the same folder using the same spirv-tools configuration, for example: mlir/test/Target/SPIRV/consecutive-selection.spv and mlir/test/Target/SPIRV/selection.spv

In any case I have tried to make it work locally without success. I have built everything enabling the SPIRV target and enable the SPIRV tests by adding -DLLVM_TARGETS_TO_BUILD='host;SPIRV' and -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON

Full command:

cmake -G Ninja -B build -S llvm
    -DLLVM_ENABLE_PROJECTS=mlir
    -DLLVM_TARGETS_TO_BUILD='host;SPIRV'
    -DCMAKE_BUILD_TYPE=Release
    -DLLVM_REQUIRES_RTTI=ON
    -DLLVM_REQUIRES_EH=ON
    -DLLVM_ENABLE_RTTI=ON
    -DLLVM_ENABLE_EH=ON
    -DLLVM_ENABLE_ASSERTIONS=ON
    -DLLVM_ABI_BREAKING_CHECKS=FORCE_OFF
    -DMLIR_INCLUDE_INTEGRATION_TESTS=ON
    -DLLVM_ENABLE_TERMINFO=OFF
    -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON

Any ideas about what might I be missing?

In any case I have manually tested that the command mlir-translate -no-implicit-module -serialize-spirv %s | spirv-val works for mlir/test/Target/SPIRV/constant.mlir

@kuhar
Copy link
Member

kuhar commented Jun 11, 2025

Any ideas about what might I be missing?

I'd try to use the git history and see how this was initially plumbed through in clang tests. Once we figure this out, it will be very valuable in other tests that deal with (de)serialization. If some cmake configuration is required, we can document it under https://mlir.llvm.org/getting_started/TestingGuide/ .

-DLLVM_TARGETS_TO_BUILD='host;SPIRV'

This looks unrelated to me.

@kuhar
Copy link
Member

kuhar commented Jun 11, 2025

See https://github.com/llvm/llvm-project/pull/73044/files . I think we are missing a local lit config entry.

@davidegrohmann
Copy link
Contributor Author

See https://github.com/llvm/llvm-project/pull/73044/files . I think we are missing a local lit config entry.

Thanks for the link I'll have a look

@davidegrohmann
Copy link
Contributor Author

-DLLVM_TARGETS_TO_BUILD='host;SPIRV'

This looks unrelated to me.

Including the SPIRV target is needed otherwise the following error path triggers in llvm/tools/spirv-tools/CMakeLists.txt:

option(LLVM_INCLUDE_SPIRV_TOOLS_TESTS "Include tests that use SPIRV-Tools" Off)
mark_as_advanced(LLVM_INCLUDE_SPIRV_TOOLS_TESTS)

if (NOT LLVM_INCLUDE_SPIRV_TOOLS_TESTS)
  return()
endif ()

if (NOT "SPIRV" IN_LIST LLVM_TARGETS_TO_BUILD)
  message(FATAL_ERROR "Building SPIRV-Tools tests is unsupported without the SPIR-V target")
endif ()

@kuhar
Copy link
Member

kuhar commented Jun 11, 2025

Including the SPIRV target is needed otherwise the following error path triggers in llvm/tools/spirv-tools/CMakeLists.txt:

Ah, I understand. Supporting spirv-tools in tests and building the spirv backend can be decoupled in the future -- fine to leave as-is for now though.

Also fix validation failures caused by a rebase

Signed-off-by: Davide Grohmann <[email protected]>
Change-Id: I221881875c77a30a4df4bd9b15288ca10e9c8e9c
@davidegrohmann
Copy link
Contributor Author

See https://github.com/llvm/llvm-project/pull/73044/files . I think we are missing a local lit config entry.

Thanks for the link I'll have a look

It works for me locally now with the latest commit

@davidegrohmann
Copy link
Contributor Author

@kuhar is there anything more that is required for this PR to be merged?

@kuhar kuhar merged commit 549bc55 into llvm:main Jun 17, 2025
7 checks passed
Copy link

@davidegrohmann Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@kuhar
Copy link
Member

kuhar commented Jun 17, 2025

@kuhar is there anything more that is required for this PR to be merged?

Merged. @davidegrohmann could you submit a follow up PR and update the mlir documentation to explain how to run tests that use spirv tools and also decouple the cmake flag for spirv tools from the flag that enables the spirv backend?

@davidegrohmann davidegrohmann deleted the fix-unsign-signless-type-serialization-duplication branch June 17, 2025 14:49
basioli-k added a commit to basioli-k/llvm-project that referenced this pull request Jun 17, 2025
basioli-k added a commit that referenced this pull request Jun 17, 2025
ajaden-codes pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 17, 2025
…lvm#143108)

At the MLIR level unsigned integer and signless integers are different
types. Indeed when looking up the two types in type definition cache
they do not match.

Hence when translating a SPIR-V module which contains both usign and
signless integers will contain the same type declaration twice
(something like OpTypeInt 32 0) which is not permitted in SPIR-V and
such generated modules fail validation.

This patch solves the problem by mapping unisgned integer types to
singless integer types before looking up in the type definition cache.

---------

Signed-off-by: Davide Grohmann <[email protected]>
ajaden-codes pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 17, 2025
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jun 17, 2025

LLVM Buildbot has detected a new failure on builder flang-aarch64-latest-gcc running on linaro-flang-aarch64-latest-gcc while building mlir at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/130/builds/13792

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure)
...
617.566 [545/112/6815] Building CXX object tools/flang/lib/Parser/CMakeFiles/FortranParser.dir/expr-parsers.cpp.o
620.168 [545/111/6816] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/attr.cpp.o
620.623 [545/110/6817] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-return.cpp.o
621.559 [545/109/6818] Building CXX object tools/flang/lib/Parser/CMakeFiles/FortranParser.dir/io-parsers.cpp.o
622.895 [545/108/6819] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-arithmeticif.cpp.o
622.970 [545/107/6820] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-stop.cpp.o
626.204 [545/106/6821] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-purity.cpp.o
628.821 [545/105/6822] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-if-stmt.cpp.o
630.864 [545/104/6823] Building CXX object tools/flang/lib/Evaluate/CMakeFiles/FortranEvaluate.dir/constant.cpp.o
646.281 [545/103/6824] Building CXX object tools/flang/lib/Semantics/CMakeFiles/FortranSemantics.dir/check-nullify.cpp.o
command timed out: 1200 seconds without output running [b'ninja'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=1846.474063

@@ -0,0 +1,7 @@
if not "SPIRV" in config.root.targets:
config.unsupported = True
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need the SPIRV backend to run MLIR unit-tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see this comment: #143108 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

I missed this -- if this disables all mlir tests, we should revert the PR

Copy link
Member

Choose a reason for hiding this comment

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

This was partially fixed in #144685 but now disables all spirv tests...
@davidegrohmann can you prepare a fix by EoD? Otherwise we should revert this.

Copy link
Member

Choose a reason for hiding this comment

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

Revert PR: #144773

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I miss your comment it was already EoD for me. Thanks for reverting the patch. I will prepare a new PR without the problematic code.

fschlimb pushed a commit to fschlimb/llvm-project that referenced this pull request Jun 18, 2025
…lvm#143108)

At the MLIR level unsigned integer and signless integers are different
types. Indeed when looking up the two types in type definition cache
they do not match.

Hence when translating a SPIR-V module which contains both usign and
signless integers will contain the same type declaration twice
(something like OpTypeInt 32 0) which is not permitted in SPIR-V and
such generated modules fail validation.

This patch solves the problem by mapping unisgned integer types to
singless integer types before looking up in the type definition cache.

---------

Signed-off-by: Davide Grohmann <[email protected]>
fschlimb pushed a commit to fschlimb/llvm-project that referenced this pull request Jun 18, 2025
kuhar added a commit that referenced this pull request Jun 18, 2025
…lizing" and follow up commits (#144773)

This reverts the following PRs:
* #143108
* #144538
* #144685

Reverting because this disabled tests when building without the llvm
spirv backend enabled.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jun 18, 2025
… when serializing" and follow up commits (#144773)

This reverts the following PRs:
* llvm/llvm-project#143108
* llvm/llvm-project#144538
* llvm/llvm-project#144685

Reverting because this disabled tests when building without the llvm
spirv backend enabled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants