@@ -19,6 +19,7 @@ include "mlir/Dialect/EmitC/IR/EmitCTypes.td"
1919include "mlir/Interfaces/CastInterfaces.td"
2020include "mlir/Interfaces/ControlFlowInterfaces.td"
2121include "mlir/Interfaces/SideEffectInterfaces.td"
22+ include "mlir/IR/RegionKindInterface.td"
2223
2324//===----------------------------------------------------------------------===//
2425// EmitC op definitions
@@ -247,6 +248,83 @@ def EmitC_DivOp : EmitC_BinaryOp<"div", []> {
247248 let results = (outs FloatIntegerIndexOrOpaqueType);
248249}
249250
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+
250328def EmitC_ForOp : EmitC_Op<"for",
251329 [AllTypesMatch<["lowerBound", "upperBound", "step"]>,
252330 SingleBlockImplicitTerminator<"emitc::YieldOp">,
@@ -494,18 +572,24 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
494572}
495573
496574def EmitC_YieldOp : EmitC_Op<"yield",
497- [Pure, Terminator, ParentOneOf<["IfOp", "ForOp"]>]> {
575+ [Pure, Terminator, ParentOneOf<["ExpressionOp", " IfOp", "ForOp"]>]> {
498576 let summary = "block termination operation";
499577 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.
503586 }];
504587
505- let arguments = (ins);
588+ let arguments = (ins Optional<AnyType>:$result );
506589 let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
507590
508- let assemblyFormat = [{ attr-dict }];
591+ let hasVerifier = 1;
592+ let assemblyFormat = [{ attr-dict ($result^ `:` type($result))? }];
509593}
510594
511595def EmitC_IfOp : EmitC_Op<"if",
0 commit comments