-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[CIR] Upstream initial for-loop support #132266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis 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. Patch is 46.30 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132266.diff 21 Files Affected:
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);
+ m...
[truncated]
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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. Patch is 46.30 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132266.diff 21 Files Affected:
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);
+ m...
[truncated]
|
/// Create a for operation. | ||
cir::ForOp createFor( | ||
mlir::Location loc, | ||
llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentionally missing the init? ForStmt
has an Init, Condition, Step, and Body, but this handles only 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an excellent question. The incubator (and this PR) emits the initialization in front of the for
op, but it does seem like it is something we would want more tightly associated with the for
in the emitted CIR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initialization happens within the scope preceding the actual cir.for
, etc. We have no intend of reproducing the AST 100% here, because of nested regions within the loop. the alloca needs to be visible and dominate everything inside cir.for
.
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.
20c0162
to
4dcff61
Compare
The force push was a rebase to resolve conflicts with the UnaryOp support in CIRCanonicalize.cpp. There were no other changes. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
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.