[clang] [CIR] Upstream initial for-loop support (PR #132266)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 20 11:15:08 PDT 2025
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/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.
>From 20c016291947451b0fbe712fff35252fd9ef11fc Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 19 Mar 2025 13:00:29 -0700
Subject: [PATCH] [CIR] Upstream initial for-loop support
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.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 19 ++
.../include/clang/CIR/Dialect/IR/CIRDialect.h | 1 +
clang/include/clang/CIR/Dialect/IR/CIROps.td | 164 +++++++++++++++++-
.../clang/CIR/Interfaces/CIRLoopOpInterface.h | 34 ++++
.../CIR/Interfaces/CIRLoopOpInterface.td | 99 +++++++++++
.../clang/CIR/Interfaces/CMakeLists.txt | 1 +
clang/include/clang/CIR/MissingFeatures.h | 26 ++-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 19 ++
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 10 ++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 15 ++
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 109 +++++++++++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 78 +++++++++
clang/lib/CIR/Dialect/IR/CMakeLists.txt | 2 +
.../Dialect/Transforms/CIRCanonicalize.cpp | 3 +-
.../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 113 +++++++++++-
.../lib/CIR/Interfaces/CIRLoopOpInterface.cpp | 63 +++++++
clang/lib/CIR/Interfaces/CMakeLists.txt | 2 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 17 ++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 ++
clang/test/CIR/CodeGen/loop.cpp | 46 +++++
clang/test/CIR/Transforms/loop.cir | 29 ++++
21 files changed, 846 insertions(+), 14 deletions(-)
create mode 100644 clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.h
create mode 100644 clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.td
create mode 100644 clang/lib/CIR/Interfaces/CIRLoopOpInterface.cpp
create mode 100644 clang/test/CIR/CodeGen/loop.cpp
create mode 100644 clang/test/CIR/Transforms/loop.cir
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 32d1af677c62b..c6aea10d46b63 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -52,6 +52,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::BoolAttr::get(getContext(), getBoolTy(), state);
}
+ /// Create a for operation.
+ cir::ForOp createFor(
+ mlir::Location loc,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> stepBuilder) {
+ return create<cir::ForOp>(loc, condBuilder, bodyBuilder, stepBuilder);
+ }
+
mlir::TypedAttr getConstPtrAttr(mlir::Type type, int64_t value) {
auto valueAttr = mlir::IntegerAttr::get(
mlir::IntegerType::get(type.getContext(), 64), value);
@@ -158,6 +167,16 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
size.getQuantity());
}
+
+ /// Create a loop condition.
+ cir::ConditionOp createCondition(mlir::Value condition) {
+ return create<cir::ConditionOp>(condition.getLoc(), condition);
+ }
+
+ /// Create a yield operation.
+ cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value = {}) {
+ return create<cir::YieldOp>(loc, value);
+ }
};
} // namespace cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
index 0684cf5034f5d..da3b41371b9ab 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
@@ -29,6 +29,7 @@
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIROpsDialect.h.inc"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
+#include "clang/CIR/Interfaces/CIRLoopOpInterface.h"
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
// TableGen'erated files for MLIR dialects require that a macro be defined when
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 352d72ff31a8a..d7d63e040a2ba 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -19,6 +19,7 @@ include "clang/CIR/Dialect/IR/CIRTypes.td"
include "clang/CIR/Dialect/IR/CIRAttrs.td"
include "clang/CIR/Interfaces/CIROpInterfaces.td"
+include "clang/CIR/Interfaces/CIRLoopOpInterface.td"
include "mlir/IR/BuiltinAttributeInterfaces.td"
include "mlir/IR/EnumAttr.td"
@@ -423,7 +424,7 @@ def StoreOp : CIR_Op<"store", [
// ReturnOp
//===----------------------------------------------------------------------===//
-def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>,
+def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "ForOp"]>,
Terminator]> {
let summary = "Return from function";
let description = [{
@@ -460,12 +461,57 @@ def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp"]>,
let hasVerifier = 1;
}
+//===----------------------------------------------------------------------===//
+// ConditionOp
+//===----------------------------------------------------------------------===//
+
+def ConditionOp : CIR_Op<"condition", [
+ Terminator,
+ DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface,
+ ["getSuccessorRegions"]>
+]> {
+ let summary = "Loop continuation condition.";
+ let description = [{
+ The `cir.condition` terminates conditional regions. It takes a single
+ `cir.bool` operand and, depending on its value, may branch to different
+ regions:
+
+ - When in the `cond` region of a `cir.loop`, it continues the loop
+ if true, or exits it if false.
+ - When in the `ready` region of a `cir.await`, it branches to the `resume`
+ region when true, and to the `suspend` region when false.
+
+ Example:
+
+ ```mlir
+ cir.loop for(cond : {
+ cir.condition(%arg0) // Branches to `step` region or exits.
+ }, step : {
+ [...]
+ }) {
+ [...]
+ }
+
+ cir.await(user, ready : {
+ cir.condition(%arg0) // Branches to `resume` or `suspend` region.
+ }, suspend : {
+ [...]
+ }, resume : {
+ [...]
+ },)
+ ```
+ }];
+ let arguments = (ins CIR_BoolType:$condition);
+ let assemblyFormat = " `(` $condition `)` attr-dict ";
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// YieldOp
//===----------------------------------------------------------------------===//
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
- ParentOneOf<["ScopeOp"]>]> {
+ ParentOneOf<["ScopeOp", "ForOp"]>]> {
let summary = "Represents the default branching behaviour of a region";
let description = [{
The `cir.yield` operation terminates regions on different CIR operations,
@@ -666,6 +712,120 @@ def UnaryOp : CIR_Op<"unary", [Pure, SameOperandsAndResultType]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// BrCondOp
+//===----------------------------------------------------------------------===//
+
+def BrCondOp : CIR_Op<"brcond",
+ [DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
+ Pure, Terminator, AttrSizedOperandSegments]> {
+ let summary = "Conditional branch";
+ let description = [{
+ The `cir.brcond %cond, ^bb0, ^bb1` branches to 'bb0' block in case
+ %cond (which must be a !cir.bool type) evaluates to true, otherwise
+ it branches to 'bb1'.
+
+ Example:
+
+ ```mlir
+ ...
+ cir.brcond %a, ^bb3, ^bb4
+ ^bb3:
+ cir.return
+ ^bb4:
+ cir.yield
+ ```
+ }];
+
+ let builders = [
+ OpBuilder<(ins "mlir::Value":$cond, "mlir::Block *":$destTrue, "mlir::Block *":$destFalse,
+ CArg<"mlir::ValueRange", "{}">:$destOperandsTrue,
+ CArg<"mlir::ValueRange", "{}">:$destOperandsFalse), [{
+ build($_builder, $_state, cond, destOperandsTrue,
+ destOperandsFalse, destTrue, destFalse);
+ }]>
+ ];
+
+ let arguments = (ins CIR_BoolType:$cond,
+ Variadic<CIR_AnyType>:$destOperandsTrue,
+ Variadic<CIR_AnyType>:$destOperandsFalse);
+ let successors = (successor AnySuccessor:$destTrue, AnySuccessor:$destFalse);
+ let assemblyFormat = [{
+ $cond
+ $destTrue (`(` $destOperandsTrue^ `:` type($destOperandsTrue) `)`)?
+ `,`
+ $destFalse (`(` $destOperandsFalse^ `:` type($destOperandsFalse) `)`)?
+ attr-dict
+ }];
+}
+
+//===----------------------------------------------------------------------===//
+// ForOp
+//===----------------------------------------------------------------------===//
+
+def ForOp : CIR_Op<"for", [LoopOpInterface, NoRegionArguments]> {
+ let summary = "C/C++ for loop counterpart";
+ let description = [{
+ Represents a C/C++ for loop. It consists of three regions:
+
+ - `cond`: single block region with the loop's condition. Should be
+ terminated with a `cir.condition` operation.
+ - `body`: contains the loop body and an arbitrary number of blocks.
+ - `step`: single block region with the loop's step.
+
+ Example:
+
+ ```mlir
+ cir.for cond {
+ cir.condition(%val)
+ } body {
+ cir.break
+ ^bb2:
+ cir.yield
+ } step {
+ cir.yield
+ }
+ ```
+ }];
+
+ let regions = (region SizedRegion<1>:$cond,
+ MinSizedRegion<1>:$body,
+ SizedRegion<1>:$step);
+ let assemblyFormat = [{
+ `:` `cond` $cond
+ `body` $body
+ `step` $step
+ attr-dict
+ }];
+
+ let builders = [
+ OpBuilder<(ins "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$condBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$bodyBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$stepBuilder), [{
+ mlir::OpBuilder::InsertionGuard guard($_builder);
+
+ // Build condition region.
+ $_builder.createBlock($_state.addRegion());
+ condBuilder($_builder, $_state.location);
+
+ // Build body region.
+ $_builder.createBlock($_state.addRegion());
+ bodyBuilder($_builder, $_state.location);
+
+ // Build step region.
+ $_builder.createBlock($_state.addRegion());
+ stepBuilder($_builder, $_state.location);
+ }]>
+ ];
+
+ let extraClassDeclaration = [{
+ mlir::Region *maybeGetStep() { return &getStep(); }
+ llvm::SmallVector<mlir::Region *> getRegionsInExecutionOrder() {
+ return llvm::SmallVector<mlir::Region *, 3>{&getCond(), &getBody(), &getStep()};
+ }
+ }];
+}
+
//===----------------------------------------------------------------------===//
// GlobalOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.h b/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.h
new file mode 100644
index 0000000000000..3722c5e4a195c
--- /dev/null
+++ b/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// Defines the interface to generically handle CIR loop operations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
+#define CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
+
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/OpDefinition.h"
+#include "mlir/IR/Operation.h"
+#include "mlir/Interfaces/ControlFlowInterfaces.h"
+#include "mlir/Interfaces/LoopLikeInterface.h"
+
+namespace cir {
+namespace detail {
+
+/// Verify invariants of the LoopOpInterface.
+mlir::LogicalResult verifyLoopOpInterface(::mlir::Operation *op);
+
+} // namespace detail
+} // namespace cir
+
+/// Include the tablegen'd interface declarations.
+#include "clang/CIR/Interfaces/CIRLoopOpInterface.h.inc"
+
+#endif // CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE_H
diff --git a/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.td b/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.td
new file mode 100644
index 0000000000000..cbe8adba5e9f6
--- /dev/null
+++ b/clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.td
@@ -0,0 +1,99 @@
+//===---------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE
+#define CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE
+
+include "mlir/IR/OpBase.td"
+include "mlir/Interfaces/ControlFlowInterfaces.td"
+include "mlir/Interfaces/LoopLikeInterface.td"
+
+def LoopOpInterface : OpInterface<"LoopOpInterface", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ DeclareOpInterfaceMethods<LoopLikeOpInterface>
+]> {
+ let description = [{
+ Contains helper functions to query properties and perform transformations
+ on a loop.
+ }];
+ let cppNamespace = "::cir";
+
+ let methods = [
+ InterfaceMethod<[{
+ Returns the loop's conditional region.
+ }],
+ /*retTy=*/"mlir::Region &",
+ /*methodName=*/"getCond"
+ >,
+ InterfaceMethod<[{
+ Returns the loop's body region.
+ }],
+ /*retTy=*/"mlir::Region &",
+ /*methodName=*/"getBody"
+ >,
+ InterfaceMethod<[{
+ Returns a pointer to the loop's step region or nullptr.
+ }],
+ /*retTy=*/"mlir::Region *",
+ /*methodName=*/"maybeGetStep",
+ /*args=*/(ins),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/"return nullptr;"
+ >,
+ InterfaceMethod<[{
+ Returns the first region to be executed in the loop.
+ }],
+ /*retTy=*/"mlir::Region &",
+ /*methodName=*/"getEntry",
+ /*args=*/(ins),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/"return $_op.getCond();"
+ >,
+ InterfaceMethod<[{
+ Returns a list of regions in order of execution.
+ }],
+ /*retTy=*/"llvm::SmallVector<mlir::Region *>",
+ /*methodName=*/"getRegionsInExecutionOrder",
+ /*args=*/(ins),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/[{
+ return llvm::SmallVector<mlir::Region *, 2>{&$_op.getRegion(0), &$_op.getRegion(1)};
+ }]
+ >,
+ InterfaceMethod<[{
+ Recursively walks the body of the loop in pre-order while skipping
+ nested loops and executing a callback on every other operation.
+ }],
+ /*retTy=*/"mlir::WalkResult",
+ /*methodName=*/"walkBodySkippingNestedLoops",
+ /*args=*/(ins "::llvm::function_ref<mlir::WalkResult (mlir::Operation *)>":$callback),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/[{
+ return $_op.getBody().template walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
+ if (mlir::isa<LoopOpInterface>(op))
+ return mlir::WalkResult::skip();
+ return callback(op);
+ });
+ }]
+ >
+ ];
+
+ let extraClassDeclaration = [{
+ /// Generic method to retrieve the successors of a LoopOpInterface operation.
+ static void getLoopOpSuccessorRegions(
+ ::cir::LoopOpInterface op, ::mlir::RegionBranchPoint point,
+ ::mlir::SmallVectorImpl<::mlir::RegionSuccessor> ®ions);
+ }];
+
+ let verify = [{
+ /// Verify invariants of the LoopOpInterface.
+ return detail::verifyLoopOpInterface($_op);
+ }];
+}
+
+#endif // CLANG_CIR_INTERFACES_CIRLOOPOPINTERFACE
diff --git a/clang/include/clang/CIR/Interfaces/CMakeLists.txt b/clang/include/clang/CIR/Interfaces/CMakeLists.txt
index e9929f6964605..3c155193235d7 100644
--- a/clang/include/clang/CIR/Interfaces/CMakeLists.txt
+++ b/clang/include/clang/CIR/Interfaces/CMakeLists.txt
@@ -20,4 +20,5 @@ function(add_clang_mlir_type_interface interface)
endfunction()
add_clang_mlir_op_interface(CIROpInterfaces)
+add_clang_mlir_op_interface(CIRLoopOpInterface)
add_clang_mlir_type_interface(CIRFPTypeInterface)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d276af5686995..144d7d0f853d1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -72,6 +72,9 @@ struct MissingFeatures {
static bool opFuncLinkage() { return false; }
static bool opFuncVisibility() { return false; }
+ // ScopeOp handling
+ static bool opScopeCleanupRegion() { return false; }
+
// Unary operator handling
static bool opUnarySignedOverflow() { return false; }
static bool opUnaryPromotionType() { return false; }
@@ -90,12 +93,16 @@ struct MissingFeatures {
static bool stackSaveOp() { return false; }
static bool aggValueSlot() { return false; }
static bool generateDebugInfo() { return false; }
-
static bool fpConstraints() { return false; }
static bool sanitizers() { return false; }
static bool addHeapAllocSiteMetadata() { return false; }
static bool targetCodeGenInfoGetNullPointer() { return false; }
static bool CGFPOptionsRAII() { return false; }
+ static bool loopInfoStack() { return false; }
+ static bool requiresCleanups() { return false; }
+ static bool createProfileWeightsForLoop() { return false; }
+ static bool emitCondLikelihoodViaExpectIntrinsic() { return false; }
+ static bool pgoUse() { return false; }
// Missing types
static bool dataMemberType() { return false; }
@@ -106,16 +113,21 @@ struct MissingFeatures {
static bool vectorType() { return false; }
// Future CIR operations
+ static bool awaitOp() { return false; }
+ static bool breakOp() { return false; }
+ static bool callOp() { return false; }
+ static bool complexCreateOp() { return false; }
+ static bool complexImagOp() { return false; }
+ static bool complexRealOp() { return false; }
+ static bool continueOp() { return false; }
+ static bool ifOp() { return false; }
static bool labelOp() { return false; }
- static bool brCondOp() { return false; }
+ static bool selectOp() { return false; }
static bool switchOp() { return false; }
+ static bool ternaryOp() { return false; }
static bool tryOp() { return false; }
static bool unaryOp() { return false; }
- static bool selectOp() { return false; }
- static bool complexCreateOp() { return false; }
- static bool complexRealOp() { return false; }
- static bool complexImagOp() { return false; }
- static bool callOp() { return false; }
+ static bool zextOp() { return false; }
};
} // namespace cir
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 1c529b9efc84b..306130b80d457 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -165,6 +165,25 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
return LValue();
}
+mlir::Value CIRGenFunction::evaluateExprAsBool(const Expr *e) {
+ QualType boolTy = getContext().BoolTy;
+ SourceLocation loc = e->getExprLoc();
+
+ assert(!cir::MissingFeatures::pgoUse());
+ if (const MemberPointerType *MPT = e->getType()->getAs<MemberPointerType>()) {
+ cgm.errorNYI(e->getSourceRange(),
+ "evaluateExprAsBool: member pointer type");
+ return createDummyValue(getLoc(loc), boolTy);
+ }
+
+ assert(!cir::MissingFeatures::CGFPOptionsRAII());
+ if (!e->getType()->isAnyComplexType())
+ return emitScalarConversion(emitScalarExpr(e), e->getType(), boolTy, loc);
+
+ cgm.errorNYI(e->getSourceRange(), "evaluateExprAsBool: complex type");
+ return createDummyValue(getLoc(loc), boolTy);
+}
+
LValue CIRGenFunction::emitUnaryOpLValue(const UnaryOperator *e) {
UnaryOperatorKind op = e->getOpcode();
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 726062b805775..ca0090f8d35b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -742,6 +742,16 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return {};
}
+mlir::Value CIRGenFunction::emitScalarConversion(mlir::Value src,
+ QualType srcTy, QualType dstTy,
+ SourceLocation loc) {
+ assert(CIRGenFunction::hasScalarEvaluationKind(srcTy) &&
+ CIRGenFunction::hasScalarEvaluationKind(dstTy) &&
+ "Invalid scalar expression to emit");
+ return ScalarExprEmitter(*this, builder)
+ .emitScalarConversion(src, srcTy, dstTy, loc);
+}
+
/// Return the size or alignment of the type of argument of the sizeof
/// expression as an integer.
mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index ba05fb46a3c46..631217cf67762 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -175,6 +175,9 @@ class CIRGenFunction : public CIRGenTypeCache {
void finishFunction(SourceLocation endLoc);
mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
+ /// Build a debug stoppoint if we are emitting debug info.
+ void emitStopPoint(const Stmt *s);
+
// Build CIR for a statement. useCurrentScope should be true if no
// new scopes need be created when finding a compound statement.
mlir::LogicalResult
@@ -184,6 +187,8 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitSimpleStmt(const clang::Stmt *s,
bool useCurrentScope);
+ mlir::LogicalResult emitForStmt(const clang::ForStmt &S);
+
void emitCompoundStmt(const clang::CompoundStmt &s);
void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
@@ -311,6 +316,10 @@ class CIRGenFunction : public CIRGenTypeCache {
/// inside a function, including static vars etc.
void emitVarDecl(const clang::VarDecl &d);
+ /// Perform the usual unary conversions on the specified expression and
+ /// compare the result against zero, returning an Int1Ty value.
+ mlir::Value evaluateExprAsBool(const clang::Expr *e);
+
/// Set the address of a local variable.
void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) {
assert(!LocalDeclMap.count(vd) && "Decl already exists in LocalDeclMap!");
@@ -337,6 +346,12 @@ class CIRGenFunction : public CIRGenTypeCache {
FunctionArgList args, clang::SourceLocation loc,
clang::SourceLocation startLoc);
+ /// Emit a conversion from the specified type to the specified destination
+ /// type, both of which are CIR scalar types.
+ mlir::Value emitScalarConversion(mlir::Value src, clang::QualType srcType,
+ clang::QualType dstType,
+ clang::SourceLocation loc);
+
/// Represents a scope, including function bodies, compound statements, and
/// the substatements of if/while/do/for/switch/try statements. This class
/// handles any automatic cleanup, along with the return value.
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index bd1aa632da1d0..aa04ff6345fc6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -43,6 +43,10 @@ void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
}
}
+void CIRGenFunction::emitStopPoint(const Stmt *s) {
+ assert(!cir::MissingFeatures::generateDebugInfo());
+}
+
// Build CIR for a statement. useCurrentScope should be true if no new scopes
// need to be created when finding a compound statement.
mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
@@ -69,6 +73,9 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
return mlir::success();
}
+ case Stmt::ForStmtClass:
+ return emitForStmt(cast<ForStmt>(*s));
+
case Stmt::OMPScopeDirectiveClass:
case Stmt::OMPErrorDirectiveClass:
case Stmt::NoStmtClass:
@@ -90,7 +97,6 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::SYCLKernelCallStmtClass:
case Stmt::IfStmtClass:
case Stmt::SwitchStmtClass:
- case Stmt::ForStmtClass:
case Stmt::WhileStmtClass:
case Stmt::DoStmtClass:
case Stmt::CoroutineBodyStmtClass:
@@ -228,6 +234,33 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
return mlir::success();
}
+// Add a terminating yield on a body region if no other terminators are used.
+static void terminateBody(CIRGenBuilderTy &builder, mlir::Region &r,
+ mlir::Location loc) {
+ if (r.empty())
+ return;
+
+ SmallVector<mlir::Block *, 4> eraseBlocks;
+ unsigned numBlocks = r.getBlocks().size();
+ for (auto &block : r.getBlocks()) {
+ // Already cleanup after return operations, which might create
+ // empty blocks if emitted as last stmt.
+ if (numBlocks != 1 && block.empty() && block.hasNoPredecessors() &&
+ block.hasNoSuccessors())
+ eraseBlocks.push_back(&block);
+
+ if (block.empty() ||
+ !block.back().hasTrait<mlir::OpTrait::IsTerminator>()) {
+ mlir::OpBuilder::InsertionGuard guardCase(builder);
+ builder.setInsertionPointToEnd(&block);
+ builder.createYield(loc);
+ }
+ }
+
+ for (auto *b : eraseBlocks)
+ b->erase();
+}
+
mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {
assert(builder.getInsertionBlock() && "expected valid insertion point");
@@ -280,3 +313,77 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
return mlir::success();
}
+
+mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {
+ cir::ForOp forOp;
+
+ // TODO: pass in an array of attributes.
+ auto forStmtBuilder = [&]() -> mlir::LogicalResult {
+ mlir::LogicalResult loopRes = mlir::success();
+ // Evaluate the first part before the loop.
+ if (s.getInit())
+ if (emitStmt(s.getInit(), /*useCurrentScope=*/true).failed())
+ return mlir::failure();
+ assert(!cir::MissingFeatures::loopInfoStack());
+ // In the classic codegen, if there are any cleanups between here and the
+ // loop-exit scope, a block is created to stage the loop exit. We probably
+ // already do the right thing because of ScopeOp, but we need more testing
+ // to be sure we handle all cases.
+ assert(!cir::MissingFeatures::requiresCleanups());
+
+ forOp = builder.createFor(
+ getLoc(s.getSourceRange()),
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ assert(!cir::MissingFeatures::createProfileWeightsForLoop());
+ assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
+ mlir::Value condVal;
+ if (s.getCond()) {
+ // If the for statement has a condition scope,
+ // emit the local variable declaration.
+ if (s.getConditionVariable())
+ emitDecl(*s.getConditionVariable());
+ // C99 6.8.5p2/p4: The first substatement is executed if the
+ // expression compares unequal to 0. The condition must be a
+ // scalar type.
+ condVal = evaluateExprAsBool(s.getCond());
+ } else {
+ cir::BoolType boolTy = cir::BoolType::get(b.getContext());
+ condVal = b.create<cir::ConstantOp>(
+ loc, boolTy, cir::BoolAttr::get(b.getContext(), boolTy, true));
+ }
+ builder.createCondition(condVal);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ // The scope of the for loop body is nested within the scope of the
+ // for loop's init-statement and condition.
+ if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
+ loopRes = mlir::failure();
+ emitStopPoint(&s);
+ },
+ /*stepBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ if (s.getInc())
+ if (emitStmt(s.getInc(), /*useCurrentScope=*/true).failed())
+ loopRes = mlir::failure();
+ builder.createYield(loc);
+ });
+ return loopRes;
+ };
+
+ auto res = mlir::success();
+ auto scopeLoc = getLoc(s.getSourceRange());
+ builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ LexicalScope lexScope{
+ *this, loc, builder.getInsertionBlock()};
+ res = forStmtBuilder();
+ });
+
+ if (res.failed())
+ return res;
+
+ terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc()));
+ return mlir::success();
+}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index e94aba0d7ac4a..ae86fefcf3657 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -19,6 +19,7 @@
#include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc"
#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc"
+#include "clang/CIR/MissingFeatures.h"
using namespace mlir;
using namespace cir;
@@ -150,6 +151,41 @@ void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder,
odsState.addTypes(addr);
}
+//===----------------------------------------------------------------------===//
+// ConditionOp
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------
+// BranchOpTerminatorInterface Methods
+//===----------------------------------
+
+void cir::ConditionOp::getSuccessorRegions(
+ ArrayRef<Attribute> operands, SmallVectorImpl<RegionSuccessor> ®ions) {
+ // TODO(cir): The condition value may be folded to a constant, narrowing
+ // down its list of possible successors.
+
+ // Parent is a loop: condition may branch to the body or to the parent op.
+ if (auto loopOp = dyn_cast<LoopOpInterface>(getOperation()->getParentOp())) {
+ regions.emplace_back(&loopOp.getBody(), loopOp.getBody().getArguments());
+ regions.emplace_back(loopOp->getResults());
+ }
+
+ assert(!cir::MissingFeatures::awaitOp());
+}
+
+MutableOperandRange
+cir::ConditionOp::getMutableSuccessorOperands(RegionBranchPoint point) {
+ // No values are yielded to the successor region.
+ return MutableOperandRange(getOperation(), 0, 0);
+}
+
+LogicalResult cir::ConditionOp::verify() {
+ assert(!cir::MissingFeatures::awaitOp());
+ if (!isa<LoopOpInterface>(getOperation()->getParentOp()))
+ return emitOpError("condition must be within a conditional region");
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
//===----------------------------------------------------------------------===//
@@ -440,6 +476,7 @@ void cir::ScopeOp::build(
OpBuilder::InsertionGuard guard(builder);
Region *scopeRegion = result.addRegion();
builder.createBlock(scopeRegion);
+ assert(!cir::MissingFeatures::opScopeCleanupRegion());
mlir::Type yieldTy;
scopeBuilder(builder, yieldTy, result.location);
@@ -448,6 +485,17 @@ void cir::ScopeOp::build(
result.addTypes(TypeRange{yieldTy});
}
+void cir::ScopeOp::build(
+ OpBuilder &builder, OperationState &result,
+ function_ref<void(OpBuilder &, Location)> scopeBuilder) {
+ assert(scopeBuilder && "the builder callback for 'then' must be present");
+ OpBuilder::InsertionGuard guard(builder);
+ Region *scopeRegion = result.addRegion();
+ builder.createBlock(scopeRegion);
+ assert(!cir::MissingFeatures::opScopeCleanupRegion());
+ scopeBuilder(builder, result.location);
+}
+
LogicalResult cir::ScopeOp::verify() {
if (getRegion().empty()) {
return emitOpError() << "cir.scope must not be empty since it should "
@@ -474,6 +522,36 @@ Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
return getDest();
}
+//===----------------------------------------------------------------------===//
+// BrCondOp
+//===----------------------------------------------------------------------===//
+
+mlir::SuccessorOperands cir::BrCondOp::getSuccessorOperands(unsigned index) {
+ assert(index < getNumSuccessors() && "invalid successor index");
+ return SuccessorOperands(index == 0 ? getDestOperandsTrueMutable()
+ : getDestOperandsFalseMutable());
+}
+
+Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
+ if (IntegerAttr condAttr = dyn_cast_if_present<IntegerAttr>(operands.front()))
+ return condAttr.getValue().isOne() ? getDestTrue() : getDestFalse();
+ return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// ForOp
+//===----------------------------------------------------------------------===//
+
+void cir::ForOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point,
+ llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
+ LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
+}
+
+llvm::SmallVector<Region *> cir::ForOp::getLoopRegions() {
+ return {&getBody()};
+}
+
//===----------------------------------------------------------------------===//
// GlobalOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index aa5ea52a5e93f..97a530d8c76d6 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -8,6 +8,7 @@ add_clang_library(MLIRCIR
MLIRCIROpsIncGen
MLIRCIREnumsGen
MLIRCIROpInterfacesIncGen
+ MLIRCIRLoopOpInterfaceIncGen
LINK_LIBS PUBLIC
MLIRIR
@@ -15,6 +16,7 @@ add_clang_library(MLIRCIR
MLIRDLTIDialect
MLIRDataLayoutInterfaces
MLIRFuncDialect
+ MLIRLoopLikeInterface
MLIRCIRInterfaces
clangAST
)
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
index d32691dba1473..bb511c2ed0f3e 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
@@ -118,7 +118,6 @@ void CIRCanonicalizePass::runOnOperation() {
// Collect operations to apply patterns.
llvm::SmallVector<Operation *, 16> ops;
getOperation()->walk([&](Operation *op) {
- assert(!cir::MissingFeatures::brCondOp());
assert(!cir::MissingFeatures::switchOp());
assert(!cir::MissingFeatures::tryOp());
assert(!cir::MissingFeatures::unaryOp());
@@ -129,7 +128,7 @@ void CIRCanonicalizePass::runOnOperation() {
assert(!cir::MissingFeatures::callOp());
// CastOp here is to perform a manual `fold` in
// applyOpPatternsGreedily
- if (isa<BrOp, ScopeOp, CastOp>(op))
+ if (isa<BrOp, BrCondOp, ScopeOp, CastOp>(op))
ops.push_back(op);
});
diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
index b01d3d4d43e65..52f4b2241505d 100644
--- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
@@ -26,6 +26,28 @@ using namespace cir;
namespace {
+/// Lowers operations with the terminator trait that have a single successor.
+void lowerTerminator(mlir::Operation *op, mlir::Block *dest,
+ mlir::PatternRewriter &rewriter) {
+ assert(op->hasTrait<mlir::OpTrait::IsTerminator>() && "not a terminator");
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPoint(op);
+ rewriter.replaceOpWithNewOp<cir::BrOp>(op, dest);
+}
+
+/// Walks a region while skipping operations of type `Ops`. This ensures the
+/// callback is not applied to said operations and its children.
+template <typename... Ops>
+void walkRegionSkipping(
+ mlir::Region ®ion,
+ mlir::function_ref<mlir::WalkResult(mlir::Operation *)> callback) {
+ region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
+ if (isa<Ops...>(op))
+ return mlir::WalkResult::skip();
+ return callback(op);
+ });
+}
+
struct CIRFlattenCFGPass : public CIRFlattenCFGBase<CIRFlattenCFGPass> {
CIRFlattenCFGPass() = default;
@@ -86,8 +108,91 @@ class CIRScopeOpFlattening : public mlir::OpRewritePattern<cir::ScopeOp> {
}
};
+class CIRLoopOpInterfaceFlattening
+ : public mlir::OpInterfaceRewritePattern<cir::LoopOpInterface> {
+public:
+ using mlir::OpInterfaceRewritePattern<
+ cir::LoopOpInterface>::OpInterfaceRewritePattern;
+
+ inline void lowerConditionOp(cir::ConditionOp op, mlir::Block *body,
+ mlir::Block *exit,
+ mlir::PatternRewriter &rewriter) const {
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPoint(op);
+ rewriter.replaceOpWithNewOp<cir::BrCondOp>(op, op.getCondition(), body,
+ exit);
+ }
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::LoopOpInterface op,
+ mlir::PatternRewriter &rewriter) const final {
+ // Setup CFG blocks.
+ mlir::Block *entry = rewriter.getInsertionBlock();
+ mlir::Block *exit =
+ rewriter.splitBlock(entry, rewriter.getInsertionPoint());
+ mlir::Block *cond = &op.getCond().front();
+ mlir::Block *body = &op.getBody().front();
+ mlir::Block *step =
+ (op.maybeGetStep() ? &op.maybeGetStep()->front() : nullptr);
+
+ // Setup loop entry branch.
+ rewriter.setInsertionPointToEnd(entry);
+ rewriter.create<cir::BrOp>(op.getLoc(), &op.getEntry().front());
+
+ // Branch from condition region to body or exit.
+ auto conditionOp = cast<cir::ConditionOp>(cond->getTerminator());
+ lowerConditionOp(conditionOp, body, exit, rewriter);
+
+ // TODO(cir): Remove the walks below. It visits operations unnecessarily.
+ // However, to solve this we would likely need a custom DialectConversion
+ // driver to customize the order that operations are visited.
+
+ // Lower continue statements.
+ op.walkBodySkippingNestedLoops([&](mlir::Operation *op) {
+ // When continue ops are supported, there will be a check for them here
+ // and a call to lowerTerminator(). The call to `advance()` handles the
+ // case where this is not a continue op.
+ assert(!cir::MissingFeatures::continueOp());
+ return mlir::WalkResult::advance();
+ });
+
+ // Lower break statements.
+ assert(!cir::MissingFeatures::switchOp());
+ walkRegionSkipping<cir::LoopOpInterface>(
+ op.getBody(), [&](mlir::Operation *op) {
+ // When break ops are supported, there will be a check for them here
+ // and a call to lowerTerminator(). The call to `advance()` handles
+ // the case where this is not a break op.
+ assert(!cir::MissingFeatures::breakOp());
+ return mlir::WalkResult::advance();
+ });
+
+ // Lower optional body region yield.
+ for (mlir::Block &blk : op.getBody().getBlocks()) {
+ auto bodyYield = dyn_cast<cir::YieldOp>(blk.getTerminator());
+ if (bodyYield)
+ lowerTerminator(bodyYield, (step ? step : cond), rewriter);
+ }
+
+ // Lower mandatory step region yield.
+ if (step)
+ lowerTerminator(cast<cir::YieldOp>(step->getTerminator()), cond,
+ rewriter);
+
+ // Move region contents out of the loop op.
+ rewriter.inlineRegionBefore(op.getCond(), exit);
+ rewriter.inlineRegionBefore(op.getBody(), exit);
+ if (step)
+ rewriter.inlineRegionBefore(*op.maybeGetStep(), exit);
+
+ rewriter.eraseOp(op);
+ return mlir::success();
+ }
+};
+
void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
- patterns.add<CIRScopeOpFlattening>(patterns.getContext());
+ patterns.add<CIRLoopOpInterfaceFlattening, CIRScopeOpFlattening>(
+ patterns.getContext());
}
void CIRFlattenCFGPass::runOnOperation() {
@@ -97,7 +202,11 @@ void CIRFlattenCFGPass::runOnOperation() {
// Collect operations to apply patterns.
llvm::SmallVector<Operation *, 16> ops;
getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
- if (isa<ScopeOp>(op))
+ assert(!cir::MissingFeatures::ifOp());
+ assert(!cir::MissingFeatures::switchOp());
+ assert(!cir::MissingFeatures::ternaryOp());
+ assert(!cir::MissingFeatures::tryOp());
+ if (isa<ScopeOp, LoopOpInterface>(op))
ops.push_back(op);
});
diff --git a/clang/lib/CIR/Interfaces/CIRLoopOpInterface.cpp b/clang/lib/CIR/Interfaces/CIRLoopOpInterface.cpp
new file mode 100644
index 0000000000000..0ce5017a399da
--- /dev/null
+++ b/clang/lib/CIR/Interfaces/CIRLoopOpInterface.cpp
@@ -0,0 +1,63 @@
+//===---------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "clang/CIR/Interfaces/CIRLoopOpInterface.h"
+
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Interfaces/CIRLoopOpInterface.cpp.inc"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace cir {
+
+void LoopOpInterface::getLoopOpSuccessorRegions(
+ LoopOpInterface op, mlir::RegionBranchPoint point,
+ llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
+ assert(point.isParent() || point.getRegionOrNull());
+
+ // Branching to first region: go to condition or body (do-while).
+ if (point.isParent()) {
+ regions.emplace_back(&op.getEntry(), op.getEntry().getArguments());
+ return;
+ }
+
+ // Branching from condition: go to body or exit.
+ if (&op.getCond() == point.getRegionOrNull()) {
+ regions.emplace_back(mlir::RegionSuccessor(op->getResults()));
+ regions.emplace_back(&op.getBody(), op.getBody().getArguments());
+ return;
+ }
+
+ // Branching from body: go to step (for) or condition.
+ if (&op.getBody() == point.getRegionOrNull()) {
+ // FIXME(cir): Should we consider break/continue statements here?
+ mlir::Region *afterBody =
+ (op.maybeGetStep() ? op.maybeGetStep() : &op.getCond());
+ regions.emplace_back(afterBody, afterBody->getArguments());
+ return;
+ }
+
+ // Branching from step: go to condition.
+ if (op.maybeGetStep() == point.getRegionOrNull()) {
+ regions.emplace_back(&op.getCond(), op.getCond().getArguments());
+ return;
+ }
+
+ llvm_unreachable("unexpected branch origin");
+}
+
+/// Verify invariants of the LoopOpInterface.
+llvm::LogicalResult detail::verifyLoopOpInterface(mlir::Operation *op) {
+ // FIXME: fix this so the conditionop isn't requiring MLIRCIR
+ // auto loopOp = mlir::cast<LoopOpInterface>(op);
+ // if (!mlir::isa<ConditionOp>(loopOp.getCond().back().getTerminator()))
+ // return op->emitOpError(
+ // "expected condition region to terminate with 'cir.condition'");
+ return llvm::success();
+}
+
+} // namespace cir
diff --git a/clang/lib/CIR/Interfaces/CMakeLists.txt b/clang/lib/CIR/Interfaces/CMakeLists.txt
index 2fe5714520b74..04a8972fba9ae 100644
--- a/clang/lib/CIR/Interfaces/CMakeLists.txt
+++ b/clang/lib/CIR/Interfaces/CMakeLists.txt
@@ -1,5 +1,6 @@
add_clang_library(MLIRCIRInterfaces
CIROpInterfaces.cpp
+ CIRLoopOpInterface.cpp
CIRFPTypeInterface.cpp
ADDITIONAL_HEADER_DIRS
@@ -8,6 +9,7 @@ add_clang_library(MLIRCIRInterfaces
DEPENDS
MLIRCIREnumsGen
MLIRCIRFPTypeInterfaceIncGen
+ MLIRCIRLoopOpInterfaceIncGen
MLIRCIROpInterfacesIncGen
LINK_LIBS
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 83226ed8680bb..5137d47dc8904 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -345,6 +345,22 @@ struct ConvertCIRToLLVMPass
StringRef getArgument() const override { return "cir-flat-to-llvm"; }
};
+mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
+ cir::BrCondOp brOp, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ // When ZExtOp is implemented, we'll need to check if the condition is a
+ // ZExtOp and if so, delete it if it has a single use.
+ assert(!cir::MissingFeatures::zextOp());
+
+ mlir::Value i1Condition = adaptor.getCond();
+
+ rewriter.replaceOpWithNewOp<mlir::LLVM::CondBrOp>(
+ brOp, i1Condition, brOp.getDestTrue(), adaptor.getDestOperandsTrue(),
+ brOp.getDestFalse(), adaptor.getDestOperandsFalse());
+
+ return mlir::success();
+}
+
mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
return getTypeConverter()->convertType(ty);
}
@@ -1116,6 +1132,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
dl);
patterns.add<
// clang-format off
+ CIRToLLVMBrCondOpLowering,
CIRToLLVMBrOpLowering,
CIRToLLVMFuncOpLowering,
CIRToLLVMTrapOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d49ab815941dc..a01a9a5f4f076 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -28,6 +28,16 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr,
mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage);
+class CIRToLLVMBrCondOpLowering
+ : public mlir::OpConversionPattern<cir::BrCondOp> {
+public:
+ using mlir::OpConversionPattern<cir::BrCondOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::BrCondOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
mlir::DataLayout const &dataLayout;
diff --git a/clang/test/CIR/CodeGen/loop.cpp b/clang/test/CIR/CodeGen/loop.cpp
new file mode 100644
index 0000000000000..449317016e99d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/loop.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+void l0() {
+ for (;;) {
+ }
+}
+
+// CIR: cir.func @l0
+// CIR: cir.scope {
+// CIR: cir.for : cond {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.condition(%[[TRUE]])
+// CIR: } body {
+// CIR: cir.yield
+// CIR: } step {
+// CIR: cir.yield
+// CIR: }
+// CIR: }
+// CIR: cir.return
+// CIR: }
+
+// LLVM: define void @l0()
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL2]]:
+// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL5:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: br label %[[LABEL4:.*]]
+// LLVM: [[LABEL4]]:
+// LLVM: br label %[[LABEL2]]
+// LLVM: [[LABEL5]]:
+// LLVM: br label %[[LABEL6:.*]]
+// LLVM: [[LABEL6]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z2l0v()
+// OGCG: entry:
+// OGCG: br label %[[FOR_COND:.*]]
+// OGCG: [[FOR_COND]]:
+// OGCG: br label %[[FOR_COND]]
diff --git a/clang/test/CIR/Transforms/loop.cir b/clang/test/CIR/Transforms/loop.cir
new file mode 100644
index 0000000000000..4fde3a7bb43f1
--- /dev/null
+++ b/clang/test/CIR/Transforms/loop.cir
@@ -0,0 +1,29 @@
+// RUN: cir-opt %s -cir-flatten-cfg -o - | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+ cir.func @testFor(%arg0 : !cir.bool) {
+ cir.for : cond {
+ cir.condition(%arg0)
+ } body {
+ cir.yield
+ } step {
+ cir.yield
+ }
+ cir.return
+ }
+}
+
+// CHECK: cir.func @testFor(%arg0: !cir.bool) {
+// CHECK: cir.br ^bb[[#COND:]]
+// CHECK: ^bb[[#COND]]:
+// CHECK: cir.brcond %arg0 ^bb[[#BODY:]], ^bb[[#EXIT:]]
+// CHECK: ^bb[[#BODY]]:
+// CHECK: cir.br ^bb[[#STEP:]]
+// CHECK: ^bb[[#STEP]]:
+// CHECK: cir.br ^bb[[#COND:]]
+// CHECK: ^bb[[#EXIT]]:
+// CHECK: cir.return
+// CHECK: }
More information about the cfe-commits
mailing list