diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index 452302c565139c..626b0576efd9c8 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -908,6 +908,48 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", [CExpression]> { let hasVerifier = 1; } +def EmitC_MemberOp : EmitC_Op<"member"> { + let summary = "Member operation"; + let description = [{ + With the `member` operation the member access operator `.` can be + applied. + + Example: + + ```mlir + %0 = "emitc.member" (%arg0) {member = "a"} + : (!emitc.opaque<"mystruct">) -> i32 + ``` + }]; + + let arguments = (ins + Arg:$member, + EmitC_OpaqueType:$operand + ); + let results = (outs EmitCType); +} + +def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> { + let summary = "Member of pointer operation"; + let description = [{ + With the `member_of_ptr` operation the member access operator `->` + can be applied. + + Example: + + ```mlir + %0 = "emitc.member_of_ptr" (%arg0) {member = "a"} + : (!emitc.ptr>) -> i32 + ``` + }]; + + let arguments = (ins + Arg:$member, + AnyTypeOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand + ); + let results = (outs EmitCType); +} + def EmitC_ConditionalOp : EmitC_Op<"conditional", [AllTypesMatch<["true_value", "false_value", "result"]>, CExpression]> { let summary = "Conditional (ternary) operation"; diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index 9f99eb1233cb1f..70e3e728e01950 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -213,10 +213,11 @@ LogicalResult emitc::AssignOp::verify() { Value variable = getVar(); Operation *variableDef = variable.getDefiningOp(); if (!variableDef || - !llvm::isa( - variableDef)) + !llvm::isa(variableDef)) return emitOpError() << "requires first operand (" << variable - << ") to be a get_global, subscript or variable"; + << ") to be a get_global, member, member of pointer, " + "subscript or variable"; Value value = getValue(); if (variable.getType() != value.getType()) diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index eda8d5c9ad8cb7..1dadb9dd691e70 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -183,6 +183,12 @@ struct CppEmitter { // Returns the textual representation of a subscript operation. std::string getSubscriptName(emitc::SubscriptOp op); + // Returns the textual representation of a member (of object) operation. + std::string createMemberAccess(emitc::MemberOp op); + + // Returns the textual representation of a member of pointer operation. + std::string createMemberAccess(emitc::MemberOfPtrOp op); + /// Return the existing or a new label of a Block. StringRef getOrCreateName(Block &block); @@ -278,8 +284,8 @@ struct CppEmitter { /// Determine whether expression \p op should be emitted in a deferred way. static bool hasDeferredEmission(Operation *op) { - return isa_and_nonnull(op); + return isa_and_nonnull(op); } /// Determine whether expression \p expressionOp should be emitted inline, i.e. @@ -1125,6 +1131,22 @@ std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) { return out; } +std::string CppEmitter::createMemberAccess(emitc::MemberOp op) { + std::string out; + llvm::raw_string_ostream ss(out); + ss << getOrCreateName(op.getOperand()); + ss << "." << op.getMember(); + return out; +} + +std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) { + std::string out; + llvm::raw_string_ostream ss(out); + ss << getOrCreateName(op.getOperand()); + ss << "->" << op.getMember(); + return out; +} + void CppEmitter::cacheDeferredOpResult(Value value, StringRef str) { if (!valueMapper.count(value)) valueMapper.insert(value, str.str()); @@ -1501,6 +1523,14 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { cacheDeferredOpResult(op.getResult(), op.getValue()); return success(); }) + .Case([&](auto op) { + cacheDeferredOpResult(op.getResult(), createMemberAccess(op)); + return success(); + }) + .Case([&](auto op) { + cacheDeferredOpResult(op.getResult(), createMemberAccess(op)); + return success(); + }) .Case([&](auto op) { cacheDeferredOpResult(op.getResult(), getSubscriptName(op)); return success(); diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir index e9b11421882f9a..4181b726593e4a 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -235,7 +235,7 @@ func.func @test_misplaced_yield() { // ----- func.func @test_assign_to_non_variable(%arg1: f32, %arg2: f32) { - // expected-error @+1 {{'emitc.assign' op requires first operand ( of type 'f32' at index: 1) to be a get_global, subscript or variable}} + // expected-error @+1 {{'emitc.assign' op requires first operand ( of type 'f32' at index: 1) to be a get_global, member, member of pointer, subscript or variable}} emitc.assign %arg1 : f32 to %arg2 : f32 return } @@ -450,3 +450,19 @@ func.func @use_global() { %0 = emitc.get_global @myglobal : f32 return } + +// ----- + +func.func @member(%arg0: i32) { + // expected-error @+1 {{'emitc.member' op operand #0 must be EmitC opaque type, but got 'i32'}} + %0 = "emitc.member" (%arg0) {member = "a"} : (i32) -> i32 + return +} + +// ----- + +func.func @member_of_ptr(%arg0: i32) { + // expected-error @+1 {{'emitc.member_of_ptr' op operand #0 must be EmitC opaque type or EmitC pointer type, but got 'i32}} + %0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (i32) -> i32 + return +} diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir index 1d3ca5c9bc9393..20ac077e4402b4 100644 --- a/mlir/test/Dialect/EmitC/ops.mlir +++ b/mlir/test/Dialect/EmitC/ops.mlir @@ -254,3 +254,10 @@ func.func @assign_global(%arg0 : i32) { emitc.assign %arg0 : i32 to %0 : i32 return } + +func.func @member_access(%arg0: !emitc.opaque<"mystruct">, %arg1: !emitc.opaque<"mystruct_ptr">, %arg2: !emitc.ptr>) { + %0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32 + %1 = "emitc.member_of_ptr" (%arg1) {member = "a"} : (!emitc.opaque<"mystruct_ptr">) -> i32 + %2 = "emitc.member_of_ptr" (%arg2) {member = "a"} : (!emitc.ptr>) -> i32 + return +} diff --git a/mlir/test/Target/Cpp/member.mlir b/mlir/test/Target/Cpp/member.mlir new file mode 100644 index 00000000000000..1b4a3dcab879da --- /dev/null +++ b/mlir/test/Target/Cpp/member.mlir @@ -0,0 +1,34 @@ +// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT + +func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32) { + %0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32 + emitc.assign %arg1 : i32 to %0 : i32 + + %1 = "emitc.member" (%arg0) {member = "b"} : (!emitc.opaque<"mystruct">) -> i32 + %2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32 + emitc.assign %1 : i32 to %2 : i32 + + return +} + +// CPP-DEFAULT: void member(mystruct [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) { +// CPP-DEFAULT-NEXT: [[V0:[^ ]*]].a = [[V1:[^ ]*]]; +// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]]; +// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]].b; + + +func.func @member_of_pointer(%arg0: !emitc.ptr>, %arg1: i32) { + %0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (!emitc.ptr>) -> i32 + emitc.assign %arg1 : i32 to %0 : i32 + + %1 = "emitc.member_of_ptr" (%arg0) {member = "b"} : (!emitc.ptr>) -> i32 + %2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32 + emitc.assign %1 : i32 to %2 : i32 + + return +} + +// CPP-DEFAULT: void member_of_pointer(mystruct* [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) { +// CPP-DEFAULT-NEXT: [[V0:[^ ]*]]->a = [[V1:[^ ]*]]; +// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]]; +// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]]->b;