Skip to content

Commit 0ec223f

Browse files
committed
[mlir][emitc] Deferred emission as expressions
The translator currently implements deferred emission for certain ops. Like expressions, these ops are emitted as part of their users but unlike expressions, this is mandatory. Besides complicating the code with a second inlining mechanism, deferred emission's inlining is limited as it's not recursive. This patch extends EmitC's expressions to deferred emission ops by (a) marking them as CExpressions, (b) extending expression interface to mark ops as always-inline and (c) support inlining of always-inline CExpressions even when not packed of an `emitc.expression` op, retaining current behavior.
1 parent a9e6f90 commit 0ec223f

File tree

7 files changed

+610
-164
lines changed

7 files changed

+610
-164
lines changed

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

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ def EmitC_ExpressionOp
505505

506506
let arguments = (ins Variadic<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$defs,
507507
UnitAttr:$do_not_inline);
508-
let results = (outs EmitCType:$result);
508+
let results = (outs AnyTypeOf<[EmitCType, EmitC_LValueType]>:$result);
509509
let regions = (region SizedRegion<1>:$region);
510510

511511
let hasVerifier = 1;
@@ -871,7 +871,7 @@ def EmitC_IncludeOp
871871
let hasCustomAssemblyFormat = 1;
872872
}
873873

