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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits