From d46d2b294881782126257020e4563e286c86ff05 Mon Sep 17 00:00:00 2001 From: Matthias Gehre <93204396+mgehre-amd@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:42:29 +0100 Subject: [PATCH 1/4] [mlir][emitc] Add ArrayType This models a one or multi-dimensional C/C++ array. The type implements the ShapedTypeInterface and prints similar to memref/tensor: ``` %arg0: !emitc.array<1xf32>, %arg1: !emitc.array<10x20x30xi32>, %arg2: !emitc.array<30x!emitc.ptr>, %arg3: !emitc.array<30x!emitc.opaque<"int">> ``` It can be translated to C++ when used as function parameter or as emitc.variable type. --- .../mlir/Dialect/EmitC/IR/EmitCTypes.td | 52 ++++++++++++++- mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 63 +++++++++++++++++++ mlir/lib/Target/Cpp/TranslateToCpp.cpp | 32 ++++++++-- mlir/test/Dialect/EmitC/invalid_types.mlir | 54 ++++++++++++++++ mlir/test/Dialect/EmitC/types.mlir | 14 +++++ mlir/test/Target/Cpp/common-cpp.mlir | 5 ++ mlir/test/Target/Cpp/variable.mlir | 3 + 7 files changed, 215 insertions(+), 8 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td index 8818c049ed771..5ab729df67882 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td @@ -16,16 +16,64 @@ include "mlir/IR/AttrTypeBase.td" include "mlir/Dialect/EmitC/IR/EmitCBase.td" +include "mlir/IR/BuiltinTypeInterfaces.td" //===----------------------------------------------------------------------===// // EmitC type definitions //===----------------------------------------------------------------------===// -class EmitC_Type - : TypeDef { +class EmitC_Type traits = []> + : TypeDef { let mnemonic = typeMnemonic; } +def EmitC_ArrayType : EmitC_Type<"Array", "array", [ShapedTypeInterface]> { + let summary = "EmitC array type"; + + let description = [{ + An array data type. + + Example: + + ```mlir + // Array emitted as `int32_t[10]` + !emitc.array<10xi32> + // Array emitted as `float[10][20]` + !emitc.ptr<10x20xf32> + ``` + }]; + + let parameters = (ins + ArrayRefParameter<"int64_t">:$shape, + "Type":$elementType + ); + + let builders = [ + TypeBuilderWithInferredContext<(ins + "ArrayRef":$shape, + "Type":$elementType + ), [{ + return $_get(elementType.getContext(), shape, elementType); + }]> + ]; + let extraClassDeclaration = [{ + /// Returns if this type is ranked (always true). + bool hasRank() const { return true; } + + /// Clone this vector type with the given shape and element type. If the + /// provided shape is `std::nullopt`, the current shape of the type is used. + ArrayType cloneWith(std::optional> shape, + Type elementType) const; + + static bool isValidElementType(Type type) { + return type.isIntOrIndexOrFloat() || + llvm::isa(type); + } + }]; + let genVerifyDecl = 1; + let hasCustomAssemblyFormat = 1; +} + def EmitC_OpaqueType : EmitC_Type<"Opaque", "opaque"> { let summary = "EmitC opaque type"; diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index 4df8149b94c95..3d74737495c6b 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -762,6 +762,69 @@ LogicalResult emitc::YieldOp::verify() { #define GET_TYPEDEF_CLASSES #include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc" +//===----------------------------------------------------------------------===// +// ArrayType +//===----------------------------------------------------------------------===// + +Type emitc::ArrayType::parse(AsmParser &parser) { + if (parser.parseLess()) + return Type(); + + SmallVector dimensions; + if (parser.parseDimensionList(dimensions, /*allowDynamic=*/false, + /*withTrailingX=*/true)) + return Type(); + // Parse the element type. + auto typeLoc = parser.getCurrentLocation(); + Type elementType; + if (parser.parseType(elementType)) + return Type(); + + // Check that memref is formed from allowed types. + if (!isValidElementType(elementType)) + return parser.emitError(typeLoc, "invalid array element type"), Type(); + if (parser.parseGreater()) + return Type(); + return parser.getChecked(dimensions, elementType); +} + +void emitc::ArrayType::print(AsmPrinter &printer) const { + printer << "<"; + for (int64_t dim : getShape()) { + printer << dim << 'x'; + } + printer.printType(getElementType()); + printer << ">"; +} + +LogicalResult emitc::ArrayType::verify( + ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, + ::llvm::ArrayRef shape, Type elementType) { + if (shape.empty()) + return emitError() << "shape must not be empty"; + + for (auto d : shape) { + if (d <= 0) + return emitError() << "dimensions must have positive size"; + } + + if (!elementType) + return emitError() << "element type must not be none"; + + if (!isValidElementType(elementType)) + return emitError() << "invalid array element type"; + + return success(); +} + +emitc::ArrayType +emitc::ArrayType::cloneWith(std::optional> shape, + Type elementType) const { + if (!shape) + return emitc::ArrayType::get(getShape(), elementType); + return emitc::ArrayType::get(*shape, elementType); +} + //===----------------------------------------------------------------------===// // OpaqueType //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 4bc707c43ad92..849f7e4793406 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -128,6 +128,10 @@ struct CppEmitter { LogicalResult emitVariableDeclaration(OpResult result, bool trailingSemicolon); + /// Emits a declaration of a variable with the given type and name. + LogicalResult emitVariableDeclaration(Location loc, Type type, + StringRef name); + /// Emits the variable declaration and assignment prefix for 'op'. /// - emits separate variable followed by std::tie for multi-valued operation; /// - emits single type followed by variable for single result; @@ -855,10 +859,8 @@ static LogicalResult printFunctionArgs(CppEmitter &emitter, return (interleaveCommaWithError( arguments, os, [&](BlockArgument arg) -> LogicalResult { - if (failed(emitter.emitType(functionOp->getLoc(), arg.getType()))) - return failure(); - os << " " << emitter.getOrCreateName(arg); - return success(); + return emitter.emitVariableDeclaration( + functionOp->getLoc(), arg.getType(), emitter.getOrCreateName(arg)); })); } @@ -1291,9 +1293,10 @@ LogicalResult CppEmitter::emitVariableDeclaration(OpResult result, return result.getDefiningOp()->emitError( "result variable for the operation already declared"); } - if (failed(emitType(result.getOwner()->getLoc(), result.getType()))) + if (failed(emitVariableDeclaration(result.getOwner()->getLoc(), + result.getType(), + getOrCreateName(result)))) return failure(); - os << " " << getOrCreateName(result); if (trailingSemicolon) os << ";\n"; return success(); @@ -1390,6 +1393,23 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { return success(); } +LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type, + StringRef name) { + if (auto arrType = dyn_cast(type)) { + if (failed(emitType(loc, arrType.getElementType()))) + return failure(); + os << " " << name; + for (auto dim : arrType.getShape()) { + os << "[" << dim << "]"; + } + return success(); + } + if (failed(emitType(loc, type))) + return failure(); + os << " " << name; + return success(); +} + LogicalResult CppEmitter::emitType(Location loc, Type type) { if (auto iType = dyn_cast(type)) { switch (iType.getWidth()) { diff --git a/mlir/test/Dialect/EmitC/invalid_types.mlir b/mlir/test/Dialect/EmitC/invalid_types.mlir index 54e3775ddb8ed..4c526aa93dffb 100644 --- a/mlir/test/Dialect/EmitC/invalid_types.mlir +++ b/mlir/test/Dialect/EmitC/invalid_types.mlir @@ -11,3 +11,57 @@ func.func @illegal_opaque_type_2() { // expected-error @+1 {{pointer not allowed as outer type with !emitc.opaque, use !emitc.ptr instead}} %1 = "emitc.variable"(){value = "nullptr" : !emitc.opaque<"int32_t*">} : () -> !emitc.opaque<"int32_t*"> } + +// ----- + +func.func @illegal_array_missing_spec( + // expected-error @+1 {{expected non-function type}} + %arg0: !emitc.array<>) { +} + +// ----- + +func.func @illegal_array_missing_shape( + // expected-error @+1 {{shape must not be empty}} + %arg9: !emitc.array) { +} + +// ----- + +func.func @illegal_array_missing_x( + // expected-error @+1 {{expected 'x' in dimension list}} + %arg0: !emitc.array<10> +) { +} + +// ----- + +func.func @illegal_array_non_positive_dimenson( + // expected-error @+1 {{dimensions must have positive size}} + %arg0: !emitc.array<0xi32> +) { +} + +// ----- + +func.func @illegal_array_missing_type( + // expected-error @+1 {{expected non-function type}} + %arg0: !emitc.array<10x> +) { +} + +// ----- + +func.func @illegal_array_dynamic_shape( + // expected-error @+1 {{expected static shape}} + %arg0: !emitc.array<10x?xi32> +) { +} + +// ----- + +func.func @illegal_array_unranked( + // expected-error @+1 {{expected non-function type}} + %arg0: !emitc.array<*xi32> +) { +} diff --git a/mlir/test/Dialect/EmitC/types.mlir b/mlir/test/Dialect/EmitC/types.mlir index 26d6f43a5824e..8477b0ed05977 100644 --- a/mlir/test/Dialect/EmitC/types.mlir +++ b/mlir/test/Dialect/EmitC/types.mlir @@ -39,3 +39,17 @@ func.func @pointer_types() { return } + +// CHECK-LABEL: func @array_types( +func.func @array_types( + // CHECK-SAME: !emitc.array<1xf32>, + %arg0: !emitc.array<1xf32>, + // CHECK-SAME: !emitc.array<10x20x30xi32>, + %arg1: !emitc.array<10x20x30xi32>, + // CHECK-SAME: !emitc.array<30x!emitc.ptr>, + %arg2: !emitc.array<30x!emitc.ptr>, + // CHECK-SAME: !emitc.array<30x!emitc.opaque<"int">> + %arg3: !emitc.array<30x!emitc.opaque<"int">> +) { + return +} diff --git a/mlir/test/Target/Cpp/common-cpp.mlir b/mlir/test/Target/Cpp/common-cpp.mlir index b537e7098deb5..a87b33a10844d 100644 --- a/mlir/test/Target/Cpp/common-cpp.mlir +++ b/mlir/test/Target/Cpp/common-cpp.mlir @@ -89,3 +89,8 @@ func.func @apply(%arg0: i32) -> !emitc.ptr { %1 = emitc.apply "*"(%0) : (!emitc.ptr) -> (i32) return %0 : !emitc.ptr } + +// CHECK: void array_type(int32_t v1[3], float v2[10][20]) +func.func @array_type(%arg0: !emitc.array<3xi32>, %arg1: !emitc.array<10x20xf32>) { + return +} diff --git a/mlir/test/Target/Cpp/variable.mlir b/mlir/test/Target/Cpp/variable.mlir index 77a060a32f9d4..5d061a6c87505 100644 --- a/mlir/test/Target/Cpp/variable.mlir +++ b/mlir/test/Target/Cpp/variable.mlir @@ -9,6 +9,7 @@ func.func @emitc_variable() { %c4 = "emitc.variable"(){value = 255 : ui8} : () -> ui8 %c5 = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.ptr %c6 = "emitc.variable"(){value = #emitc.opaque<"NULL">} : () -> !emitc.ptr + %c7 = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.array<3x7xi32> return } // CPP-DEFAULT: void emitc_variable() { @@ -19,6 +20,7 @@ func.func @emitc_variable() { // CPP-DEFAULT-NEXT: uint8_t [[V4:[^ ]*]] = 255; // CPP-DEFAULT-NEXT: int32_t* [[V5:[^ ]*]]; // CPP-DEFAULT-NEXT: int32_t* [[V6:[^ ]*]] = NULL; +// CPP-DEFAULT-NEXT: int32_t [[V7:[^ ]*]][3][7]; // CPP-DECLTOP: void emitc_variable() { // CPP-DECLTOP-NEXT: int32_t [[V0:[^ ]*]]; @@ -28,6 +30,7 @@ func.func @emitc_variable() { // CPP-DECLTOP-NEXT: uint8_t [[V4:[^ ]*]]; // CPP-DECLTOP-NEXT: int32_t* [[V5:[^ ]*]]; // CPP-DECLTOP-NEXT: int32_t* [[V6:[^ ]*]]; +// CPP-DECLTOP-NEXT: int32_t [[V7:[^ ]*]][3][7]; // CPP-DECLTOP-NEXT: ; // CPP-DECLTOP-NEXT: [[V1]] = 42; // CPP-DECLTOP-NEXT: [[V2]] = -1; From 4ded2c6c076e55ae9718a291c4316d71dde376b3 Mon Sep 17 00:00:00 2001 From: Matthias Gehre Date: Mon, 4 Mar 2024 12:20:42 +0100 Subject: [PATCH 2/4] Address comments --- mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 12 ++++++- mlir/lib/Target/Cpp/TranslateToCpp.cpp | 22 +++++++++++++ mlir/test/Dialect/EmitC/invalid_ops.mlir | 32 +++++++++++++++++++ mlir/test/Dialect/EmitC/invalid_types.mlir | 16 ++++++++++ mlir/test/Dialect/EmitC/types.mlir | 28 ++++++++-------- mlir/test/Target/Cpp/declare_func.mlir | 8 +++++ mlir/test/Target/Cpp/func.mlir | 3 ++ mlir/test/Target/Cpp/invalid.mlir | 28 ++++++++++++++++ .../Cpp/invalid_declare_variables_at_top.mlir | 9 ++++++ mlir/test/Target/Cpp/variable.mlir | 3 ++ 10 files changed, 146 insertions(+), 15 deletions(-) create mode 100644 mlir/test/Target/Cpp/invalid_declare_variables_at_top.mlir diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index 3d74737495c6b..f97db7dcd5073 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -140,6 +140,8 @@ LogicalResult emitc::AssignOp::verify() { return emitOpError() << "requires value's type (" << value.getType() << ") to match variable's type (" << variable.getType() << ")"; + if (isa(variable.getType())) + return emitOpError() << "cannot assign to array type"; return success(); } @@ -191,6 +193,11 @@ LogicalResult emitc::CallOpaqueOp::verify() { } } + if (llvm::any_of(getResultTypes(), + [](Type type) { return isa(type); })) { + return emitOpError() << "cannot return array type"; + } + return success(); } @@ -455,6 +462,9 @@ LogicalResult FuncOp::verify() { return emitOpError("requires zero or exactly one result, but has ") << getNumResults(); + if (getNumResults() == 1 && isa(getResultTypes()[0])) + return emitOpError("cannot return array type"); + return success(); } @@ -780,7 +790,7 @@ Type emitc::ArrayType::parse(AsmParser &parser) { if (parser.parseType(elementType)) return Type(); - // Check that memref is formed from allowed types. + // Check that array is formed from allowed types. if (!isValidElementType(elementType)) return parser.emitError(typeLoc, "invalid array element type"), Type(); if (parser.parseGreater()) diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 849f7e4793406..58fa91277fcc8 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -904,6 +904,9 @@ static LogicalResult printFunctionBody(CppEmitter &emitter, if (emitter.hasValueInScope(arg)) return functionOp->emitOpError(" block argument #") << arg.getArgNumber() << " is out of scope"; + if (isa(arg.getType())) + return functionOp->emitOpError("cannot emit block argument #") + << arg.getArgNumber() << " with array type"; if (failed( emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) { return failure(); @@ -947,6 +950,11 @@ static LogicalResult printOperation(CppEmitter &emitter, "with multiple blocks needs variables declared at top"); } + if (llvm::any_of(functionOp.getResultTypes(), + [](Type type) { return isa(type); })) { + return functionOp.emitOpError() << "cannot emit array type as result type"; + } + CppEmitter::Scope scope(emitter); raw_indented_ostream &os = emitter.ostream(); if (failed(emitter.emitTypes(functionOp.getLoc(), @@ -1445,6 +1453,8 @@ LogicalResult CppEmitter::emitType(Location loc, Type type) { if (!tType.hasStaticShape()) return emitError(loc, "cannot emit tensor type with non static shape"); os << "Tensor<"; + if (isa(tType.getElementType())) + return emitError(loc, "cannot emit tensor of array type ") << type; if (failed(emitType(loc, tType.getElementType()))) return failure(); auto shape = tType.getShape(); @@ -1461,7 +1471,16 @@ LogicalResult CppEmitter::emitType(Location loc, Type type) { os << oType.getValue(); return success(); } + if (auto aType = dyn_cast(type)) { + if (failed(emitType(loc, aType.getElementType()))) + return failure(); + for (auto dim : aType.getShape()) + os << "[" << dim << "]"; + return success(); + } if (auto pType = dyn_cast(type)) { + if (isa(pType.getPointee())) + return emitError(loc, "cannot emit pointer to array type ") << type; if (failed(emitType(loc, pType.getPointee()))) return failure(); os << "*"; @@ -1483,6 +1502,9 @@ LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef types) { } LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef types) { + if (llvm::any_of(types, [](Type type) { return isa(type); })) { + return emitError(loc, "cannot emit tuple of array type"); + } os << "std::tuple<"; if (failed(interleaveCommaWithError( types, os, [&](Type type) { return emitType(loc, type); }))) diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir index 5f64b535d684f..58b3a11ed93e1 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -80,6 +80,14 @@ func.func @dense_template_argument(%arg : i32) { // ----- +func.func @array_result() { + // expected-error @+1 {{'emitc.call_opaque' op cannot return array type}} + emitc.call_opaque "array_result"() : () -> !emitc.array<4xi32> + return +} + +// ----- + func.func @empty_operator(%arg : i32) { // expected-error @+1 {{'emitc.apply' op applicable operator must not be empty}} %2 = emitc.apply ""(%arg) : (i32) -> !emitc.ptr @@ -129,6 +137,14 @@ func.func @cast_tensor(%arg : tensor) { // ----- +func.func @cast_array(%arg : !emitc.array<4xf32>) { + // expected-error @+1 {{'emitc.cast' op operand type '!emitc.array<4xf32>' and result type '!emitc.array<4xf32>' are cast incompatible}} + %1 = emitc.cast %arg: !emitc.array<4xf32> to !emitc.array<4xf32> + return +} + +// ----- + func.func @add_two_pointers(%arg0: !emitc.ptr, %arg1: !emitc.ptr) { // expected-error @+1 {{'emitc.add' op requires that at most one operand is a pointer}} %1 = "emitc.add" (%arg0, %arg1) : (!emitc.ptr, !emitc.ptr) -> !emitc.ptr @@ -235,6 +251,15 @@ func.func @test_assign_type_mismatch(%arg1: f32) { // ----- +func.func @test_assign_to_array(%arg1: !emitc.array<4xi32>) { + %v = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.array<4xi32> + // expected-error @+1 {{'emitc.assign' op cannot assign to array type}} + emitc.assign %arg1 : !emitc.array<4xi32> to %v : !emitc.array<4xi32> + return +} + +// ----- + func.func @test_expression_no_yield() -> i32 { // expected-error @+1 {{'emitc.expression' op must yield a value at termination}} %r = emitc.expression : i32 { @@ -313,6 +338,13 @@ emitc.func @return_type_mismatch() -> i32 { // ----- +// expected-error@+1 {{'emitc.func' op cannot return array type}} +emitc.func @return_type_array(%arg : !emitc.array<4xi32>) -> !emitc.array<4xi32> { + emitc.return %arg : !emitc.array<4xi32> +} + +// ----- + func.func @return_inside_func.func(%0: i32) -> (i32) { // expected-error@+1 {{'emitc.return' op expects parent op 'emitc.func'}} emitc.return %0 : i32 diff --git a/mlir/test/Dialect/EmitC/invalid_types.mlir b/mlir/test/Dialect/EmitC/invalid_types.mlir index 4c526aa93dffb..079371b39b9d1 100644 --- a/mlir/test/Dialect/EmitC/invalid_types.mlir +++ b/mlir/test/Dialect/EmitC/invalid_types.mlir @@ -65,3 +65,19 @@ func.func @illegal_array_unranked( %arg0: !emitc.array<*xi32> ) { } + +// ----- + +func.func @illegal_array_with_array_element_type( + // expected-error @+1 {{invalid array element type}} + %arg0: !emitc.array<4x!emitc.array<4xi32>> +) { +} + +// ----- + +func.func @illegal_array_with_tensor_element_type( + // expected-error @+1 {{invalid array element type}} + %arg0: !emitc.array<4xtensor<4xi32>> +) { +} diff --git a/mlir/test/Dialect/EmitC/types.mlir b/mlir/test/Dialect/EmitC/types.mlir index 8477b0ed05977..752f2c10c17be 100644 --- a/mlir/test/Dialect/EmitC/types.mlir +++ b/mlir/test/Dialect/EmitC/types.mlir @@ -2,6 +2,20 @@ // check parser // RUN: mlir-opt -verify-diagnostics %s | mlir-opt -verify-diagnostics | FileCheck %s +// CHECK-LABEL: func @array_types( +func.func @array_types( + // CHECK-SAME: !emitc.array<1xf32>, + %arg0: !emitc.array<1xf32>, + // CHECK-SAME: !emitc.array<10x20x30xi32>, + %arg1: !emitc.array<10x20x30xi32>, + // CHECK-SAME: !emitc.array<30x!emitc.ptr>, + %arg2: !emitc.array<30x!emitc.ptr>, + // CHECK-SAME: !emitc.array<30x!emitc.opaque<"int">> + %arg3: !emitc.array<30x!emitc.opaque<"int">> +) { + return +} + // CHECK-LABEL: func @opaque_types() { func.func @opaque_types() { // CHECK-NEXT: !emitc.opaque<"int"> @@ -39,17 +53,3 @@ func.func @pointer_types() { return } - -// CHECK-LABEL: func @array_types( -func.func @array_types( - // CHECK-SAME: !emitc.array<1xf32>, - %arg0: !emitc.array<1xf32>, - // CHECK-SAME: !emitc.array<10x20x30xi32>, - %arg1: !emitc.array<10x20x30xi32>, - // CHECK-SAME: !emitc.array<30x!emitc.ptr>, - %arg2: !emitc.array<30x!emitc.ptr>, - // CHECK-SAME: !emitc.array<30x!emitc.opaque<"int">> - %arg3: !emitc.array<30x!emitc.opaque<"int">> -) { - return -} diff --git a/mlir/test/Target/Cpp/declare_func.mlir b/mlir/test/Target/Cpp/declare_func.mlir index 72c087a3388e2..00680d71824ae 100644 --- a/mlir/test/Target/Cpp/declare_func.mlir +++ b/mlir/test/Target/Cpp/declare_func.mlir @@ -14,3 +14,11 @@ emitc.declare_func @foo emitc.func @foo(%arg0: i32) -> i32 attributes {specifiers = ["static","inline"]} { emitc.return %arg0 : i32 } + + +// CHECK: void array_arg(int32_t [[V2:[^ ]*]][3]); +emitc.declare_func @array_arg +// CHECK: void array_arg(int32_t [[V2:[^ ]*]][3]) { +emitc.func @array_arg(%arg0: !emitc.array<3xi32>) { + emitc.return +} diff --git a/mlir/test/Target/Cpp/func.mlir b/mlir/test/Target/Cpp/func.mlir index a639cae6f623c..9c9ea55bfc4e1 100644 --- a/mlir/test/Target/Cpp/func.mlir +++ b/mlir/test/Target/Cpp/func.mlir @@ -40,3 +40,6 @@ emitc.func @emitc_call() -> i32 { emitc.func private @extern_func(i32) attributes {specifiers = ["extern"]} // CPP-DEFAULT: extern void extern_func(int32_t); + +emitc.func private @array_arg(!emitc.array<3xi32>) attributes {specifiers = ["extern"]} +// CPP-DEFAULT: extern void array_arg(int32_t[3]); diff --git a/mlir/test/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir index 18dabb9155866..8672850e4a653 100644 --- a/mlir/test/Target/Cpp/invalid.mlir +++ b/mlir/test/Target/Cpp/invalid.mlir @@ -57,3 +57,31 @@ func.func @non_static_shape(%arg0 : tensor) { func.func @unranked_tensor(%arg0 : tensor<*xf32>) { return } + +// ----- + +// expected-error@+1 {{cannot emit tensor of array type}} +func.func @tensor_of_array(%arg0 : tensor<4x!emitc.array<4xf32>>) { + return +} + +// ----- + +// expected-error@+1 {{cannot emit pointer to array type}} +func.func @tensor_of_array(%arg0 : !emitc.ptr>) { + return +} + +// ----- + +// expected-error@+1 {{cannot emit array type as result type}} +func.func @array_as_result(%arg: !emitc.array<4xi8>) -> (!emitc.array<4xi8>) { + return %arg : !emitc.array<4xi8> +} + +// ----- +func.func @ptr_to_array() { + // expected-error@+1 {{cannot emit pointer to array type '!emitc.ptr>'}} + %v = "emitc.variable"(){value = #emitc.opaque<"NULL">} : () -> !emitc.ptr> + return +} diff --git a/mlir/test/Target/Cpp/invalid_declare_variables_at_top.mlir b/mlir/test/Target/Cpp/invalid_declare_variables_at_top.mlir new file mode 100644 index 0000000000000..844fe03bad4ab --- /dev/null +++ b/mlir/test/Target/Cpp/invalid_declare_variables_at_top.mlir @@ -0,0 +1,9 @@ +// RUN: mlir-translate -split-input-file -declare-variables-at-top -mlir-to-cpp -verify-diagnostics %s + +// expected-error@+1 {{'func.func' op cannot emit block argument #0 with array type}} +func.func @array_as_block_argument(!emitc.array<4xi8>) { +^bb0(%arg0 : !emitc.array<4xi8>): + cf.br ^bb1(%arg0 : !emitc.array<4xi8>) +^bb1(%a : !emitc.array<4xi8>): + return +} diff --git a/mlir/test/Target/Cpp/variable.mlir b/mlir/test/Target/Cpp/variable.mlir index 5d061a6c87505..126dd384b47a2 100644 --- a/mlir/test/Target/Cpp/variable.mlir +++ b/mlir/test/Target/Cpp/variable.mlir @@ -10,6 +10,7 @@ func.func @emitc_variable() { %c5 = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.ptr %c6 = "emitc.variable"(){value = #emitc.opaque<"NULL">} : () -> !emitc.ptr %c7 = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.array<3x7xi32> + %c8 = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.array<5x!emitc.ptr> return } // CPP-DEFAULT: void emitc_variable() { @@ -21,6 +22,7 @@ func.func @emitc_variable() { // CPP-DEFAULT-NEXT: int32_t* [[V5:[^ ]*]]; // CPP-DEFAULT-NEXT: int32_t* [[V6:[^ ]*]] = NULL; // CPP-DEFAULT-NEXT: int32_t [[V7:[^ ]*]][3][7]; +// CPP-DEFAULT-NEXT: int8_t* [[V8:[^ ]*]][5]; // CPP-DECLTOP: void emitc_variable() { // CPP-DECLTOP-NEXT: int32_t [[V0:[^ ]*]]; @@ -31,6 +33,7 @@ func.func @emitc_variable() { // CPP-DECLTOP-NEXT: int32_t* [[V5:[^ ]*]]; // CPP-DECLTOP-NEXT: int32_t* [[V6:[^ ]*]]; // CPP-DECLTOP-NEXT: int32_t [[V7:[^ ]*]][3][7]; +// CPP-DECLTOP-NEXT: int8_t* [[V8:[^ ]*]][5]; // CPP-DECLTOP-NEXT: ; // CPP-DECLTOP-NEXT: [[V1]] = 42; // CPP-DECLTOP-NEXT: [[V2]] = -1; From f0b73b6415d21051eb29ddac5614230cca97e695 Mon Sep 17 00:00:00 2001 From: Matthias Gehre Date: Wed, 6 Mar 2024 18:34:06 +0100 Subject: [PATCH 3/4] Address last two nits --- mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td | 2 +- mlir/test/Target/Cpp/invalid.mlir | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td index 5ab729df67882..1ff41022eba8c 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td @@ -60,7 +60,7 @@ def EmitC_ArrayType : EmitC_Type<"Array", "array", [ShapedTypeInterface]> { /// Returns if this type is ranked (always true). bool hasRank() const { return true; } - /// Clone this vector type with the given shape and element type. If the + /// Clone this array type with the given shape and element type. If the /// provided shape is `std::nullopt`, the current shape of the type is used. ArrayType cloneWith(std::optional> shape, Type elementType) const; diff --git a/mlir/test/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir index 8672850e4a653..552c04a9b07f7 100644 --- a/mlir/test/Target/Cpp/invalid.mlir +++ b/mlir/test/Target/Cpp/invalid.mlir @@ -68,7 +68,7 @@ func.func @tensor_of_array(%arg0 : tensor<4x!emitc.array<4xf32>>) { // ----- // expected-error@+1 {{cannot emit pointer to array type}} -func.func @tensor_of_array(%arg0 : !emitc.ptr>) { +func.func @pointer_to_array(%arg0 : !emitc.ptr>) { return } From 8e70202870678be5bf8adf153f964cedd7e9dbfb Mon Sep 17 00:00:00 2001 From: Matthias Gehre Date: Mon, 11 Mar 2024 16:40:33 +0100 Subject: [PATCH 4/4] Add last nit --- mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index f97db7dcd5073..4821dc9f588f1 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -813,8 +813,8 @@ LogicalResult emitc::ArrayType::verify( if (shape.empty()) return emitError() << "shape must not be empty"; - for (auto d : shape) { - if (d <= 0) + for (int64_t dim : shape) { + if (dim <= 0) return emitError() << "dimensions must have positive size"; }