From 17dab4e3776c9824a7910d15467490d40edb32e8 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/2] [EmitC] Add ArrayType (#26) This models a one or multi-dimensional C/C++ array. Reviewers: TinaAMD Reviewed By: TinaAMD Pull Request: https://github.com/xilinx/llvm-project/pull/114 --- .../mlir/Dialect/EmitC/IR/EmitCTypes.td | 52 ++++++++++++++- mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 63 +++++++++++++++++++ mlir/test/Dialect/EmitC/invalid_types.mlir | 54 ++++++++++++++++ mlir/test/Dialect/EmitC/types.mlir | 14 +++++ 4 files changed, 181 insertions(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCTypes.td index 7874aa2c9e304..8dfda3be99d5f 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 e8ea4da0b089c..776285d842db9 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -578,6 +578,69 @@ void emitc::OpaqueAttr::print(AsmPrinter &printer) const { #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/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 +} From 995b14cf9becd807b8eb3a27fc3df51b1bc5745a Mon Sep 17 00:00:00 2001 From: Matthias Gehre <93204396+mgehre-amd@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:49:11 +0100 Subject: [PATCH 2/2] [MLIR] Target/Cpp: Translate emitc.array (#29) Reviewers: TinaAMD Reviewed By: TinaAMD Pull Request: https://github.com/xilinx/llvm-project/pull/115 --- mlir/lib/Target/Cpp/TranslateToCpp.cpp | 40 +++++++++++++++++++------- mlir/test/Target/Cpp/common-cpp.mlir | 5 ++++ mlir/test/Target/Cpp/variable.mlir | 3 ++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 1b4ec9eae9367..d4dadc12d41de 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -96,6 +96,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; @@ -623,14 +627,12 @@ static LogicalResult printOperation(CppEmitter &emitter, os << " " << functionOp.getName(); os << "("; - if (failed(interleaveCommaWithError( - functionOp.getArguments(), os, - [&](BlockArgument arg) -> LogicalResult { - if (failed(emitter.emitType(functionOp.getLoc(), arg.getType()))) - return failure(); - os << " " << emitter.getOrCreateName(arg); - return success(); - }))) + if (failed(interleaveCommaWithError(functionOp.getArguments(), os, + [&](BlockArgument arg) -> LogicalResult { + return emitter.emitVariableDeclaration( + functionOp.getLoc(), arg.getType(), + emitter.getOrCreateName(arg)); + }))) return failure(); os << ") {\n"; os.indent(); @@ -893,9 +895,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(); @@ -977,6 +980,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/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;