Skip to content

Conversation

@Jaddyen
Copy link
Contributor

@Jaddyen Jaddyen commented Jun 24, 2025

Add support to the emitter for ClassOp, FieldOp and GetFieldOp.
These ops were introduced in #141158

@Jaddyen Jaddyen changed the title [emitC] Edit emitter to emit classop, 'fieldop and getfieldop` correctly [emitC] Edit emitter to emit classop, fieldop and getfieldop correctly Jun 24, 2025
@Jaddyen Jaddyen marked this pull request as ready for review June 24, 2025 22:12
@llvmbot
Copy link
Member

llvmbot commented Jun 24, 2025

@llvm/pr-subscribers-mlir-emitc

@llvm/pr-subscribers-mlir

Author: Jaden Angella (Jaddyen)

Changes

Extends the emitter to accurately emit classes from classops, fieldops and getfieldops


Full diff: https://github.com/llvm/llvm-project/pull/145605.diff

2 Files Affected:

  • (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+57-1)
  • (added) mlir/test/mlir-translate/emitc_classops.mlir (+36)
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 067a0470b14e4..9cd30eae8d36d 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -997,6 +997,61 @@ static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) {
   return success();
 }
 
+static LogicalResult printOperation(CppEmitter &emitter, ClassOp classOp) {
+  CppEmitter::Scope classScope(emitter);
+  raw_indented_ostream &os = emitter.ostream();
+  os << "class " << classOp.getSymName() << " final {\n";
+  os << "public:\n\n";
+
+  os.indent();
+  os << "const std::map<std::string, char*> _buffer_map {\n";
+  for (Operation &op : classOp) {
+    if (auto fieldOp = dyn_cast<FieldOp>(op))
+      os << "  { \"" << fieldOp.getSymName() << "\", reinterpret_cast<char*>(&"
+         << fieldOp.getAttrs() << ") },\n";
+  }
+  os << "};\n";
+
+  os << "char* getBufferForName(const std::string& name) const {\n";
+  os << "  auto it = _buffer_map.find(name);\n";
+  os << "  return (it == _buffer_map.end()) ? nullptr : it->second;\n";
+  os << "}\n\n";
+  for (Operation &op : classOp) {
+    if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))
+      return failure();
+  }
+
+  os.unindent();
+  os << "};";
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, FieldOp fieldOp) {
+  raw_ostream &os = emitter.ostream();
+  if (failed(emitter.emitType(fieldOp->getLoc(), fieldOp.getType())))
+    return failure();
+  os << " " << fieldOp.getSymName() << ";";
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    GetFieldOp getFieldOp) {
+  raw_indented_ostream &os = emitter.ostream();
+  Location loc = getFieldOp->getLoc();
+
+  if (getFieldOp->getNumResults() > 0) {
+    Value result = getFieldOp->getResult(0);
+    if (failed(emitter.emitType(loc, result.getType())))
+      return failure();
+    os << " ";
+    if (failed(emitter.emitOperand(result)))
+      return failure();
+    os << " = ";
+  }
+  os << getFieldOp.getFieldName().str();
+  return success();
+}
+
 static LogicalResult printOperation(CppEmitter &emitter, FileOp file) {
   if (!emitter.shouldEmitFile(file))
     return success();
@@ -1612,7 +1667,8 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
                 emitc::LoadOp, emitc::LogicalAndOp, emitc::LogicalNotOp,
                 emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
                 emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp,
-                emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>(
+                emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp,
+                emitc::ClassOp, emitc::FieldOp, emitc::GetFieldOp>(
 
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.
diff --git a/mlir/test/mlir-translate/emitc_classops.mlir b/mlir/test/mlir-translate/emitc_classops.mlir
new file mode 100644
index 0000000000000..3232954f1149a
--- /dev/null
+++ b/mlir/test/mlir-translate/emitc_classops.mlir
@@ -0,0 +1,36 @@
+// RUN: mlir-translate --mlir-to-cpp %s | FileCheck %s
+
+emitc.class @modelClass {
+    emitc.field @input_tensor : !emitc.array<1xf32> 
+    emitc.field @some_feature : !emitc.array<1xf32>  {emitc.opaque = ["some_feature"]}
+    emitc.func @execute() {
+        %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+        %1 = get_field @input_tensor : !emitc.array<1xf32>
+        %2 = get_field @some_feature : !emitc.array<1xf32>
+        %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+        return
+    }
+}
+
+// CHECK: class modelClass final {
+// CHECK-NEXT: public:
+// CHECK-EMPTY:
+// CHECK-NEXT:  const std::map<std::string, char*> _buffer_map {
+// CHECK-NEXT:    { "input_tensor", reinterpret_cast<char*>(&None) },
+// CHECK-NEXT:    { "some_feature", reinterpret_cast<char*>(&{emitc.opaque = ["some_feature"]}) }, 
+// CHECK-NEXT:  };
+// CHECK-NEXT:  char* getBufferForName(const std::string& name) const {
+// CHECK-NEXT:     auto it = _buffer_map.find(name);
+// CHECK-NEXT:     return (it == _buffer_map.end()) ? nullptr : it->second;
+// CHECK-NEXT:  }
+// CHECK-EMPTY:
+// CHECK-NEXT:  float[1] input_tensor;
+// CHECK-NEXT:  float[1] some_feature;
+// CHECK-NEXT:  void execute() {
+// CHECK-NEXT:    size_t v1 = 0;
+// CHECK-NEXT:    float[1] v2 = input_tensor;
+// CHECK-NEXT:    float[1] v3 = some_feature;
+// CHECK-NEXT:    return;
+// CHECK-NEXT:  }
+// CHECK-EMPTY:
+// CHECK-NEXT: };

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reflection bits should be predicated on there being those attributes.

@Jaddyen Jaddyen changed the title [emitC] Edit emitter to emit classop, fieldop and getfieldop correctly [mlir][emitC] Add support to emitter for classop, fieldop and getfieldop Jun 24, 2025
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a second test where the fields don't have the attribute - everything should work, except no reflection support.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emitc.name_hint not emitc.opaque, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem specific to ClassOp but some usage of it. So these should be introduced before translation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, will do in different step.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep this list sorted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why final?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be specific to the MLGO use-case. Perhaps that could be something that could be encoded in the ClassOp? Then its just a matter of lowering/refining the IR correctly ahead of time, like for other constructs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we can specify in the ClassOp so this print allows for more use cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this could either be an attribute of type ::mlir::UnitAttr or implemented similar to the specifiers attribute (type ::mlir::ArrayAttr) of the emitc::FuncOp.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't look correct.

Copy link
Contributor Author

@Jaddyen Jaddyen Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing this out! will handle this when working on buffer_map op in next stage

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be specific to the MLGO use-case. Perhaps that could be something that could be encoded in the ClassOp? Then its just a matter of lowering/refining the IR correctly ahead of time, like for other constructs.

Comment on lines 1016 to 1017
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these lines be indented manually? It's certainly easier to read, but you could end up w/ inconsistent indentation, right?

Copy link
Contributor Author

@Jaddyen Jaddyen Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true! it would be nicer to indent using the os. since i am deferring the emission of the buffermap to a stage where we have a buffermapop, i will handle this at that point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't GetFieldOp defined to have 1 result? (

let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result);
).

Can also do here getFieldOp.getResult() rather than using the generic accessor on Operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it is!
thanks for the pointer.
ive addressed this now.

@mtrofin mtrofin merged commit 1dfdd1e into llvm:main Jun 26, 2025
7 checks passed
@Jaddyen Jaddyen deleted the emit-class branch June 26, 2025 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants