Skip to content

Commit f51e5f3

Browse files
authored
[CIR] Upstream initial for-loop support (#132266)
This change adds support for empty for-loops. This will be the infrastructre needed for complete for loops, but that depends on compare operations, which have not been upstreamed yet.
1 parent bd6df0f commit f51e5f3

21 files changed

+846
-14
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
5252
return cir::BoolAttr::get(getContext(), getBoolTy(), state);
5353
}
5454

55+
/// Create a for operation.
56+
cir::ForOp createFor(
57+
mlir::Location loc,
58+
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
59+
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder,
60+
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> stepBuilder) {
61+
return create<cir::ForOp>(loc, condBuilder, bodyBuilder, stepBuilder);
62+
}
63+
5564
mlir::TypedAttr getConstPtrAttr(mlir::Type type, int64_t value) {
5665
auto valueAttr = mlir::IntegerAttr::get(
5766
mlir::IntegerType::get(type.getContext(), 64), value);
@@ -158,6 +167,16 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
158167
return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
159168
size.getQuantity());
160169
}
170+
171+
/// Create a loop condition.
172+
cir::ConditionOp createCondition(mlir::Value condition) {
173+
return create<cir::ConditionOp>(condition.getLoc(), condition);
174+
}
175+
176+
/// Create a yield operation.
177+
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value = {}) {
178+
return create<cir::YieldOp>(loc, value);
179+
}
161180
};
162181

163182
} // namespace cir

clang/include/clang/CIR/Dialect/IR/CIRDialect.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
3030
#include "clang/CIR/Dialect/IR/CIROpsDialect.h.inc"
3131
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
32+
#include "clang/CIR/Interfaces/CIRLoopOpInterface.h"
3233
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
3334

3435
// TableGen'erated files for MLIR dialects require that a macro be defined when

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 162 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ include "clang/CIR/Dialect/IR/CIRTypes.td"
1919
include "clang/CIR/Dialect/IR/CIRAttrs.td"
2020

2121
include "clang/CIR/Interfaces/CIROpInterfaces.td"
22+
include "clang/CIR/Interfaces/CIRLoopOpInterface.td"
2223

