Skip to content

[mlir][llvm dialect] Verify element type of nested types #148975

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

newling
Copy link
Contributor

@newling newling commented Jul 15, 2025

Before this PR, this was valid

 %0 = llvm.mlir.constant(dense<[1, 2]> : vector<2xi32>) : vector<2xf32>

but this was not:

%0 = llvm.mlir.constant(1 : i32) : f32

because only scalar types were checked for compatibility, not the element types of nested types.

Another additional check that this PR adds is to verify the float semantics. Before this PR,

 %cst = llvm.mlir.constant(1.0 : bf16) : f16

was considered valid (because bf16 and f16 both have 16 bits), but with this PR it is not considered valid.

This PR also moves all tests on the verifier of the llvm constant op into a single file.

The total number of tests with this PR increases by 3.

To summarize the state after this PR. Valid:

Invalid:

%0 = llvm.mlir.constant(dense<[128, 1024]> : vector<2xi32>) : vector<2xf32>
%0 = llvm.mlir.constant(dense<[128., 1024.]> : vector<2xbf16>) : vector<2xf16>

Valid:

%0 = llvm.mlir.constant(dense<[128., 1024.]> : vector<2xf32>) : vector<2xi32>
%0 = llvm.mlir.constant(dense<[128, 1024]> : vector<2xi64>) : vector<2xi8>

and identical valid/invalid cases for the scalar cases.

@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2025

@llvm/pr-subscribers-mlir

Author: James Newling (newling)

Changes

Move some tests from Target/LLVMIR/llvmir-invalid.mlir to Dialect/LLVMIR/invalid.mlir so that all tests of ConstantOp::verify() are in the same file.


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

2 Files Affected:

  • (modified) mlir/test/Dialect/LLVMIR/invalid.mlir (+25)
  • (modified) mlir/test/Target/LLVMIR/llvmir-invalid.mlir (-24)
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index bd1106e304c60..e5fe78c077314 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -439,6 +439,31 @@ llvm.func @scalable_vec_requires_splat() -> vector<[4]xf64> {
   llvm.return %0 : vector<[4]xf64>
 }
 
