Skip to content

Commit 0049adf

Browse files
authored
[MLIR] emitc: Add fmtArgs to verbatim (#123294)
Allows to print code snippets that refer to arguments or local variables. E.g. `emitc.verbatim "#pragma abc var={}" args %arg0 : !emitc.ptr<i32>` is printed as `#pragma abc var=v1` when the translator had decided to print `%arg0` as `v1`. As a follow-up PR, we will use the same infra to extend opaque type, which provides a way to generate template types depending on the spelling of other types.
1 parent f7d0370 commit 0049adf

File tree

7 files changed

+239
-5
lines changed

7 files changed

+239
-5
lines changed

mlir/include/mlir/Dialect/EmitC/IR/EmitC.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "mlir/Dialect/EmitC/IR/EmitCDialect.h.inc"
2828
#include "mlir/Dialect/EmitC/IR/EmitCEnums.h.inc"
2929

30+
#include <variant>
31+
3032
namespace mlir {
3133
namespace emitc {
3234
void buildTerminatedBody(OpBuilder &builder, Location loc);
@@ -47,6 +49,10 @@ bool isSupportedFloatType(mlir::Type type);
4749
/// Determines whether \p type is a emitc.size_t/ssize_t type.
4850
bool isPointerWideType(mlir::Type type);
4951

52+
// Either a literal string, or an placeholder for the fmtArgs.
53+
struct Placeholder {};
54+
using ReplacementItem = std::variant<StringRef, Placeholder>;
55+
5056
} // namespace emitc
5157
} // namespace mlir
5258

mlir/include/mlir/Dialect/EmitC/IR/EmitC.td

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,10 +1269,29 @@ def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
12691269
}
12701270
#endif
12711271
```
1272+
1273+
If the `emitc.verbatim` op has operands, then the `value` is interpreted as
1274+
format string, where `{}` is a placeholder for an operand in their order.
1275+
For example, `emitc.verbatim "#pragma my src={} dst={}" %src, %dest : i32, i32`
1276+
would be emitted as `#pragma my src=a dst=b` if `%src` became `a` and
1277+
`%dest` became `b` in the C code.
1278+
`{{` in the format string is interpreted as a single `{` and doesn't introduce
1279+
a placeholder.
12721280
}];
12731281

1274-
let arguments = (ins StrAttr:$value);
1275-
let assemblyFormat = "$value attr-dict";
1282+
let extraClassDeclaration = [{
1283+
FailureOr<SmallVector<::mlir::emitc::ReplacementItem>> parseFormatString();
1284+
}];
1285+
1286+
let arguments = (ins StrAttr:$value, Variadic<EmitCType>:$fmtArgs);
1287+
1288+
let builders = [OpBuilder<(ins "::mlir::StringAttr":$value),
1289+
[{ build($_builder, $_state, value, {}); }]>];
1290+
let builders = [OpBuilder<(ins "::llvm::StringRef":$value),
1291+
[{ build($_builder, $_state, value, {}); }]>];
1292+
let hasVerifier = 1;
1293+
let assemblyFormat =
1294+
"$value (`args` $fmtArgs^ `:` type($fmtArgs))? attr-dict";
12761295
}
12771296

