[llvm-branch-commits] [clang] [openmp] [Clang][OpenMP][Tile] Allow non-constant tile sizes. (PR #91345)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed May 8 04:33:47 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Michael Kruse (Meinersbur)
<details>
<summary>Changes</summary>
Allow non-constants in the `sizes` clause such as
```
#pragma omp tile sizes(a)
for (int i = 0; i < n; ++i)
```
This is permitted since tile was introduced in [OpenMP 5.1](https://www.openmp.org/spec-html/5.1/openmpsu53.html#x78-860002.11.9).
It is possible to sneak-in negative numbers at runtime as in
```
int a = -1;
#pragma omp tile sizes(a)
```
Even though it is not well-formed, it should still result in every loop iteration to be executed exactly once, an invariant of the tile construct that we should ensure. `ParseOpenMPExprListClause` is extracted-out to be reused by the `permutation` clause if the `interchange` construct. Some care was put in to ensure correct behavior in template contexts.
This patch also adds end-to-end tests. This is to avoid errors like the off-by-one error I caused with the initial implementation of the unroll construct.
---
Patch is 41.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/91345.diff
9 Files Affected:
- (modified) clang/include/clang/Parse/Parser.h (+17)
- (modified) clang/lib/Parse/ParseOpenMP.cpp (+40-25)
- (modified) clang/lib/Sema/SemaOpenMP.cpp (+88-25)
- (modified) clang/test/OpenMP/tile_ast_print.cpp (+17)
- (modified) clang/test/OpenMP/tile_codegen.cpp (+201-15)
- (modified) clang/test/OpenMP/tile_messages.cpp (+43-7)
- (added) openmp/runtime/test/transform/tile/intfor.c (+191)
- (added) openmp/runtime/test/transform/tile/negtile_intfor.c (+44)
- (added) openmp/runtime/test/transform/tile/parallel-wsloop-collapse-intfor.cpp (+100)
``````````diff
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index daefd4f28f011..1b500c11457f4 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3553,6 +3553,23 @@ class Parser : public CodeCompletionHandler {
OMPClause *ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
OpenMPClauseKind Kind, bool ParseOnly);
+ /// Parses a clause consisting of a list of expressions.
+ ///
+ /// \param Kind The clause to parse.
+ /// \param ClauseNameLoc [out] The location of the clause name.
+ /// \param OpenLoc [out] The location of '('.
+ /// \param CloseLoc [out] The location of ')'.
+ /// \param Exprs [out] The parsed expressions.
+ /// \param ReqIntConst If true, each expression must be an integer constant.
+ ///
+ /// \return Whether the clause was parsed successfully.
+ bool ParseOpenMPExprListClause(OpenMPClauseKind Kind,
+ SourceLocation &ClauseNameLoc,
+ SourceLocation &OpenLoc,
+ SourceLocation &CloseLoc,
+ SmallVectorImpl<Expr *> &Exprs,
+ bool ReqIntConst = false);
+
/// Parses and creates OpenMP 5.0 iterators expression:
/// <iterators> = 'iterator' '(' { [ <iterator-type> ] identifier =
/// <range-specification> }+ ')'
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 18ba1185ee8de..b8b32f9546c4f 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3107,34 +3107,14 @@ bool Parser::ParseOpenMPSimpleVarList(
}
OMPClause *Parser::ParseOpenMPSizesClause() {
- SourceLocation ClauseNameLoc = ConsumeToken();
+ SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
SmallVector<Expr *, 4> ValExprs;
-
- BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
- if (T.consumeOpen()) {
- Diag(Tok, diag::err_expected) << tok::l_paren;
+ if (ParseOpenMPExprListClause(OMPC_sizes, ClauseNameLoc, OpenLoc, CloseLoc,
+ ValExprs))
return nullptr;
- }
-
- while (true) {
- ExprResult Val = ParseConstantExpression();
- if (!Val.isUsable()) {
- T.skipToEnd();
- return nullptr;
- }
-
- ValExprs.push_back(Val.get());
-
- if (Tok.is(tok::r_paren) || Tok.is(tok::annot_pragma_openmp_end))
- break;
-
- ExpectAndConsume(tok::comma);
- }
-
- T.consumeClose();
- return Actions.OpenMP().ActOnOpenMPSizesClause(
- ValExprs, ClauseNameLoc, T.getOpenLocation(), T.getCloseLocation());
+ return Actions.OpenMP().ActOnOpenMPSizesClause(ValExprs, ClauseNameLoc,
+ OpenLoc, CloseLoc);
}
OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) {
@@ -4991,3 +4971,38 @@ OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
OMPVarListLocTy Locs(Loc, LOpen, Data.RLoc);
return Actions.OpenMP().ActOnOpenMPVarListClause(Kind, Vars, Locs, Data);
}
+
+bool Parser::ParseOpenMPExprListClause(OpenMPClauseKind Kind,
+ SourceLocation &ClauseNameLoc,
+ SourceLocation &OpenLoc,
+ SourceLocation &CloseLoc,
+ SmallVectorImpl<Expr *> &Exprs,
+ bool ReqIntConst) {
+ assert(getOpenMPClauseName(Kind) == PP.getSpelling(Tok) &&
+ "Expected parsing to start at clause name");
+ ClauseNameLoc = ConsumeToken();
+
+ // Parse inside of '(' and ')'.
+ BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return true;
+ }
+
+ // Parse the list with interleaved commas.
+ do {
+ ExprResult Val =
+ ReqIntConst ? ParseConstantExpression() : ParseAssignmentExpression();
+ if (!Val.isUsable()) {
+ // Encountered something other than an expression; abort to ')'.
+ T.skipToEnd();
+ return true;
+ }
+ Exprs.push_back(Val.get());
+ } while (TryConsumeToken(tok::comma));
+
+ bool Result = T.consumeClose();
+ OpenLoc = T.getOpenLocation();
+ CloseLoc = T.getCloseLocation();
+ return Result;
+}
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index fff4c7350f0f7..6e19f47356964 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -15111,13 +15111,11 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
ASTContext &Context = getASTContext();
Scope *CurScope = SemaRef.getCurScope();
- auto SizesClauses =
- OMPExecutableDirective::getClausesOfKind<OMPSizesClause>(Clauses);
- if (SizesClauses.empty()) {
- // A missing 'sizes' clause is already reported by the parser.
+ const OMPSizesClause *SizesClause =
+ OMPExecutableDirective::getSingleClause<OMPSizesClause>(Clauses);
+ if (!SizesClause ||
+ llvm::any_of(SizesClause->getSizesRefs(), [](Expr *E) { return !E; }))
return StmtError();
- }
- const OMPSizesClause *SizesClause = *SizesClauses.begin();
unsigned NumLoops = SizesClause->getNumSizes();
// Empty statement should only be possible if there already was an error.
@@ -15138,6 +15136,13 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
return OMPTileDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, nullptr, nullptr);
+ assert(LoopHelpers.size() == NumLoops &&
+ "Expecting loop iteration space dimensionality to match number of "
+ "affected loops");
+ assert(OriginalInits.size() == NumLoops &&
+ "Expecting loop iteration space dimensionality to match number of "
+ "affected loops");
+
SmallVector<Decl *, 4> PreInits;
CaptureVars CopyTransformer(SemaRef);
@@ -15197,6 +15202,36 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
// Once the original iteration values are set, append the innermost body.
Stmt *Inner = Body;
+ auto MakeDimTileSize = [&SemaRef = this->SemaRef, &CopyTransformer, &Context,
+ SizesClause, CurScope](int I) -> Expr * {
+ Expr *DimTileSizeExpr = SizesClause->getSizesRefs()[I];
+ if (isa<ConstantExpr>(DimTileSizeExpr))
+ return AssertSuccess(CopyTransformer.TransformExpr(DimTileSizeExpr));
+
+ // When the tile size is not a constant but a variable, it is possible to
+ // pass non-positive numbers. To preserve the invariant that every loop
+ // iteration is executed at least once and not cause an infinite loop, apply
+ // a minimum tile size of one.
+ // Build expr:
+ // \code{c}
+ // (TS <= 0) ? 1 : TS
+ // \endcode
+ QualType DimTy = DimTileSizeExpr->getType();
+ uint64_t DimWidth = Context.getTypeSize(DimTy);
+ IntegerLiteral *Zero = IntegerLiteral::Create(
+ Context, llvm::APInt::getZero(DimWidth), DimTy, {});
+ IntegerLiteral *One =
+ IntegerLiteral::Create(Context, llvm::APInt(DimWidth, 1), DimTy, {});
+ Expr *Cond = AssertSuccess(SemaRef.BuildBinOp(
+ CurScope, {}, BO_LE,
+ AssertSuccess(CopyTransformer.TransformExpr(DimTileSizeExpr)), Zero));
+ Expr *MinOne = new (Context) ConditionalOperator(
+ Cond, {}, One, {},
+ AssertSuccess(CopyTransformer.TransformExpr(DimTileSizeExpr)), DimTy,
+ VK_PRValue, OK_Ordinary);
+ return MinOne;
+ };
+
// Create tile loops from the inside to the outside.
for (int I = NumLoops - 1; I >= 0; --I) {
OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I];
@@ -15207,11 +15242,6 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
// Commonly used variables. One of the constraints of an AST is that every
// node object must appear at most once, hence we define lamdas that create
// a new AST node at every use.
- auto MakeDimTileSize = [&SemaRef = this->SemaRef, &CopyTransformer, I,
- SizesClause]() -> Expr * {
- Expr *DimTileSize = SizesClause->getSizesRefs()[I];
- return AssertSuccess(CopyTransformer.TransformExpr(DimTileSize));
- };
auto MakeTileIVRef = [&SemaRef = this->SemaRef, &TileIndVars, I, CntTy,
OrigCntVar]() {
return buildDeclRefExpr(SemaRef, TileIndVars[I], CntTy,
@@ -15238,7 +15268,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
// .tile.iv < min(.floor.iv + DimTileSize, NumIterations)
ExprResult EndOfTile =
SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_Add,
- MakeFloorIVRef(), MakeDimTileSize());
+ MakeFloorIVRef(), MakeDimTileSize(I));
if (!EndOfTile.isUsable())
return StmtError();
ExprResult IsPartialTile =
@@ -15298,11 +15328,6 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
QualType CntTy = OrigCntVar->getType();
// Commonly used variables.
- auto MakeDimTileSize = [&SemaRef = this->SemaRef, &CopyTransformer, I,
- SizesClause]() -> Expr * {
- Expr *DimTileSize = SizesClause->getSizesRefs()[I];
- return AssertSuccess(CopyTransformer.TransformExpr(DimTileSize));
- };
auto MakeFloorIVRef = [&SemaRef = this->SemaRef, &FloorIndVars, I, CntTy,
OrigCntVar]() {
return buildDeclRefExpr(SemaRef, FloorIndVars[I], CntTy,
@@ -15331,7 +15356,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
// For incr-statement: .floor.iv += DimTileSize
ExprResult IncrStmt =
SemaRef.BuildBinOp(CurScope, LoopHelper.Inc->getExprLoc(), BO_AddAssign,
- MakeFloorIVRef(), MakeDimTileSize());
+ MakeFloorIVRef(), MakeDimTileSize(I));
if (!IncrStmt.isUsable())
return StmtError();
@@ -17432,16 +17457,54 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef<Expr *> SizeExprs,
SourceLocation StartLoc,
SourceLocation LParenLoc,
SourceLocation EndLoc) {
- for (Expr *SizeExpr : SizeExprs) {
- ExprResult NumForLoopsResult = VerifyPositiveIntegerConstantInClause(
- SizeExpr, OMPC_sizes, /*StrictlyPositive=*/true);
- if (!NumForLoopsResult.isUsable())
- return nullptr;
+ SmallVector<Expr *> SanitizedSizeExprs;
+ llvm::append_range(SanitizedSizeExprs, SizeExprs);
+
+ for (Expr *&SizeExpr : SanitizedSizeExprs) {
+ // Skip if already sanitized, e.g. during a partial template instantiation.
+ if (!SizeExpr)
+ continue;
+
+ bool IsValid = isNonNegativeIntegerValue(SizeExpr, SemaRef, OMPC_sizes,
+ /*StrictlyPositive=*/true);
+
+ // isNonNegativeIntegerValue returns true for non-integral types (but still
+ // emits error diagnostic), so check for the expected type explicitly.
+ QualType SizeTy = SizeExpr->getType();
+ if (!SizeTy->isIntegerType())
+ IsValid = false;
+
+ // Handling in templates is tricky. There are four possibilities to
+ // consider:
+ //
+ // 1a. The expression is valid and we are in a instantiated template or not
+ // in a template:
+ // Pass valid expression to be further analysed later in Sema.
+ // 1b. The expression is valid and we are in a template (including partial
+ // instantiation):
+ // isNonNegativeIntegerValue skipped any checks so there is no
+ // guarantee it will be correct after instantiation.
+ // ActOnOpenMPSizesClause will be called again at instantiation when
+ // it is not in a dependent context anymore. This may cause warnings
+ // to be emitted multiple times.
+ // 2a. The expression is invalid and we are in an instantiated template or
+ // not in a template:
+ // Invalidate the expression with a clearly wrong value (nullptr) so
+ // later in Sema we do not have to do the same validity analysis again
+ // or crash from unexpected data. Error diagnostics have already been
+ // emitted.
+ // 2b. The expression is invalid and we are in a template (including partial
+ // instantiation):
+ // Pass the invalid expression as-is, template instantiation may
+ // replace unexpected types/values with valid ones. The directives
+ // with this clause must not try to use these expressions in dependent
+ // contexts.
+ if (!SizeExpr->isInstantiationDependent() && !IsValid)
+ SizeExpr = nullptr;
}
- DSAStack->setAssociatedLoops(SizeExprs.size());
return OMPSizesClause::Create(getASTContext(), StartLoc, LParenLoc, EndLoc,
- SizeExprs);
+ SanitizedSizeExprs);
}
OMPClause *SemaOpenMP::ActOnOpenMPFullClause(SourceLocation StartLoc,
diff --git a/clang/test/OpenMP/tile_ast_print.cpp b/clang/test/OpenMP/tile_ast_print.cpp
index afc8b34911e3b..c4dff2c4be448 100644
--- a/clang/test/OpenMP/tile_ast_print.cpp
+++ b/clang/test/OpenMP/tile_ast_print.cpp
@@ -183,4 +183,21 @@ void tfoo7() {
}
+// PRINT-LABEL: void foo8(
+// DUMP-LABEL: FunctionDecl {{.*}} foo8
+void foo8(int a) {
+ // PRINT: #pragma omp tile sizes(a)
+ // DUMP: OMPTileDirective
+ // DUMP-NEXT: OMPSizesClause
+ // DUMP-NEXT: ImplicitCastExpr
+ // DUMP-NEXT: DeclRefExpr {{.*}} 'a'
+ #pragma omp tile sizes(a)
+ // PRINT-NEXT: for (int i = 7; i < 19; i += 3)
+ // DUMP-NEXT: ForStmt
+ for (int i = 7; i < 19; i += 3)
+ // PRINT: body(i);
+ // DUMP: CallExpr
+ body(i);
+}
+
#endif
diff --git a/clang/test/OpenMP/tile_codegen.cpp b/clang/test/OpenMP/tile_codegen.cpp
index 76cf2d8f1992d..93a3a14133ab5 100644
--- a/clang/test/OpenMP/tile_codegen.cpp
+++ b/clang/test/OpenMP/tile_codegen.cpp
@@ -83,6 +83,14 @@ extern "C" void tfoo7() {
foo7<int,3,5>(0, 42);
}
+
+extern "C" void foo8(int a) {
+#pragma omp tile sizes(a)
+ for (int i = 7; i < 17; i += 3)
+ body(i);
+}
+
+
#endif /* HEADER */
// CHECK1-LABEL: define {{[^@]+}}@body
// CHECK1-SAME: (...) #[[ATTR0:[0-9]+]] {
@@ -98,7 +106,7 @@ extern "C" void tfoo7() {
//
//
// CHECK1-LABEL: define {{[^@]+}}@_ZN1SC1Ev
-// CHECK1-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] comdat align 2 {
+// CHECK1-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] comdat align 2 {
// CHECK1-NEXT: entry:
// CHECK1-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK1-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
@@ -108,7 +116,7 @@ extern "C" void tfoo7() {
//
//
// CHECK1-LABEL: define {{[^@]+}}@_ZN1SC2Ev
-// CHECK1-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2]] comdat align 2 {
+// CHECK1-SAME: (ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR0]] comdat align 2 {
// CHECK1-NEXT: entry:
// CHECK1-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8
// CHECK1-NEXT: [[I:%.*]] = alloca ptr, align 8
@@ -885,7 +893,7 @@ extern "C" void tfoo7() {
//
//
// CHECK1-LABEL: define {{[^@]+}}@foo6.omp_outlined
-// CHECK1-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #[[ATTR5:[0-9]+]] {
+// CHECK1-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]]) #[[ATTR4:[0-9]+]] {
// CHECK1-NEXT: entry:
// CHECK1-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8
// CHECK1-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8
@@ -1071,6 +1079,95 @@ extern "C" void tfoo7() {
// CHECK1-NEXT: ret void
//
//
+// CHECK1-LABEL: define {{[^@]+}}@foo8
+// CHECK1-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] {
+// CHECK1-NEXT: entry:
+// CHECK1-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK1-NEXT: [[I:%.*]] = alloca i32, align 4
+// CHECK1-NEXT: [[DOTFLOOR_0_IV_I:%.*]] = alloca i32, align 4
+// CHECK1-NEXT: [[DOTTILE_0_IV_I:%.*]] = alloca i32, align 4
+// CHECK1-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: store i32 7, ptr [[I]], align 4
+// CHECK1-NEXT: store i32 0, ptr [[DOTFLOOR_0_IV_I]], align 4
+// CHECK1-NEXT: br label [[FOR_COND:%.*]]
+// CHECK1: for.cond:
+// CHECK1-NEXT: [[TMP0:%.*]] = load i32, ptr [[DOTFLOOR_0_IV_I]], align 4
+// CHECK1-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 4
+// CHECK1-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END24:%.*]]
+// CHECK1: for.body:
+// CHECK1-NEXT: [[TMP1:%.*]] = load i32, ptr [[DOTFLOOR_0_IV_I]], align 4
+// CHECK1-NEXT: store i32 [[TMP1]], ptr [[DOTTILE_0_IV_I]], align 4
+// CHECK1-NEXT: br label [[FOR_COND1:%.*]]
+// CHECK1: for.cond1:
+// CHECK1-NEXT: [[TMP2:%.*]] = load i32, ptr [[DOTTILE_0_IV_I]], align 4
+// CHECK1-NEXT: [[TMP3:%.*]] = load i32, ptr [[DOTFLOOR_0_IV_I]], align 4
+// CHECK1-NEXT: [[TMP4:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: [[CMP2:%.*]] = icmp sle i32 [[TMP4]], 0
+// CHECK1-NEXT: br i1 [[CMP2]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
+// CHECK1: cond.true:
+// CHECK1-NEXT: br label [[COND_END:%.*]]
+// CHECK1: cond.false:
+// CHECK1-NEXT: [[TMP5:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: br label [[COND_END]]
+// CHECK1: cond.end:
+// CHECK1-NEXT: [[COND:%.*]] = phi i32 [ 1, [[COND_TRUE]] ], [ [[TMP5]], [[COND_FALSE]] ]
+// CHECK1-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP3]], [[COND]]
+// CHECK1-NEXT: [[CMP3:%.*]] = icmp slt i32 4, [[ADD]]
+// CHECK1-NEXT: br i1 [[CMP3]], label [[COND_TRUE4:%.*]], label [[COND_FALSE5:%.*]]
+// CHECK1: cond.true4:
+// CHECK1-NEXT: br label [[COND_END12:%.*]]
+// CHECK1: cond.false5:
+// CHECK1-NEXT: [[TMP6:%.*]] = load i32, ptr [[DOTFLOOR_0_IV_I]], align 4
+// CHECK1-NEXT: [[TMP7:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: [[CMP6:%.*]] = icmp sle i32 [[TMP7]], 0
+// CHECK1-NEXT: br i1 [[CMP6]], label [[COND_TRUE7:%.*]], label [[COND_FALSE8:%.*]]
+// CHECK1: cond.true7:
+// CHECK1-NEXT: br label [[COND_END9:%.*]]
+// CHECK1: cond.false8:
+// CHECK1-NEXT: [[TMP8:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: br label [[COND_END9]]
+// CHECK1: cond.end9:
+// CHECK1-NEXT: [[COND10:%.*]] = phi i32 [ 1, [[COND_TRUE7]] ], [ [[TMP8]], [[COND_FALSE8]] ]
+// CHECK1-NEXT: [[ADD11:%.*]] = add nsw i32 [[TMP6]], [[COND10]]
+// CHECK1-NEXT: br label [[COND_END12]]
+// CHECK1: cond.end12:
+// CHECK1-NEXT: [[COND13:%.*]] = phi i32 [ 4, [[COND_TRUE4]] ], [ [[ADD11]], [[COND_END9]] ]
+// CHECK1-NEXT: [[CMP14:%.*]] = icmp slt i32 [[TMP2]], [[COND13]]
+// CHECK1-NEXT: br i1 [[CMP14]], label [[FOR_BODY15:%.*]], label [[FOR_END:%.*]]
+// CHECK1: for.body15:
+// CHECK1-NEXT: [[TMP9:%.*]] = load i32, ptr [[DOTTILE_0_IV_I]], align 4
+// CHECK1-NEXT: [[MUL:%.*]] = mul nsw i32 [[TMP9]], 3
+// CHECK1-NEXT: [[ADD16:%.*]] = add nsw i32 7, [[MUL]]
+// CHECK1-NEXT: store i32 [[ADD16]], ptr [[I]], align 4
+// CHECK1-NEXT: [[TMP10:%.*]] = load i32, ptr [[I]], align 4
+// CHECK1-NEXT: call void (...) @body(i32 noundef [[TMP10]])
+// CHECK1-NEXT: br label [[FOR_INC:%.*]]
+// CHECK1: for.inc:
+// CHECK1-NEXT: [[TMP11:%.*]] = load i32, ptr [[DOTTILE_0_IV_I]], align 4
+// CHECK1-NEXT: [[INC:%.*]] = add nsw i32 [[TMP11]], 1
+// CHECK1-NEXT: store i32 [[INC]], ptr [[DOTTILE_0_IV_I]], align 4
+// CHECK1-NEXT: br label [[FOR_COND1]], !llvm.loop [[LOOP23:![0-9]+]]
+// CHECK1: for.end:
+// CHECK1-NEXT: br label [[FOR_INC17:%.*]]
+// CHECK1: for.inc17:
+// CHECK1-NEXT: [[TMP12:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK1-NEXT: [[CMP18:%.*]] = icmp sle i32 [[TMP12]], 0
+// CHECK1-NEXT: br i1 [[CMP18]], label [[COND_TRUE19:%.*]], label [[COND_FALSE20:%.*]]
+// CHECK1: cond.true19:
+// CHECK1-NEXT: br label [[COND_END21:%...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/91345
More information about the llvm-branch-commits
mailing list