llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> 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. --- 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: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+19) - (modified) clang/include/clang/CIR/Dialect/IR/CIRDialect.h (+1) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+162-2) - (added) clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.h (+34) - (added) clang/include/clang/CIR/Interfaces/CIRLoopOpInterface.td (+99) - (modified) clang/include/clang/CIR/Interfaces/CMakeLists.txt (+1) - (modified) clang/include/clang/CIR/MissingFeatures.h (+19-7) - (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+19) - (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+10) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+15) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+108-1) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+78) - (modified) clang/lib/CIR/Dialect/IR/CMakeLists.txt (+2) - (modified) clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp (+1-2) - (modified) clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp (+111-2) - (added) clang/lib/CIR/Interfaces/CIRLoopOpInterface.cpp (+63) - (modified) clang/lib/CIR/Interfaces/CMakeLists.txt (+2) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+17) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+10) - (added) clang/test/CIR/CodeGen/loop.cpp (+46) - (added) clang/test/CIR/Transforms/loop.cir (+29) ``````````diff 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] `````````` </details> https://github.com/llvm/llvm-project/pull/132266 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits