Skip to content

[mlir][EmitC] Add verbatim op #79584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 31, 2024
Merged

[mlir][EmitC] Add verbatim op #79584

merged 3 commits into from
Jan 31, 2024

Conversation

simon-camp
Copy link
Contributor

@simon-camp simon-camp commented Jan 26, 2024

The verbatim operation produces no results and the value is emitted as is followed by a line break ('\n' character) during translation.

Note: Use with caution. This operation can have arbitrary effects on the semantics of the emitted code. Use semantically more meaningful operations whenever possible. Additionally this op is NOT intended to be used to inject large snippets of code.

This operation can be used in situations where a more suitable operation is not yet implemented in the dialect or where preprocessor directives interfere with the structure of the code.

Co-authored-by: Marius Brehler [email protected]

This operation can be used in situations where a more suitable operation is not yet implemented in the dialect or where preprocessor directives interfere with the structure of the code.
@llvmbot
Copy link
Member

llvmbot commented Jan 26, 2024

@llvm/pr-subscribers-mlir-emitc

@llvm/pr-subscribers-mlir

Author: Simon Camphausen (simon-camp)

Changes

The verbatim operation produces no results and the value is emitted as is durung translation. If trailing_semicolon is present an additional semicolon is emitted.

Note: Use with caution. This operation can have arbitrary effects on the semantics of the emitted code. Use semantically more meaningful operations whenever possible. Additionally this op is NOT intended to be used to inject large snippets of code.

This operation can be used in situations where a more suitable operation is not yet implemented in the dialect or where preprocessor directives interfere with the structure of the code.


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

6 Files Affected:

  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+39)
  • (modified) mlir/lib/Dialect/EmitC/IR/EmitC.cpp (+11)
  • (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+17-6)
  • (modified) mlir/test/Dialect/EmitC/invalid_ops.mlir (+8)
  • (modified) mlir/test/Dialect/EmitC/ops.mlir (+11)
  • (added) mlir/test/Target/Cpp/verbatim.mlir (+21)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index b8f8f1e2d818d5..4d3d6c20e9b2c3 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -544,6 +544,45 @@ def EmitC_VariableOp : EmitC_Op<"variable", []> {
   let hasVerifier = 1;
 }
 