+
+// -----
+
+llvm.func @integer_with_float_type() -> f32 {
+  // expected-error @+1 {{expected integer type}}
+  %0 = llvm.mlir.constant(1 : index) : f32
+  llvm.return %0 : f32
+}
+
+// -----
+
+llvm.func @incompatible_float_attribute_type() -> f32 {
+  // expected-error @below{{expected float type of width 64}}
+  %cst = llvm.mlir.constant(1.0 : f64) : f32
+  llvm.return %cst : f32
+}
+
+// -----
+
+llvm.func @incompatible_integer_type_for_float_attr() -> i32 {
+  // expected-error @below{{expected integer type of width 16}}
+  %cst = llvm.mlir.constant(1.0 : f16) : i32
+  llvm.return %cst : i32
+}
+
 // -----
 
 func.func @insertvalue_non_llvm_type(%a : i32, %b : i32) {
diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
index a8ef401fff27e..6c7a218d0676e 100644
--- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
@@ -55,30 +55,6 @@ llvm.func @struct_wrong_attribute_element_type() -> !llvm.struct<(f64, f64)> {
 
 // -----
 
-llvm.func @integer_with_float_type() -> f32 {
-  // expected-error @+1 {{expected integer type}}
-  %0 = llvm.mlir.constant(1 : index) : f32
-  llvm.return %0 : f32
-}
-
-// -----
-
-llvm.func @incompatible_float_attribute_type() -> f32 {
-  // expected-error @below{{expected float type of width 64}}
-  %cst = llvm.mlir.constant(1.0 : f64) : f32
-  llvm.return %cst : f32
-}
-
-// -----
-
-llvm.func @incompatible_integer_type_for_float_attr() -> i32 {
-  // expected-error @below{{expected integer type of width 16}}
-  %cst = llvm.mlir.constant(1.0 : f16) : i32
-  llvm.return %cst : i32
-}
-
-// -----
-
 // expected-error @below{{LLVM attribute 'readonly' does not expect a value}}
 llvm.func @passthrough_unexpected_value() attributes {passthrough = [["readonly", "42"]]}
 

@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2025

@llvm/pr-subscribers-mlir-llvm

Author: James Newling (newling)

Changes

Move some tests from Target/LLVMIR/llvmir-invalid.mlir to Dialect/LLVMIR/invalid.mlir so that all tests of ConstantOp::verify() are in the same file.


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

2 Files Affected:

  • (modified) mlir/test/Dialect/LLVMIR/invalid.mlir (+25)
  • (modified) mlir/test/Target/LLVMIR/llvmir-invalid.mlir (-24)
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index bd1106e304c60..e5fe78c077314 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -439,6 +439,31 @@ llvm.func @scalable_vec_requires_splat() -> vector<[4]xf64> {
   llvm.return %0 : vector<[4]xf64>
 }
 
+
+// -----
+
+llvm.func @integer_with_float_type() -> f32 {
+  // expected-error @+1 {{expected integer type}}
+  %0 = llvm.mlir.constant(1 : index) : f32
+  llvm.return %0 : f32
+}
+
+// -----
+
+llvm.func @incompatible_float_attribute_type() -> f32 {
+  // expected-error @below{{expected float type of width 64}}
+  %cst = llvm.mlir.constant(1.0 : f64) : f32
+  llvm.return %cst : f32
+}
+
+// -----
+
+llvm.func @incompatible_integer_type_for_float_attr() -> i32 {
+  // expected-error @below{{expected integer type of width 16}}
+  %cst = llvm.mlir.constant(1.0 : f16) : i32
+  llvm.return %cst : i32
+}
+
 // -----
 
 func.func @insertvalue_non_llvm_type(%a : i32, %b : i32) {
diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
index a8ef401fff27e..6c7a218d0676e 100644
--- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
@@ -55,30 +55,6 @@ llvm.func @struct_wrong_attribute_element_type() -> !llvm.struct<(f64, f64)> {
 
 // -----
 
-llvm.func @integer_with_float_type() -> f32 {
-  // expected-error @+1 {{expected integer type}}
-  %0 = llvm.mlir.constant(1 : index) : f32
-  llvm.return %0 : f32
-}
-
-// -----
-
-llvm.func @incompatible_float_attribute_type() -> f32 {
-  // expected-error @below{{expected float type of width 64}}
-  %cst = llvm.mlir.constant(1.0 : f64) : f32
-  llvm.return %cst : f32
-}
-
-// -----
-
-llvm.func @incompatible_integer_type_for_float_attr() -> i32 {
-  // expected-error @below{{expected integer type of width 16}}
-  %cst = llvm.mlir.constant(1.0 : f16) : i32
-  llvm.return %cst : i32
-}
-
-// -----
-
 // expected-error @below{{LLVM attribute 'readonly' does not expect a value}}
 llvm.func @passthrough_unexpected_value() attributes {passthrough = [["readonly", "42"]]}
 

@newling newling changed the title [mlir][llvm dialect][NFC] Ensure tests of constant op verifier to common file [mlir][llvm dialect][NFC] Put tests of constant op verifier in common file Jul 15, 2025
@newling newling marked this pull request as draft July 16, 2025 00:50
@newling newling changed the title [mlir][llvm dialect][NFC] Put tests of constant op verifier in common file [mlir][llvm dialect] Verify element type of nested types Jul 16, 2025
@newling newling marked this pull request as ready for review July 16, 2025 01:10
Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

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

Thanks a lot for improving the verifiers!

I have added some small nit comments but otherwise looks good to go.

type = arrayType.getElementType();
if (auto vecType = dyn_cast<VectorType>(type))
return vecType.getElementType();
if (auto tenType = dyn_cast<TensorType>(type))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the TensorType can be dropped. It is not a compatible LLVM type (see isCompatibleOuterType) and since ConstantOp returns an LLVM type there should be no tensors here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should the following fail to verify?

  %0 = llvm.mlir.constant(dense<[1.0, 1.0]> : tensor<2xf64>) : vector<2xf64>

It currently roundtrips without issue.

Maybe what is confusing here is that I'm using this method to test both the attribute's element type as well the constant op's result's element type?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would have expected that we use:

%0 = llvm.mlir.constant(dense<[1.0, 1.0]> : vector<2xf64>) : vector<2xf64>

But if the tensor type shows up in the wild on the attribute then we can keep your version of the code.

Comment on lines 3270 to 3272
if (!constantElementType.isInteger(floatWidth)) {
return emitOpError() << "expected integer type of width " << floatWidth;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!constantElementType.isInteger(floatWidth)) {
return emitOpError() << "expected integer type of width " << floatWidth;
}
if (!constantElementType.isInteger(floatWidth))
return emitOpError() << "expected integer type of width " << floatWidth;

nit: for single line ifs we usually drop the braces

Comment on lines +3307 to +3309
if (auto floatType = dyn_cast<FloatType>(attrElmType)) {
return verifyFloatSemantics(floatType.getFloatSemantics(), resultElmType);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (auto floatType = dyn_cast<FloatType>(attrElmType)) {
return verifyFloatSemantics(floatType.getFloatSemantics(), resultElmType);
}
if (auto floatType = dyn_cast<FloatType>(attrElmType))
return verifyFloatSemantics(floatType.getFloatSemantics(), resultElmType);

nit: Same as above

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a leftover?

llvm.return %0 : !llvm.struct<(f64, f64)>
}

// -----
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// -----

nit: one split can be dropped

// -----

llvm.func @int_attr_requires_int_type() -> f32 {
// expected-error @+1 {{expected integer type}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// expected-error @+1 {{expected integer type}}
// expected-error @below {{expected integer type}}

ulta nit: can you use below everywhere in the moved tests? It is preferred over @+1

@newling
Copy link
Contributor Author

newling commented Jul 16, 2025

@gysit Thank you for reviewing this! I think I have addressed your comments. The one I'm not sure about is the removal of TensorType from the new static function, getElementType. Please see my inline comment.

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the comment!

LGTM modulo some leftover nit.

I did one more search and realized that the tensor type is also used for llvm.array constants. That is actually a somewhat justified use case. Let's keep your current version of the code then!

Comment on lines +3307 to +3309
if (auto floatType = dyn_cast<FloatType>(attrElmType)) {
return verifyFloatSemantics(floatType.getFloatSemantics(), resultElmType);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a leftover?

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.

3 participants