Skip to content

Commit 2efc3aa

Browse files
authored
Add concat operation (#63)
Signed-off-by: Vladimir Shiryaev <[email protected]>
1 parent 797b447 commit 2efc3aa

File tree

5 files changed

+293
-0
lines changed

5 files changed

+293
-0
lines changed

include/p4mlir/Dialect/P4HIR/P4HIR_Ops.td

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,47 @@ def BinOp : P4HIR_Op<"binop", [Pure,
294294
let hasVerifier = 0;
295295
}
296296

297+
def ConcatOp : P4HIR_Op<"concat", [Pure]> {
298+
299+
let summary = "Concatenation of bit-strings and/or fixed-width signed integers";
300+
let description = [{
301+
`p4hir.concat` performs concatenation of bit-strings and/or fixed-width signed integers.
302+
303+
The two operands must be either `p4hir.bit<N>` or `p4hir.int<N>`, and they can be of different signedness and width.
304+
The result has the same signedness as the left operand and the width equal to the sum of the two operands' width.
305+
In concatenation, the left operand is placed as the most significant bits.
306+
307+
```mlir
308+
%0 = p4hir.const #p4hir.int<3> : !p4hir.bit<5>
309+
%1 = p4hir.const #p4hir.int<4> : !p4hir.int<10>
310+
%2 = p4hir.concat(%0 : !p4hir.bit<5>, %0 : !p4hir.bit<5>) : !p4hir.bit<10>
311+
%3 = p4hir.concat(%1 : !p4hir.int<10>, %1 : !p4hir.int<10>) : !p4hir.int<20>
312+
%4 = p4hir.concat(%0: !p4hir.bit<5>, %1 : !p4hir.int<10>) : !p4hir.bit<15>
313+
%5 = p4hir.concat(%1 : !p4hir.int<5>, %0 : !p4hir.bit<10>) : !p4hir.int<15>
314+
```
315+
}];
316+
317+
let results = (outs BitsType:$result);
318+
let arguments = (ins BitsType:$lhs, BitsType:$rhs);
319+
320+
// Custom builder to infer the result type with the proper width
321+
let builders = [
322+
OpBuilder<(ins "::mlir::Value":$lhs, "::mlir::Value":$rhs), [{
323+
auto lhsBits = lhs.getType().cast<BitsType>();
324+
auto rhsBits = rhs.getType().cast<BitsType>();
325+
auto resultWidth = lhsBits.getWidth() + rhsBits.getWidth();
326+
auto resultType = BitsType::get($_builder.getContext(), resultWidth, lhsBits.isSigned());
327+
build($_builder, $_state, resultType, lhs, rhs);
328+
}]>
329+
];
330+
331+
let hasVerifier = 1;
332+
333+
let assemblyFormat = [{
334+
`(` $lhs `:` type($lhs) `,` $rhs `:` type($rhs) `)` `:` type($result) attr-dict
335+
}];
336+
}
337+
297338
def CmpOpKind_LT : I32EnumAttrCase<"Lt", 1, "lt">;
298339
def CmpOpKind_LE : I32EnumAttrCase<"Le", 2, "le">;
299340
def CmpOpKind_GT : I32EnumAttrCase<"Gt", 3, "gt">;

lib/Dialect/P4HIR/P4HIR_Ops.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,27 @@ void P4HIR::BinOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
115115
setNameFn(getResult(), stringifyEnum(getKind()));
116116
}
117117

118+
//===----------------------------------------------------------------------===//
119+
// ConcatOp
120+
//===----------------------------------------------------------------------===//
121+
122+
LogicalResult P4HIR::ConcatOp::verify() {
123+
auto lhsType = cast<BitsType>(getOperand(0).getType());
124+
auto rhsType = cast<BitsType>(getOperand(1).getType());
125+
auto resultType = cast<BitsType>(getResult().getType());
126+
127+
auto expectedWidth = lhsType.getWidth() + rhsType.getWidth();
128+
if (resultType.getWidth() != expectedWidth)
129+
return emitOpError() << "the resulting width of a concatenation operation must equal the "
130+
"sum of the operand widths";
131+
132+
if (resultType.isSigned() != lhsType.isSigned())
133+
return emitOpError() << "the signedness of the concatenation result must match the "
134+
"signedness of the left-hand side operand";
135+
136+
return success();
137+
}
138+
118139
//===----------------------------------------------------------------------===//
119140
// CmpOp
120141
//===----------------------------------------------------------------------===//

test/Dialect/P4HIR/concat.mlir

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: p4mlir-opt %s | FileCheck %s
2+
3+
// No need to check stuff. If it parses, it's fine.
4+
// CHECK: module
5+
module {
6+
%bit5 = p4hir.const #p4hir.int<2> : !p4hir.bit<5>
7+
%bit10 = p4hir.const #p4hir.int<4> : !p4hir.bit<10>
8+
%int5 = p4hir.const #p4hir.int<6> : !p4hir.int<5>
9+
%int10 = p4hir.const #p4hir.int<8> : !p4hir.int<10>
10+
11+
%0 = p4hir.concat(%bit5 : !p4hir.bit<5>, %bit5 : !p4hir.bit<5>) : !p4hir.bit<10>
12+
%1 = p4hir.concat(%bit5 : !p4hir.bit<5>, %bit10 : !p4hir.bit<10>) : !p4hir.bit<15>
13+
14+
%2 = p4hir.concat(%int5 : !p4hir.int<5>, %int5 : !p4hir.int<5>) : !p4hir.int<10>
15+
%3 = p4hir.concat(%int5 : !p4hir.int<5>, %int10 : !p4hir.int<10>) : !p4hir.int<15>
16+
17+
%4 = p4hir.concat(%bit5 : !p4hir.bit<5>, %int5 : !p4hir.int<5>) : !p4hir.bit<10>
18+
%5 = p4hir.concat(%bit5 : !p4hir.bit<5>, %int10 : !p4hir.int<10>) : !p4hir.bit<15>
19+
20+
%6 = p4hir.concat(%int5 : !p4hir.int<5>, %bit5 : !p4hir.bit<5>) : !p4hir.int<10>
21+
%7 = p4hir.concat(%int5 : !p4hir.int<5>, %bit10 : !p4hir.bit<10>) : !p4hir.int<15>
22+
}

test/Translate/Ops/concat.p4

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// RUN: p4mlir-translate --typeinference-only %s | FileCheck %s
2+
3+
// NOTE: Assertions have been autogenerated by utils/generate-test-checks.py
4+
5+
action concat_bit5_and_bit5() {
6+
bit<5> lhs = 1;
7+
bit<5> rhs = 2;
8+
bit<10> r = lhs ++ rhs;
9+
}
10+
11+
action concat_bit5_and_bit10() {
12+
bit<5> lhs = 1;
13+
bit<10> rhs = 2;
14+
bit<15> result = lhs ++ rhs;
15+
}
16+
17+
action concat_int5_and_int5() {
18+
int<5> lhs = 1;
19+
int<5> rhs = 2;
20+
int<10> result = lhs ++ rhs;
21+
}
22+
23+
action concat_int5_and_int10() {
24+
int<5> lhs = 1;
25+
int<10> rhs = 2;
26+
int<15> result = lhs ++ rhs;
27+
}
28+
29+
action concat_bit5_and_int5() {
30+
bit<5> lhs = 1;
31+
int<5> rhs = 2;
32+
bit<10> result = lhs ++ rhs;
33+
}
34+
35+
action concat_bit5_and_int10() {
36+
bit<5> lhs = 1;
37+
int<10> rhs = 2;
38+
bit<15> result = lhs ++ rhs;
39+
}
40+
41+
action concat_int5_and_bit5() {
42+
int<5> lhs = 1;
43+
bit<5> rhs = 2;
44+
int<10> result = lhs ++ rhs;
45+
}
46+
47+
action concat_int5_and_bit10() {
48+
int<5> lhs = 1;
49+
bit<10> rhs = 2;
50+
int<15> result = lhs ++ rhs;
51+
}
52+
53+
// CHECK: #[[$ATTR_0:.+]] = #p4hir.int<1> : !b5i
54+
// CHECK: #[[$ATTR_1:.+]] = #p4hir.int<1> : !i5i
55+
// CHECK: #[[$ATTR_2:.+]] = #p4hir.int<2> : !b10i
56+
// CHECK: #[[$ATTR_3:.+]] = #p4hir.int<2> : !b5i
57+
// CHECK: #[[$ATTR_4:.+]] = #p4hir.int<2> : !i10i
58+
// CHECK: #[[$ATTR_5:.+]] = #p4hir.int<2> : !i5i
59+
60+
// CHECK-LABEL: p4hir.func action @concat_bit5_and_bit5() {
61+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_0]]
62+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !b5i) : !b5i
63+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!b5i>
64+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!b5i>
65+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_3]]
66+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !b5i) : !b5i
67+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!b5i>
68+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!b5i>
69+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!b5i>
70+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!b5i>
71+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !b5i, %[[VAL_7]] : !b5i) : !b10i
72+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["r", init] : <!b10i>
73+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!b10i>
74+
// CHECK: p4hir.return
75+
// CHECK: }
76+
77+
// CHECK-LABEL: p4hir.func action @concat_bit5_and_bit10() {
78+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_0]]
79+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !b5i) : !b5i
80+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!b5i>
81+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!b5i>
82+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_2]]
83+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !b10i) : !b10i
84+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!b10i>
85+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!b10i>
86+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!b5i>
87+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!b10i>
88+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !b5i, %[[VAL_7]] : !b10i) : !b15i
89+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!b15i>
90+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!b15i>
91+
// CHECK: p4hir.return
92+
// CHECK: }
93+
94+
// CHECK-LABEL: p4hir.func action @concat_int5_and_int5() {
95+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_1]]
96+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !i5i) : !i5i
97+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!i5i>
98+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!i5i>
99+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_5]]
100+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !i5i) : !i5i
101+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!i5i>
102+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!i5i>
103+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!i5i>
104+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!i5i>
105+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !i5i, %[[VAL_7]] : !i5i) : !i10i
106+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!i10i>
107+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!i10i>
108+
// CHECK: p4hir.return
109+
// CHECK: }
110+
111+
// CHECK-LABEL: p4hir.func action @concat_int5_and_int10() {
112+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_1]]
113+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !i5i) : !i5i
114+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!i5i>
115+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!i5i>
116+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_4]]
117+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !i10i) : !i10i
118+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!i10i>
119+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!i10i>
120+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!i5i>
121+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!i10i>
122+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !i5i, %[[VAL_7]] : !i10i) : !i15i
123+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!i15i>
124+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!i15i>
125+
// CHECK: p4hir.return
126+
// CHECK: }
127+
128+
// CHECK-LABEL: p4hir.func action @concat_bit5_and_int5() {
129+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_0]]
130+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !b5i) : !b5i
131+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!b5i>
132+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!b5i>
133+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_5]]
134+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !i5i) : !i5i
135+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!i5i>
136+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!i5i>
137+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!b5i>
138+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!i5i>
139+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !b5i, %[[VAL_7]] : !i5i) : !b10i
140+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!b10i>
141+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!b10i>
142+
// CHECK: p4hir.return
143+
// CHECK: }
144+
145+
// CHECK-LABEL: p4hir.func action @concat_bit5_and_int10() {
146+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_0]]
147+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !b5i) : !b5i
148+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!b5i>
149+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!b5i>
150+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_4]]
151+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !i10i) : !i10i
152+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!i10i>
153+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!i10i>
154+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!b5i>
155+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!i10i>
156+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !b5i, %[[VAL_7]] : !i10i) : !b15i
157+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!b15i>
158+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!b15i>
159+
// CHECK: p4hir.return
160+
// CHECK: }
161+
162+
// CHECK-LABEL: p4hir.func action @concat_int5_and_bit5() {
163+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_1]]
164+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !i5i) : !i5i
165+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!i5i>
166+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!i5i>
167+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_3]]
168+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !b5i) : !b5i
169+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!b5i>
170+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!b5i>
171+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!i5i>
172+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!b5i>
173+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !i5i, %[[VAL_7]] : !b5i) : !i10i
174+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!i10i>
175+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!i10i>
176+
// CHECK: p4hir.return
177+
// CHECK: }
178+
179+
// CHECK-LABEL: p4hir.func action @concat_int5_and_bit10() {
180+
// CHECK: %[[VAL_0:.*]] = p4hir.const #[[$ATTR_1]]
181+
// CHECK: %[[VAL_1:.*]] = p4hir.cast(%[[VAL_0]] : !i5i) : !i5i
182+
// CHECK: %[[VAL_2:.*]] = p4hir.variable ["lhs", init] : <!i5i>
183+
// CHECK: p4hir.assign %[[VAL_1]], %[[VAL_2]] : <!i5i>
184+
// CHECK: %[[VAL_3:.*]] = p4hir.const #[[$ATTR_2]]
185+
// CHECK: %[[VAL_4:.*]] = p4hir.cast(%[[VAL_3]] : !b10i) : !b10i
186+
// CHECK: %[[VAL_5:.*]] = p4hir.variable ["rhs", init] : <!b10i>
187+
// CHECK: p4hir.assign %[[VAL_4]], %[[VAL_5]] : <!b10i>
188+
// CHECK: %[[VAL_6:.*]] = p4hir.read %[[VAL_2]] : <!i5i>
189+
// CHECK: %[[VAL_7:.*]] = p4hir.read %[[VAL_5]] : <!b10i>
190+
// CHECK: %[[VAL_8:.*]] = p4hir.concat(%[[VAL_6]] : !i5i, %[[VAL_7]] : !b10i) : !i15i
191+
// CHECK: %[[VAL_9:.*]] = p4hir.variable ["result", init] : <!i15i>
192+
// CHECK: p4hir.assign %[[VAL_8]], %[[VAL_9]] : <!i15i>
193+
// CHECK: p4hir.return
194+
// CHECK: }

