@@ -19,6 +19,7 @@ include "mlir/Dialect/EmitC/IR/EmitCTypes.td"
19
19
include "mlir/Interfaces/CastInterfaces.td"
20
20
include "mlir/Interfaces/ControlFlowInterfaces.td"
21
21
include "mlir/Interfaces/SideEffectInterfaces.td"
22
+ include "mlir/IR/RegionKindInterface.td"
22
23
23
24
//===----------------------------------------------------------------------===//
24
25
// EmitC op definitions
@@ -247,6 +248,83 @@ def EmitC_DivOp : EmitC_BinaryOp<"div", []> {
247
248
let results = (outs FloatIntegerIndexOrOpaqueType);
248
249
}
249
250
251
+ def EmitC_ExpressionOp : EmitC_Op<"expression",
252
+ [HasOnlyGraphRegion, SingleBlockImplicitTerminator<"emitc::YieldOp">,
253
+ NoRegionArguments]> {
254
+ let summary = "Expression operation";
255
+ let description = [{
256
+ The `expression` operation returns a single SSA value which is yielded by
257
+ its single-basic-block region. The operation doesn't take any arguments.
258
+
259
+ As the operation is to be emitted as a C expression, the operations within
260
+ its body must form a single Def-Use tree of emitc ops whose result is
261
+ yielded by a terminating `yield`.
262
+
263
+ Example:
264
+
265
+ ```mlir
266
+ %r = emitc.expression : () -> i32 {
267
+ %0 = emitc.add %a, %b : (i32, i32) -> i32
268
+ %1 = emitc.call "foo"(%0) : () -> i32
269
+ %2 = emitc.add %c, %d : (i32, i32) -> i32
270
+ %3 = emitc.mul %1, %2 : (i32, i32) -> i32
271
+ yield %3
272
+ }
273
+ ```
274
+
275
+ May be emitted as
276
+
277
+ ```c++
278
+ int32_t v7 = foo(v1 + v2) * (v3 + v4);
279
+ ```
280
+
281
+ The operations allowed within expression body are emitc.add, emitc.apply,
282
+ emitc.call, emitc.cast, emitc.cmp, emitc.div, emitc.mul, emitc.rem and
283
+ emitc.sub.
284
+
285
+ When specified, the optional `do_not_inline` indicates that the expression is
286
+ to be emitted as seen above, i.e. as the rhs of an EmitC SSA value
287
+ definition. Otherwise, the expression may be emitted inline, i.e. directly
288
+ at its use.
289
+ }];
290
+
291
+ let arguments = (ins UnitAttr:$do_not_inline);
292
+ let results = (outs AnyType:$result);
293
+ let regions = (region SizedRegion<1>:$region);
294
+
295
+ let hasVerifier = 1;
296
+ let assemblyFormat = "attr-dict (`noinline` $do_not_inline^)? `:` type($result) $region";
297
+
298
+ let extraClassDeclaration = [{
299
+ static bool isCExpression(Operation &op) {
300
+ return isa<emitc::AddOp, emitc::ApplyOp, emitc::CallOpaqueOp,
301
+ emitc::CastOp, emitc::CmpOp, emitc::DivOp, emitc::MulOp,
302
+ emitc::RemOp, emitc::SubOp>(op);
303
+ }
304
+ bool hasSideEffects() {
305
+ auto predicate = [](Operation &op) {
306
+ assert(isCExpression(op) && "Expected a C expression");
307
+ // Conservatively assume calls to read and write memory.
308
+ if (isa<emitc::CallOpaqueOp>(op))
309
+ return true;
310
+ // De-referencing reads modifiable memory, address-taking has no
311
+ // side-effect.
312
+ auto applyOp = dyn_cast<emitc::ApplyOp>(op);
313
+ if (applyOp)
314
+ return applyOp.getApplicableOperator() == "*";
315
+ // Any operation using variables is assumed to have a side effect of
316
+ // reading memory mutable by emitc::assign ops.
317
+ return llvm::any_of(op.getOperands(), [](Value operand) {
318
+ Operation *def = operand.getDefiningOp();
319
+ return def && isa<emitc::VariableOp>(def);
320
+ });
321
+ };
322
+ return llvm::any_of(getRegion().front().without_terminator(), predicate);
323
+ };
324
+ Operation *getRootOp();
325
+ }];
326
+ }
327
+
250
328
def EmitC_ForOp : EmitC_Op<"for",
251
329
[AllTypesMatch<["lowerBound", "upperBound", "step"]>,
252
330
SingleBlockImplicitTerminator<"emitc::YieldOp">,
@@ -494,18 +572,24 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
494
572
}
495
573
496
574
def EmitC_YieldOp : EmitC_Op<"yield",
497
- [Pure, Terminator, ParentOneOf<["IfOp", "ForOp"]>]> {
575
+ [Pure, Terminator, ParentOneOf<["ExpressionOp", " IfOp", "ForOp"]>]> {
498
576
let summary = "block termination operation";
499
577
let description = [{
500
- "yield" terminates blocks within EmitC control-flow operations. Since
501
- control-flow constructs in C do not return values, this operation doesn't
502
- take any arguments.
578
+ "yield" terminates its parent EmitC op's region, optionally yielding
579
+ an SSA value. The semantics of how the values are yielded is defined by the
580
+ parent operation.
581
+ If "yield" has an operand, the operand must match the parent operation's
582
+ result. If the parent operation defines no values, then the "emitc.yield"
583
+ may be left out in the custom syntax and the builders will insert one
584
+ implicitly. Otherwise, it has to be present in the syntax to indicate which
585
+ value is yielded.
503
586
}];
504
587
505
- let arguments = (ins);
588
+ let arguments = (ins Optional<AnyType>:$result );
506
589
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
507
590
508
- let assemblyFormat = [{ attr-dict }];
591
+ let hasVerifier = 1;
592
+ let assemblyFormat = [{ attr-dict ($result^ `:` type($result))? }];
509
593
}
510
594
511
595
def EmitC_IfOp : EmitC_Op<"if",
0 commit comments