[clang] [Clang][OpenMP] Optimize `collapse` IV bit-width precision expression (PR #225612)
Amit Tiwari via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 22 23:59:20 PDT 2026
https://github.com/loopacino created https://github.com/llvm/llvm-project/pull/225612
`collapse` used to build both a 32-bit and a 64-bit trip-count, then keep one.
This patch does the same in `checkOpenMPLoop`:
If we know 32-bit is enough, build only 32-bit.
Else build 64-bit first.
Build 32-bit only when the product is a compile-time constant and fits.
Origin: `flatten` already builds only the width it keeps.
>From 3a6a435bfa37bcb9676c61debc99899745a1eae3 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Wed, 23 Sep 2026 02:55:31 -0400
Subject: [PATCH] optimize bit-width
---
clang/lib/Sema/SemaOpenMP.cpp | 102 +++++++++---------
.../test/OpenMP/collapse_iv_width_codegen.cpp | 52 +++++++++
2 files changed, 105 insertions(+), 49 deletions(-)
create mode 100644 clang/test/OpenMP/collapse_iv_width_codegen.cpp
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 88b642b73895f..865da6f5aa0fd 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -10363,29 +10363,10 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
// Precondition tests if there is at least one iteration (all conditions are
// true).
auto PreCond = ExprResult(IterSpaces[0].PreCond);
- Expr *N0 = IterSpaces[0].NumIterations;
- ExprResult LastIteration32 = widenIterationCount(
- /*Bits=*/32,
- SemaRef
- .PerformImplicitConversion(N0->IgnoreImpCasts(), N0->getType(),
- AssignmentAction::Converting,
- /*AllowExplicit=*/true)
- .get(),
- SemaRef);
- ExprResult LastIteration64 = widenIterationCount(
- /*Bits=*/64,
- SemaRef
- .PerformImplicitConversion(N0->IgnoreImpCasts(), N0->getType(),
- AssignmentAction::Converting,
- /*AllowExplicit=*/true)
- .get(),
- SemaRef);
-
- if (!LastIteration32.isUsable() || !LastIteration64.isUsable())
- return NestedLoopCount;
-
ASTContext &C = SemaRef.Context;
- bool AllCountsNeedLessThan32Bits = C.getTypeSize(N0->getType()) < 32;
+ unsigned FirstCountBits =
+ C.getTypeSize(IterSpaces[0].NumIterations->getType());
+ bool AllCountsNeedLessThan32Bits = FirstCountBits < 32;
Scope *CurScope = DSA.getCurScope();
for (unsigned Cnt = 1; Cnt < NestedLoopCount; ++Cnt) {
@@ -10395,37 +10376,63 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
PreCond.get(), IterSpaces[Cnt].PreCond);
}
Expr *N = IterSpaces[Cnt].NumIterations;
- SourceLocation Loc = N->getExprLoc();
AllCountsNeedLessThan32Bits &= C.getTypeSize(N->getType()) < 32;
- if (LastIteration32.isUsable())
- LastIteration32 = SemaRef.BuildBinOp(
- CurScope, Loc, BO_Mul, LastIteration32.get(),
- SemaRef
- .PerformImplicitConversion(N->IgnoreImpCasts(), N->getType(),
- AssignmentAction::Converting,
- /*AllowExplicit=*/true)
- .get());
- if (LastIteration64.isUsable())
- LastIteration64 = SemaRef.BuildBinOp(
- CurScope, Loc, BO_Mul, LastIteration64.get(),
+ }
+
+ auto BuildLastIteration = [&](unsigned Bits) -> ExprResult {
+ ExprResult Result;
+ for (unsigned Cnt = 0; Cnt < NestedLoopCount; ++Cnt) {
+ Expr *N = IterSpaces[Cnt].NumIterations;
+ ExprResult Count = widenIterationCount(
+ Bits,
SemaRef
.PerformImplicitConversion(N->IgnoreImpCasts(), N->getType(),
AssignmentAction::Converting,
/*AllowExplicit=*/true)
- .get());
- }
+ .get(),
+ SemaRef);
+ if (!Count.isUsable())
+ return ExprError();
+ if (Cnt == 0)
+ Result = Count;
+ else
+ Result = SemaRef.BuildBinOp(CurScope, N->getExprLoc(), BO_Mul,
+ Result.get(), Count.get());
+ if (!Result.isUsable())
+ return ExprError();
+ }
+ return Result;
+ };
- // Choose either the 32-bit or 64-bit version.
- ExprResult LastIteration = LastIteration64;
+ // Build the 32-bit tree immediately only when it is always selected.
+ // Otherwise, build the 64-bit tree first and build the 32-bit tree only when
+ // the constant product may fit.
+ ExprResult LastIteration;
if (SemaRef.getLangOpts().OpenMPOptimisticCollapse ||
- (LastIteration32.isUsable() &&
- C.getTypeSize(LastIteration32.get()->getType()) == 32 &&
- (AllCountsNeedLessThan32Bits || NestedLoopCount == 1 ||
- fitsInto(
- /*Bits=*/32,
- LastIteration32.get()->getType()->hasSignedIntegerRepresentation(),
- LastIteration64.get(), SemaRef))))
- LastIteration = LastIteration32;
+ AllCountsNeedLessThan32Bits ||
+ (NestedLoopCount == 1 && FirstCountBits == 32)) {
+ LastIteration = BuildLastIteration(/*Bits=*/32);
+ } else {
+ ExprResult LastIteration64 = BuildLastIteration(/*Bits=*/64);
+ if (!LastIteration64.isUsable())
+ return NestedLoopCount;
+ LastIteration = LastIteration64;
+ if (LastIteration64.get()->isIntegerConstantExpr(C)) {
+ ExprResult LastIteration32 = BuildLastIteration(/*Bits=*/32);
+ if (LastIteration32.isUsable() &&
+ C.getTypeSize(LastIteration32.get()->getType()) == 32 &&
+ fitsInto(
+ /*Bits=*/32,
+ LastIteration32.get()
+ ->getType()
+ ->hasSignedIntegerRepresentation(),
+ LastIteration64.get(), SemaRef))
+ LastIteration = LastIteration32;
+ }
+ }
+ if (!LastIteration.isUsable())
+ return NestedLoopCount;
+
QualType VType = LastIteration.get()->getType();
QualType RealVType = VType;
QualType StrideVType = VType;
@@ -10436,9 +10443,6 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
SemaRef.Context.getIntTypeForBitwidth(/*DestWidth=*/64, /*Signed=*/1);
}
- if (!LastIteration.isUsable())
- return 0;
-
// Save the number of iterations.
ExprResult NumIterations = LastIteration;
{
diff --git a/clang/test/OpenMP/collapse_iv_width_codegen.cpp b/clang/test/OpenMP/collapse_iv_width_codegen.cpp
new file mode 100644
index 0000000000000..3e1e5c93a4da0
--- /dev/null
+++ b/clang/test/OpenMP/collapse_iv_width_codegen.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -verify -fopenmp -std=c++20 -Wno-bit-int-extension \
+// RUN: -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+void one_i32(unsigned n) {
+#pragma omp parallel for collapse(1)
+ for (unsigned i = 0; i < n; ++i)
+ ;
+}
+
+// CHECK-LABEL: define internal void @_Z7one_i32j.omp_outlined(
+// CHECK: call void @__kmpc_for_static_init_4u(
+
+void one_i40(_BitInt(40) n) {
+#pragma omp parallel for collapse(1)
+ for (_BitInt(40) i = 0; i < n; ++i)
+ ;
+}
+
+// CHECK-LABEL: define internal void @_Z7one_i40DB40_.omp_outlined(
+// CHECK: call void @__kmpc_for_static_init_8(
+
+void dynamic_two(unsigned n, unsigned m) {
+#pragma omp parallel for collapse(2)
+ for (unsigned i = 0; i < n; ++i)
+ for (unsigned j = 0; j < m; ++j)
+ ;
+}
+
+// CHECK-LABEL: define internal void @_Z11dynamic_twojj.omp_outlined(
+// CHECK: call void @__kmpc_for_static_init_8(
+
+void fit_constant() {
+#pragma omp parallel for collapse(2)
+ for (int i = 0; i < 100; ++i)
+ for (int j = 0; j < 100; ++j)
+ ;
+}
+
+// CHECK-LABEL: define internal void @_Z12fit_constantv.omp_outlined(
+// CHECK: call void @__kmpc_for_static_init_4(
+
+void wide_constant() {
+#pragma omp parallel for collapse(2)
+ for (int i = 0; i < 100000; ++i)
+ for (int j = 0; j < 100000; ++j)
+ ;
+}
+
+// CHECK-LABEL: define internal void @_Z13wide_constantv.omp_outlined(
+// CHECK: call void @__kmpc_for_static_init_8(
More information about the cfe-commits
mailing list