From b81bb0e1d0acc3db64557ed699ccba751b8b511a Mon Sep 17 00:00:00 2001 From: Marius Brehler Date: Wed, 28 Feb 2024 20:41:05 +0100 Subject: [PATCH] [mlir][EmitC] Add logical operators (#83123) This adds operations for the logical operators AND, NOT and OR. --- mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 64 +++++++++++++++++++++ mlir/lib/Target/Cpp/TranslateToCpp.cpp | 30 +++++++++- mlir/test/Dialect/EmitC/invalid_ops.mlir | 24 ++++++++ mlir/test/Dialect/EmitC/ops.mlir | 7 +++ mlir/test/Target/Cpp/logical_operators.mlir | 14 +++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 mlir/test/Target/Cpp/logical_operators.mlir diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index c50fdf397a0fec..7b9fbb494e895a 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -658,6 +658,70 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> { let assemblyFormat = "$value attr-dict `:` type($result)"; } +def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> { + let summary = "Logical and operation"; + let description = [{ + With the `logical_and` operation the logical operator && (and) can + be applied. + + Example: + + ```mlir + %0 = emitc.logical_and %arg0, %arg1 : i32, i32 + ``` + ```c++ + // Code emitted for the operation above. + bool v3 = v1 && v2; + ``` + }]; + + let results = (outs I1); + let assemblyFormat = "operands attr-dict `:` type(operands)"; +} + +def EmitC_LogicalNotOp : EmitC_Op<"logical_not", []> { + let summary = "Logical not operation"; + let description = [{ + With the `logical_not` operation the logical operator ! (negation) can + be applied. + + Example: + + ```mlir + %0 = emitc.logical_not %arg0 : i32 + ``` + ```c++ + // Code emitted for the operation above. + bool v2 = !v1; + ``` + }]; + + let arguments = (ins AnyType); + let results = (outs I1); + let assemblyFormat = "operands attr-dict `:` type(operands)"; +} + +def EmitC_LogicalOrOp : EmitC_BinaryOp<"logical_or", []> { + let summary = "Logical or operation"; + let description = [{ + With the `logical_or` operation the logical operator || (inclusive or) + can be applied. + + Example: + + ```mlir + %0 = emitc.logical_or %arg0, %arg1 : i32, i32 + ``` + ```c++ + // Code emitted for the operation above. + bool v3 = v1 || v2; + ``` + }]; + + let results = (outs I1); + let assemblyFormat = "operands attr-dict `:` type(operands)"; +} + def EmitC_MulOp : EmitC_BinaryOp<"mul", []> { let summary = "Multiplication operation"; let description = [{ diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 2ba3dec0a9a57f..16aa136c5a4e28 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -627,6 +627,33 @@ static LogicalResult printOperation(CppEmitter &emitter, return success(); } +static LogicalResult printOperation(CppEmitter &emitter, + emitc::LogicalAndOp logicalAndOp) { + Operation *operation = logicalAndOp.getOperation(); + return printBinaryOperation(emitter, operation, "&&"); +} + +static LogicalResult printOperation(CppEmitter &emitter, + emitc::LogicalNotOp logicalNotOp) { + raw_ostream &os = emitter.ostream(); + + if (failed(emitter.emitAssignPrefix(*logicalNotOp.getOperation()))) + return failure(); + + os << "!"; + + if (failed(emitter.emitOperand(logicalNotOp.getOperand()))) + return failure(); + + return success(); +} + +static LogicalResult printOperation(CppEmitter &emitter, + emitc::LogicalOrOp logicalOrOp) { + Operation *operation = logicalOrOp.getOperation(); + return printBinaryOperation(emitter, operation, "||"); +} + static LogicalResult printOperation(CppEmitter &emitter, emitc::ForOp forOp) { raw_indented_ostream &os = emitter.ostream(); @@ -1284,7 +1311,8 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp, emitc::ConstantOp, emitc::DeclareFuncOp, emitc::DivOp, emitc::ExpressionOp, emitc::ForOp, emitc::FuncOp, emitc::IfOp, - emitc::IncludeOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp, + emitc::IncludeOp, emitc::LogicalAndOp, emitc::LogicalNotOp, + emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp, 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 121a2163d38320..5f64b535d684f3 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -331,3 +331,27 @@ emitc.declare_func @bar // expected-error@+1 {{'emitc.declare_func' op requires attribute 'sym_name'}} "emitc.declare_func"() : () -> () + +// ----- + +func.func @logical_and_resulterror(%arg0: i32, %arg1: i32) { + // expected-error @+1 {{'emitc.logical_and' op result #0 must be 1-bit signless integer, but got 'i32'}} + %0 = "emitc.logical_and"(%arg0, %arg1) : (i32, i32) -> i32 + return +} + +// ----- + +func.func @logical_not_resulterror(%arg0: i32) { + // expected-error @+1 {{'emitc.logical_not' op result #0 must be 1-bit signless integer, but got 'i32'}} + %0 = "emitc.logical_not"(%arg0) : (i32) -> i32 + return +} + +// ----- + +func.func @logical_or_resulterror(%arg0: i32, %arg1: i32) { + // expected-error @+1 {{'emitc.logical_or' op result #0 must be 1-bit signless integer, but got 'i32'}} + %0 = "emitc.logical_or"(%arg0, %arg1) : (i32, i32) -> i32 + return +} diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir index 93119be14c908b..045fb24cb67f8d 100644 --- a/mlir/test/Dialect/EmitC/ops.mlir +++ b/mlir/test/Dialect/EmitC/ops.mlir @@ -117,6 +117,13 @@ func.func @cmp(%arg0 : i32, %arg1 : f32, %arg2 : i64, %arg3 : f64, %arg4 : !emit return } +func.func @logical(%arg0: i32, %arg1: i32) { + %0 = emitc.logical_and %arg0, %arg1 : i32, i32 + %1 = emitc.logical_not %arg0 : i32 + %2 = emitc.logical_or %arg0, %arg1 : i32, i32 + return +} + func.func @test_if(%arg0: i1, %arg1: f32) { emitc.if %arg0 { %0 = emitc.call_opaque "func_const"(%arg1) : (f32) -> i32 diff --git a/mlir/test/Target/Cpp/logical_operators.mlir b/mlir/test/Target/Cpp/logical_operators.mlir new file mode 100644 index 00000000000000..7083dc218fca99 --- /dev/null +++ b/mlir/test/Target/Cpp/logical_operators.mlir @@ -0,0 +1,14 @@ +// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s + +func.func @logical(%arg0: i32, %arg1: i32) -> () { + %0 = emitc.logical_and %arg0, %arg1 : i32, i32 + %1 = emitc.logical_not %arg0 : i32 + %2 = emitc.logical_or %arg0, %arg1 : i32, i32 + + return +} + +// CHECK-LABEL: void logical +// CHECK-NEXT: bool [[V2:[^ ]*]] = [[V0:[^ ]*]] && [[V1:[^ ]*]]; +// CHECK-NEXT: bool [[V3:[^ ]*]] = ![[V0]]; +// CHECK-NEXT: bool [[V4:[^ ]*]] = [[V0]] || [[V1]];