From 9fd8d27036fd550d98f271a88713ee144d301e89 Mon Sep 17 00:00:00 2001 From: Matthias Gehre Date: Mon, 7 Oct 2024 21:20:50 +0200 Subject: [PATCH 1/2] emitc: Add fmtArgs to verbatim --- mlir/include/mlir/Dialect/EmitC/IR/EmitC.h | 2 + mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 25 ++++++- mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 72 +++++++++++++++++++++ mlir/lib/Target/Cpp/TranslateToCpp.cpp | 16 ++++- mlir/test/Dialect/EmitC/invalid_ops.mlir | 48 ++++++++++++++ mlir/test/Dialect/EmitC/ops.mlir | 10 +++ mlir/test/Target/Cpp/verbatim.mlir | 28 +++++++- 7 files changed, 196 insertions(+), 5 deletions(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h index 0c595a6b109caa..bc82f58a7ee95c 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h @@ -27,6 +27,8 @@ #include "mlir/Dialect/EmitC/IR/EmitCDialect.h.inc" #include "mlir/Dialect/EmitC/IR/EmitCEnums.h.inc" +#include + namespace mlir { namespace emitc { void buildTerminatedBody(OpBuilder &builder, Location loc); diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index 1ce1fe8bbf87e5..5f3bec5637b458 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -1157,10 +1157,31 @@ def EmitC_VerbatimOp : EmitC_Op<"verbatim"> { } #endif ``` + + If the `emitc.verbatim` op has operands, then the `value` is interpreted as + format string, where `{}` is a placeholder for an operand in their order. + For example, `emitc.verbatim "#pragma my src={} dst={}" %src, %dest : i32, i32` + would be emitted as `#pragma my src=a dst=b` if `%src` became `a` and + `%dest` as `b` in the C code. + `{{` in the format string is interpreted as a single `{` and doesn't introduce + a placeholder. }]; - let arguments = (ins StrAttr:$value); - let assemblyFormat = "$value attr-dict"; + let extraClassDeclaration = [{ + // Either a literal string, or an placeholder for the fmtArgs. + struct Placeholder {}; + using ReplacementItem = std::variant; + + FailureOr> parseFormatString(); + }]; + + let arguments = (ins StrAttr:$value, + Variadic:$fmtArgs); + + let builders = [OpBuilder<(ins "::mlir::StringAttr":$value), [{ build($_builder, $_state, value, {}); }] >]; + let builders = [OpBuilder<(ins "::llvm::StringRef":$value), [{ build($_builder, $_state, value, {}); }] >]; + let hasVerifier = 1; + let assemblyFormat = "$value ($fmtArgs^ `:` type($fmtArgs))? attr-dict"; } def EmitC_AssignOp : EmitC_Op<"assign", []> { diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index 67b9695057a9e2..7bc40b4f555cc6 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -909,6 +909,78 @@ LogicalResult emitc::SubscriptOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// VerbatimOp +//===----------------------------------------------------------------------===// + +LogicalResult emitc::VerbatimOp::verify() { + FailureOr> fmt = parseFormatString(); + if (failed(fmt)) + return failure(); + + size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) { + return std::holds_alternative(item); + }); + + if (numPlaceholders != getFmtArgs().size()) { + return emitOpError() + << "requires operands for each placeholder in the format string"; + } + return success(); +} + +/// Parse a format string and return a list of its parts. +/// A part is either a StringRef that has to be printed as-is, or +/// a Placeholder which requires printing the next operand of the VerbatimOp. +/// In the format string, all `{}` are replaced by Placeholders, except if the +/// `{` is escaped by `{{` - then it doesn't start a placeholder. +FailureOr> +emitc::VerbatimOp::parseFormatString() { + SmallVector items; + + // If there are not operands, the format string is not interpreted. + if (getFmtArgs().empty()) { + items.push_back(getValue()); + return items; + } + + StringRef toParse = getValue(); + while (!toParse.empty()) { + size_t idx = toParse.find('{'); + if (idx == StringRef::npos) { + // No '{' + items.push_back(toParse); + break; + } + if (idx > 0) { + // Take all chars excluding the '{'. + items.push_back(toParse.take_front(idx)); + toParse = toParse.drop_front(idx); + continue; + } + if (toParse.size() < 2) { + // '{' is last character + items.push_back(toParse); + break; + } + // toParse contains at least two characters and starts with `{`. + char nextChar = toParse[1]; + if (nextChar == '{') { + // Double '{{' -> '{' (escaping). + items.push_back(toParse.take_front(1)); + toParse = toParse.drop_front(2); + continue; + } + if (nextChar == '}') { + items.push_back(Placeholder{}); + toParse = toParse.drop_front(2); + continue; + } + return emitOpError() << "expected '}' after unescaped '{'"; + } + return items; +} + //===----------------------------------------------------------------------===// // EmitC Enums //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index d59de9f7580c24..c8a5cb5fbd3f61 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -512,7 +512,21 @@ static LogicalResult printOperation(CppEmitter &emitter, emitc::VerbatimOp verbatimOp) { raw_ostream &os = emitter.ostream(); - os << verbatimOp.getValue(); + FailureOr> items = + verbatimOp.parseFormatString(); + if (failed(items)) + return failure(); + + auto fmtArg = verbatimOp.getFmtArgs().begin(); + + for (emitc::VerbatimOp::ReplacementItem &item : *items) { + if (auto *str = std::get_if(&item)) { + os << *str; + } else { + if (failed(emitter.emitOperand(*fmtArg++))) + return failure(); + } + } return success(); } diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir index a801ad4fdc2ebc..4d52dd39b02489 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -476,3 +476,51 @@ emitc.global const @myref : !emitc.array<2xi16> = dense<128> ref // expected-error @+1 {{'emitc.global' op global reference must be initialized}} emitc.global const @myref : !emitc.array<2xi16> ref + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}} + emitc.verbatim "" %arg0, %arg1 : !emitc.ptr, i32 + return +} + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}} + emitc.verbatim "abc" %arg0, %arg1 : !emitc.ptr, i32 + return +} + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}} + emitc.verbatim "{}" %arg0, %arg1 : !emitc.ptr, i32 + return +} + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}} + emitc.verbatim "{} {} {}" %arg0, %arg1 : !emitc.ptr, i32 + return +} + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op expected '}' after unescaped '{'}} + emitc.verbatim "{ " %arg0, %arg1 : !emitc.ptr, i32 + return +} + +// ----- + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + // expected-error @+1 {{'emitc.verbatim' op expected '}' after unescaped '{'}} + emitc.verbatim "{a} " %arg0, %arg1 : !emitc.ptr, i32 + return +} diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir index 912335fefd00be..23b5421d860ff3 100644 --- a/mlir/test/Dialect/EmitC/ops.mlir +++ b/mlir/test/Dialect/EmitC/ops.mlir @@ -240,6 +240,16 @@ emitc.verbatim "#endif // __cplusplus" emitc.verbatim "typedef int32_t i32;" emitc.verbatim "typedef float f32;" +// The value is not interpreted as format string if there are no operands. +emitc.verbatim "{} { }" + +func.func @test_verbatim(%arg0 : !emitc.ptr, %arg1 : i32) { + emitc.verbatim "{} + {};" %arg0, %arg1 : !emitc.ptr, i32 + + // Trailing '{' are ok and don't start a placeholder. + emitc.verbatim "{} + {} {" %arg0, %arg1 : !emitc.ptr, i32 + return +} emitc.global @uninit : i32 emitc.global @myglobal_int : i32 = 4 diff --git a/mlir/test/Target/Cpp/verbatim.mlir b/mlir/test/Target/Cpp/verbatim.mlir index 10465dd781a81d..41e39dcb6ca900 100644 --- a/mlir/test/Target/Cpp/verbatim.mlir +++ b/mlir/test/Target/Cpp/verbatim.mlir @@ -1,5 +1,5 @@ -// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s +// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s --match-full-lines +// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s --match-full-lines emitc.verbatim "#ifdef __cplusplus" @@ -19,3 +19,27 @@ emitc.verbatim "typedef int32_t i32;" // CHECK-NEXT: typedef int32_t i32; emitc.verbatim "typedef float f32;" // CHECK-NEXT: typedef float f32; + +emitc.func @func(%arg: f32) { + // CHECK: void func(float [[V0:[^ ]*]]) { + %a = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.array<3x7xi32> + // CHECK: int32_t [[A:[^ ]*]][3][7]; + + emitc.verbatim "{}" %arg : f32 + // CHECK: [[V0]] + + emitc.verbatim "{} {{a" %arg : f32 + // CHECK-NEXT: [[V0]] {a + + emitc.verbatim "#pragma my var={} property" %arg : f32 + // CHECK-NEXT: #pragma my var=[[V0]] property + + // Trailing '{' are printed as-is. + emitc.verbatim "#pragma my var={} {" %arg : f32 + // CHECK-NEXT: #pragma my var=[[V0]] { + + emitc.verbatim "#pragma my2 var={} property" %a : !emitc.array<3x7xi32> + // CHECK-NEXT: #pragma my2 var=[[A]] property + + emitc.return +} From edbe32102b17a6507423bc8c548621711cb81e5b Mon Sep 17 00:00:00 2001 From: Matthias Gehre Date: Wed, 9 Oct 2024 09:25:01 +0200 Subject: [PATCH 2/2] Review comment --- mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index 5f3bec5637b458..8d434d88e910a9 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -1162,7 +1162,7 @@ def EmitC_VerbatimOp : EmitC_Op<"verbatim"> { format string, where `{}` is a placeholder for an operand in their order. For example, `emitc.verbatim "#pragma my src={} dst={}" %src, %dest : i32, i32` would be emitted as `#pragma my src=a dst=b` if `%src` became `a` and - `%dest` as `b` in the C code. + `%dest` became `b` in the C code. `{{` in the format string is interpreted as a single `{` and doesn't introduce a placeholder. }];