[Mlir-commits] [mlir] 8ae7803 - [mlir][emitc] Add compound assignment ops (#210233)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Jul 22 11:52:45 PDT 2026


Author: Jianjian Guan
Date: 2026-07-22T11:52:41-07:00
New Revision: 8ae7803bede98cca73e6fd6a8fabbf95da9d0b46

URL: https://github.com/llvm/llvm-project/commit/8ae7803bede98cca73e6fd6a8fabbf95da9d0b46
DIFF: https://github.com/llvm/llvm-project/commit/8ae7803bede98cca73e6fd6a8fabbf95da9d0b46.diff

LOG: [mlir][emitc] Add compound assignment ops (#210233)

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
    mlir/lib/Dialect/EmitC/IR/EmitC.cpp
    mlir/lib/Target/Cpp/TranslateToCpp.cpp
    mlir/test/Dialect/EmitC/invalid_ops.mlir
    mlir/test/Dialect/EmitC/ops.mlir
    mlir/test/Target/Cpp/common-cpp.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index b6604cf45139f..49412d1dfb01c 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -88,6 +88,20 @@ class EmitC_IncDecOp<string mnemonic, string summaryStr>
   }];
 }
 
+// Base class for compound assignment operations.
+class EmitC_CompoundAssignOp<string mnemonic, string summaryStr>
+    : EmitC_Op<mnemonic, []> {
+  let summary = summaryStr;
+  let arguments = (ins
+    Res<EmitC_LValueType, "",
+        [MemRead<DefaultResource, 0, FullEffect>,
+         MemWrite<DefaultResource, 0, FullEffect>]>:$var,
+    EmitCType:$value);
+  let results = (outs);
+  let hasVerifier = 1;
+  let assemblyFormat = "$value `:` type($value) `to` $var `:` type($var) attr-dict";
+}
+
 // Types only used in binary arithmetic operations.
 def IntegerIndexOrOpaqueType : Type<CPred<"emitc::isIntegerIndexOrOpaqueType($_self)">,
 "integer, index or opaque type supported by EmitC">;
@@ -1591,6 +1605,76 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
   let assemblyFormat = "$value `:` type($value) `to` $var `:` type($var) attr-dict";
 }
 
