Skip to content

[mlir][emitc] Add EmitC index types #93155

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

Merged
merged 13 commits into from
Jun 17, 2024

Conversation

cferry-AMD
Copy link
Contributor

@cferry-AMD cferry-AMD commented May 23, 2024

This commit adds emitc.size_t, emitc.ssize_t and emitc.ptrdiff_t types to the EmitC dialect. These are used to map index types to C/C++ types with an explicit signedness, and are emitted in C/C++ as size_t, ssize_t and ptrdiff_t.

@llvmbot
Copy link
Member

llvmbot commented May 23, 2024

@llvm/pr-subscribers-mlir

Author: Corentin Ferry (cferry-AMD)

Changes

This commit adds emitc.size_t and emitc.ssize_t types to the EmitC dialect. These correspond to index types with an explicit signedness, and are emitted in C/C++ as size_t and ssize_t.


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

12 Files Affected:

  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.h (+4)
  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+3-2)
  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td (+9)
  • (added) mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h (+17)
  • (modified) mlir/lib/Dialect/EmitC/IR/EmitC.cpp (+19-6)
  • (modified) mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt (+1)
  • (added) mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp (+39)
  • (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+4)
  • (modified) mlir/test/Dialect/EmitC/invalid_ops.mlir (+4-4)
  • (modified) mlir/test/Dialect/EmitC/invalid_types.mlir (+2-2)
  • (modified) mlir/test/Dialect/EmitC/types.mlir (+10)
  • (modified) mlir/test/Target/Cpp/types.mlir (+10)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index 5d9531cd12415..575b91516b919 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -43,6 +43,10 @@ bool isIntegerIndexOrOpaqueType(Type type);
 
 /// Determines whether \p type is a valid floating-point type in EmitC.
 bool isSupportedFloatType(mlir::Type type);
+
+/// Determines whether \p type is a emitc.size_t/ssize_t type.
+bool isAnySizeTType(mlir::Type type);
+
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 5da8593f59563..071c4bc6bdc8a 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -51,7 +51,8 @@ class EmitC_BinaryOp<string mnemonic, list<Trait> traits = []> :
 def CExpression : NativeOpTrait<"emitc::CExpression">;
 
 // Types only used in binary arithmetic operations.
-def IntegerIndexOrOpaqueType : AnyTypeOf<[EmitCIntegerType, Index, EmitC_OpaqueType]>;
+def IntegerIndexOrOpaqueType : AnyTypeOf<[EmitCIntegerType, Index,
+  EmitC_SignedSizeT, EmitC_SizeT, EmitC_OpaqueType]>;
 def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
 
 def EmitC_AddOp : EmitC_BinaryOp<"add", [CExpression]> {
@@ -470,7 +471,7 @@ def EmitC_ForOp : EmitC_Op<"for",
     upper bound and step respectively, and defines an SSA value for its
     induction variable. It has one region capturing the loop body. The induction
     variable is represented as an argument of this region. This SSA value is a
-    signless integer or index. The step is a value of same type.
+    signless integer, or an index. The step is a value of same type.
 
     This operation has no result. The body region must contain exactly one block
     that terminates with `emitc.yield`. Calling ForOp::build will create such a
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
index 444395b915e25..f6a8bd4ef59e1 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
@@ -76,6 +76,7 @@ def EmitC_ArrayType : EmitC_Type<"Array", "array", [ShapedTypeInterface]> {
 
     static bool isValidElementType(Type type) {
       return type.isIntOrIndexOrFloat() ||
+         emitc::isAnySizeTType(type) ||
          llvm::isa<PointerType, OpaqueType>(type);
     }
   }];
@@ -130,4 +131,12 @@ def EmitC_PointerType : EmitC_Type<"Pointer", "ptr"> {
   let assemblyFormat = "`<` qualified($pointee) `>`";
 }
 
