Skip to content

Commit 61c2975

Browse files
committed
[mlir][emitc] Add 'emitc.while' and 'emitc.do' ops to the dialect
This MR adds: - 'emitc::WhileOp' and 'emitc::DoOp' to the EmitC dialect - Emission of the corresponding ops in the CppEmitter - Conversion from the SCF dialect to the EmitC dialect for the ops - Corresponding tests
1 parent b2379bd commit 61c2975

File tree

9 files changed

+1214
-17
lines changed

9 files changed

+1214
-17
lines changed

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

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,7 +1345,7 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
13451345
}
13461346

13471347
def EmitC_YieldOp : EmitC_Op<"yield",
1348-
[Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> {
1348+
[Pure, Terminator, ParentOneOf<["DoOp", "ExpressionOp", "ForOp", "IfOp", "SwitchOp", "WhileOp"]>]> {
13491349
let summary = "Block termination operation";
13501350
let description = [{
13511351
The `emitc.yield` terminates its parent EmitC op's region, optionally yielding
@@ -1572,4 +1572,156 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
15721572
let hasVerifier = 1;
15731573
}
15741574

1575+
def EmitC_WhileOp : EmitC_Op<"while",
1576+
[HasOnlyGraphRegion, RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
1577+
let summary = "While operation";
1578+
let description = [{
1579+
The `emitc.while` operation represents a C/C++ while loop construct that
1580+
repeatedly executes a body region as long as a condition region evaluates to
1581+
true. The operation has two regions:
1582+
1583+
1. A condition region that must yield a boolean value (i1)
1584+
2. A body region that contains the loop body
1585+
1586+
The condition region is evaluated before each iteration. If it yields true,
1587+
the body region is executed. The loop terminates when the condition yields
1588+
false. The condition region must contain exactly one block that terminates
1589+
with an `emitc.yield` operation producing an i1 value.
1590+
1591+
Example:
1592+
1593+
```mlir
1594+
emitc.func @foo(%arg0 : !emitc.ptr<i32>) {
1595+
%var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
1596+
%0 = emitc.literal "10" : i32
1597+
%1 = emitc.literal "1" : i32
1598+
1599+
emitc.while {
1600+
%var_load = load %var : <i32>
1601+
%res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
1602+
emitc.yield %res : i1
1603+
} do {
1604+
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
1605+
%var_load = load %var : <i32>
1606+
%tmp_add = add %var_load, %1 : (i32, i32) -> i32
1607+
"emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
1608+
}
1609+
1610+
return
1611+
}
1612+
```
1613+
1614+
```c++
1615+
// Code emitted for the operation above.
1616+
void foo(int32_t* v1) {
1617+
int32_t v2 = 0;
1618+
while (v2 <= 10) {
1619+
printf("%d", *v1);
1620+
int32_t v3 = v2;
1621+
int32_t v4 = v3 + 1;
1622+
v2 = v4;
1623+
}
1624+
return;
1625+
}
1626+
```
1627+
}];
1628+
1629+
let arguments = (ins);
1630+
let results = (outs);
1631+
let regions = (region MaxSizedRegion<1>:$conditionRegion,
1632+
MaxSizedRegion<1>:$bodyRegion);
1633+
1634+
let hasCustomAssemblyFormat = 1;
1635+
let hasVerifier = 1;
1636+
1637+
let extraClassDeclaration = [{
1638+
Operation *getRootOp();
1639+
1640+
//===------------------------------------------------------------------===//
1641+
// OpAsmOpInterface Methods
1642+
//===------------------------------------------------------------------===//
1643+
1644+
/// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
1645+
static ::llvm::StringRef getDefaultDialect() {
1646+
return "emitc";
1647+
}
1648+
}];
1649+
}
1650+
1651+
def EmitC_DoOp : EmitC_Op<"do",
1652+
[RecursiveMemoryEffects, NoRegionArguments, OpAsmOpInterface, NoTerminator]> {
1653+
let summary = "Do-while operation";
1654+
let description = [{
1655+
The `emitc.do` operation represents a C/C++ do-while loop construct that
1656+
executes a body region first and then repeatedly executes it as long as a
1657+
condition region evaluates to true. The operation has two regions:
1658+
1659+
1. A body region that contains the loop body
1660+
2. A condition region that must yield a boolean value (i1)
1661+
1662+
Unlike a while loop, the body region is executed before the first evaluation
1663+
of the condition. The loop terminates when the condition yields false. The
1664+
condition region must contain exactly one block that terminates with an
1665+
`emitc.yield` operation producing an i1 value.
1666+
1667+
Example:
1668+
1669+
```mlir
1670+
emitc.func @foo(%arg0 : !emitc.ptr<i32>) {
1671+
%var = "emitc.variable"() <{value = 0 : i32}> : () -> !emitc.lvalue<i32>
1672+
%0 = emitc.literal "10" : i32
1673+
%1 = emitc.literal "1" : i32
1674+
1675+
emitc.do {
1676+
emitc.verbatim "printf(\"%d\", *{});" args %arg0 : !emitc.ptr<i32>
1677+
%var_load = load %var : <i32>
1678+
%tmp_add = add %var_load, %1 : (i32, i32) -> i32
1679+
"emitc.assign"(%var, %tmp_add) : (!emitc.lvalue<i32>, i32) -> ()
1680+
} while {
1681+
%var_load = load %var : <i32>
1682+
%res = emitc.cmp le, %var_load, %0 : (i32, i32) -> i1
1683+
emitc.yield %res : i1
1684+
}
1685+
1686+
return
1687+
}
1688+
```
1689+
1690+
```c++
1691+
// Code emitted for the operation above.
1692+
void foo(int32_t* v1) {
1693+
int32_t v2 = 0;
1694+
do {
1695+
printf("%d", *v1);
1696+
int32_t v3 = v2;
1697+
int32_t v4 = v3 + 1;
1698+
v2 = v4;
1699+
} while (v2 <= 10);
1700+
return;
1701+
}
1702+
```
1703+
}];
1704+
1705+
let arguments = (ins);
1706+
let results = (outs);
1707+
let regions = (region MaxSizedRegion<1>:$bodyRegion,
1708+
MaxSizedRegion<1>:$conditionRegion);
1709+
1710+
let hasCustomAssemblyFormat = 1;
1711+
let hasVerifier = 1;
1712+
1713+
let extraClassDeclaration = [{
1714+
Operation *getRootOp();
1715+
1716+
//===------------------------------------------------------------------===//
1717+
// OpAsmOpInterface Methods
1718+
//===------------------------------------------------------------------===//
1719+
1720+
/// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
1721+
static ::llvm::StringRef getDefaultDialect() {
1722+
return "emitc";
1723+
}
1724+
}];
1725+
}
1726+
15751727
#endif // MLIR_DIALECT_EMITC_IR_EMITC

0 commit comments

Comments
 (0)