+def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
+  let summary = "Verbatim operation";
+  let description = [{
+    The `verbatim` operation produces no results and the value is emitted as is
+    durung translation. If `trailing_semicolon` is present an additional 
+    semicolon is emitted.
+
+    Note: Use with caution. This operation can have arbitrary effects on the
+    semantics of the emitted code. Use semantically more meaningful operations
+    whenever possible. Additionally this op is *NOT* intended to be used to
+    inject large snippets of code.
+
+    This operation can be used in situations where a more suitable operation is
+    not yet implemented in the dialect or where preprocessor directives
+    interfere with the structure of the code. One example of this is to declare
+    the linkage of external symbols to make the generated code usable in both C
+    and C++ contexts:
+
+    ```c++
+    #ifdef __cplusplus
+    extern "C" {
+    #endif
+
+    ...
+    
+    #ifdef __cplusplus
+    }
+    #endif
+    ```
+  }];
+
+  let arguments = (ins
+    StrAttr:$value,
+    UnitAttr:$trailing_semicolon
+  );
+  let assemblyFormat = "$value (`trailing_semicolon` $trailing_semicolon^)? attr-dict";
+  let hasVerifier = 1;
+}
+
 def EmitC_AssignOp : EmitC_Op<"assign", []> {
   let summary = "Assign operation";
   let description = [{
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 5f502f1f7a1714..921cce58294315 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -584,6 +584,17 @@ LogicalResult emitc::VariableOp::verify() {
   return verifyInitializationAttribute(getOperation(), getValueAttr());
 }
 
+//===----------------------------------------------------------------------===//
+// VerbatimOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult emitc::VerbatimOp::verify() {
+  if (getValue().back() == ';')
+    return emitOpError() << "';' not allowed as the last character, use the "
+                            "`trailing_semicolon` attribute instead";
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // YieldOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index c32cb03caf9db6..657f6ae092aef8 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -429,6 +429,17 @@ static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
   return printBinaryOperation(emitter, operation, binaryOperator);
 }
 
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::VerbatimOp verbatimOp) {
+  raw_ostream &os = emitter.ostream();
+
+  os << verbatimOp.getValue();
+
+  if (verbatimOp.getTrailingSemicolon())
+    os << ";";
+  return success();
+}
+
 static LogicalResult printOperation(CppEmitter &emitter,
                                     cf::BranchOp branchOp) {
   raw_ostream &os = emitter.ostream();
@@ -814,11 +825,10 @@ static LogicalResult printOperation(CppEmitter &emitter,
     for (Operation &op : block.getOperations()) {
       // When generating code for an emitc.if or cf.cond_br op no semicolon
       // needs to be printed after the closing brace.
-      // When generating code for an emitc.for op, printing a trailing semicolon
-      // is handled within the printOperation function.
-      bool trailingSemicolon =
-          !isa<cf::CondBranchOp, emitc::ForOp, emitc::IfOp, emitc::LiteralOp>(
-              op);
+      // When generating code for an emitc.for and emitc.verbatim op, printing a
+      // trailing semicolon is handled within the printOperation function.
+      bool trailingSemicolon = !isa<cf::CondBranchOp, emitc::ForOp, emitc::IfOp,
+                                    emitc::LiteralOp, emitc::VerbatimOp>(op);
 
       if (failed(emitter.emitOperation(
               op, /*trailingSemicolon=*/trailingSemicolon)))
@@ -1144,7 +1154,8 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
                 emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
                 emitc::ConstantOp, emitc::DivOp, emitc::ExpressionOp,
                 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::MulOp,
-                emitc::RemOp, emitc::SubOp, emitc::VariableOp>(
+                emitc::RemOp, emitc::SubOp, emitc::VariableOp,
+                emitc::VerbatimOp>(
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.
           .Case<func::CallOp, func::ConstantOp, func::FuncOp, func::ReturnOp>(
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 46eccb1c24eea2..2d98c505ac2435 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -289,3 +289,11 @@ func.func @test_expression_multiple_results(%arg0: i32) -> i32 {
   }
   return %r : i32
 }
+
+// -----
+
+func.func @verbatim_trailing_semicolon() {
+    // expected-error @+1 {{'emitc.verbatim' op ';' not allowed as the last character, use the `trailing_semicolon` attribute instead}}
+    emitc.verbatim "typedef int32_t i32;"
+    return
+}
\ No newline at end of file
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 45ce2bcb99092c..a50883b38d14a2 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -166,3 +166,14 @@ func.func @test_for_not_index_induction(%arg0 : i16, %arg1 : i16, %arg2 : i16) {
   }
   return
 }
+
+emitc.verbatim "#ifdef __cplusplus"
+emitc.verbatim "extern \"C\" {"
+emitc.verbatim "#endif  // __cplusplus"
+
+emitc.verbatim "#ifdef __cplusplus"
+emitc.verbatim "}  // extern \"C\""
+emitc.verbatim "#endif  // __cplusplus"
+
+emitc.verbatim "typedef int32_t i32" {trailing_semicolon = unit}
+emitc.verbatim "typedef float f32" trailing_semicolon
diff --git a/mlir/test/Target/Cpp/verbatim.mlir b/mlir/test/Target/Cpp/verbatim.mlir
new file mode 100644
index 00000000000000..cf17486050f981
--- /dev/null
+++ b/mlir/test/Target/Cpp/verbatim.mlir
@@ -0,0 +1,21 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
+// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s
+
+
+emitc.verbatim "#ifdef __cplusplus"
+// CHECK: #ifdef __cplusplus
+emitc.verbatim "extern \"C\" {"
+// CHECK-NEXT: extern "C" {
+emitc.verbatim "#endif  // __cplusplus"
+// CHECK-NEXT: #endif  // __cplusplus
+emitc.verbatim "#ifdef __cplusplus"
+// CHECK-NEXT: #ifdef __cplusplus
+emitc.verbatim "}  // extern \"C\""
+// CHECK-NEXT: }  // extern "C"
+emitc.verbatim "#endif  // __cplusplus"
+// CHECK-NEXT: #endif  // __cplusplus
+
+emitc.verbatim "typedef int32_t i32" {trailing_semicolon = unit}
+// CHECK-NEXT: typedef int32_t i32;
+emitc.verbatim "typedef float f32" trailing_semicolon
+// CHECK-NEXT: typedef float f32;

Copy link
Member

@marbre marbre left a comment

Choose a reason for hiding this comment

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

Thanks @simon-camp :) As we've already discussed this offline for quite a while, I don't have much to say. From a first quick review, there are only minor things to change.

Comment on lines 178 to 179
emitc.verbatim "typedef int32_t i32" {trailing_semicolon = unit}
emitc.verbatim "typedef float f32" trailing_semicolon
Copy link
Member

Choose a reason for hiding this comment

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

Do you want to add one of those or even both to the example in the description?

@AlexDenisov
Copy link
Member

This is very useful!

Out of curiosity, what makes the trailing_semicolon necessary? Isn't it easier to include ; into the string itself, and the overcome the validation by adding a space at the end of the string?

@jpienaar
Copy link
Member

This is very useful!

Out of curiosity, what makes the trailing_semicolon necessary? Isn't it easier to include ; into the string itself, and the overcome the validation by adding a space at the end of the string?

+1, could the attribute be avoided?

@simon-camp
Copy link
Contributor Author

This is very useful!

Out of curiosity, what makes the trailing_semicolon necessary? Isn't it easier to include ; into the string itself, and the overcome the validation by adding a space at the end of the string?

Good suggestion, thank you.

I'm not sure I follow the validation part. The semicolon is now part of the attribute, so there is nothing to verify. Nonetheless this made me think of the implicit \n generated during emission, so this is now mentioned in the op description.

@AlexDenisov
Copy link
Member

I'm not sure I follow the validation part. The semicolon is now part of the attribute, so there is nothing to verify.

I was referring to these lines (which are now gone).

@simon-camp simon-camp merged commit e624648 into llvm:main Jan 31, 2024
@simon-camp simon-camp deleted the emitc.verbatim branch January 31, 2024 10:56
simon-camp pushed a commit that referenced this pull request Jan 31, 2024
The uses of the attribute were removed in code review of #79584, but
it's definition was inadvertently kept.
mgehre-amd pushed a commit to Xilinx/llvm-project that referenced this pull request Mar 11, 2024
The `verbatim` operation produces no results and the value is emitted as
is followed by a line break ('\n' character) during translation.

Note: Use with caution. This operation can have arbitrary effects on the
semantics of the emitted code. Use semantically more meaningful
operations whenever possible. Additionally this op is *NOT* intended to
be used to inject large snippets of code.

This operation can be used in situations where a more suitable operation
is not yet implemented in the dialect or where preprocessor directives
interfere with the structure of the code.

Co-authored-by: Marius Brehler <[email protected]>
mgehre-amd pushed a commit to Xilinx/llvm-project that referenced this pull request Mar 11, 2024
The uses of the attribute were removed in code review of llvm#79584, but
it's definition was inadvertently kept.
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.

5 participants