+def EmitC_SignedSizeT : EmitC_Type<"SignedSizeT", "ssize_t"> {
+  let summary = "EmitC signed size type";
+}
+
+def EmitC_SizeT : EmitC_Type<"SizeT", "size_t"> {
+  let summary = "EmitC unsigned size type";
+}
+
 #endif // MLIR_DIALECT_EMITC_IR_EMITCTYPES
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h b/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h
new file mode 100644
index 0000000000000..1c05a927e948a
--- /dev/null
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h
@@ -0,0 +1,17 @@
+//===- TypeConversions.h - Convert signless types into C/C++ types -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
+#define MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
+
+namespace mlir {
+class TypeConverter;
+void populateEmitCSizeTypeConversions(TypeConverter &converter);
+} // namespace mlir
+
+#endif // MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index ef7b7a19489d4..e855f2119abb3 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -68,7 +68,8 @@ bool mlir::emitc::isSupportedEmitCType(Type type) {
     return !llvm::isa<emitc::ArrayType>(elemType) &&
            isSupportedEmitCType(elemType);
   }
-  if (type.isIndex())
+  if (type.isIndex() ||
+      llvm::isa<emitc::SignedSizeTType, emitc::SizeTType>(type))
     return true;
   if (llvm::isa<IntegerType>(type))
     return isSupportedIntegerType(type);
@@ -109,7 +110,8 @@ bool mlir::emitc::isSupportedIntegerType(Type type) {
 }
 
 bool mlir::emitc::isIntegerIndexOrOpaqueType(Type type) {
-  return llvm::isa<IndexType, emitc::OpaqueType>(type) ||
+  return llvm::isa<IndexType, emitc::SignedSizeTType, emitc::SizeTType,
+                   emitc::OpaqueType>(type) ||
          isSupportedIntegerType(type);
 }
 
@@ -126,6 +128,10 @@ bool mlir::emitc::isSupportedFloatType(Type type) {
   return false;
 }
 
+bool mlir::emitc::isAnySizeTType(Type type) {
+  return isa<emitc::SignedSizeTType, emitc::SizeTType>(type);
+}
+
 /// Check that the type of the initial value is compatible with the operations
 /// result type.
 static LogicalResult verifyInitializationAttribute(Operation *op,
@@ -142,6 +148,10 @@ static LogicalResult verifyInitializationAttribute(Operation *op,
   Type resultType = op->getResult(0).getType();
   Type attrType = cast<TypedAttr>(value).getType();
 
+  if (isa<emitc::SignedSizeTType, emitc::SizeTType>(resultType) &&
+      attrType.isIndex())
+    return success();
+
   if (resultType != attrType)
     return op->emitOpError()
            << "requires attribute to either be an #emitc.opaque attribute or "
@@ -226,10 +236,13 @@ LogicalResult emitc::AssignOp::verify() {
 bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
   Type input = inputs.front(), output = outputs.front();
 
-  return ((llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
-                     emitc::PointerType>(input)) &&
-          (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
-                     emitc::PointerType>(output)));
+  return (
+      (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                 emitc::PointerType, emitc::SignedSizeTType, emitc::SizeTType>(
+          input)) &&
+      (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                 emitc::PointerType, emitc::SignedSizeTType, emitc::SizeTType>(
+          output)));
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
index bfcc14523f137..19b80b22bd84b 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_mlir_dialect_library(MLIREmitCTransforms
   Transforms.cpp
   FormExpressions.cpp
+  TypeConversions.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/EmitC/Transforms
diff --git a/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp b/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp
new file mode 100644
index 0000000000000..02987df56f042
--- /dev/null
+++ b/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp
@@ -0,0 +1,39 @@
+//===- TypeConversions.cpp - Convert signless types into C/C++ types ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/EmitC/Transforms/TypeConversions.h"
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include <optional>
+
+using namespace mlir;
+
+namespace {
+
+std::optional<Value> materializeAsUnrealizedCast(OpBuilder &builder,
+                                                 Type resultType,
+                                                 ValueRange inputs,
+                                                 Location loc) {
+  if (inputs.size() != 1)
+    return std::nullopt;
+
+  return builder.create<UnrealizedConversionCastOp>(loc, resultType, inputs)
+      .getResult(0);
+}
+
+} // namespace
+
+void mlir::populateEmitCSizeTypeConversions(TypeConverter &converter) {
+  converter.addConversion(
+      [](IndexType type) { return emitc::SizeTType::get(type.getContext()); });
+
+  converter.addSourceMaterialization(materializeAsUnrealizedCast);
+  converter.addTargetMaterialization(materializeAsUnrealizedCast);
+  converter.addArgumentMaterialization(materializeAsUnrealizedCast);
+}
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 7db7163bac4ab..d2ba57f679de2 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -1570,6 +1570,10 @@ LogicalResult CppEmitter::emitType(Location loc, Type type) {
   }
   if (auto iType = dyn_cast<IndexType>(type))
     return (os << "size_t"), success();
+  if (auto iType = dyn_cast<emitc::SizeTType>(type))
+    return (os << "size_t"), success();
+  if (auto iType = dyn_cast<emitc::SignedSizeTType>(type))
+    return (os << "ssize_t"), success();
   if (auto tType = dyn_cast<TensorType>(type)) {
     if (!tType.hasRank())
       return emitError(loc, "cannot emit unranked tensor type");
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 21ea6a5df91b9..3eede2c72dca4 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -170,7 +170,7 @@ func.func @add_float_pointer(%arg0: f32, %arg1: !emitc.ptr<f32>) {
 // -----
 
 func.func @div_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.div' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.div' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.div" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -178,7 +178,7 @@ func.func @div_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @mul_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.mul" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -186,7 +186,7 @@ func.func @mul_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @rem_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.rem" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -194,7 +194,7 @@ func.func @rem_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @rem_float(%arg0: f32, %arg1: f32) {
-    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC opaque type, but got 'f32'}}
+    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'f32'}}
     %1 = "emitc.rem" (%arg0, %arg1) : (f32, f32) -> f32
     return
 }
diff --git a/mlir/test/Dialect/EmitC/invalid_types.mlir b/mlir/test/Dialect/EmitC/invalid_types.mlir
index 0ad8d4eabe6b8..111fbc7de640b 100644
--- a/mlir/test/Dialect/EmitC/invalid_types.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_types.mlir
@@ -85,7 +85,7 @@ func.func @illegal_array_with_tensor_element_type(
 // -----
 
 func.func @illegal_integer_type(%arg0: i11, %arg1: i11) -> i11 {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'i11'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'i11'}}
     %mul = "emitc.mul" (%arg0, %arg1) : (i11, i11) -> i11
     return
 }
