llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir @llvm/pr-subscribers-clang Author: Erich Keane (erichkeane) <details> <summary>Changes</summary> Classic codegen has a lot of infrastructure to emit the loop-info-stack, we can do it as a pretty simple attribute. This handles lowering to CIR for these hints, after extracting the stuff that determines the values to a separate type in CodeGenUtils. Flattening the loops/emitting this to LLVM-IR is left to a follow up. AI Notice: I made claude help me with the tests, so it did much of that to make sure we got reasonable test coverage. --- Patch is 62.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232975.diff 17 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+134) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+11-4) - (added) clang/include/clang/CodeGenUtils/LoopAttributes.h (+98) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+11-3) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+101-17) - (modified) clang/lib/CodeGen/CGLoopInfo.cpp (+2-262) - (modified) clang/lib/CodeGen/CGLoopInfo.h (+4-56) - (modified) clang/lib/CodeGenUtils/CMakeLists.txt (+1) - (added) clang/lib/CodeGenUtils/LoopAttributes.cpp (+285) - (added) clang/test/CIR/CodeGen/loop-hints-stds.cpp (+84) - (added) clang/test/CIR/CodeGen/loop-hints.c (+52) - (added) clang/test/CIR/CodeGen/loop-hints.cpp (+139) - (added) clang/test/CIR/CodeGenHLSL/loop-hints.hlsl (+16) - (modified) clang/test/CIR/CodeGenOpenACC/loop.cpp (+1-1) - (added) clang/test/CIR/CodeGenOpenCL/loop-hints.cl (+18) - (added) clang/test/CIR/IR/invalid-loop-hints.cir (+73) - (added) clang/test/CIR/IR/loop-hints.cir (+40) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index aa9562586d2f5..b3cc021989fde 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -953,6 +953,140 @@ def CIR_FenvAttr : CIR_Attr<"Fenv", "fenv"> { let canHaveIllegalCXXABIType = 0; } +//===----------------------------------------------------------------------===// +// CIR_FlagParam +//===----------------------------------------------------------------------===// + +/// A simple UnitAttr wrapper so that an attributes parameter can print JUST its +/// name and be used with the ($keyword^)? syntax in assemblyFormat. +class CIR_FlagParam<string keyword> : OptionalParameter<"mlir::UnitAttr"> { + let printer = "$_printer << \"" # keyword # "\""; + let parser = "[&]() -> ::mlir::FailureOr<::mlir::UnitAttr> {\n" + " if (::mlir::succeeded($_parser.parseOptionalKeyword(\"" + # keyword # "\")))\n" + " return ::mlir::UnitAttr::get($_ctxt);\n" + " return ::mlir::UnitAttr();\n" + "}()"; +} + +//===----------------------------------------------------------------------===// +// LoopUnrollMode +//===----------------------------------------------------------------------===// + +def CIR_LoopUnrollMode : CIR_I32Enum< + "LoopUnrollMode", "loop unrolling mode", [ + I32EnumCase<"Enable", 0, "enable">, + I32EnumCase<"Disable", 1, "disable">, + I32EnumCase<"Full", 2, "full"> +]> { + let description = [{ + How a loop is unrolled (or unrolled and jammed): `enable` asks for the + optimizer to unroll the loop, `disable` forbids it, and `full` asks for it + to be unrolled completely. + }]; +} + +//===----------------------------------------------------------------------===// +// LoopHintAttr +//===----------------------------------------------------------------------===// + +def CIR_LoopHintAttr : CIR_Attr<"LoopHint", "loop_hint"> { + let summary = "Optimizer hints and guarantees attached to a loop"; + let description = [{ + The `#cir.loop_hint` attribute carries per-loop information for the + optimizer: the forward-progress guarantee and the hints from + `#pragma clang loop`, `#pragma unroll`, `[[clang::code_align]]` and their + OpenCL and HLSL spellings. It is attached to the loop operations + (`cir.for`, `cir.while`, `cir.do`). + + - `must_progress`: the loop must eventually terminate or have an + observable side effect (the forward-progress guarantee of C++11 and + later, and C11 loops with a non-constant condition). + - `vectorize_enable`: force vectorization on. + - `vectorize_width`: the vectorization factor. A width of 1 disables + vectorization. + - `vectorize_scalable`: whether the vectorization factor is + scalable, or explicitly fixed. + - `vectorize_predicate`: enable or disable predicated vectorization. + - `interleave_count`: the interleave count. A count of 1 disables + interleaving. + - `unroll`: The unrolling mode: enable, disable, or full. + - `unroll_count`: the unroll factor. + - `unroll_and_jam`: the unroll-and-jam mode: enable, disable, or full. + - `distribute`: enable or disable loop distribution. + - `pipeline_disabled`: disable software pipelining. + - `pipeline_initiation_interval`: the pipelining initiation interval. + - `licm_disabled`: disable loop-invariant code motion. + - `parallel`: the loop's iterations are independent, so its memory + accesses are parallel accesses. + - `code_align`: the alignment of the loop, in bytes. + + Example: + ``` + #cir.loop_hint<> + #cir.loop_hint< must_progress> + #cir.loop_hint< must_progress vectorize_enable interleave_count = 2 + unroll_count = 4> + ``` + }]; + + let parameters = (ins + CIR_FlagParam<"must_progress">:$must_progress, + CIR_FlagParam<"vectorize_enable">:$vectorize_enable, + OptionalParameter<"uint32_t">:$vectorize_width, + OptionalParameter<"mlir::BoolAttr">:$vectorize_scalable, + OptionalParameter<"mlir::BoolAttr">:$vectorize_predicate, + OptionalParameter<"uint32_t">:$interleave_count, + OptionalParameter<"std::optional<cir::LoopUnrollMode>">:$unroll, + OptionalParameter<"uint32_t">:$unroll_count, + OptionalParameter<"std::optional<cir::LoopUnrollMode>">:$unroll_and_jam, + OptionalParameter<"uint32_t">:$unroll_and_jam_count, + OptionalParameter<"mlir::BoolAttr">:$distribute, + CIR_FlagParam<"pipeline_disabled">:$pipeline_disabled, + OptionalParameter<"uint32_t">:$pipeline_initiation_interval, + CIR_FlagParam<"licm_disabled">:$licm_disabled, + CIR_FlagParam<"parallel">:$parallel, + OptionalParameter<"uint32_t">:$code_align); + + let assemblyFormat = [{ + `<` + (` ` $must_progress^)? + (` ` $vectorize_enable^)? + (` ` `vectorize_width` `=` $vectorize_width^)? + (` ` `vectorize_scalable` `=` $vectorize_scalable^)? + (` ` `vectorize_predicate` `=` $vectorize_predicate^)? + (` ` `interleave_count` `=` $interleave_count^)? + (` ` `unroll` `=` $unroll^)? + (` ` `unroll_count` `=` $unroll_count^)? + (` ` `unroll_and_jam` `=` $unroll_and_jam^)? + (` ` `unroll_and_jam_count` `=` $unroll_and_jam_count^)? + (` ` `distribute` `=` $distribute^)? + (` ` $pipeline_disabled^)? + (` ` `pipeline_initiation_interval` `=` $pipeline_initiation_interval^)? + (` ` $licm_disabled^)? + (` ` $parallel^)? + (` ` `code_align` `=` $code_align^)? + `>` + }]; + + let extraClassDeclaration = [{ + /// Returns true if no hint is set, in which case the attribute has no + /// information and there is no reason to use it. Every parameter is + /// unset when it is null, empty or 0, so this must name all of them. + bool isEmpty() const { + return !getMustProgress() && !getVectorizeEnable() && + !getVectorizeWidth() && !getVectorizeScalable() && + !getVectorizePredicate() && !getInterleaveCount() && + !getUnroll() && !getUnrollCount() && !getUnrollAndJam() && + !getUnrollAndJamCount() && !getDistribute() && + !getPipelineDisabled() && !getPipelineInitiationInterval() && + !getLicmDisabled() && !getParallel() && !getCodeAlign(); + } + }]; + + let canHaveIllegalCXXABIType = 0; +} + //===----------------------------------------------------------------------===// // FastMathFlagsAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 63d50761806a6..533039f44c3f2 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2369,11 +2369,13 @@ class CIR_WhileOpBase<string mnemonic> : CIR_LoopOpBase<mnemonic> { } def CIR_WhileOp : CIR_WhileOpBase<"while"> { - let arguments = (ins OptionalAttr<CIR_CleanupKindAttr>:$cleanupKind); + let arguments = (ins OptionalAttr<CIR_CleanupKindAttr>:$cleanupKind, + OptionalAttr<CIR_LoopHintAttr>:$loop_hints); let regions = (region MinSizedRegion<1>:$cond, MinSizedRegion<1>:$body, MaxSizedRegion<1>:$cleanup); let assemblyFormat = [{ - $cond `do` $body (`cleanup` enum($cleanupKind) $cleanup^)? attr-dict + $cond `do` $body (`cleanup` enum($cleanupKind) $cleanup^)? + (`loop_hints` $loop_hints^)? attr-dict }]; let description = [{ @@ -2449,8 +2451,11 @@ def CIR_WhileOp : CIR_WhileOpBase<"while"> { } def CIR_DoWhileOp : CIR_WhileOpBase<"do"> { + let arguments = (ins OptionalAttr<CIR_LoopHintAttr>:$loop_hints); let regions = (region MinSizedRegion<1>:$body, MinSizedRegion<1>:$cond); - let assemblyFormat = " $body `while` $cond attr-dict"; + let assemblyFormat = [{ + $body `while` $cond (`loop_hints` $loop_hints^)? attr-dict + }]; let extraClassDeclaration = [{ mlir::Region &getEntry() { return getBody(); } @@ -2524,7 +2529,8 @@ def CIR_ForOp : CIR_LoopOpBase<"for"> { ``` }]; - let arguments = (ins OptionalAttr<CIR_CleanupKindAttr>:$cleanupKind); + let arguments = (ins OptionalAttr<CIR_CleanupKindAttr>:$cleanupKind, + OptionalAttr<CIR_LoopHintAttr>:$loop_hints); let regions = (region MinSizedRegion<1>:$cond, MinSizedRegion<1>:$body, MinSizedRegion<1>:$step, @@ -2534,6 +2540,7 @@ def CIR_ForOp : CIR_LoopOpBase<"for"> { `body` $body `step` $step (`cleanup` enum($cleanupKind) $cleanup^)? + (`loop_hints` $loop_hints^)? attr-dict }]; diff --git a/clang/include/clang/CodeGenUtils/LoopAttributes.h b/clang/include/clang/CodeGenUtils/LoopAttributes.h new file mode 100644 index 0000000000000..49c1bbfcacfba --- /dev/null +++ b/clang/include/clang/CodeGenUtils/LoopAttributes.h @@ -0,0 +1,98 @@ +//===--- LoopAttributes.h - Shared loop hint state ---------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the state of the attributes and hints that may be specified +// on a loop, and the logic to determine it from the loop's statement +// attributes and the command line options. Both classic CodeGen and CIR +// CodeGen use it, and each translates the state into its own representation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_LOOPATTRIBUTES_H +#define LLVM_CLANG_CODEGENUTILS_LOOPATTRIBUTES_H + +#include "llvm/ADT/ArrayRef.h" + +namespace clang { +class ASTContext; +class Attr; +class CodeGenOptions; +} // namespace clang + +namespace clang::CodeGenUtils { + +/// Attributes that may be specified on loops. +struct LoopAttributes { + explicit LoopAttributes(bool IsParallel = false); + void clear(); + + /// Generate llvm.loop.parallel metadata for loads and stores. + bool IsParallel; + + /// State of loop vectorization or unrolling. + enum LVEnableState { Unspecified, Enable, Disable, Full }; + + /// Value for llvm.loop.vectorize.enable metadata. + LVEnableState VectorizeEnable; + + /// Value for llvm.loop.unroll.* metadata (enable, disable, or full). + LVEnableState UnrollEnable; + + /// Value for llvm.loop.unroll_and_jam.* metadata (enable, disable, or full). + LVEnableState UnrollAndJamEnable; + + /// Value for llvm.loop.vectorize.predicate metadata + LVEnableState VectorizePredicateEnable; + + /// Value for llvm.loop.vectorize.width metadata. + unsigned VectorizeWidth; + + // Value for llvm.loop.vectorize.scalable.enable + LVEnableState VectorizeScalable; + + /// Value for llvm.loop.interleave.count metadata. + unsigned InterleaveCount; + + /// llvm.unroll. + unsigned UnrollCount; + + /// llvm.unroll. + unsigned UnrollAndJamCount; + + /// Value for llvm.loop.distribute.enable metadata. + LVEnableState DistributeEnable; + + /// Value for llvm.loop.pipeline.disable metadata. + bool PipelineDisabled; + + /// Value for llvm.licm.disable metadata. + bool LICMDisabled; + + /// Value for llvm.loop.pipeline.iicount metadata. + unsigned PipelineInitiationInterval; + + /// Value for 'llvm.loop.align' metadata. + unsigned CodeAlign; + + /// Value for whether the loop is required to make progress. + bool MustProgress; +}; + +/// Update \p Attrs with the loop hints in \p StmtAttrs (`#pragma clang loop`, +/// `#pragma unroll`, `[[clang::code_align]]`, the OpenCL and HLSL spellings), +/// the command line options that adjust every loop (-falign-loops and +/// -fno-unroll-loops), and whether the loop must make progress. +void updateLoopAttributes(LoopAttributes &Attrs, ASTContext &Ctx, + const CodeGenOptions &CGOpts, + llvm::ArrayRef<const Attr *> StmtAttrs, + bool MustProgress); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_LOOPATTRIBUTES_H diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 90da139c0f19c..ee7a1e38f0017 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -2240,7 +2240,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitDeleteCall(const FunctionDecl *deleteFD, mlir::Value ptr, QualType deleteTy); - mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); + mlir::LogicalResult emitDoStmt(const clang::DoStmt &s, + llvm::ArrayRef<const Attr *> attrs = {}); mlir::Value emitCXXTypeidExpr(const CXXTypeidExpr *e); mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce); @@ -2302,7 +2303,8 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitSimpleStmt(const clang::Stmt *s, bool useCurrentScope); - mlir::LogicalResult emitForStmt(const clang::ForStmt &s); + mlir::LogicalResult emitForStmt(const clang::ForStmt &s, + llvm::ArrayRef<const Attr *> attrs = {}); void emitForwardingCallToLambda(const CXXMethodDecl *lambdaCallOperator, CallArgList &callArgs); @@ -2585,7 +2587,13 @@ class CIRGenFunction : public CIRGenTypeCache { void emitVariablyModifiedType(QualType ty); - mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s); + mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s, + llvm::ArrayRef<const Attr *> attrs = {}); + + /// Build the hints that get attached to a loop, or a null attribute + /// if there is nothing to say about it. + cir::LoopHintAttr getLoopHints(llvm::ArrayRef<const Attr *> attrs, + bool mustProgress); std::optional<mlir::Value> emitRISCVBuiltinExpr(unsigned builtinID, const CallExpr *expr); diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 9e14ac420aa9f..5f308751f6f54 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -22,6 +22,7 @@ #include "clang/AST/StmtOpenMP.h" #include "clang/AST/StmtSYCL.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/LoopAttributes.h" #include "clang/CodeGenUtils/StmtUtils.h" #include "llvm/Support/SaveAndRestore.h" @@ -256,11 +257,11 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, case Stmt::SwitchStmtClass: return emitSwitchStmt(cast<SwitchStmt>(*s)); case Stmt::ForStmtClass: - return emitForStmt(cast<ForStmt>(*s)); + return emitForStmt(cast<ForStmt>(*s), attr); case Stmt::WhileStmtClass: - return emitWhileStmt(cast<WhileStmt>(*s)); + return emitWhileStmt(cast<WhileStmt>(*s), attr); case Stmt::DoStmtClass: - return emitDoStmt(cast<DoStmt>(*s)); + return emitDoStmt(cast<DoStmt>(*s), attr); case Stmt::CXXTryStmtClass: return emitCXXTryStmt(cast<CXXTryStmt>(*s)); case Stmt::CXXForRangeStmtClass: @@ -991,12 +992,83 @@ mlir::LogicalResult CIRGenFunction::emitSwitchCase(const SwitchCase &s, llvm_unreachable("expect case or default stmt"); } +cir::LoopHintAttr CIRGenFunction::getLoopHints(ArrayRef<const Attr *> attrs, + bool mustProgress) { + // Determine the state of the hints, the same way classic codegen does. + CodeGenUtils::LoopAttributes loopAttrs; + CodeGenUtils::updateLoopAttributes(loopAttrs, getContext(), + cgm.getCodeGenOpts(), attrs, mustProgress); + + // Memory accesses in a `parallel` loop are tagged as parallel accesses when + // CIR is lowered. + assert(!cir::MissingFeatures::loopInfoStack()); + mlir::MLIRContext *ctx = &getMLIRContext(); + + auto convertFlag = [&](bool set) { + return set ? mlir::UnitAttr::get(ctx) : mlir::UnitAttr(); + }; + auto convertStateToBool = + [&](CodeGenUtils::LoopAttributes::LVEnableState state) { + switch (state) { + case CodeGenUtils::LoopAttributes::LVEnableState::Unspecified: + return mlir::BoolAttr(); + case CodeGenUtils::LoopAttributes::LVEnableState::Enable: + return mlir::BoolAttr::get(ctx, true); + case CodeGenUtils::LoopAttributes::LVEnableState::Disable: + return mlir::BoolAttr::get(ctx, false); + case CodeGenUtils::LoopAttributes::LVEnableState::Full: + llvm_unreachable("'full' only applies to unrolling"); + } + llvm_unreachable("unknown bool state"); + }; + + auto convertUnrollMode = [](CodeGenUtils::LoopAttributes::LVEnableState state) + -> std::optional<cir::LoopUnrollMode> { + switch (state) { + case CodeGenUtils::LoopAttributes::LVEnableState::Unspecified: + return std::nullopt; + case CodeGenUtils::LoopAttributes::LVEnableState::Enable: + return cir::LoopUnrollMode::Enable; + case CodeGenUtils::LoopAttributes::LVEnableState::Disable: + return cir::LoopUnrollMode::Disable; + case CodeGenUtils::LoopAttributes::LVEnableState::Full: + return cir::LoopUnrollMode::Full; + } + + llvm_unreachable("unknown unroll state"); + }; + + // Only OMP seems to generate this, so leave as an NYI for now. + if (loopAttrs.VectorizeEnable == + CodeGenUtils::LoopAttributes::LVEnableState::Disable || + loopAttrs.VectorizeEnable == + CodeGenUtils::LoopAttributes::LVEnableState::Full) + cgm.errorNYI(SourceLocation{}, "unsupported vector-enable loop state"); + + bool vectorizeEnable = loopAttrs.VectorizeEnable == + CodeGenUtils::LoopAttributes::LVEnableState::Enable; + auto hints = cir::LoopHintAttr::get( + ctx, convertFlag(loopAttrs.MustProgress), convertFlag(vectorizeEnable), + loopAttrs.VectorizeWidth, convertStateToBool(loopAttrs.VectorizeScalable), + convertStateToBool(loopAttrs.VectorizePredicateEnable), + loopAttrs.InterleaveCount, convertUnrollMode(loopAttrs.UnrollEnable), + loopAttrs.UnrollCount, convertUnrollMode(loopAttrs.UnrollAndJamEnable), + loopAttrs.UnrollAndJamCount, + convertStateToBool(loopAttrs.DistributeEnable), + convertFlag(loopAttrs.PipelineDisabled), + loopAttrs.PipelineInitiationInterval, convertFlag(loopAttrs.LICMDisabled), + convertFlag(loopAttrs.IsParallel), loopAttrs.CodeAlign); + + if (hints.isEmpty()) + return {}; + return hints; +} + mlir::LogicalResult CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s, ArrayRef<const Attr *> forAttrs) { cir::ForOp forOp; - // TODO(cir): pass in array of attributes. auto forStmtBuilder = [&]() -> mlir::LogicalResult { mlir::LogicalResult loopRes = mlir::success(); // Evaluate the first pieces before the loop. @@ -1010,7 +1082,10 @@ CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s, if (emitStmt(s.getEndStmt(), /*useCurrentScope=*/true).failed()) return mlir::failure(); - assert(!cir::MissingFeatures::loopInfoStack()); + // FIXME: Classic codegen never sets 'must progress' on these loops, but + // probably could? + cir::LoopHintAttr loopHints = + getLoopHints(forAttrs, /*mustProgress=*/false); forOp = builder.createFor( getLoc(s.getSourceRange()), @@ -1041,6 +1116,7 @@ CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s, loopRes = mlir::failure(); builder.createYield(loc); }); + forOp.setLoopHintsAttr(loopHints); return loopRes; }; @@ -1064,18 +1140,19 @@ CIRGenFunction::emitCXXForRangeStmt(const CXXForRangeStmt &s, return mlir::success(); } -mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) { +mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s, + ArrayRef<const Attr *> attrs) { cir::ForOp forOp; - // TODO: pass in an array of attributes. auto forStmtBuilder = [&]() -> mlir::LogicalResult { mlir::LogicalResult loopRes = mlir:... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/232975 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