12781297
def EmitC_AssignOp : EmitC_Op<"assign", []> {

mlir/lib/Dialect/EmitC/IR/EmitC.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/StringExtras.h"
2020
#include "llvm/ADT/TypeSwitch.h"
2121
#include "llvm/Support/Casting.h"
22+
#include "llvm/Support/FormatVariadic.h"
2223

2324
using namespace mlir;
2425
using namespace mlir::emitc;
@@ -167,6 +168,63 @@ static LogicalResult verifyInitializationAttribute(Operation *op,
167168
return success();
168169
}
169170

171+
/// Parse a format string and return a list of its parts.
172+
/// A part is either a StringRef that has to be printed as-is, or
173+
/// a Placeholder which requires printing the next operand of the VerbatimOp.
174+
/// In the format string, all `{}` are replaced by Placeholders, except if the
175+
/// `{` is escaped by `{{` - then it doesn't start a placeholder.
176+
template <class ArgType>
177+
FailureOr<SmallVector<ReplacementItem>>
178+
parseFormatString(StringRef toParse, ArgType fmtArgs,
179+
std::optional<llvm::function_ref<mlir::InFlightDiagnostic()>>
180+
emitError = {}) {
181+
SmallVector<ReplacementItem> items;
182+
183+
// If there are not operands, the format string is not interpreted.
184+
if (fmtArgs.empty()) {
185+
items.push_back(toParse);
186+
return items;
187+
}
188+
189+
while (!toParse.empty()) {
190+
size_t idx = toParse.find('{');
191+
if (idx == StringRef::npos) {
192+
// No '{'
193+
items.push_back(toParse);
194+
break;
195+
}
196+
if (idx > 0) {
197+
// Take all chars excluding the '{'.
198+
items.push_back(toParse.take_front(idx));
199+
toParse = toParse.drop_front(idx);
200+
continue;
201+
}
202+
if (toParse.size() < 2) {
203+
return (*emitError)()
204+
<< "expected '}' after unescaped '{' at end of string";
205+
}
206+
// toParse contains at least two characters and starts with `{`.
207+
char nextChar = toParse[1];
208+
if (nextChar == '{') {
209+
// Double '{{' -> '{' (escaping).
210+
items.push_back(toParse.take_front(1));
211+
toParse = toParse.drop_front(2);
212+
continue;
213+
}
214+
if (nextChar == '}') {
215+
items.push_back(Placeholder{});
216+
toParse = toParse.drop_front(2);
217+
continue;
218+
}
219+
220+
if (emitError.has_value()) {
221+
return (*emitError)() << "expected '}' after unescaped '{'";
222+
}
223+
return failure();
224+
}
225+
return items;
226+
}
227+
170228
//===----------------------------------------------------------------------===//
171229
// AddOp
172230
//===----------------------------------------------------------------------===//
@@ -909,6 +967,55 @@ LogicalResult emitc::SubscriptOp::verify() {
909967
return success();
910968
}
911969

970+
//===----------------------------------------------------------------------===//
971+
// VerbatimOp
972+
//===----------------------------------------------------------------------===//
973+
974+
LogicalResult emitc::VerbatimOp::verify() {
975+
auto errorCallback = [&]() -> InFlightDiagnostic {
976+
return this->emitOpError();
977+
};
978+
FailureOr<SmallVector<ReplacementItem>> fmt =
979+
::parseFormatString(getValue(), getFmtArgs(), errorCallback);
980+
if (failed(fmt))
981+
return failure();
982+
983+
size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) {
984+
return std::holds_alternative<Placeholder>(item);
985+
});
986+
987+
if (numPlaceholders != getFmtArgs().size()) {
988+
return emitOpError()
989+
<< "requires operands for each placeholder in the format string";
990+
}
991+
return success();
992+
}
993+
994+
static ParseResult parseVariadicTypeFmtArgs(AsmParser &p,
995+
SmallVector<Type> &params) {
996+
Type type;
997+
if (p.parseType(type))
998+
return failure();
999+
1000+
params.push_back(type);
1001+
while (succeeded(p.parseOptionalComma())) {
1002+
if (p.parseType(type))
1003+
return failure();
1004+
params.push_back(type);
1005+
}
1006+
1007+
return success();
1008+
}
1009+
1010+
static void printVariadicTypeFmtArgs(AsmPrinter &p, ArrayRef<Type> params) {
1011+
llvm::interleaveComma(params, p, [&](Type type) { p.printType(type); });
1012+
}
1013+
1014+
FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
1015+
// Error checking is done in verify.
1016+
return ::parseFormatString(getValue(), getFmtArgs());
1017+
}
1018+
9121019
//===----------------------------------------------------------------------===//
9131020
// EmitC Enums
9141021
//===----------------------------------------------------------------------===//

mlir/lib/Target/Cpp/TranslateToCpp.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,21 @@ static LogicalResult printOperation(CppEmitter &emitter,
568568
emitc::VerbatimOp verbatimOp) {
569569
raw_ostream &os = emitter.ostream();
570570

571-
os << verbatimOp.getValue();
571+
FailureOr<SmallVector<ReplacementItem>> items =
572+
verbatimOp.parseFormatString();
573+
if (failed(items))
574+
return failure();
575+
576+
auto fmtArg = verbatimOp.getFmtArgs().begin();
577+
578+
for (ReplacementItem &item : *items) {
579+
if (auto *str = std::get_if<StringRef>(&item)) {
580+
os << *str;
581+
} else {
582+
if (failed(emitter.emitOperand(*fmtArg++)))
583+
return failure();
584+
}
585+
}
572586

573587
return success();
574588
}

mlir/test/Dialect/EmitC/invalid_ops.mlir

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,59 @@ func.func @emitc_switch() {
566566
}
567567
return
568568
}
569+
570+
// -----
571+
572+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
573+
// expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}}
574+
emitc.verbatim "" args %arg0, %arg1 : !emitc.ptr<i32>, i32
575+
return
576+
}
577+
578+
// -----
579+
580+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
581+
// expected-error @+1 {{'emitc.verbatim' op expected '}' after unescaped '{' at end of string}}
582+
emitc.verbatim "{} + {} {" args %arg0, %arg1 : !emitc.ptr<i32>, i32
583+
return
584+
}
585+
586+
// -----
587+
588+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
589+
// expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}}
590+
emitc.verbatim "abc" args %arg0, %arg1 : !emitc.ptr<i32>, i32
591+
return
592+
}
593+
594+
// -----
595+
596+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
597+
// expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}}
598+
emitc.verbatim "{}" args %arg0, %arg1 : !emitc.ptr<i32>, i32
599+
return
600+
}
601+
602+
// -----
603+
604+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
605+
// expected-error @+1 {{'emitc.verbatim' op requires operands for each placeholder in the format string}}
606+
emitc.verbatim "{} {} {}" args %arg0, %arg1 : !emitc.ptr<i32>, i32
607+
return
608+
}
609+
610+
// -----
611+
612+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
613+
// expected-error @+1 {{'emitc.verbatim' op expected '}' after unescaped '{'}}
614+
emitc.verbatim "{ " args %arg0, %arg1 : !emitc.ptr<i32>, i32
615+
return
616+
}
617+
618+
// -----
619+
620+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
621+
// expected-error @+1 {{'emitc.verbatim' op expected '}' after unescaped '{'}}
622+
emitc.verbatim "{a} " args %arg0, %arg1 : !emitc.ptr<i32>, i32
623+
return
624+
}

mlir/test/Dialect/EmitC/ops.mlir

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,18 @@ emitc.verbatim "#endif // __cplusplus"
238238
emitc.verbatim "typedef int32_t i32;"
239239
emitc.verbatim "typedef float f32;"
240240

241+
// The value is not interpreted as format string if there are no operands.
242+
emitc.verbatim "{} { }"
243+
244+
func.func @test_verbatim(%arg0 : !emitc.ptr<i32>, %arg1 : i32) {
245+
emitc.verbatim "{} + {};" args %arg0, %arg1 : !emitc.ptr<i32>, i32
246+
247+
// Check there is no ambiguity whether %a is the argument to the emitc.verbatim op.
248+
emitc.verbatim "a"
249+
%a = "emitc.constant"(){value = 42 : i32} : () -> i32
250+
251+
return
252+
}
241253

242254
emitc.global @uninit : i32
243255
emitc.global @myglobal_int : i32 = 4

mlir/test/Target/Cpp/verbatim.mlir

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
2-
// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s
1+
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s --match-full-lines
2+
// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s --match-full-lines
33

44

55
emitc.verbatim "#ifdef __cplusplus"
@@ -19,3 +19,23 @@ emitc.verbatim "typedef int32_t i32;"
1919
// CHECK-NEXT: typedef int32_t i32;
2020
emitc.verbatim "typedef float f32;"
2121
// CHECK-NEXT: typedef float f32;
22+
23+
emitc.func @func(%arg: f32) {
24+
// CHECK: void func(float [[V0:[^ ]*]]) {
25+
%a = "emitc.variable"(){value = #emitc.opaque<"">} : () -> !emitc.array<3x7xi32>
26+
// CHECK: int32_t [[A:[^ ]*]][3][7];
27+
28+
emitc.verbatim "{}" args %arg : f32
29+
// CHECK: [[V0]]
30+
31+
emitc.verbatim "{} {{a" args %arg : f32
32+
// CHECK-NEXT: [[V0]] {a
33+
34+
emitc.verbatim "#pragma my var={} property" args %arg : f32
35+
// CHECK-NEXT: #pragma my var=[[V0]] property
36+
37+
emitc.verbatim "#pragma my2 var={} property" args %a : !emitc.array<3x7xi32>
38+
// CHECK-NEXT: #pragma my2 var=[[A]] property
39+
40+
emitc.return
41+
}

0 commit comments

Comments
 (0)