@@ -93,7 +93,7 @@ func.func @illegal_integer_type(%arg0: i11, %arg1: i11) -> i11 {
 // -----
 
 func.func @illegal_float_type(%arg0: f80, %arg1: f80) {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'f80'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'f80'}}
     %mul = "emitc.mul" (%arg0, %arg1) : (f80, f80) -> f80
     return
 }
diff --git a/mlir/test/Dialect/EmitC/types.mlir b/mlir/test/Dialect/EmitC/types.mlir
index 752f2c10c17be..8b630d5a320f4 100644
--- a/mlir/test/Dialect/EmitC/types.mlir
+++ b/mlir/test/Dialect/EmitC/types.mlir
@@ -53,3 +53,13 @@ func.func @pointer_types() {
 
   return
 }
+
+// CHECK-LABEL: func @index_types() 
+func.func @index_types() {
+  // CHECK-NEXT: !emitc.ssize_t
+  emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
+  // CHECK-NEXT: !emitc.size_t
+  emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
+
+  return
+}
diff --git a/mlir/test/Target/Cpp/types.mlir b/mlir/test/Target/Cpp/types.mlir
index 0585b27eb6c22..647777c8ac718 100644
--- a/mlir/test/Target/Cpp/types.mlir
+++ b/mlir/test/Target/Cpp/types.mlir
@@ -35,3 +35,13 @@ func.func @ptr_types() {
 
   return
 }
+
+// CHECK-LABEL: void size_types() {
+func.func @size_types() {
+  // CHECK-NEXT: f<ssize_t>();
+  emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
+  // CHECK-NEXT: f<size_t>();
+  emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
+
+  return
+}

@llvmbot
Copy link
Member

llvmbot commented May 23, 2024

@llvm/pr-subscribers-mlir-emitc

Author: Corentin Ferry (cferry-AMD)

Changes

This commit adds emitc.size_t and emitc.ssize_t types to the EmitC dialect. These correspond to index types with an explicit signedness, and are emitted in C/C++ as size_t and ssize_t.


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