tools/p4mlir-translate/translate.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ class P4HIRConverter : public P4::Inspector, public P4::ResolutionContext {
337337
HANDLE_IN_POSTORDER(BAnd)
338338
HANDLE_IN_POSTORDER(BXor)
339339

340+
// Concat
341+
HANDLE_IN_POSTORDER(Concat)
342+
340343
// Comparisons
341344
HANDLE_IN_POSTORDER(Equ)
342345
HANDLE_IN_POSTORDER(Neq)
@@ -366,6 +369,7 @@ class P4HIRConverter : public P4::Inspector, public P4::ResolutionContext {
366369

367370
mlir::Value emitUnOp(const P4::IR::Operation_Unary *unop, P4HIR::UnaryOpKind kind);
368371
mlir::Value emitBinOp(const P4::IR::Operation_Binary *binop, P4HIR::BinOpKind kind);
372+
mlir::Value emitConcatOp(const P4::IR::Concat *concatop);
369373
mlir::Value emitCmp(const P4::IR::Operation_Relation *relop, P4HIR::CmpOpKind kind);
370374
};
371375

@@ -588,6 +592,12 @@ mlir::Value P4HIRConverter::emitBinOp(const P4::IR::Operation_Binary *binop,
588592
return builder.create<P4HIR::BinOp>(getLoc(builder, binop), kind, getValue(binop->left),
589593
getValue(binop->right));
590594
}
595+
596+
mlir::Value P4HIRConverter::emitConcatOp(const P4::IR::Concat *concatop) {
597+
return builder.create<P4HIR::ConcatOp>(getLoc(builder, concatop), getValue(concatop->left),
598+
getValue(concatop->right));
599+
}
600+
591601
mlir::Value P4HIRConverter::emitCmp(const P4::IR::Operation_Relation *relop,
592602
P4HIR::CmpOpKind kind) {
593603
return builder.create<P4HIR::CmpOp>(getLoc(builder, relop), kind, getValue(relop->left),
@@ -626,6 +636,11 @@ CONVERT_BINOP(BXor, Xor);
626636

627637
#undef CONVERT_BINOP
628638

639+
void P4HIRConverter::postorder(const P4::IR::Concat *concat) {
640+
ConversionTracer trace("Converting ", concat);
641+
setValue(concat, emitConcatOp(concat));
642+
}
643+
629644
#define CONVERT_CMP(Node, Kind) \
630645
void P4HIRConverter::postorder(const P4::IR::Node *node) { \
631646
ConversionTracer trace("Converting ", node); \

0 commit comments

Comments
 (0)