[clang] [CIR] Implement 'loop hint' lowering (PR #232975)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 16 16:00:38 PDT 2026
https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/232975
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.
>From 350c195622afab60464a5e53cbfd175b24486d19 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Fri, 16 Oct 2026 13:51:27 -0700
Subject: [PATCH] [CIR] Implement 'loop hint' lowering
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.
---
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 134 ++++++++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 15 +-
.../clang/CodeGenUtils/LoopAttributes.h | 98 ++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 14 +-
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 118 ++++++--
clang/lib/CodeGen/CGLoopInfo.cpp | 264 +---------------
clang/lib/CodeGen/CGLoopInfo.h | 60 +---
clang/lib/CodeGenUtils/CMakeLists.txt | 1 +
clang/lib/CodeGenUtils/LoopAttributes.cpp | 285 ++++++++++++++++++
clang/test/CIR/CodeGen/loop-hints-stds.cpp | 84 ++++++
clang/test/CIR/CodeGen/loop-hints.c | 52 ++++
clang/test/CIR/CodeGen/loop-hints.cpp | 139 +++++++++
clang/test/CIR/CodeGenHLSL/loop-hints.hlsl | 16 +
clang/test/CIR/CodeGenOpenACC/loop.cpp | 2 +-
clang/test/CIR/CodeGenOpenCL/loop-hints.cl | 18 ++
clang/test/CIR/IR/invalid-loop-hints.cir | 73 +++++
clang/test/CIR/IR/loop-hints.cir | 40 +++
17 files changed, 1070 insertions(+), 343 deletions(-)
create mode 100644 clang/include/clang/CodeGenUtils/LoopAttributes.h
create mode 100644 clang/lib/CodeGenUtils/LoopAttributes.cpp
create mode 100644 clang/test/CIR/CodeGen/loop-hints-stds.cpp
create mode 100644 clang/test/CIR/CodeGen/loop-hints.c
create mode 100644 clang/test/CIR/CodeGen/loop-hints.cpp
create mode 100644 clang/test/CIR/CodeGenHLSL/loop-hints.hlsl
create mode 100644 clang/test/CIR/CodeGenOpenCL/loop-hints.cl
create mode 100644 clang/test/CIR/IR/invalid-loop-hints.cir
create mode 100644 clang/test/CIR/IR/loop-hints.cir
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::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());
- checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ bool mustProgress =
+ checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ cir::LoopHintAttr loopHints = getLoopHints(attrs, mustProgress);
// A condition variable's lifetime is a single iteration, so capture its
// destructor and lifetime-end cleanups, and those of the temporaries that
@@ -1143,6 +1220,8 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {
forOp = builder.createFor(getLoc(s.getSourceRange()), condBuilder,
bodyBuilder, stepBuilder);
}
+
+ forOp.setLoopHintsAttr(loopHints);
return loopRes;
};
@@ -1162,14 +1241,15 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {
return mlir::success();
}
-mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {
+mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s,
+ ArrayRef<const Attr *> attrs) {
cir::DoWhileOp doWhileOp;
- // TODO: pass in array of attributes.
auto doStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
- assert(!cir::MissingFeatures::loopInfoStack());
- checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ bool mustProgress =
+ checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ cir::LoopHintAttr loopHints = getLoopHints(attrs, mustProgress);
doWhileOp = builder.createDoWhile(
getLoc(s.getSourceRange()),
@@ -1191,6 +1271,7 @@ mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {
loopRes = mlir::failure();
emitStopPoint(&s);
});
+ doWhileOp.setLoopHintsAttr(loopHints);
return loopRes;
};
@@ -1210,14 +1291,16 @@ mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {
return mlir::success();
}
-mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) {
+mlir::LogicalResult
+CIRGenFunction::emitWhileStmt(const WhileStmt &s,
+ ArrayRef<const Attr *> attrs) {
cir::WhileOp whileOp;
- // TODO: pass in array of attributes.
auto whileStmtBuilder = [&]() -> mlir::LogicalResult {
mlir::LogicalResult loopRes = mlir::success();
- assert(!cir::MissingFeatures::loopInfoStack());
- checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ bool mustProgress =
+ checkIfLoopMustProgress(s.getCond(), CodeGenUtils::hasEmptyLoopBody(s));
+ cir::LoopHintAttr loopHints = getLoopHints(attrs, mustProgress);
// A condition variable's lifetime is a single iteration, so capture its
// destructor and lifetime-end cleanups, and those of the temporaries that
@@ -1272,6 +1355,7 @@ mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) {
whileOp = builder.createWhile(getLoc(s.getSourceRange()), condBuilder,
bodyBuilder);
}
+ whileOp.setLoopHintsAttr(loopHints);
return loopRes;
};
diff --git a/clang/lib/CodeGen/CGLoopInfo.cpp b/clang/lib/CodeGen/CGLoopInfo.cpp
index 14e13af69b011..61cb14793d268 100644
--- a/clang/lib/CodeGen/CGLoopInfo.cpp
+++ b/clang/lib/CodeGen/CGLoopInfo.cpp
@@ -432,36 +432,6 @@ SmallVector<Metadata *, 4> clang::CodeGen::LoopInfo::createMetadata(
return createFullUnrollMetadata(Attrs, LoopProperties, HasUserTransforms);
}
-LoopAttributes::LoopAttributes(bool IsParallel)
- : IsParallel(IsParallel), VectorizeEnable(LoopAttributes::Unspecified),
- UnrollEnable(LoopAttributes::Unspecified),
- UnrollAndJamEnable(LoopAttributes::Unspecified),
- VectorizePredicateEnable(LoopAttributes::Unspecified), VectorizeWidth(0),
- VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
- UnrollCount(0), UnrollAndJamCount(0),
- DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
- LICMDisabled(false), PipelineInitiationInterval(0), CodeAlign(0),
- MustProgress(false) {}
-
-void LoopAttributes::clear() {
- IsParallel = false;
- VectorizeWidth = 0;
- VectorizeScalable = LoopAttributes::Unspecified;
- InterleaveCount = 0;
- UnrollCount = 0;
- UnrollAndJamCount = 0;
- VectorizeEnable = LoopAttributes::Unspecified;
- UnrollEnable = LoopAttributes::Unspecified;
- UnrollAndJamEnable = LoopAttributes::Unspecified;
- VectorizePredicateEnable = LoopAttributes::Unspecified;
- DistributeEnable = LoopAttributes::Unspecified;
- PipelineDisabled = false;
- LICMDisabled = false;
- PipelineInitiationInterval = 0;
- CodeAlign = 0;
- MustProgress = false;
-}
-
clang::CodeGen::LoopInfo::LoopInfo(BasicBlock *Header,
const LoopAttributes &Attrs,
const llvm::DebugLoc &StartLoc,
@@ -598,239 +568,9 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
ArrayRef<const clang::Attr *> Attrs,
const llvm::DebugLoc &StartLoc,
const llvm::DebugLoc &EndLoc, bool MustProgress) {
- // Identify loop hint attributes from Attrs.
- for (const auto *Attr : Attrs) {
- const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(Attr);
- const OpenCLUnrollHintAttr *OpenCLHint =
- dyn_cast<OpenCLUnrollHintAttr>(Attr);
- const HLSLLoopHintAttr *HLSLLoopHint = dyn_cast<HLSLLoopHintAttr>(Attr);
- // Skip non loop hint attributes
- if (!LH && !OpenCLHint && !HLSLLoopHint) {
- continue;
- }
-
- LoopHintAttr::OptionType Option = LoopHintAttr::Unroll;
- LoopHintAttr::LoopHintState State = LoopHintAttr::Disable;
- unsigned ValueInt = 1;
- // Translate opencl_unroll_hint attribute argument to
- // equivalent LoopHintAttr enums.
- // OpenCL v2.0 s6.11.5:
- // 0 - enable unroll (no argument).
- // 1 - disable unroll.
- // other positive integer n - unroll by n.
- if (OpenCLHint) {
- ValueInt = OpenCLHint->getUnrollHint();
- if (ValueInt == 0) {
- State = LoopHintAttr::Enable;
- } else if (ValueInt != 1) {
- Option = LoopHintAttr::UnrollCount;
- State = LoopHintAttr::Numeric;
- }
- } else if (HLSLLoopHint) {
- ValueInt = HLSLLoopHint->getDirective();
- if (HLSLLoopHint->getSemanticSpelling() ==
- HLSLLoopHintAttr::Spelling::Microsoft_unroll) {
- if (ValueInt == 0)
- State = LoopHintAttr::Enable;
- if (ValueInt > 0) {
- Option = LoopHintAttr::UnrollCount;
- State = LoopHintAttr::Numeric;
- }
- }
- } else if (LH) {
- auto *ValueExpr = LH->getValue();
- if (ValueExpr) {
- llvm::APSInt ValueAPS = ValueExpr->EvaluateKnownConstInt(Ctx);
- ValueInt = ValueAPS.getSExtValue();
- }
-
- Option = LH->getOption();
- State = LH->getState();
- }
- switch (State) {
- case LoopHintAttr::Disable:
- switch (Option) {
- case LoopHintAttr::Vectorize:
- // Disable vectorization by specifying a width of 1 and disabling
- // scalable vectorization explicitly.
- setVectorizeWidth(1);
- setVectorizeScalable(LoopAttributes::Disable);
- break;
- case LoopHintAttr::Interleave:
- // Disable interleaving by speciyfing a count of 1.
- setInterleaveCount(1);
- break;
- case LoopHintAttr::Unroll:
- setUnrollState(LoopAttributes::Disable);
- break;
- case LoopHintAttr::UnrollAndJam:
- setUnrollAndJamState(LoopAttributes::Disable);
- break;
- case LoopHintAttr::VectorizePredicate:
- setVectorizePredicateState(LoopAttributes::Disable);
- break;
- case LoopHintAttr::Distribute:
- setDistributeState(false);
- break;
- case LoopHintAttr::PipelineDisabled:
- setPipelineDisabled(true);
- break;
- case LoopHintAttr::LICMDisabled:
- setLICMDisabled(true);
- break;
- case LoopHintAttr::UnrollCount:
- case LoopHintAttr::UnrollAndJamCount:
- case LoopHintAttr::VectorizeWidth:
- case LoopHintAttr::InterleaveCount:
- case LoopHintAttr::PipelineInitiationInterval:
- llvm_unreachable("Options cannot be disabled.");
- break;
- }
- break;
- case LoopHintAttr::Enable:
- switch (Option) {
- case LoopHintAttr::Vectorize:
- case LoopHintAttr::Interleave:
- setVectorizeEnable(true);
- break;
- case LoopHintAttr::Unroll:
- setUnrollState(LoopAttributes::Enable);
- break;
- case LoopHintAttr::UnrollAndJam:
- setUnrollAndJamState(LoopAttributes::Enable);
- break;
- case LoopHintAttr::VectorizePredicate:
- setVectorizePredicateState(LoopAttributes::Enable);
- break;
- case LoopHintAttr::Distribute:
- setDistributeState(true);
- break;
- case LoopHintAttr::UnrollCount:
- case LoopHintAttr::UnrollAndJamCount:
- case LoopHintAttr::VectorizeWidth:
- case LoopHintAttr::InterleaveCount:
- case LoopHintAttr::PipelineDisabled:
- case LoopHintAttr::PipelineInitiationInterval:
- case LoopHintAttr::LICMDisabled:
- llvm_unreachable("Options cannot enabled.");
- break;
- }
- break;
- case LoopHintAttr::AssumeSafety:
- switch (Option) {
- case LoopHintAttr::Vectorize:
- case LoopHintAttr::Interleave:
- // Apply "llvm.mem.parallel_loop_access" metadata to load/stores.
- setParallel(true);
- setVectorizeEnable(true);
- break;
- case LoopHintAttr::Unroll:
- case LoopHintAttr::UnrollAndJam:
- case LoopHintAttr::VectorizePredicate:
- case LoopHintAttr::UnrollCount:
- case LoopHintAttr::UnrollAndJamCount:
- case LoopHintAttr::VectorizeWidth:
- case LoopHintAttr::InterleaveCount:
- case LoopHintAttr::Distribute:
- case LoopHintAttr::PipelineDisabled:
- case LoopHintAttr::PipelineInitiationInterval:
- case LoopHintAttr::LICMDisabled:
- llvm_unreachable("Options cannot be used to assume mem safety.");
- break;
- }
- break;
- case LoopHintAttr::Full:
- switch (Option) {
- case LoopHintAttr::Unroll:
- setUnrollState(LoopAttributes::Full);
- break;
- case LoopHintAttr::UnrollAndJam:
- setUnrollAndJamState(LoopAttributes::Full);
- break;
- case LoopHintAttr::Vectorize:
- case LoopHintAttr::Interleave:
- case LoopHintAttr::UnrollCount:
- case LoopHintAttr::UnrollAndJamCount:
- case LoopHintAttr::VectorizeWidth:
- case LoopHintAttr::InterleaveCount:
- case LoopHintAttr::Distribute:
- case LoopHintAttr::PipelineDisabled:
- case LoopHintAttr::PipelineInitiationInterval:
- case LoopHintAttr::VectorizePredicate:
- case LoopHintAttr::LICMDisabled:
- llvm_unreachable("Options cannot be used with 'full' hint.");
- break;
- }
- break;
- case LoopHintAttr::FixedWidth:
- case LoopHintAttr::ScalableWidth:
- switch (Option) {
- case LoopHintAttr::VectorizeWidth:
- setVectorizeScalable(State == LoopHintAttr::ScalableWidth
- ? LoopAttributes::Enable
- : LoopAttributes::Disable);
- if (LH->getValue())
- setVectorizeWidth(ValueInt);
- break;
- default:
- llvm_unreachable("Options cannot be used with 'scalable' hint.");
- break;
- }
- break;
- case LoopHintAttr::Numeric:
- switch (Option) {
- case LoopHintAttr::InterleaveCount:
- setInterleaveCount(ValueInt);
- break;
- case LoopHintAttr::UnrollCount:
- setUnrollCount(ValueInt);
- break;
- case LoopHintAttr::UnrollAndJamCount:
- setUnrollAndJamCount(ValueInt);
- break;
- case LoopHintAttr::PipelineInitiationInterval:
- setPipelineInitiationInterval(ValueInt);
- break;
- case LoopHintAttr::Unroll:
- case LoopHintAttr::UnrollAndJam:
- case LoopHintAttr::VectorizePredicate:
- case LoopHintAttr::Vectorize:
- case LoopHintAttr::VectorizeWidth:
- case LoopHintAttr::Interleave:
- case LoopHintAttr::Distribute:
- case LoopHintAttr::PipelineDisabled:
- case LoopHintAttr::LICMDisabled:
- llvm_unreachable("Options cannot be assigned a value.");
- break;
- }
- break;
- }
- }
-
- // Identify loop attribute 'code_align' from Attrs.
- // For attribute code_align:
- // n - 'llvm.loop.align i32 n' metadata will be emitted.
- // A source-level [[clang::code_align]] attribute takes precedence over the
- // -falign-loops=N command-line default.
- if (const auto *CodeAlign = getSpecificAttr<CodeAlignAttr>(Attrs)) {
- const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
- llvm::APSInt ArgVal = CE->getResultAsAPSInt();
- setCodeAlign(ArgVal.getSExtValue());
- } else if (CGOpts.LoopAlignment) {
- setCodeAlign(CGOpts.LoopAlignment);
- }
-
- setMustProgress(MustProgress);
-
- if (CGOpts.OptimizationLevel > 0)
- // Disable unrolling for the loop, if unrolling is disabled (via
- // -fno-unroll-loops) and no pragmas override the decision.
- if (!CGOpts.UnrollLoops &&
- (StagedAttrs.UnrollEnable == LoopAttributes::Unspecified &&
- StagedAttrs.UnrollCount == 0))
- setUnrollState(LoopAttributes::Disable);
-
/// Stage the attributes.
+ clang::CodeGenUtils::updateLoopAttributes(StagedAttrs, Ctx, CGOpts, Attrs,
+ MustProgress);
push(Header, StartLoc, EndLoc);
}
diff --git a/clang/lib/CodeGen/CGLoopInfo.h b/clang/lib/CodeGen/CGLoopInfo.h
index 065d489fb9bb2..d287ec52ed52e 100644
--- a/clang/lib/CodeGen/CGLoopInfo.h
+++ b/clang/lib/CodeGen/CGLoopInfo.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_CODEGEN_CGLOOPINFO_H
#define LLVM_CLANG_LIB_CODEGEN_CGLOOPINFO_H
+#include "clang/CodeGenUtils/LoopAttributes.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/DebugLoc.h"
@@ -32,62 +33,9 @@ class ASTContext;
class CodeGenOptions;
namespace CodeGen {
-/// 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;
-};
+/// Attributes that may be specified on loops. The state, and how it is
+/// determined from the loop's attributes, is shared with CIR CodeGen.
+using LoopAttributes = clang::CodeGenUtils::LoopAttributes;
/// Information used when generating a structured loop.
class LoopInfo {
diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt
index dc701f8dbc72f..1a836bcab29c1 100644
--- a/clang/lib/CodeGenUtils/CMakeLists.txt
+++ b/clang/lib/CodeGenUtils/CMakeLists.txt
@@ -13,6 +13,7 @@ add_clang_library(clangCodeGenUtils
ExprUtils.cpp
FunctionUtils.cpp
ItaniumCXXABIUtils.cpp
+ LoopAttributes.cpp
ModuleUtils.cpp
RecordLayoutUtils.cpp
StmtUtils.cpp
diff --git a/clang/lib/CodeGenUtils/LoopAttributes.cpp b/clang/lib/CodeGenUtils/LoopAttributes.cpp
new file mode 100644
index 0000000000000..d8dd0dad9eca4
--- /dev/null
+++ b/clang/lib/CodeGenUtils/LoopAttributes.cpp
@@ -0,0 +1,285 @@
+//===--- LoopAttributes.cpp - Shared loop hint state ----------------------===//
+//
+// 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/CodeGenUtils/LoopAttributes.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/CodeGenOptions.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang::CodeGenUtils {
+
+LoopAttributes::LoopAttributes(bool IsParallel)
+ : IsParallel(IsParallel), VectorizeEnable(LoopAttributes::Unspecified),
+ UnrollEnable(LoopAttributes::Unspecified),
+ UnrollAndJamEnable(LoopAttributes::Unspecified),
+ VectorizePredicateEnable(LoopAttributes::Unspecified), VectorizeWidth(0),
+ VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
+ UnrollCount(0), UnrollAndJamCount(0),
+ DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
+ LICMDisabled(false), PipelineInitiationInterval(0), CodeAlign(0),
+ MustProgress(false) {}
+
+void LoopAttributes::clear() {
+ IsParallel = false;
+ VectorizeWidth = 0;
+ VectorizeScalable = LoopAttributes::Unspecified;
+ InterleaveCount = 0;
+ UnrollCount = 0;
+ UnrollAndJamCount = 0;
+ VectorizeEnable = LoopAttributes::Unspecified;
+ UnrollEnable = LoopAttributes::Unspecified;
+ UnrollAndJamEnable = LoopAttributes::Unspecified;
+ VectorizePredicateEnable = LoopAttributes::Unspecified;
+ DistributeEnable = LoopAttributes::Unspecified;
+ PipelineDisabled = false;
+ LICMDisabled = false;
+ PipelineInitiationInterval = 0;
+ CodeAlign = 0;
+ MustProgress = false;
+}
+
+void updateLoopAttributes(LoopAttributes &Attrs, ASTContext &Ctx,
+ const CodeGenOptions &CGOpts,
+ llvm::ArrayRef<const Attr *> StmtAttrs,
+ bool MustProgress) {
+ // Identify loop hint attributes from Attrs.
+ for (const auto *Attr : StmtAttrs) {
+ const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(Attr);
+ const OpenCLUnrollHintAttr *OpenCLHint =
+ dyn_cast<OpenCLUnrollHintAttr>(Attr);
+ const HLSLLoopHintAttr *HLSLLoopHint = dyn_cast<HLSLLoopHintAttr>(Attr);
+ // Skip non loop hint attributes
+ if (!LH && !OpenCLHint && !HLSLLoopHint) {
+ continue;
+ }
+
+ LoopHintAttr::OptionType Option = LoopHintAttr::Unroll;
+ LoopHintAttr::LoopHintState State = LoopHintAttr::Disable;
+ unsigned ValueInt = 1;
+ // Translate opencl_unroll_hint attribute argument to
+ // equivalent LoopHintAttr enums.
+ // OpenCL v2.0 s6.11.5:
+ // 0 - enable unroll (no argument).
+ // 1 - disable unroll.
+ // other positive integer n - unroll by n.
+ if (OpenCLHint) {
+ ValueInt = OpenCLHint->getUnrollHint();
+ if (ValueInt == 0) {
+ State = LoopHintAttr::Enable;
+ } else if (ValueInt != 1) {
+ Option = LoopHintAttr::UnrollCount;
+ State = LoopHintAttr::Numeric;
+ }
+ } else if (HLSLLoopHint) {
+ ValueInt = HLSLLoopHint->getDirective();
+ if (HLSLLoopHint->getSemanticSpelling() ==
+ HLSLLoopHintAttr::Spelling::Microsoft_unroll) {
+ if (ValueInt == 0)
+ State = LoopHintAttr::Enable;
+ if (ValueInt > 0) {
+ Option = LoopHintAttr::UnrollCount;
+ State = LoopHintAttr::Numeric;
+ }
+ }
+ } else if (LH) {
+ auto *ValueExpr = LH->getValue();
+ if (ValueExpr) {
+ llvm::APSInt ValueAPS = ValueExpr->EvaluateKnownConstInt(Ctx);
+ ValueInt = ValueAPS.getSExtValue();
+ }
+
+ Option = LH->getOption();
+ State = LH->getState();
+ }
+ switch (State) {
+ case LoopHintAttr::Disable:
+ switch (Option) {
+ case LoopHintAttr::Vectorize:
+ // Disable vectorization by specifying a width of 1 and disabling
+ // scalable vectorization explicitly.
+ Attrs.VectorizeWidth = 1;
+ Attrs.VectorizeScalable = LoopAttributes::Disable;
+ break;
+ case LoopHintAttr::Interleave:
+ // Disable interleaving by speciyfing a count of 1.
+ Attrs.InterleaveCount = 1;
+ break;
+ case LoopHintAttr::Unroll:
+ Attrs.UnrollEnable = LoopAttributes::Disable;
+ break;
+ case LoopHintAttr::UnrollAndJam:
+ Attrs.UnrollAndJamEnable = LoopAttributes::Disable;
+ break;
+ case LoopHintAttr::VectorizePredicate:
+ Attrs.VectorizePredicateEnable = LoopAttributes::Disable;
+ break;
+ case LoopHintAttr::Distribute:
+ Attrs.DistributeEnable = LoopAttributes::Disable;
+ break;
+ case LoopHintAttr::PipelineDisabled:
+ Attrs.PipelineDisabled = true;
+ break;
+ case LoopHintAttr::LICMDisabled:
+ Attrs.LICMDisabled = true;
+ break;
+ case LoopHintAttr::UnrollCount:
+ case LoopHintAttr::UnrollAndJamCount:
+ case LoopHintAttr::VectorizeWidth:
+ case LoopHintAttr::InterleaveCount:
+ case LoopHintAttr::PipelineInitiationInterval:
+ llvm_unreachable("Options cannot be disabled.");
+ break;
+ }
+ break;
+ case LoopHintAttr::Enable:
+ switch (Option) {
+ case LoopHintAttr::Vectorize:
+ case LoopHintAttr::Interleave:
+ Attrs.VectorizeEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::Unroll:
+ Attrs.UnrollEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::UnrollAndJam:
+ Attrs.UnrollAndJamEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::VectorizePredicate:
+ Attrs.VectorizePredicateEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::Distribute:
+ Attrs.DistributeEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::UnrollCount:
+ case LoopHintAttr::UnrollAndJamCount:
+ case LoopHintAttr::VectorizeWidth:
+ case LoopHintAttr::InterleaveCount:
+ case LoopHintAttr::PipelineDisabled:
+ case LoopHintAttr::PipelineInitiationInterval:
+ case LoopHintAttr::LICMDisabled:
+ llvm_unreachable("Options cannot enabled.");
+ break;
+ }
+ break;
+ case LoopHintAttr::AssumeSafety:
+ switch (Option) {
+ case LoopHintAttr::Vectorize:
+ case LoopHintAttr::Interleave:
+ // Apply "llvm.mem.parallel_loop_access" metadata to load/stores.
+ Attrs.IsParallel = true;
+ Attrs.VectorizeEnable = LoopAttributes::Enable;
+ break;
+ case LoopHintAttr::Unroll:
+ case LoopHintAttr::UnrollAndJam:
+ case LoopHintAttr::VectorizePredicate:
+ case LoopHintAttr::UnrollCount:
+ case LoopHintAttr::UnrollAndJamCount:
+ case LoopHintAttr::VectorizeWidth:
+ case LoopHintAttr::InterleaveCount:
+ case LoopHintAttr::Distribute:
+ case LoopHintAttr::PipelineDisabled:
+ case LoopHintAttr::PipelineInitiationInterval:
+ case LoopHintAttr::LICMDisabled:
+ llvm_unreachable("Options cannot be used to assume mem safety.");
+ break;
+ }
+ break;
+ case LoopHintAttr::Full:
+ switch (Option) {
+ case LoopHintAttr::Unroll:
+ Attrs.UnrollEnable = LoopAttributes::Full;
+ break;
+ case LoopHintAttr::UnrollAndJam:
+ Attrs.UnrollAndJamEnable = LoopAttributes::Full;
+ break;
+ case LoopHintAttr::Vectorize:
+ case LoopHintAttr::Interleave:
+ case LoopHintAttr::UnrollCount:
+ case LoopHintAttr::UnrollAndJamCount:
+ case LoopHintAttr::VectorizeWidth:
+ case LoopHintAttr::InterleaveCount:
+ case LoopHintAttr::Distribute:
+ case LoopHintAttr::PipelineDisabled:
+ case LoopHintAttr::PipelineInitiationInterval:
+ case LoopHintAttr::VectorizePredicate:
+ case LoopHintAttr::LICMDisabled:
+ llvm_unreachable("Options cannot be used with 'full' hint.");
+ break;
+ }
+ break;
+ case LoopHintAttr::FixedWidth:
+ case LoopHintAttr::ScalableWidth:
+ switch (Option) {
+ case LoopHintAttr::VectorizeWidth:
+ Attrs.VectorizeScalable = State == LoopHintAttr::ScalableWidth
+ ? LoopAttributes::Enable
+ : LoopAttributes::Disable;
+ if (LH->getValue())
+ Attrs.VectorizeWidth = ValueInt;
+ break;
+ default:
+ llvm_unreachable("Options cannot be used with 'scalable' hint.");
+ break;
+ }
+ break;
+ case LoopHintAttr::Numeric:
+ switch (Option) {
+ case LoopHintAttr::InterleaveCount:
+ Attrs.InterleaveCount = ValueInt;
+ break;
+ case LoopHintAttr::UnrollCount:
+ Attrs.UnrollCount = ValueInt;
+ break;
+ case LoopHintAttr::UnrollAndJamCount:
+ Attrs.UnrollAndJamCount = ValueInt;
+ break;
+ case LoopHintAttr::PipelineInitiationInterval:
+ Attrs.PipelineInitiationInterval = ValueInt;
+ break;
+ case LoopHintAttr::Unroll:
+ case LoopHintAttr::UnrollAndJam:
+ case LoopHintAttr::VectorizePredicate:
+ case LoopHintAttr::Vectorize:
+ case LoopHintAttr::VectorizeWidth:
+ case LoopHintAttr::Interleave:
+ case LoopHintAttr::Distribute:
+ case LoopHintAttr::PipelineDisabled:
+ case LoopHintAttr::LICMDisabled:
+ llvm_unreachable("Options cannot be assigned a value.");
+ break;
+ }
+ break;
+ }
+ }
+
+ // Identify loop attribute 'code_align' from Attrs.
+ // For attribute code_align:
+ // n - 'llvm.loop.align i32 n' metadata will be emitted.
+ // A source-level [[clang::code_align]] attribute takes precedence over the
+ // -falign-loops=N command-line default.
+ if (const auto *CodeAlign = getSpecificAttr<CodeAlignAttr>(StmtAttrs)) {
+ const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
+ llvm::APSInt ArgVal = CE->getResultAsAPSInt();
+ Attrs.CodeAlign = ArgVal.getSExtValue();
+ } else if (CGOpts.LoopAlignment) {
+ Attrs.CodeAlign = CGOpts.LoopAlignment;
+ }
+
+ Attrs.MustProgress = MustProgress;
+
+ if (CGOpts.OptimizationLevel > 0)
+ // Disable unrolling for the loop, if unrolling is disabled (via
+ // -fno-unroll-loops) and no pragmas override the decision.
+ if (!CGOpts.UnrollLoops &&
+ (Attrs.UnrollEnable == LoopAttributes::Unspecified &&
+ Attrs.UnrollCount == 0))
+ Attrs.UnrollEnable = LoopAttributes::Disable;
+}
+
+} // namespace clang::CodeGenUtils
diff --git a/clang/test/CIR/CodeGen/loop-hints-stds.cpp b/clang/test/CIR/CodeGen/loop-hints-stds.cpp
new file mode 100644
index 0000000000000..f23e8076041cb
--- /dev/null
+++ b/clang/test/CIR/CodeGen/loop-hints-stds.cpp
@@ -0,0 +1,84 @@
+// 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-prefixes=CIR,CXX17
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-98.cir
+// RUN: FileCheck --input-file=%t-98.cir %s --check-prefixes=CIR,CXX98
+
+// C++17 has different mustprogress guarantees that dont' exist before this, so
+// mustprogress changes behavior.
+
+void use(int);
+
+// CIR-LABEL: cir.func {{.*}} @_Z5hintsi
+void hints(int n) {
+// CXX17: } loop_hints < must_progress unroll_count = 4>
+// CXX98: } loop_hints < unroll_count = 4>
+#pragma GCC unroll 4
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop unroll(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+// CXX17: } loop_hints < must_progress unroll = disable>
+// CXX98: } loop_hints < unroll = disable>
+#pragma clang loop unroll(enable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+// CXX17: } loop_hints < must_progress unroll = enable>
+// CXX98: } loop_hints < unroll = enable>
+#pragma clang loop interleave(enable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+// CXX17: } loop_hints < must_progress vectorize_enable>
+// CXX98: } loop_hints < vectorize_enable>
+#pragma clang loop vectorize_width(4) unroll_count(2)
+ for (int i = 0; i < n; ++i)
+ use(i);
+// CXX17: } loop_hints < must_progress vectorize_width = 4 vectorize_scalable = false unroll_count = 2>
+// CXX98: } loop_hints < vectorize_width = 4 vectorize_scalable = false unroll_count = 2>
+ for (int i = 0; i < n; ++i)
+ use(i);
+// CXX17: } loop_hints < must_progress>
+// CXX98-NOT: loop_hints
+
+// CIR: cir.return
+}
+
+
+// CIR-LABEL: cir.func {{.*}} @_Z6nestedi
+void nested(int n) {
+#pragma clang loop unroll(full)
+ for (int i = 0; i < n; ++i) {
+#pragma clang loop vectorize(enable)
+ for (int j = 0; j < n; ++j)
+ use(j);
+// CXX17: } loop_hints < must_progress vectorize_enable>
+// CXX98: } loop_hints < vectorize_enable>
+ }
+// CXX17: } loop_hints < must_progress unroll = full>
+// CXX98: } loop_hints < unroll = full>
+}
+
+
+template <typename T> void tmpl(T n) {
+#pragma clang loop unroll_count(3)
+ while (n--)
+ use(n);
+}
+
+void instantiate() {
+ tmpl(4);
+// CIR-LABEL: cir.func {{.*}} @_Z4tmplIiEvT_
+// CXX17: } loop_hints < must_progress unroll_count = 3>
+// CXX98: } loop_hints < unroll_count = 3>
+ auto l = [](int n) {
+#pragma nounroll
+ do {
+ use(n);
+ } while (--n);
+ };
+ l(2);
+// CIR-LABEL: cir.func {{.*}} @_ZZ11instantiatevENK3$_0clEi
+// CXX17: } loop_hints < must_progress unroll = disable>
+// CXX98: } loop_hints < unroll = disable>
+}
+
diff --git a/clang/test/CIR/CodeGen/loop-hints.c b/clang/test/CIR/CodeGen/loop-hints.c
new file mode 100644
index 0000000000000..98b9e9291b296
--- /dev/null
+++ b/clang/test/CIR/CodeGen/loop-hints.c
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=c99 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-c99.cir
+// RUN: FileCheck --input-file=%t-c99.cir %s --check-prefixes=CIR,C99
+// RUN: %clang_cc1 -std=c11 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-c11.cir
+// RUN: FileCheck --input-file=%t-c11.cir %s --check-prefixes=CIR,C11
+// RUN: %clang_cc1 -std=c99 -ffinite-loops -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-finite.cir
+// RUN: FileCheck --input-file=%t-finite.cir %s --check-prefixes=CIR,FINITE
+// RUN: %clang_cc1 -std=c11 -fno-finite-loops -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-nofinite.cir
+// RUN: FileCheck --input-file=%t-nofinite.cir %s --check-prefixes=CIR,NOFINITE
+
+// In C11 a loop must make progress unless its controlling expression is a
+// constant. C99 loops don't, unless -ffinite-loops says so, and
+// -fno-finite-loops turns it off. A trivial infinite loop never does.
+
+void use(int);
+
+void f(int n) {
+ for (int i = 0; i < n; ++i)
+ use(i);
+ while (n)
+ --n;
+ do {
+ --n;
+ } while (n > 0);
+ while (1)
+ use(n);
+ for (;;)
+ use(n);
+ while (1) {
+ }
+}
+
+// CIR-LABEL: cir.func {{.*}} @f
+// CIR: cir.for
+// C99-NOT: loop_hints
+// C11: } loop_hints < must_progress>
+// FINITE: } loop_hints < must_progress>
+// NOFINITE-NOT: loop_hints
+// CIR: cir.while
+// C11: } loop_hints < must_progress>
+// FINITE: } loop_hints < must_progress>
+// CIR: cir.do
+// C11: } loop_hints < must_progress>
+// FINITE: } loop_hints < must_progress>
+// With a constant condition, only -ffinite-loops marks the loops, unless they
+// are trivially empty.
+// CIR: cir.while
+// FINITE: } loop_hints < must_progress>
+// CIR: cir.for
+// FINITE: } loop_hints < must_progress>
+// CIR: cir.while
+// CIR-NOT: loop_hints
+// CIR: cir.return
diff --git a/clang/test/CIR/CodeGen/loop-hints.cpp b/clang/test/CIR/CodeGen/loop-hints.cpp
new file mode 100644
index 0000000000000..706dec0baf11a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/loop-hints.cpp
@@ -0,0 +1,139 @@
+// 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 -O2 -fno-unroll-loops -falign-loops=32 -emit-cir %s -o %t-opts.cir
+// RUN: FileCheck --input-file=%t-opts.cir %s --check-prefix=OPTS
+
+void use(int);
+int arr[3];
+
+// C++11 and later loops are 'mustprogress', unless they are trivial infinite
+// loops.
+void must_progress(int n) {
+ for (int i = 0; i < n; ++i)
+ use(i);
+ while (n--)
+ use(n);
+ do {
+ use(n);
+ } while (n > 0);
+ // Like classic codegen, range-for loops are not 'mustprogress'.
+ for (int x : arr)
+ use(x);
+ for (;;) {
+ }
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z13must_progressi
+// CIR: cir.for
+// CIR: } loop_hints < must_progress>
+// CIR: cir.while
+// CIR: } loop_hints < must_progress>
+// CIR: cir.do
+// CIR: } loop_hints < must_progress>
+// CIR: cir.for
+// CIR-NOT: loop_hints
+// CIR: cir.for
+// CIR-NOT: loop_hints
+// CIR: cir.return
+
+void pragmas(int n) {
+#pragma clang loop unroll_count(4) vectorize(enable) interleave_count(2)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma nounroll
+ while (n)
+ --n;
+#pragma unroll
+ do {
+ --n;
+ } while (n > 0);
+#pragma unroll 8
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop unroll(full)
+ for (int i = 0; i < 4; ++i)
+ use(i);
+#pragma clang loop vectorize(disable) interleave(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop vectorize_width(8)
+ for (int x : arr)
+ use(x);
+#pragma clang loop vectorize_width(4, scalable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop vectorize_width(4, fixed)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop vectorize(assume_safety)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop vectorize_predicate(enable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop vectorize_predicate(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop distribute(enable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop distribute(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma unroll_and_jam
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma nounroll_and_jam
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma unroll_and_jam(4)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop pipeline(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop pipeline_initiation_interval(3)
+ for (int i = 0; i < n; ++i)
+ use(i);
+#pragma clang loop licm(disable)
+ for (int i = 0; i < n; ++i)
+ use(i);
+ [[clang::code_align(16)]]
+ for (int i = 0; i < n; ++i)
+ use(i);
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z7pragmasi
+// CIR: } loop_hints < must_progress vectorize_enable interleave_count = 2 unroll_count = 4>
+// CIR: } loop_hints < must_progress unroll = disable>
+// CIR: } loop_hints < must_progress unroll = enable>
+// CIR: } loop_hints < must_progress unroll_count = 8>
+// CIR: } loop_hints < must_progress unroll = full>
+// CIR: } loop_hints < must_progress vectorize_width = 1 vectorize_scalable = false interleave_count = 1>
+// CIR: } loop_hints < vectorize_width = 8 vectorize_scalable = false>
+// CIR: } loop_hints < must_progress vectorize_width = 4 vectorize_scalable = true>
+// CIR: } loop_hints < must_progress vectorize_width = 4 vectorize_scalable = false>
+// CIR: } loop_hints < must_progress vectorize_enable parallel>
+// CIR: } loop_hints < must_progress vectorize_predicate = true>
+// CIR: } loop_hints < must_progress vectorize_predicate = false>
+// CIR: } loop_hints < must_progress distribute = true>
+// CIR: } loop_hints < must_progress distribute = false>
+// CIR: } loop_hints < must_progress unroll_and_jam = enable>
+// CIR: } loop_hints < must_progress unroll_and_jam = disable>
+// CIR: } loop_hints < must_progress unroll_and_jam_count = 4>
+// CIR: } loop_hints < must_progress pipeline_disabled>
+// CIR: } loop_hints < must_progress pipeline_initiation_interval = 3>
+// CIR: } loop_hints < must_progress licm_disabled>
+// CIR: } loop_hints < must_progress code_align = 16>
+
+// -falign-loops applies to every loop, unless [[clang::code_align]] overrides
+// it. -fno-unroll-loops disables unrolling of every loop that no pragma
+// overrides.
+// OPTS-LABEL: cir.func {{.*}} @_Z7pragmasi
+// OPTS: } loop_hints < must_progress vectorize_enable interleave_count = 2 unroll_count = 4 code_align = 32>
+// OPTS: } loop_hints < must_progress unroll = disable code_align = 32>
+// OPTS: } loop_hints < must_progress unroll = enable code_align = 32>
+// OPTS: } loop_hints < must_progress unroll_count = 8 code_align = 32>
+// OPTS: } loop_hints < must_progress unroll = full code_align = 32>
+// OPTS: } loop_hints < must_progress vectorize_width = 1 vectorize_scalable = false interleave_count = 1 unroll = disable code_align = 32>
+// OPTS: } loop_hints < must_progress unroll = disable code_align = 16>
diff --git a/clang/test/CIR/CodeGenHLSL/loop-hints.hlsl b/clang/test/CIR/CodeGenHLSL/loop-hints.hlsl
new file mode 100644
index 0000000000000..4aa683b00dd5e
--- /dev/null
+++ b/clang/test/CIR/CodeGenHLSL/loop-hints.hlsl
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -x hlsl -triple spirv-unknown-vulkan-library -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+void use(int);
+
+[numthreads(1, 1, 1)] void main() {
+ int n = 4;
+ [unroll] for (int i = 0; i < n; ++i) use(i);
+ [unroll(4)] for (int i = 0; i < n; ++i) use(i);
+ [loop] for (int i = 0; i < n; ++i) use(i);
+}
+
+// CHECK-LABEL: cir.func {{.*}} @_Z4mainv
+// CHECK: } loop_hints < must_progress unroll = enable>
+// CHECK: } loop_hints < must_progress unroll_count = 4>
+// CHECK: } loop_hints < must_progress unroll = disable>
diff --git a/clang/test/CIR/CodeGenOpenACC/loop.cpp b/clang/test/CIR/CodeGenOpenACC/loop.cpp
index 65ab68c5adb6b..b0feb82387a50 100644
--- a/clang/test/CIR/CodeGenOpenACC/loop.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/loop.cpp
@@ -26,7 +26,7 @@ extern "C" void acc_loop(int *A, int *B, int *C, int N) {
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK: cir.yield
- // CHECK-NEXT: } loc
+ // CHECK-NEXT: } loop_hints < must_progress> loc
// CHECK-NEXT: } loc
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } independent loc
diff --git a/clang/test/CIR/CodeGenOpenCL/loop-hints.cl b/clang/test/CIR/CodeGenOpenCL/loop-hints.cl
new file mode 100644
index 0000000000000..dcf1ec86bc68e
--- /dev/null
+++ b/clang/test/CIR/CodeGenOpenCL/loop-hints.cl
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -x cl -triple spir64 -cl-std=CL2.0 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+void use(int);
+
+kernel void k(int n) {
+ __attribute__((opencl_unroll_hint)) for (int i = 0; i < n; ++i)
+ use(i);
+ __attribute__((opencl_unroll_hint(1))) for (int i = 0; i < n; ++i)
+ use(i);
+ __attribute__((opencl_unroll_hint(8))) for (int i = 0; i < n; ++i)
+ use(i);
+}
+
+// CHECK-LABEL: cir.func {{.*}} @k
+// CHECK: } loop_hints < unroll = enable>
+// CHECK: } loop_hints < unroll = disable>
+// CHECK: } loop_hints < unroll_count = 8>
diff --git a/clang/test/CIR/IR/invalid-loop-hints.cir b/clang/test/CIR/IR/invalid-loop-hints.cir
new file mode 100644
index 0000000000000..2f09d8a26d96e
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-loop-hints.cir
@@ -0,0 +1,73 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+cir.func @bad_bool(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +2 {{expected attribute value}}
+ // expected-error at +1 {{failed to parse CIR_LoopHintAttr parameter 'vectorize_scalable'}}
+ } loop_hints < vectorize_scalable = maybe>
+ cir.return
+}
+
+// -----
+
+cir.func @bad_mode(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +1 {{expected '>'}}
+ } loop_hints < unroll = sometimes>
+ cir.return
+}
+
+// -----
+
+cir.func @missing_value(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +2 {{expected integer value}}
+ // expected-error at +1 {{failed to parse CIR_LoopHintAttr parameter 'vectorize_width'}}
+ } loop_hints < vectorize_width = >
+ cir.return
+}
+
+// -----
+
+cir.func @unknown_entry(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +1 {{expected '>'}}
+ } loop_hints < bogus>
+ cir.return
+}
+
+// -----
+
+cir.func @out_of_order(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +1 {{expected '>'}}
+ } loop_hints < unroll_count = 4 must_progress>
+ cir.return
+}
+
+// -----
+
+cir.func @repeated(%c: !cir.bool) {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // expected-error at +1 {{expected '>'}}
+ } loop_hints < must_progress must_progress>
+ cir.return
+}
diff --git a/clang/test/CIR/IR/loop-hints.cir b/clang/test/CIR/IR/loop-hints.cir
new file mode 100644
index 0000000000000..3653d7d1b172b
--- /dev/null
+++ b/clang/test/CIR/IR/loop-hints.cir
@@ -0,0 +1,40 @@
+// RUN: cir-opt %s | cir-opt | FileCheck %s
+
+cir.func @loops(%c: !cir.bool) {
+ // CHECK: cir.while {
+ cir.while {
+ cir.condition(%c)
+ } do {
+ cir.yield
+ // CHECK: } loop_hints < must_progress>
+ } loop_hints < must_progress>
+
+ // CHECK: cir.do {
+ cir.do {
+ cir.yield
+ } while {
+ cir.condition(%c)
+ // CHECK: } loop_hints <>
+ } loop_hints <>
+
+ // CHECK: cir.for : cond {
+ cir.for : cond {
+ cir.condition(%c)
+ } body {
+ cir.yield
+ } step {
+ cir.yield
+ // CHECK: } loop_hints < must_progress vectorize_enable vectorize_width = 4 vectorize_scalable = true vectorize_predicate = false interleave_count = 2 unroll = enable unroll_count = 8 distribute = true>
+ } loop_hints < must_progress vectorize_enable vectorize_width = 4 vectorize_scalable = true vectorize_predicate = false interleave_count = 2 unroll = enable unroll_count = 8 distribute = true>
+
+ // CHECK: cir.for : cond {
+ cir.for : cond {
+ cir.condition(%c)
+ } body {
+ cir.yield
+ } step {
+ cir.yield
+ // CHECK: } loop_hints < unroll = disable unroll_and_jam = full unroll_and_jam_count = 2 pipeline_disabled pipeline_initiation_interval = 3 licm_disabled parallel code_align = 64>
+ } loop_hints < unroll = disable unroll_and_jam = full unroll_and_jam_count = 2 pipeline_disabled pipeline_initiation_interval = 3 licm_disabled parallel code_align = 64>
+ cir.return
+}
More information about the cfe-commits
mailing list