12 Files Affected:

  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.h (+4)
  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+3-2)
  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td (+9)
  • (added) mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h (+17)
  • (modified) mlir/lib/Dialect/EmitC/IR/EmitC.cpp (+19-6)
  • (modified) mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt (+1)
  • (added) mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp (+39)
  • (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+4)
  • (modified) mlir/test/Dialect/EmitC/invalid_ops.mlir (+4-4)
  • (modified) mlir/test/Dialect/EmitC/invalid_types.mlir (+2-2)
  • (modified) mlir/test/Dialect/EmitC/types.mlir (+10)
  • (modified) mlir/test/Target/Cpp/types.mlir (+10)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index 5d9531cd12415..575b91516b919 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -43,6 +43,10 @@ bool isIntegerIndexOrOpaqueType(Type type);
 
 /// Determines whether \p type is a valid floating-point type in EmitC.
 bool isSupportedFloatType(mlir::Type type);
+
+/// Determines whether \p type is a emitc.size_t/ssize_t type.
+bool isAnySizeTType(mlir::Type type);
+
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 5da8593f59563..071c4bc6bdc8a 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -51,7 +51,8 @@ class EmitC_BinaryOp<string mnemonic, list<Trait> traits = []> :
 def CExpression : NativeOpTrait<"emitc::CExpression">;
 
 // Types only used in binary arithmetic operations.
-def IntegerIndexOrOpaqueType : AnyTypeOf<[EmitCIntegerType, Index, EmitC_OpaqueType]>;
+def IntegerIndexOrOpaqueType : AnyTypeOf<[EmitCIntegerType, Index,
+  EmitC_SignedSizeT, EmitC_SizeT, EmitC_OpaqueType]>;
 def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
 
 def EmitC_AddOp : EmitC_BinaryOp<"add", [CExpression]> {
@@ -470,7 +471,7 @@ def EmitC_ForOp : EmitC_Op<"for",
     upper bound and step respectively, and defines an SSA value for its
     induction variable. It has one region capturing the loop body. The induction
     variable is represented as an argument of this region. This SSA value is a
-    signless integer or index. The step is a value of same type.
+    signless integer, or an index. The step is a value of same type.
 
     This operation has no result. The body region must contain exactly one block
     that terminates with `emitc.yield`. Calling ForOp::build will create such a
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
index 444395b915e25..f6a8bd4ef59e1 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td
@@ -76,6 +76,7 @@ def EmitC_ArrayType : EmitC_Type<"Array", "array", [ShapedTypeInterface]> {
 
     static bool isValidElementType(Type type) {
       return type.isIntOrIndexOrFloat() ||
+         emitc::isAnySizeTType(type) ||
          llvm::isa<PointerType, OpaqueType>(type);
     }
   }];
@@ -130,4 +131,12 @@ def EmitC_PointerType : EmitC_Type<"Pointer", "ptr"> {
   let assemblyFormat = "`<` qualified($pointee) `>`";
 }
 
+def EmitC_SignedSizeT : EmitC_Type<"SignedSizeT", "ssize_t"> {
+  let summary = "EmitC signed size type";
+}
+
+def EmitC_SizeT : EmitC_Type<"SizeT", "size_t"> {
+  let summary = "EmitC unsigned size type";
+}
+
 #endif // MLIR_DIALECT_EMITC_IR_EMITCTYPES
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h b/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h
new file mode 100644
index 0000000000000..1c05a927e948a
--- /dev/null
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/TypeConversions.h
@@ -0,0 +1,17 @@
+//===- TypeConversions.h - Convert signless types into C/C++ types -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
+#define MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
+
+namespace mlir {
+class TypeConverter;
+void populateEmitCSizeTypeConversions(TypeConverter &converter);
+} // namespace mlir
+
+#endif // MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index ef7b7a19489d4..e855f2119abb3 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -68,7 +68,8 @@ bool mlir::emitc::isSupportedEmitCType(Type type) {
     return !llvm::isa<emitc::ArrayType>(elemType) &&
            isSupportedEmitCType(elemType);
   }
-  if (type.isIndex())
+  if (type.isIndex() ||
+      llvm::isa<emitc::SignedSizeTType, emitc::SizeTType>(type))
     return true;
   if (llvm::isa<IntegerType>(type))
     return isSupportedIntegerType(type);
@@ -109,7 +110,8 @@ bool mlir::emitc::isSupportedIntegerType(Type type) {
 }
 
 bool mlir::emitc::isIntegerIndexOrOpaqueType(Type type) {
-  return llvm::isa<IndexType, emitc::OpaqueType>(type) ||
+  return llvm::isa<IndexType, emitc::SignedSizeTType, emitc::SizeTType,
+                   emitc::OpaqueType>(type) ||
          isSupportedIntegerType(type);
 }
 