2324
include "mlir/IR/BuiltinAttributeInterfaces.td"
2425
include "mlir/IR/EnumAttr.td"
@@ -423,7 +424,7 @@ def StoreOp : CIR_Op<"store", [
423424
// ReturnOp
424425
//===----------------------------------------------------------------------===//
425426

426-
def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>,
427+
def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "ForOp"]>,
427428
Terminator]> {
428429
let summary = "Return from function";
429430
let description = [{
@@ -460,12 +461,57 @@ def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>,
460461
let hasVerifier = 1;
461462
}
462463

464+
//===----------------------------------------------------------------------===//
465+
// ConditionOp
466+
//===----------------------------------------------------------------------===//
467+
468+
def ConditionOp : CIR_Op<"condition", [
469+
Terminator,
470+
DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface,
471+
["getSuccessorRegions"]>
472+
]> {
473+
let summary = "Loop continuation condition.";
474+
let description = [{
475+
The `cir.condition` terminates conditional regions. It takes a single
476+
`cir.bool` operand and, depending on its value, may branch to different
477+
regions:
478+
479+
- When in the `cond` region of a `cir.loop`, it continues the loop
480+
if true, or exits it if false.
481+
- When in the `ready` region of a `cir.await`, it branches to the `resume`
482+
region when true, and to the `suspend` region when false.
483+
484+
Example:
485+
486+
```mlir
487+
cir.loop for(cond : {
488+
cir.condition(%arg0) // Branches to `step` region or exits.
489+
}, step : {
490+
[...]
491+
}) {
492+
[...]
493+
}
494+
495+
cir.await(user, ready : {
496+
cir.condition(%arg0) // Branches to `resume` or `suspend` region.
497+
}, suspend : {
498+
[...]
499+
}, resume : {
500+
[...]
501+
},)
502+
```
503+
}];
504+
let arguments = (ins CIR_BoolType:$condition);
505+
let assemblyFormat = " `(` $condition `)` attr-dict ";
506+
let hasVerifier = 1;
507+
}
508+
463509
//===----------------------------------------------------------------------===//
464510
// YieldOp
465511
//===----------------------------------------------------------------------===//
466512

467513
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
468-
ParentOneOf<["ScopeOp"]>]> {
514+
ParentOneOf<["ScopeOp", "ForOp"]>]> {
469515
let summary = "Represents the default branching behaviour of a region";
470516
let description = [{
471517
The `cir.yield` operation terminates regions on different CIR operations,
@@ -666,6 +712,120 @@ def UnaryOp : CIR_Op<"unary", [Pure, SameOperandsAndResultType]> {
666712
let hasFolder = 1;
667713
}
668714

715+
//===----------------------------------------------------------------------===//
716+
// BrCondOp
717+
//===----------------------------------------------------------------------===//
718+
719+
def BrCondOp : CIR_Op<"brcond",
720+
[DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
721+
Pure, Terminator, AttrSizedOperandSegments]> {
722+
let summary = "Conditional branch";
723+
let description = [{
724+
The `cir.brcond %cond, ^bb0, ^bb1` branches to 'bb0' block in case
725+
%cond (which must be a !cir.bool type) evaluates to true, otherwise
726+
it branches to 'bb1'.
727+
728+
Example:
729+
730+
```mlir
731+
...
732+
cir.brcond %a, ^bb3, ^bb4
733+
^bb3:
734+
cir.return
735+
^bb4:
736+
cir.yield
737+
```
738+
}];
739+
740+
let builders = [
741+
OpBuilder<(ins "mlir::Value":$cond, "mlir::Block *":$destTrue, "mlir::Block *":$destFalse,
742+
CArg<"mlir::ValueRange", "{}">:$destOperandsTrue,
743+
CArg<"mlir::ValueRange", "{}">:$destOperandsFalse), [{
744+
build($_builder, $_state, cond, destOperandsTrue,
745+
destOperandsFalse, destTrue, destFalse);
746+
}]>
747+
];
748+
749+
let arguments = (ins CIR_BoolType:$cond,
750+
Variadic<CIR_AnyType>:$destOperandsTrue,
751+
Variadic<CIR_AnyType>:$destOperandsFalse);
752+
let successors = (successor AnySuccessor:$destTrue, AnySuccessor:$destFalse);
753+
let assemblyFormat = [{
754+
$cond
755+
$destTrue (`(` $destOperandsTrue^ `:` type($destOperandsTrue) `)`)?
756+
`,`
757+
$destFalse (`(` $destOperandsFalse^ `:` type($destOperandsFalse) `)`)?
758+
attr-dict
759+
}];
760+
}
761+
762+
//===----------------------------------------------------------------------===//
763+
// ForOp
764+
//===----------------------------------------------------------------------===//
765+
766+
def ForOp : CIR_Op<"for", [LoopOpInterface, NoRegionArguments]> {
767+
let summary = "C/C++ for loop counterpart";
768+
let description = [{
769+
Represents a C/C++ for loop. It consists of three regions:
770+
771+
- `cond`: single block region with the loop's condition. Should be
772+
terminated with a `cir.condition` operation.
773+
- `body`: contains the loop body and an arbitrary number of blocks.
774+
- `step`: single block region with the loop's step.
775+
776+
Example:
777+
778+
```mlir
779+
cir.for cond {
780+
cir.condition(%val)
781+
} body {
782+
cir.break
783+
^bb2:
784+
cir.yield
785+
} step {
786+
cir.yield
787+
}
788+
```
789+
}];
790+
791+
let regions = (region SizedRegion<1>:$cond,
792+
MinSizedRegion<1>:$body,
793+
SizedRegion<1>:$step);
794+
let assemblyFormat = [{
795+
`:` `cond` $cond
796+
`body` $body
797+
`step` $step
798+
attr-dict
799+
}];
800+
801+
let builders = [
802+
OpBuilder<(ins "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$condBuilder,
803+
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$bodyBuilder,
804+
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$stepBuilder), [{
805+
mlir::OpBuilder::InsertionGuard guard($_builder);
806+
807+
// Build condition region.
808+
$_builder.createBlock($_state.addRegion());
809+
condBuilder($_builder, $_state.location);
810+
811+
// Build body region.
812+
$_builder.createBlock($_state.addRegion());
813+
bodyBuilder($_builder, $_state.location);
814+
815+
// Build step region.
816+
$_builder.createBlock($_state.addRegion());
817+
stepBuilder($_builder, $_state.location);
818+
}]>
819+
];
820+
821+
let extraClassDeclaration = [{
822+
mlir::Region *maybeGetStep() { return &getStep(); }
823+
llvm::SmallVector<mlir::Region *> getRegionsInExecutionOrder() {
824+
return llvm::SmallVector<mlir::Region *, 3>{&getCond(), &getBody(), &getStep()};
825+
}
826+
}];
827+
}
828+
669829
//===----------------------------------------------------------------------===//
670830
// GlobalOp
671831
//===----------------------------------------------------------------------===//
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===---------------------------------------------------------------------===//
8+
//
9+
// Defines the interface to generically handle CIR loop operations.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
14+
#define CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
15+
16+
#include "mlir/IR/BuiltinTypes.h"
17+
#include "mlir/IR/OpDefinition.h"
18+
#include "mlir/IR/Operation.h"
19+
#include "mlir/Interfaces/ControlFlowInterfaces.h"
20+
#include "mlir/Interfaces/LoopLikeInterface.h"
21+
22+
namespace cir {
23+
namespace detail {
24+
25+
/// Verify invariants of the LoopOpInterface.
26+
mlir::LogicalResult verifyLoopOpInterface(::mlir::Operation *op);
27+
28+
} // namespace detail
29+
} // namespace cir
30+
31+
/// Include the tablegen'd interface declarations.
32+
#include "clang/CIR/Interfaces/CIRLoopOpInterface.h.inc"
33+
34+
#endif // CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//===---------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===---------------------------------------------------------------------===//
8+
9+
#ifndef CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE
10+
#define CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE
11+
12+
include "mlir/IR/OpBase.td"
13+
include "mlir/Interfaces/ControlFlowInterfaces.td"
14+
include "mlir/Interfaces/LoopLikeInterface.td"
15+
16+
def LoopOpInterface : OpInterface<"LoopOpInterface", [
17+
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
18+
DeclareOpInterfaceMethods<LoopLikeOpInterface>
19+
]> {
20+
let description = [{
21+
Contains helper functions to query properties and perform transformations
22+
on a loop.
23+
}];
24+
let cppNamespace = "::cir";
25+
26+
let methods = [
27+
InterfaceMethod<[{
28+
Returns the loop's conditional region.
29+
}],
30+
/*retTy=*/"mlir::Region &",
31+
/*methodName=*/"getCond"
32+
>,
33+
InterfaceMethod<[{
34+
Returns the loop's body region.
35+
}],
36+
/*retTy=*/"mlir::Region &",
37+
/*methodName=*/"getBody"
38+
>,
39+
InterfaceMethod<[{
40+
Returns a pointer to the loop's step region or nullptr.
41+
}],
42+
/*retTy=*/"mlir::Region *",
43+
/*methodName=*/"maybeGetStep",
44+
/*args=*/(ins),
45+
/*methodBody=*/"",
46+
/*defaultImplementation=*/"return nullptr;"
47+
>,
48+
InterfaceMethod<[{
49+
Returns the first region to be executed in the loop.
50+
}],
51+
/*retTy=*/"mlir::Region &",
52+
/*methodName=*/"getEntry",
53+
/*args=*/(ins),
54+
/*methodBody=*/"",
55+
/*defaultImplementation=*/"return $_op.getCond();"
56+
>,
57+
InterfaceMethod<[{
58+
Returns a list of regions in order of execution.
59+
}],
60+
/*retTy=*/"llvm::SmallVector<mlir::Region *>",
61+
/*methodName=*/"getRegionsInExecutionOrder",
62+
/*args=*/(ins),
63+
/*methodBody=*/"",
64+
/*defaultImplementation=*/[{
65+
return llvm::SmallVector<mlir::Region *, 2>{&$_op.getRegion(0), &$_op.getRegion(1)};
66+
}]
67+
>,
68+
InterfaceMethod<[{
69+
Recursively walks the body of the loop in pre-order while skipping
70+
nested loops and executing a callback on every other operation.
71+
}],
72+
/*retTy=*/"mlir::WalkResult",
73+
/*methodName=*/"walkBodySkippingNestedLoops",
74+
/*args=*/(ins "::llvm::function_ref<mlir::WalkResult (mlir::Operation *)>":$callback),
75+
/*methodBody=*/"",
76+
/*defaultImplementation=*/[{
77+
return $_op.getBody().template walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
78+
if (mlir::isa<LoopOpInterface>(op))
79+
return mlir::WalkResult::skip();
80+
return callback(op);
81+
});
82+
}]
83+
>
84+
];
85+
86+
let extraClassDeclaration = [{
87+
/// Generic method to retrieve the successors of a LoopOpInterface operation.
88+
static void getLoopOpSuccessorRegions(
89+
::cir::LoopOpInterface op, ::mlir::RegionBranchPoint point,
90+
::mlir::SmallVectorImpl<::mlir::RegionSuccessor> &regions);
91+
}];
92+
93+
let verify = [{
94+
/// Verify invariants of the LoopOpInterface.
95+
return detail::verifyLoopOpInterface($_op);
96+
}];
97+
}
98+
99+
#endif // CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE

clang/include/clang/CIR/Interfaces/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ function(add_clang_mlir_type_interface interface)
2020
endfunction()
2121

2222
add_clang_mlir_op_interface(CIROpInterfaces)
23+
add_clang_mlir_op_interface(CIRLoopOpInterface)
2324
add_clang_mlir_type_interface(CIRFPTypeInterface)

0 commit comments

Comments
 (0)