[Mlir-commits] [mlir] [mlir][EmitC] Add member access ops (PR #98460)

Marius Brehler llvmlistbot at llvm.org
Sat Jul 13 02:57:42 PDT 2024


https://github.com/marbre updated https://github.com/llvm/llvm-project/pull/98460

>From a3db5faf7068ac470a6833adaaa2b861eb6ae5f1 Mon Sep 17 00:00:00 2001
From: Marius Brehler <marius.brehler at gmail.com>
Date: Sat, 13 Jul 2024 11:58:25 +0200
Subject: [PATCH] [mlir][EmitC] Add member access ops

This adds an `emitc.member` and `emitc.member_of_ptr` operation for the
corresponding member access operators. Furthermore, `emitc.assign` is
adjusted to be used with the member acces operators.
---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 42 +++++++++++++++++++++
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         |  7 ++--
 mlir/lib/Target/Cpp/TranslateToCpp.cpp      | 34 ++++++++++++++++-
 mlir/test/Dialect/EmitC/invalid_ops.mlir    | 18 ++++++++-
 mlir/test/Dialect/EmitC/ops.mlir            |  7 ++++
 mlir/test/Target/Cpp/member.mlir            | 34 +++++++++++++++++
 6 files changed, 136 insertions(+), 6 deletions(-)
 create mode 100644 mlir/test/Target/Cpp/member.mlir

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 452302c565139..626b0576efd9c 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<StrAttr, "the member to access">:$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<!emitc.opaque<"mystruct">>) -> i32
+    ```
+  }];
+
+  let arguments = (ins
+    Arg<StrAttr, "the member to access">:$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 9f99eb1233cb1..70e3e728e0195 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<emitc::GetGlobalOp, emitc::SubscriptOp, emitc::VariableOp>(
-          variableDef))
+      !llvm::isa<emitc::GetGlobalOp, emitc::MemberOp, emitc::MemberOfPtrOp,
+                 emitc::SubscriptOp, emitc::VariableOp>(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 eda8d5c9ad8cb..1dadb9dd691e7 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<emitc::GetGlobalOp, emitc::LiteralOp,
-                         emitc::SubscriptOp>(op);
+  return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
+                         emitc::MemberOfPtrOp, emitc::SubscriptOp>(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<emitc::MemberOp>([&](auto op) {
+            cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
+            return success();
+          })
+          .Case<emitc::MemberOfPtrOp>([&](auto op) {
+            cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
+            return success();
+          })
           .Case<emitc::SubscriptOp>([&](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 e9b11421882f9..4181b726593e4 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 (<block argument> of type 'f32' at index: 1) to be a get_global, subscript or variable}}
+  // expected-error @+1 {{'emitc.assign' op requires first operand (<block argument> 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 1d3ca5c9bc939..20ac077e4402b 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<!emitc.opaque<"mystruct">>) {
+  %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<!emitc.opaque<"mystruct">>) -> i32
+  return
+}
diff --git a/mlir/test/Target/Cpp/member.mlir b/mlir/test/Target/Cpp/member.mlir
new file mode 100644
index 0000000000000..1b4a3dcab879d
--- /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<!emitc.opaque<"mystruct">>, %arg1: i32) {
+  %0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
+  emitc.assign %arg1 : i32 to %0 : i32
+
+  %1 = "emitc.member_of_ptr" (%arg0) {member = "b"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> 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;



More information about the Mlir-commits mailing list