@@ -126,6 +128,10 @@ bool mlir::emitc::isSupportedFloatType(Type type) {
   return false;
 }
 
+bool mlir::emitc::isAnySizeTType(Type type) {
+  return isa<emitc::SignedSizeTType, emitc::SizeTType>(type);
+}
+
 /// Check that the type of the initial value is compatible with the operations
 /// result type.
 static LogicalResult verifyInitializationAttribute(Operation *op,
@@ -142,6 +148,10 @@ static LogicalResult verifyInitializationAttribute(Operation *op,
   Type resultType = op->getResult(0).getType();
   Type attrType = cast<TypedAttr>(value).getType();
 
+  if (isa<emitc::SignedSizeTType, emitc::SizeTType>(resultType) &&
+      attrType.isIndex())
+    return success();
+
   if (resultType != attrType)
     return op->emitOpError()
            << "requires attribute to either be an #emitc.opaque attribute or "
@@ -226,10 +236,13 @@ LogicalResult emitc::AssignOp::verify() {
 bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
   Type input = inputs.front(), output = outputs.front();
 
-  return ((llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
-                     emitc::PointerType>(input)) &&
-          (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
-                     emitc::PointerType>(output)));
+  return (
+      (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                 emitc::PointerType, emitc::SignedSizeTType, emitc::SizeTType>(
+          input)) &&
+      (llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                 emitc::PointerType, emitc::SignedSizeTType, emitc::SizeTType>(
+          output)));
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
index bfcc14523f137..19b80b22bd84b 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_mlir_dialect_library(MLIREmitCTransforms
   Transforms.cpp
   FormExpressions.cpp
+  TypeConversions.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/EmitC/Transforms
diff --git a/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp b/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp
new file mode 100644
index 0000000000000..02987df56f042
--- /dev/null
+++ b/mlir/lib/Dialect/EmitC/Transforms/TypeConversions.cpp
@@ -0,0 +1,39 @@
+//===- TypeConversions.cpp - Convert signless types into C/C++ types ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/EmitC/Transforms/TypeConversions.h"
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include <optional>
+
+using namespace mlir;
+
+namespace {
+
+std::optional<Value> materializeAsUnrealizedCast(OpBuilder &builder,
+                                                 Type resultType,
+                                                 ValueRange inputs,
+                                                 Location loc) {
+  if (inputs.size() != 1)
+    return std::nullopt;
+
+  return builder.create<UnrealizedConversionCastOp>(loc, resultType, inputs)
+      .getResult(0);
+}
+
+} // namespace
+
+void mlir::populateEmitCSizeTypeConversions(TypeConverter &converter) {
+  converter.addConversion(
+      [](IndexType type) { return emitc::SizeTType::get(type.getContext()); });
+
+  converter.addSourceMaterialization(materializeAsUnrealizedCast);
+  converter.addTargetMaterialization(materializeAsUnrealizedCast);
+  converter.addArgumentMaterialization(materializeAsUnrealizedCast);
+}
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 7db7163bac4ab..d2ba57f679de2 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -1570,6 +1570,10 @@ LogicalResult CppEmitter::emitType(Location loc, Type type) {
   }
   if (auto iType = dyn_cast<IndexType>(type))
     return (os << "size_t"), success();
+  if (auto iType = dyn_cast<emitc::SizeTType>(type))
+    return (os << "size_t"), success();
+  if (auto iType = dyn_cast<emitc::SignedSizeTType>(type))
+    return (os << "ssize_t"), success();
   if (auto tType = dyn_cast<TensorType>(type)) {
     if (!tType.hasRank())
       return emitError(loc, "cannot emit unranked tensor type");
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 21ea6a5df91b9..3eede2c72dca4 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -170,7 +170,7 @@ func.func @add_float_pointer(%arg0: f32, %arg1: !emitc.ptr<f32>) {
 // -----
 
 func.func @div_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.div' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.div' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.div" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -178,7 +178,7 @@ func.func @div_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @mul_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.mul" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -186,7 +186,7 @@ func.func @mul_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @rem_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
-    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC opaque type, but got 'tensor<i32>'}}
+    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'tensor<i32>'}}
     %1 = "emitc.rem" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
     return
 }
@@ -194,7 +194,7 @@ func.func @rem_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
 // -----
 
 func.func @rem_float(%arg0: f32, %arg1: f32) {
-    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC opaque type, but got 'f32'}}
+    // expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'f32'}}
     %1 = "emitc.rem" (%arg0, %arg1) : (f32, f32) -> f32
     return
 }