+def EmitC_AddAssignOp
+    : EmitC_CompoundAssignOp<"add_assign", "Addition assignment operation"> {
+  let description = [{
+    The `emitc.add_assign` operation applies the C/C++ `+=` operator to an
+    lvalue.
+
+    Example:
+
+    ```mlir
+    emitc.add_assign %value : i32 to %var : !emitc.lvalue<i32>
+    ```
+  }];
+}
+
+def EmitC_SubAssignOp
+    : EmitC_CompoundAssignOp<"sub_assign", "Subtraction assignment operation"> {
+  let description = [{
+    The `emitc.sub_assign` operation applies the C/C++ `-=` operator to an
+    lvalue.
+
+    Example:
+
+    ```mlir
+    emitc.sub_assign %value : i32 to %var : !emitc.lvalue<i32>
+    ```
+  }];
+}
+
+def EmitC_MulAssignOp
+    : EmitC_CompoundAssignOp<"mul_assign", "Multiplication assignment operation"> {
+  let description = [{
+    The `emitc.mul_assign` operation applies the C/C++ `*=` operator to an
+    lvalue.
+
+    Example:
+
+    ```mlir
+    emitc.mul_assign %value : i32 to %var : !emitc.lvalue<i32>
+    ```
+  }];
+}
+
+def EmitC_DivAssignOp
+    : EmitC_CompoundAssignOp<"div_assign", "Division assignment operation"> {
+  let description = [{
+    The `emitc.div_assign` operation applies the C/C++ `/=` operator to an
+    lvalue.
+
+    Example:
+
+    ```mlir
+    emitc.div_assign %value : i32 to %var : !emitc.lvalue<i32>
+    ```
+  }];
+}
+
+def EmitC_RemAssignOp
+    : EmitC_CompoundAssignOp<"rem_assign", "Remainder assignment operation"> {
+  let description = [{
+    The `emitc.rem_assign` operation applies the C/C++ `%=` operator to an
+    lvalue.
+
+    Example:
+
+    ```mlir
+    emitc.rem_assign %value : i32 to %var : !emitc.lvalue<i32>
+    ```
+  }];
+}
+
 def EmitC_YieldOp : EmitC_Op<"yield",
       [Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp"]>]> {
   let summary = "Block termination operation";

diff  --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 6e68a3d0c5a41..820c4ad578a9e 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -263,27 +263,38 @@ LogicalResult AddOp::verify() {
 }
 
 //===----------------------------------------------------------------------===//
-// AssignOp
+// Assignment operations
 //===----------------------------------------------------------------------===//
 
-/// The assign op requires that the assigned value's type matches the
-/// assigned-to variable type.
-LogicalResult emitc::AssignOp::verify() {
-  TypedValue<emitc::LValueType> variable = getVar();
+template <typename AssignmentOp>
+static LogicalResult verifyAssignmentOp(AssignmentOp op) {
+  TypedValue<emitc::LValueType> variable = op.getVar();
 
   if (!variable.getDefiningOp())
-    return emitOpError() << "cannot assign to block argument";
+    return op.emitOpError() << "cannot assign to block argument";
 
-  Type valueType = getValue().getType();
+  Type valueType = op.getValue().getType();
   Type variableType = variable.getType().getValueType();
   if (variableType != valueType)
-    return emitOpError() << "requires value's type (" << valueType
-                         << ") to match variable's type (" << variableType
-                         << ")\n  variable: " << variable
-                         << "\n  value: " << getValue() << "\n";
+    return op.emitOpError() << "requires value's type (" << valueType
+                            << ") to match variable's type (" << variableType
+                            << ")\n  variable: " << variable
+                            << "\n  value: " << op.getValue() << "\n";
   return success();
 }
 
+LogicalResult emitc::AssignOp::verify() { return verifyAssignmentOp(*this); }
+
+LogicalResult emitc::AddAssignOp::verify() { return verifyAssignmentOp(*this); }
+
+LogicalResult emitc::SubAssignOp::verify() { return verifyAssignmentOp(*this); }
+
+LogicalResult emitc::MulAssignOp::verify() { return verifyAssignmentOp(*this); }
+
+LogicalResult emitc::DivAssignOp::verify() { return verifyAssignmentOp(*this); }
+
+LogicalResult emitc::RemAssignOp::verify() { return verifyAssignmentOp(*this); }
+
 //===----------------------------------------------------------------------===//
 // CastOp
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 5ae51bd580d31..d5815fafcd6a7 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -608,6 +608,42 @@ static LogicalResult printOperation(CppEmitter &emitter,
   return emitter.emitOperand(assignOp.getValue());
 }
 
+static LogicalResult
+printCompoundAssignmentOperation(CppEmitter &emitter, Operation *operation,
+                                 StringRef compoundAssignmentOperator) {
+  if (failed(emitter.emitOperand(operation->getOperand(0))))
+    return failure();
+
+  emitter.ostream() << " " << compoundAssignmentOperator << " ";
+
+  return emitter.emitOperand(operation->getOperand(1));
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::AddAssignOp addAssignOp) {
+  return printCompoundAssignmentOperation(emitter, addAssignOp, "+=");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::SubAssignOp subAssignOp) {
+  return printCompoundAssignmentOperation(emitter, subAssignOp, "-=");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::MulAssignOp mulAssignOp) {
+  return printCompoundAssignmentOperation(emitter, mulAssignOp, "*=");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::DivAssignOp divAssignOp) {
+  return printCompoundAssignmentOperation(emitter, divAssignOp, "/=");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::RemAssignOp remAssignOp) {
+  return printCompoundAssignmentOperation(emitter, remAssignOp, "%=");
+}
+
 static LogicalResult printOperation(CppEmitter &emitter, emitc::LoadOp loadOp) {
   if (failed(emitter.emitAssignPrefix(*loadOp)))
     return failure();
@@ -1927,25 +1963,26 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
           .Case<cf::BranchOp, cf::CondBranchOp>(
               [&](auto op) { return printOperation(*this, op); })
           // EmitC ops.
-          .Case<emitc::AddressOfOp, emitc::AddOp, emitc::AssignOp,
-                emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
+          .Case<emitc::AddAssignOp, emitc::AddressOfOp, emitc::AddOp,
+                emitc::AssignOp, emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
                 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
                 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
                 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
                 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
-                emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivOp,
-                emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
-                emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
+                emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivAssignOp,
+                emitc::DivOp, emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp,
+                emitc::FileOp, emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
                 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
                 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
                 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
                 emitc::MemberCallOpaqueOp, emitc::MemberOfPtrOp,
-                emitc::MemberOp, emitc::MulOp, emitc::PostDecrementOp,
-                emitc::PostIncrementOp, emitc::PreDecrementOp,
-                emitc::PreIncrementOp, emitc::RemOp, emitc::ReturnOp,
-                emitc::SubscriptOp, emitc::SubOp, emitc::SwitchOp,
-                emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
-                emitc::VerbatimOp>(
+                emitc::MemberOp, emitc::MulAssignOp, emitc::MulOp,
+                emitc::PostDecrementOp, emitc::PostIncrementOp,
+                emitc::PreDecrementOp, emitc::PreIncrementOp,
+                emitc::RemAssignOp, emitc::RemOp, emitc::ReturnOp,
+                emitc::SubAssignOp, emitc::SubscriptOp, emitc::SubOp,
+                emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
+                emitc::VariableOp, emitc::VerbatimOp>(
 
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.

diff  --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 3048160d1590c..889138a22ede3 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -997,3 +997,20 @@ func.func @post_decrement_unmatch_type(%arg0: !emitc.lvalue<i32>) {
   %1 = "emitc.post_decrement"(%arg0) : (!emitc.lvalue<i32>) -> i8
   return
 }
+
+// -----
+
+func.func @add_assign_to_block_argument(%arg0: i32, %arg1: !emitc.lvalue<i32>) {
+  // expected-error @+1 {{'emitc.add_assign' op cannot assign to block argument}}
+  emitc.add_assign %arg0 : i32 to %arg1 : !emitc.lvalue<i32>
+  return
+}
+
+// -----
+
+func.func @sub_assign_type_mismatch(%arg0: f32) {
+  %v = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+  // expected-error @+1 {{'emitc.sub_assign' op requires value's type ('f32') to match variable's type ('i32')}}
+  emitc.sub_assign %arg0 : f32 to %v : !emitc.lvalue<i32>
+  return
+}

diff  --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index e5c4af67c4896..3261b2fa467da 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -187,6 +187,23 @@ func.func @test_assign(%arg1: f32) {
   return
 }
 
+// CHECK-LABEL: func.func @compound_assign
+// CHECK: emitc.add_assign
+// CHECK: emitc.sub_assign
+// CHECK: emitc.mul_assign
+// CHECK: emitc.div_assign
+// CHECK: emitc.rem_assign
+func.func @compound_assign(%arg0: i32, %arg1: !emitc.opaque<"number">) {
+  %v = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+  %opaque = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<!emitc.opaque<"number">>
+  emitc.add_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  emitc.sub_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  emitc.mul_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  emitc.div_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  emitc.rem_assign %arg1 : !emitc.opaque<"number"> to %opaque : !emitc.lvalue<!emitc.opaque<"number">>
+  return
+}
+
 func.func @test_expression(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: f32, %arg4: f32) -> i32 {
   %c7 = "emitc.constant"() {value = 7 : i32} : () -> i32
   %q = emitc.expression %arg1, %c7 : (i32, i32) -> i32 {

diff  --git a/mlir/test/Target/Cpp/common-cpp.mlir b/mlir/test/Target/Cpp/common-cpp.mlir
index 1bffd59d4d44c..041321db46290 100644
--- a/mlir/test/Target/Cpp/common-cpp.mlir
+++ b/mlir/test/Target/Cpp/common-cpp.mlir
@@ -122,6 +122,24 @@ func.func @inc_dec() {
   return
 }
 
+// CHECK-LABEL: void compound_assign(
+// CHECK-SAME: int32_t [[ARG0:[^ ]*]]) {
+func.func @compound_assign(%arg0: i32) {
+  // CHECK-NEXT: int32_t [[V1:[^ ]*]] = 0;
+  %v = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
+  // CHECK-NEXT: [[V1]] += [[ARG0]];
+  emitc.add_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  // CHECK-NEXT: [[V1]] -= [[ARG0]];
+  emitc.sub_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  // CHECK-NEXT: [[V1]] *= [[ARG0]];
+  emitc.mul_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  // CHECK-NEXT: [[V1]] /= [[ARG0]];
+  emitc.div_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  // CHECK-NEXT: [[V1]] %= [[ARG0]];
+  emitc.rem_assign %arg0 : i32 to %v : !emitc.lvalue<i32>
+  return
+}
+
 // CHECK-LABEL: int32_t inc_dec_expression() {
 func.func @inc_dec_expression() -> i32 {
   // CHECK-NEXT: int32_t [[V1:[^ ]*]] = 0;


        


More information about the Mlir-commits mailing list