Skip to content

Commit 3cead57

Browse files
authored
[mlir][emitc] Add EmitC index types (#93155)
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`.
1 parent 770393b commit 3cead57

File tree

14 files changed

+196
-17
lines changed

14 files changed

+196
-17
lines changed

mlir/docs/Dialects/emitc.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ The following convention is followed:
1010
`emitc.call_opaque` operation, C++11 is required.
1111
* If floating-point type template arguments are passed to an `emitc.call_opaque`
1212
operation, C++20 is required.
13+
* If `ssize_t` is used, then the code requires the POSIX header `sys/types.h`
14+
or any of the C++ headers in which the type is defined.
1315
* Else the generated code is compatible with C99.
1416

1517
These restrictions are neither inherent to the EmitC dialect itself nor to the
1618
Cpp emitter and therefore need to be considered while implementing conversions.
1719

20+
Type conversions are provided for the MLIR type `index` into the unsigned `size_t`
21+
type and its signed counterpart `ptrdiff_t`. Conversions between these two types
22+
are only valid if the `index`-typed values are within
23+
`[PTRDIFF_MIN, PTRDIFF_MAX]`.
24+
1825
After the conversion, C/C++ code can be emitted with `mlir-translate`. The tool
1926
supports translating MLIR to C/C++ by passing `-mlir-to-cpp`. Furthermore, code
2027
with variables declared at top can be generated by passing the additional

mlir/include/mlir/Dialect/EmitC/IR/EmitC.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ bool isIntegerIndexOrOpaqueType(Type type);
4343

4444
/// Determines whether \p type is a valid floating-point type in EmitC.
4545
bool isSupportedFloatType(mlir::Type type);
46+
47+
/// Determines whether \p type is a emitc.size_t/ssize_t type.
48+
bool isPointerWideType(mlir::Type type);
49+
4650
} // namespace emitc
4751
} // namespace mlir
4852

mlir/include/mlir/Dialect/EmitC/IR/EmitC.td

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class EmitC_BinaryOp<string mnemonic, list<Trait> traits = []> :
5151
def CExpression : NativeOpTrait<"emitc::CExpression">;
5252

5353
// Types only used in binary arithmetic operations.
54-
def IntegerIndexOrOpaqueType : AnyTypeOf<[EmitCIntegerType, Index, EmitC_OpaqueType]>;
54+
def IntegerIndexOrOpaqueType : Type<CPred<"emitc::isIntegerIndexOrOpaqueType($_self)">,
55+
"integer, index or opaque type supported by EmitC">;
5556
def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
5657

5758
def EmitC_AddOp : EmitC_BinaryOp<"add", [CExpression]> {
@@ -470,7 +471,7 @@ def EmitC_ForOp : EmitC_Op<"for",
470471
upper bound and step respectively, and defines an SSA value for its
471472
induction variable. It has one region capturing the loop body. The induction
472473
variable is represented as an argument of this region. This SSA value is a
473-
signless integer or index. The step is a value of same type.
474+
signless integer, or an index. The step is a value of same type.
474475

475476
This operation has no result. The body region must contain exactly one block
476477
that terminates with `emitc.yield`. Calling ForOp::build will create such a

mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ def EmitC_ArrayType : EmitC_Type<"Array", "array", [ShapedTypeInterface]> {
7575
Type elementType) const;
7676

7777
static bool isValidElementType(Type type) {
78-
return type.isIntOrIndexOrFloat() ||
79-
llvm::isa<PointerType, OpaqueType>(type);
78+
return emitc::isSupportedFloatType(type) ||
79+
emitc::isIntegerIndexOrOpaqueType(type) ||
80+
llvm::isa<PointerType>(type);
8081
}
8182
}];
8283
let genVerifyDecl = 1;
@@ -130,4 +131,31 @@ def EmitC_PointerType : EmitC_Type<"Pointer", "ptr"> {
130131
let assemblyFormat = "`<` qualified($pointee) `>`";
131132
}
132133

134+
def EmitC_SignedSizeT : EmitC_Type<"SignedSizeT", "ssize_t"> {
135+
let summary = "EmitC signed size type";
136+
let description = [{
137+
Data type representing all values of `emitc.size_t`, plus -1.
138+
It corresponds to `ssize_t` found in `<sys/types.h>`.
139+
140+
Use of this type causes the code to be non-C99 compliant.
141+
}];
142+
}
143+
144+
def EmitC_PtrDiffT : EmitC_Type<"PtrDiffT", "ptrdiff_t"> {
145+
let summary = "EmitC signed pointer diff type";
146+
let description = [{
147+
Signed data type as wide as platform-specific pointer types.
148+
In particular, it is as wide as `emitc.size_t`.
149+
It corresponds to `ptrdiff_t` found in `<stddef.h>`.
150+
}];
151+
}
152+
153+
def EmitC_SizeT : EmitC_Type<"SizeT", "size_t"> {
154+
let summary = "EmitC unsigned size type";
155+
let description = [{
156+
Unsigned data type as wide as platform-specific pointer types.
157+
It corresponds to `size_t` found in `<stddef.h>`.
158+
}];
159+
}
160+
133161
#endif // MLIR_DIALECT_EMITC_IR_EMITCTYPES
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===- TypeConversions.h - Convert signless types into C/C++ types -------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
10+
#define MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H
11+
12+
#include <optional>
13+
14+
namespace mlir {
15+
class TypeConverter;
16+
class Type;
17+
void populateEmitCSizeTTypeConversions(TypeConverter &converter);
18+
19+
namespace emitc {
20+
std::optional<Type> getUnsignedTypeFor(Type ty);
21+
std::optional<Type> getSignedTypeFor(Type ty);
22+
} // namespace emitc
23+
24+
} // namespace mlir
25+
26+
#endif // MLIR_DIALECT_EMITC_TRANSFORMS_TYPECONVERSIONS_H

mlir/lib/Dialect/EmitC/IR/EmitC.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ bool mlir::emitc::isSupportedEmitCType(Type type) {
6868
return !llvm::isa<emitc::ArrayType>(elemType) &&
6969
isSupportedEmitCType(elemType);
7070
}
71-
if (type.isIndex())
71+
if (type.isIndex() || emitc::isPointerWideType(type))
7272
return true;
7373
if (llvm::isa<IntegerType>(type))
7474
return isSupportedIntegerType(type);
@@ -110,7 +110,7 @@ bool mlir::emitc::isSupportedIntegerType(Type type) {
110110

111111
bool mlir::emitc::isIntegerIndexOrOpaqueType(Type type) {
112112
return llvm::isa<IndexType, emitc::OpaqueType>(type) ||
113-
isSupportedIntegerType(type);
113+
isSupportedIntegerType(type) || isPointerWideType(type);
114114
}
115115

116116
bool mlir::emitc::isSupportedFloatType(Type type) {
@@ -126,6 +126,11 @@ bool mlir::emitc::isSupportedFloatType(Type type) {
126126
return false;
127127
}
128128

129+
bool mlir::emitc::isPointerWideType(Type type) {
130+
return isa<emitc::SignedSizeTType, emitc::SizeTType, emitc::PtrDiffTType>(
131+
type);
132+
}
133+
129134
/// Check that the type of the initial value is compatible with the operations
130135
/// result type.
131136
static LogicalResult verifyInitializationAttribute(Operation *op,
@@ -142,6 +147,9 @@ static LogicalResult verifyInitializationAttribute(Operation *op,
142147
Type resultType = op->getResult(0).getType();
143148
Type attrType = cast<TypedAttr>(value).getType();
144149

150+
if (isPointerWideType(resultType) && attrType.isIndex())
151+
return success();
152+
145153
if (resultType != attrType)
146154
return op->emitOpError()
147155
<< "requires attribute to either be an #emitc.opaque attribute or "
@@ -226,10 +234,11 @@ LogicalResult emitc::AssignOp::verify() {
226234
bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
227235
Type input = inputs.front(), output = outputs.front();
228236

229-
return ((llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
230-
emitc::PointerType>(input)) &&
231-
(llvm::isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
232-
emitc::PointerType>(output)));
237+
return (
238+
(emitc::isIntegerIndexOrOpaqueType(input) ||
239+
emitc::isSupportedFloatType(input) || isa<emitc::PointerType>(input)) &&
240+
(emitc::isIntegerIndexOrOpaqueType(output) ||
241+
emitc::isSupportedFloatType(output) || isa<emitc::PointerType>(output)));
233242
}
234243

235244
//===----------------------------------------------------------------------===//

mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_mlir_dialect_library(MLIREmitCTransforms
22
Transforms.cpp
33
FormExpressions.cpp
4+
TypeConversions.cpp
45

56
ADDITIONAL_HEADER_DIRS
67
${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/EmitC/Transforms
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===- TypeConversions.cpp - Convert signless types into C/C++ types ------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "mlir/Dialect/EmitC/Transforms/TypeConversions.h"
10+
#include "mlir/Dialect/EmitC/IR/EmitC.h"
11+
#include "mlir/IR/BuiltinTypes.h"
12+
#include "mlir/Transforms/DialectConversion.h"
13+
#include <optional>
14+
15+
using namespace mlir;
16+
17+
namespace {
18+
19+
std::optional<Value> materializeAsUnrealizedCast(OpBuilder &builder,
20+
Type resultType,
21+
ValueRange inputs,
22+
Location loc) {
23+
if (inputs.size() != 1)
24+
return std::nullopt;
25+
26+
return builder.create<UnrealizedConversionCastOp>(loc, resultType, inputs)
27+
.getResult(0);
28+
}
29+
30+
} // namespace
31+
32+
void mlir::populateEmitCSizeTTypeConversions(TypeConverter &converter) {
33+
converter.addConversion(
34+
[](IndexType type) { return emitc::SizeTType::get(type.getContext()); });
35+
36+
converter.addSourceMaterialization(materializeAsUnrealizedCast);
37+
converter.addTargetMaterialization(materializeAsUnrealizedCast);
38+
converter.addArgumentMaterialization(materializeAsUnrealizedCast);
39+
}
40+
41+
/// Get an unsigned integer or size data type corresponding to \p ty.
42+
std::optional<Type> mlir::emitc::getUnsignedTypeFor(Type ty) {
43+
if (ty.isInteger())
44+
return IntegerType::get(ty.getContext(), ty.getIntOrFloatBitWidth(),
45+
IntegerType::SignednessSemantics::Unsigned);
46+
if (isa<PtrDiffTType, SignedSizeTType>(ty))
47+
return SizeTType::get(ty.getContext());
48+
if (isa<SizeTType>(ty))
49+
return ty;
50+
return {};
51+
}
52+
53+
/// Get a signed integer or size data type corresponding to \p ty that supports
54+
/// arithmetic on negative values.
55+
std::optional<Type> mlir::emitc::getSignedTypeFor(Type ty) {
56+
if (ty.isInteger())
57+
return IntegerType::get(ty.getContext(), ty.getIntOrFloatBitWidth(),
58+
IntegerType::SignednessSemantics::Signed);
59+
if (isa<SizeTType, SignedSizeTType>(ty))
60+
return PtrDiffTType::get(ty.getContext());
61+
if (isa<PtrDiffTType>(ty))
62+
return ty;
63+
return {};
64+
}

mlir/lib/Target/Cpp/TranslateToCpp.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,12 @@ LogicalResult CppEmitter::emitType(Location loc, Type type) {
15741574
}
15751575
if (auto iType = dyn_cast<IndexType>(type))
15761576
return (os << "size_t"), success();
1577+
if (auto sType = dyn_cast<emitc::SizeTType>(type))
1578+
return (os << "size_t"), success();
1579+
if (auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1580+
return (os << "ssize_t"), success();
1581+
if (auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1582+
return (os << "ptrdiff_t"), success();
15771583
if (auto tType = dyn_cast<TensorType>(type)) {
15781584
if (!tType.hasRank())
15791585
return emitError(loc, "cannot emit unranked tensor type");

mlir/test/Dialect/EmitC/invalid_ops.mlir

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,31 +170,31 @@ func.func @add_float_pointer(%arg0: f32, %arg1: !emitc.ptr<f32>) {
170170
// -----
171171

172172
func.func @div_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
173-
// 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>'}}
173+
// expected-error @+1 {{'emitc.div' op operand #0 must be floating-point type supported by EmitC or integer, index or opaque type supported by EmitC, but got 'tensor<i32>'}}
174174
%1 = "emitc.div" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
175175
return
176176
}
177177

178178
// -----
179179

180180
func.func @mul_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
181-
// 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>'}}
181+
// expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer, index or opaque type supported by EmitC, but got 'tensor<i32>'}}
182182
%1 = "emitc.mul" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
183183
return
184184
}
185185

186186
// -----
187187

188188
func.func @rem_tensor(%arg0: tensor<i32>, %arg1: tensor<i32>) {
189-
// 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>'}}
189+
// expected-error @+1 {{'emitc.rem' op operand #0 must be integer, index or opaque type supported by EmitC, but got 'tensor<i32>'}}
190190
%1 = "emitc.rem" (%arg0, %arg1) : (tensor<i32>, tensor<i32>) -> tensor<i32>
191191
return
192192
}
193193

194194
// -----
195195

196196
func.func @rem_float(%arg0: f32, %arg1: f32) {
197-
// expected-error @+1 {{'emitc.rem' op operand #0 must be integer type supported by EmitC or index or EmitC opaque type, but got 'f32'}}
197+
// expected-error @+1 {{'emitc.rem' op operand #0 must be integer, index or opaque type supported by EmitC, but got 'f32'}}
198198
%1 = "emitc.rem" (%arg0, %arg1) : (f32, f32) -> f32
199199
return
200200
}

mlir/test/Dialect/EmitC/invalid_types.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ func.func @illegal_array_with_tensor_element_type(
8585
// -----
8686

8787
func.func @illegal_integer_type(%arg0: i11, %arg1: i11) -> i11 {
88-
// 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'}}
88+
// expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer, index or opaque type supported by EmitC, but got 'i11'}}
8989
%mul = "emitc.mul" (%arg0, %arg1) : (i11, i11) -> i11
9090
return
9191
}
9292

9393
// -----
9494

9595
func.func @illegal_float_type(%arg0: f80, %arg1: f80) {
96-
// 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'}}
96+
// expected-error @+1 {{'emitc.mul' op operand #0 must be floating-point type supported by EmitC or integer, index or opaque type supported by EmitC, but got 'f80'}}
9797
%mul = "emitc.mul" (%arg0, %arg1) : (f80, f80) -> f80
9898
return
9999
}

mlir/test/Dialect/EmitC/ops.mlir

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func.func @cast(%arg0: i32) {
4141

4242
func.func @c() {
4343
%1 = "emitc.constant"(){value = 42 : i32} : () -> i32
44+
%2 = "emitc.constant"(){value = 42 : index} : () -> !emitc.size_t
45+
%3 = "emitc.constant"(){value = 42 : index} : () -> !emitc.ssize_t
46+
%4 = "emitc.constant"(){value = 42 : index} : () -> !emitc.ptrdiff_t
4447
return
4548
}
4649

mlir/test/Dialect/EmitC/types.mlir

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ func.func @array_types(
1111
// CHECK-SAME: !emitc.array<30x!emitc.ptr<i32>>,
1212
%arg2: !emitc.array<30x!emitc.ptr<i32>>,
1313
// CHECK-SAME: !emitc.array<30x!emitc.opaque<"int">>
14-
%arg3: !emitc.array<30x!emitc.opaque<"int">>
14+
%arg3: !emitc.array<30x!emitc.opaque<"int">>,
15+
// CHECK-SAME: !emitc.array<30x!emitc.size_t>
16+
%arg4: !emitc.array<30x!emitc.size_t>,
17+
// CHECK-SAME: !emitc.array<30x!emitc.ssize_t>
18+
%arg5: !emitc.array<30x!emitc.ssize_t>,
19+
// CHECK-SAME: !emitc.array<30x!emitc.ptrdiff_t>
20+
%arg6: !emitc.array<30x!emitc.ptrdiff_t>
1521
) {
1622
return
1723
}
@@ -53,3 +59,15 @@ func.func @pointer_types() {
5359

5460
return
5561
}
62+
63+
// CHECK-LABEL: func @size_types()
64+
func.func @size_types() {
65+
// CHECK-NEXT: !emitc.ssize_t
66+
emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
67+
// CHECK-NEXT: !emitc.size_t
68+
emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
69+
// CHECK-NEXT: !emitc.ptrdiff_t
70+
emitc.call_opaque "f"() {template_args = [!emitc.ptrdiff_t]} : () -> ()
71+
72+
return
73+
}

mlir/test/Target/Cpp/types.mlir

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,15 @@ func.func @ptr_types() {
3535

3636
return
3737
}
38+
39+
// CHECK-LABEL: void size_types() {
40+
func.func @size_types() {
41+
// CHECK-NEXT: f<ssize_t>();
42+
emitc.call_opaque "f"() {template_args = [!emitc.ssize_t]} : () -> ()
43+
// CHECK-NEXT: f<size_t>();
44+
emitc.call_opaque "f"() {template_args = [!emitc.size_t]} : () -> ()
45+
// CHECK-NEXT: f<ptrdiff_t>();
46+
emitc.call_opaque "f"() {template_args = [!emitc.ptrdiff_t]} : () -> ()
47+
48+
return
49+
}

0 commit comments

Comments
 (0)