diff --git a/mlir/test/Dialect/EmitC/invalid_types.mlir b/mlir/test/Dialect/EmitC/invalid_types.mlir
index 0ad8d4eabe6b8..111fbc7de640b 100644
--- a/mlir/test/Dialect/EmitC/invalid_types.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_types.mlir
@@ -85,7 +85,7 @@ func.func @illegal_array_with_tensor_element_type(
 // -----
 
 func.func @illegal_integer_type(%arg0: i11, %arg1: i11) -> i11 {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'i11'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'i11'}}
     %mul = "emitc.mul" (%arg0, %arg1) : (i11, i11) -> i11
     return
 }
@@ -93,7 +93,7 @@ func.func @illegal_integer_type(%arg0: i11, %arg1: i11) -> i11 {
 // -----
 
 func.func @illegal_float_type(%arg0: f80, %arg1: f80) {
-    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC opaque type, but got 'f80'}}
+    // expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer type supported by EmitC or index or EmitC signed size type or EmitC unsigned size type or EmitC opaque type, but got 'f80'}}
     %mul = "emitc.mul" (%arg0, %arg1) : (f80, f80) -> f80
     return
 }
diff --git a/mlir/test/Dialect/EmitC/types.mlir b/mlir/test/Dialect/EmitC/types.mlir
index 752f2c10c17be..8b630d5a320f4 100644
--- a/mlir/test/Dialect/EmitC/types.mlir
+++ b/mlir/test/Dialect/EmitC/types.mlir
@@ -53,3 +53,13 @@ func.func @pointer_types() {
 
   return
 }
+
+// CHECK-LABEL: func @index_types() 
+func.func @index_types() {
+  // CHECK-NEXT: !emitc.ssize_t
+  emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
+  // CHECK-NEXT: !emitc.size_t
+  emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
+
+  return
+}
diff --git a/mlir/test/Target/Cpp/types.mlir b/mlir/test/Target/Cpp/types.mlir
index 0585b27eb6c22..647777c8ac718 100644
--- a/mlir/test/Target/Cpp/types.mlir
+++ b/mlir/test/Target/Cpp/types.mlir
@@ -35,3 +35,13 @@ func.func @ptr_types() {
 
   return
 }
+
+// CHECK-LABEL: void size_types() {
+func.func @size_types() {
+  // CHECK-NEXT: f<ssize_t>();
+  emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
+  // CHECK-NEXT: f<size_t>();
+  emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
+
+  return
+}

@cferry-AMD
Copy link
Contributor Author

This PR didn't get automatic CI testing. Is there a rate limit?

Copy link
Contributor

@simon-camp simon-camp left a comment

Choose a reason for hiding this comment

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

Overall this looks good to me. I've made just a few style suggestions and would like to see a few tests for operations which support the new types.

edit: Do we want to spell it XSizeTType or XSizeType on the C++ side?

Copy link

github-actions bot commented May 27, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@marbre marbre left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, I was oof. Some remarks from my side now.

@marbre
Copy link
Member

marbre commented Jun 11, 2024

Anything we (@simon-camp and me) can do here to support you @cferry-AMD?

@cferry-AMD
Copy link
Contributor Author

Sorry for the delay, I was quite busy last week and this PR went off my schedule. So, I think we can suggest that conversions between signed and unsigned types are fine so long as the values fit the bounds of the respective types (in which case the ptrdiff_t type is the signed counterpart of size_t). How does this look like?

@cferry-AMD cferry-AMD requested review from simon-camp and marbre June 17, 2024 09:24
Co-authored-by: Simon Camphausen <[email protected]>
Copy link
Member

@marbre marbre left a comment

Choose a reason for hiding this comment

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

Looks good to me, if those new types will let you do the conversions you need :)

@cferry-AMD cferry-AMD merged commit 3cead57 into llvm:main Jun 17, 2024
7 checks passed
@cferry-AMD cferry-AMD deleted the corentin.upstream_emitc_sizet branch June 17, 2024 11:53
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.

5 participants