Skip to content

Commit 9268794

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 906f934 commit 9268794

File tree

7 files changed

+612
-160
lines changed

7 files changed

+612
-160
lines changed

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

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

508508
let arguments = (ins Variadic<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$defs,
509509
UnitAttr:$do_not_inline);
510-
let results = (outs EmitCType:$result);
510+
let results = (outs AnyTypeOf<[EmitCType, EmitC_LValueType]>:$result);
511511
let regions = (region SizedRegion<1>:$region);
512512

513513
let hasVerifier = 1;
@@ -873,7 +873,7 @@ def EmitC_IncludeOp
873873
let hasCustomAssemblyFormat = 1;
874874
}
875875

876-
def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> {
876+
def EmitC_LiteralOp : EmitC_Op<"literal", [Pure, CExpressionInterface]> {
877877
let summary = "Literal operation";
878878
let description = [{
879879
The `emitc.literal` operation produces an SSA value equal to some constant
@@ -896,6 +896,15 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> {
896896

897897
let hasVerifier = 1;
898898
let assemblyFormat = "$value attr-dict `:` type($result)";
899+
900+
let extraClassDeclaration = [{
901+
bool hasSideEffects() {
902+
return false;
903+
}
904+
bool alwaysInline() {
905+
return true; // C doesn't support variable references.
906+
}
907+
}];
899908
}
900909

901910
def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> {
@@ -1062,7 +1071,7 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", []> {
10621071
let hasVerifier = 1;
10631072
}
10641073

1065-
def EmitC_MemberOp : EmitC_Op<"member"> {
1074+
def EmitC_MemberOp : EmitC_Op<"member", [CExpressionInterface]> {
10661075
let summary = "Member operation";
10671076
let description = [{
10681077
With the `emitc.member` operation the member access operator `.` can be
@@ -1083,9 +1092,18 @@ def EmitC_MemberOp : EmitC_Op<"member"> {
10831092
EmitC_LValueOf<[EmitC_OpaqueType]>:$operand
10841093
);
10851094
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>);
1095+
1096+
let extraClassDeclaration = [{
1097+
bool hasSideEffects() {
1098+
return false;
1099+
}
1100+
bool alwaysInline() {
1101+
return true; // C doesn't support variable references.
1102+
}
1103+
}];
10861104
}
10871105

1088-
def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
1106+
def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr", [CExpressionInterface]> {
10891107
let summary = "Member of pointer operation";
10901108
let description = [{
10911109
With the `emitc.member_of_ptr` operation the member access operator `->`
@@ -1108,6 +1126,15 @@ def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
11081126
EmitC_LValueOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand
11091127
);
11101128
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>);
1129+
1130+
let extraClassDeclaration = [{
1131+
bool hasSideEffects() {
1132+
return false;
1133+
}
1134+
bool alwaysInline() {
1135+
return true; // C doesn't support variable references.
1136+
}
1137+
}];
11111138
}
11121139

11131140
def EmitC_ConditionalOp : EmitC_Op<"conditional",
@@ -1277,8 +1304,10 @@ def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
12771304
let hasVerifier = 1;
12781305
}
12791306

1280-
def EmitC_GetGlobalOp : EmitC_Op<"get_global",
1281-
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
1307+
def EmitC_GetGlobalOp
1308+
: EmitC_Op<"get_global", [Pure,
1309+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
1310+
CExpressionInterface]> {
12821311
let summary = "Obtain access to a global variable";
12831312
let description = [{
12841313
The `emitc.get_global` operation retrieves the lvalue of a
@@ -1296,6 +1325,15 @@ def EmitC_GetGlobalOp : EmitC_Op<"get_global",
12961325
let arguments = (ins FlatSymbolRefAttr:$name);
12971326
let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result);
12981327
let assemblyFormat = "$name `:` type($result) attr-dict";
1328+
1329+
let extraClassDeclaration = [{
1330+
bool hasSideEffects() {
1331+
return false;
1332+
}
1333+
bool alwaysInline() {
1334+
return true; // C doesn't support variable references.
1335+
}
1336+
}];
12991337
}
13001338

13011339
def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
@@ -1406,7 +1444,8 @@ def EmitC_YieldOp : EmitC_Op<"yield",
14061444
value is yielded.
14071445
}];
14081446

1409-
let arguments = (ins Optional<EmitCType>:$result);
1447+
let arguments =
1448+
(ins Optional<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$result);
14101449
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
14111450

14121451
let hasVerifier = 1;
@@ -1477,7 +1516,7 @@ def EmitC_IfOp : EmitC_Op<"if",
14771516
let hasCustomAssemblyFormat = 1;
14781517
}
14791518

1480-
def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
1519+
def EmitC_SubscriptOp : EmitC_Op<"subscript", [CExpressionInterface]> {
14811520
let summary = "Subscript operation";
14821521
let description = [{
14831522
With the `emitc.subscript` operation the subscript operator `[]` can be applied
@@ -1525,6 +1564,15 @@ def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
15251564

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

15301578
def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
@@ -1707,8 +1755,9 @@ def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
17071755
}
17081756

17091757
def EmitC_GetFieldOp
1710-
: EmitC_Op<"get_field", [Pure, DeclareOpInterfaceMethods<
1711-
SymbolUserOpInterface>]> {
1758+
: EmitC_Op<"get_field", [Pure,
1759+
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
1760+
CExpressionInterface]> {
17121761
let summary = "Obtain access to a field within a class instance";
17131762
let description = [{
17141763
The `emitc.get_field` operation retrieves the lvalue of a
@@ -1725,6 +1774,15 @@ def EmitC_GetFieldOp
17251774
let results = (outs EmitCType:$result);
17261775
let assemblyFormat = "$field_name `:` type($result) attr-dict";
17271776
let hasVerifier = 1;
1777+
1778+
let extraClassDeclaration = [{
1779+
bool hasSideEffects() {
1780+
return false;
1781+
}
1782+
bool alwaysInline() {
1783+
return true; // C doesn't support variable references.
1784+
}
1785+
}];
17281786
}
17291787

17301788
#endif // MLIR_DIALECT_EMITC_IR_EMITC

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,9 +38,28 @@ def CExpressionInterface : OpInterface<"CExpressionInterface"> {
3838
};
3939
```
4040
}],
41-
"bool", "hasSideEffects", (ins), /*methodBody=*/[{}],
42-
/*defaultImplementation=*/[{
41+
"bool", "hasSideEffects", (ins), /*methodBody=*/[{}],
42+
/*defaultImplementation=*/[{
4343
return true;
44+
}]>,
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;
4463
}]>,
4564
];
4665
}

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

@@ -980,6 +987,10 @@ LogicalResult emitc::YieldOp::verify() {
980987
if (!result && containingOp->getNumResults() != 0)
981988
return emitOpError() << "does not yield a value to be returned by parent";
982989

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

0 commit comments

Comments
 (0)