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

This commit is contained in:
Jianjian Guan 2026-07-23 02:52:41 +08:00 committed by GitHub
parent 8c556564a2
commit 8ae7803bed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 206 additions and 22 deletions

View file

@ -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";

View file

@ -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
//===----------------------------------------------------------------------===//

View file

@ -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.

View file

@ -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
}

View file

@ -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 {

View file

@ -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;