874-
def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> {
874+
def EmitC_LiteralOp : EmitC_Op<"literal", [Pure, CExpressionInterface]> {
875875
let summary = "Literal operation";
876876
let description = [{
877877
The `emitc.literal` operation produces an SSA value equal to some constant
@@ -894,6 +894,15 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> {
894894

895895
let hasVerifier = 1;
896896
let assemblyFormat = "$value attr-dict `:` type($result)";
897+
898+
let extraClassDeclaration = [{
899+
bool hasSideEffects() {
900+
return false;
901+
}
902+
bool alwaysInline() {
903+
return true; // C doesn't support variable references.
904+
}
905+
}];
897906
}
898907

899908
def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> {
@@ -1060,7 +1069,7 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", []> {
10601069
let hasVerifier = 1;
10611070
}
10621071

1063-
def EmitC_MemberOp : EmitC_Op<"member"> {
1072+
def EmitC_MemberOp : EmitC_Op<"member", [CExpressionInterface]> {
10641073
let summary = "Member operation";
10651074
let description = [{
10661075
With the `emitc.member` operation the member access operator `.` can be
@@ -1081,9 +1090,18 @@ def EmitC_MemberOp : EmitC_Op<"member"> {
10811090
EmitC_LValueOf<[EmitC_OpaqueType]>:$operand
10821091
);
10831092
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>);
1093+
1094+
let extraClassDeclaration = [{
1095+
bool hasSideEffects() {
1096+
return false;
1097+
}
1098+
bool alwaysInline() {
1099+
return true; // C doesn't support variable references.
1100+
}
1101+
}];
10841102
}
10851103

1086-
def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
1104+
def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr", [CExpressionInterface]> {
10871105
let summary = "Member of pointer operation";
10881106
let description = [{
10891107
With the `emitc.member_of_ptr` operation the member access operator `->`
@@ -1106,6 +1124,15 @@ def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
11061124
EmitC_LValueOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand
11071125
);
11081126
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>);
1127+
1128+
let extraClassDeclaration = [{
1129+
bool hasSideEffects() {
1130+
return false;
1131+
}
1132+
bool alwaysInline() {
1133+
return true; // C doesn't support variable references.
1134+
}
1135+
}];
11091136
}
11101137

11111138
def EmitC_ConditionalOp : EmitC_Op<"conditional",
@@ -1275,8 +1302,10 @@ def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
12751302
let hasVerifier = 1;
12761303
}
12771304

1278-
def EmitC_GetGlobalOp : EmitC_Op<"get_global",
1279-
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
1305+
def EmitC_GetGlobalOp
1306+
: EmitC_Op<"get_global", [Pure,
1307+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
1308+
CExpressionInterface]> {
12801309
let summary = "Obtain access to a global variable";
12811310
let description = [{
12821311
The `emitc.get_global` operation retrieves the lvalue of a
@@ -1294,6 +1323,15 @@ def EmitC_GetGlobalOp : EmitC_Op<"get_global",
12941323
let arguments = (ins FlatSymbolRefAttr:$name);
12951324
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result);
12961325
let assemblyFormat = "$name `:` type($result) attr-dict";
1326+
1327+
let extraClassDeclaration = [{
1328+
bool hasSideEffects() {
1329+
return false;
1330+
}
1331+
bool alwaysInline() {
1332+
return true; // C doesn't support variable references.
1333+
}
1334+
}];
12971335
}
12981336

12991337
def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
@@ -1404,7 +1442,8 @@ def EmitC_YieldOp : EmitC_Op<"yield",
14041442
value is yielded.
14051443
}];
14061444

1407-
let arguments = (ins Optional<EmitCType>:$result);
1445+
let arguments =
1446+
(ins Optional<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$result);
14081447
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
14091448

14101449
let hasVerifier = 1;
@@ -1475,7 +1514,7 @@ def EmitC_IfOp : EmitC_Op<"if",
14751514
let hasCustomAssemblyFormat = 1;
14761515
}
14771516

1478-
def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
1517+
def EmitC_SubscriptOp : EmitC_Op<"subscript", [CExpressionInterface]> {
14791518
let summary = "Subscript operation";
14801519
let description = [{
14811520
With the `emitc.subscript` operation the subscript operator `[]` can be applied
@@ -1523,6 +1562,15 @@ def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
15231562

15241563
let hasVerifier = 1;
15251564
let assemblyFormat = "$value `[` $indices `]` attr-dict `:` functional-type(operands, results)";
1565+
1566+
let extraClassDeclaration = [{
1567+
bool hasSideEffects() {
1568+
return false;
1569+
}
1570+
bool alwaysInline() {
1571+
return true; // C doesn't support variable references.
1572+
}
1573+
}];
15261574
}
15271575

15281576
def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
@@ -1703,8 +1751,9 @@ def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
17031751
}
17041752

17051753
def EmitC_GetFieldOp
1706-
: EmitC_Op<"get_field", [Pure, DeclareOpInterfaceMethods<
1707-
SymbolUserOpInterface>]> {
1754+
: EmitC_Op<"get_field", [Pure,
1755+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
1756+
CExpressionInterface]> {
17081757
let summary = "Obtain access to a field within a class instance";
17091758
let description = [{
17101759
The `emitc.get_field` operation retrieves the lvalue of a
@@ -1721,6 +1770,15 @@ def EmitC_GetFieldOp
17211770
let results = (outs EmitCType:$result);
17221771
let assemblyFormat = "$field_name `:` type($result) attr-dict";
17231772
let hasVerifier = 1;
1773+
1774+
let extraClassDeclaration = [{
1775+
bool hasSideEffects() {
1776+
return false;
1777+
}
1778+
bool alwaysInline() {
1779+
return true; // C doesn't support variable references.
1780+
}
1781+
}];
17241782
}
17251783

17261784
def EmitC_DoOp : EmitC_Op<"do",

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def CExpressionInterface : OpInterface<"CExpressionInterface"> {
2121
}];
2222

2323
let cppNamespace = "::mlir::emitc";
24-
let methods = [
25-
InterfaceMethod<[{
24+
let methods =
25+
[InterfaceMethod<[{
2626
Check whether operation has side effects that may affect the expression
2727
evaluation.
2828

@@ -38,10 +38,29 @@ def CExpressionInterface : OpInterface<"CExpressionInterface"> {
3838
};
3939
```
4040
}],
41-
"bool", "hasSideEffects", (ins), /*methodBody=*/[{}],
42-
/*defaultImplementation=*/[{
41+
"bool", "hasSideEffects", (ins), /*methodBody=*/[{}],
42+
/*defaultImplementation=*/[{
4343
return true;
4444
}]>,
45+
InterfaceMethod<[{
46+
Check whether operation must be inlined into all its users.
47+
48+
By default operation is not marked as always inlined.
49+
50+
```c++
51+
class ConcreteOp ... {
52+
public:
53+
bool alwaysInline() {
54+
// That way we can override the default implementation.
55+
return true;
56+
}
57+
};
58+
```
59+
}],
60+
"bool", "alwaysInline", (ins), /*methodBody=*/[{}],
61+
/*defaultImplementation=*/[{
62+
return false;
63+
}]>,
4564
];
4665
}
4766

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,8 @@ LogicalResult ExpressionOp::verify() {
483483
Operation *op = worklist.back();
484484
worklist.pop_back();
485485
if (visited.contains(op)) {
486-
if (cast<CExpressionInterface>(op).hasSideEffects())
486+
auto cExpr = cast<CExpressionInterface>(op);
487+
if (!cExpr.alwaysInline() && cExpr.hasSideEffects())
487488
return emitOpError(
488489
"requires exactly one use for operations with side effects");
489490
}
@@ -494,6 +495,12 @@ LogicalResult ExpressionOp::verify() {
494495
}
495496
}
496497

498+
if (getDoNotInline() &&
499+
cast<emitc::CExpressionInterface>(rootOp).alwaysInline()) {
500+
return emitOpError("root operation must be inlined but expression is marked"
501+
" do-not-inline");
502+
}
503+
497504
return success();
498505
}
499506

@@ -982,6 +989,10 @@ LogicalResult emitc::YieldOp::verify() {
982989
if (!isa<DoOp>(containingOp) && !result && containingOp->getNumResults() != 0)
983990
return emitOpError() << "does not yield a value to be returned by parent";
984991

992+
if (result && isa<emitc::LValueType>(result.getType()) &&
993+
!isa<ExpressionOp>(containingOp))
994+
return emitOpError() << "yielding lvalues is not supported for this op";
995+
985996
return success();
986997
}
987998

0 commit comments

Comments
 (0)