From f83c665307fcb3cd8aaeb576cb78cb15ec334b2d Mon Sep 17 00:00:00 2001 From: Elie Cuevas Date: Fri, 1 Nov 2024 19:37:06 -0400 Subject: [PATCH 1/5] running version of PR - unintended behavior with empty containers Co-authored-by: Sualeh Asif sualehasif Co-authored-by: Arvid Lunnemark arvid220u --- clang/include/clang-c/Index.h | 6 +- clang/include/clang/AST/RecursiveASTVisitor.h | 1 + clang/include/clang/AST/StmtCilk.h | 76 +++++ .../clang/Basic/DiagnosticParseKinds.td | 5 +- .../clang/Basic/DiagnosticSemaKinds.td | 7 +- clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Sema/Sema.h | 8 +- .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/StmtCXX.cpp | 63 +++- clang/lib/AST/StmtPrinter.cpp | 18 ++ clang/lib/AST/StmtProfile.cpp | 4 + clang/lib/CodeGen/CGCilk.cpp | 288 ++++++++++++++++++ clang/lib/CodeGen/CGStmt.cpp | 3 + clang/lib/CodeGen/CodeGenFunction.h | 2 + clang/lib/Parse/ParseCilk.cpp | 51 ++-- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaStmt.cpp | 178 +++++++++++ clang/lib/Sema/SemaStmtAttr.cpp | 3 +- clang/lib/Sema/TreeTransform.h | 32 ++ clang/lib/Serialization/ASTReaderStmt.cpp | 9 + clang/lib/Serialization/ASTWriterStmt.cpp | 6 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/test/Cilk/cilkforrange-ir.cpp | 263 ++++++++++++++++ clang/test/Cilk/rangelooptest.cpp | 133 ++++++++ clang/tools/libclang/CIndex.cpp | 3 +- clang/tools/libclang/CXCursor.cpp | 4 + 26 files changed, 1138 insertions(+), 29 deletions(-) create mode 100644 clang/test/Cilk/cilkforrange-ir.cpp create mode 100644 clang/test/Cilk/rangelooptest.cpp diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 6445b5c329a7..4348d09d4b8e 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2160,7 +2160,11 @@ enum CXCursorKind { */ CXCursor_CilkScopeStmt = 311, - CXCursor_LastStmt = CXCursor_CilkScopeStmt, + /** A _Cilk_for range statement. + */ + CXCursor_CilkForRangeStmt = 312, + + CXCursor_LastStmt = CXCursor_CilkForRangeStmt, /** * Cursor that represents the translation unit itself. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 8f555ff82bfb..87485bc09adb 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2880,6 +2880,7 @@ DEF_TRAVERSE_STMT(CilkForStmt, { } }) DEF_TRAVERSE_STMT(CilkScopeStmt, {}) +DEF_TRAVERSE_STMT(CilkForRangeStmt, {}) // These operators (all of them) do not need any action except // iterating over the children. diff --git a/clang/include/clang/AST/StmtCilk.h b/clang/include/clang/AST/StmtCilk.h index 1a6f2612cd61..4a84ea293753 100644 --- a/clang/include/clang/AST/StmtCilk.h +++ b/clang/include/clang/AST/StmtCilk.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_STMTCILK_H #include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" #include "clang/Basic/SourceLocation.h" namespace clang { @@ -92,6 +93,81 @@ class CilkSyncStmt : public Stmt { } }; +/// CilkForRangeStmt - This represents a '_Cilk_for(range-declarator : +/// range-expression)' or a '_Cilk_for (init-statement range-declarator : +/// range-expression)', based on a CXXForRangeStmt which is a C++0x +/// [stmt.ranged]'s ranged for stmt +/// +/// This is stored as a FORRANGE stmt embedded inside a CILKFORRANGE with some +/// other necessary semantic components. +class CilkForRangeStmt : public Stmt { + enum { + FORRANGE, + LOOPINDEX, + LOOPINDEXSTMT, + LOCALLOOPINDEX, + LIMIT, + COND, + INC, + END + }; + Stmt *SubExprs[END]; + +public: + CilkForRangeStmt(const ASTContext &C, CXXForRangeStmt *ForRange, + VarDecl *LoopIndex, DeclStmt *LocalLoopIndex, + DeclStmt *Limit, Expr *Cond, Expr *Inc, + DeclStmt *LoopIndexStmt); + + /// \brief Build an empty cilk for range statement. + explicit CilkForRangeStmt(EmptyShell Empty) + : Stmt(CilkForRangeStmtClass, Empty) {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CilkForRangeStmtClass; + } + + CXXForRangeStmt *getCXXForRangeStmt() const; + + void setForRange(Stmt *S) { SubExprs[FORRANGE] = S; } + + VarDecl *getLoopIndex() const; + void setLoopIndex(const ASTContext &C, VarDecl *V); + + VarDecl *getLocalLoopIndex(); + const VarDecl *getLocalLoopIndex() const; + + Expr *getCond() { return reinterpret_cast(SubExprs[COND]); } + Expr *getInc() { return reinterpret_cast(SubExprs[INC]); } + DeclStmt *getLoopIndexStmt() { + return cast_or_null(SubExprs[LOOPINDEXSTMT]); + } + DeclStmt *getLimitStmt() { return cast_or_null(SubExprs[LIMIT]); } + DeclStmt *getLocalLoopIndexStmt() { + return cast(SubExprs[LOCALLOOPINDEX]); + } + + const Expr *getCond() const { + return reinterpret_cast(SubExprs[COND]); + } + const Expr *getInc() const { return reinterpret_cast(SubExprs[INC]); } + const DeclStmt *getLoopIndexStmt() const { + return cast_or_null(SubExprs[LOOPINDEXSTMT]); + } + const DeclStmt *getLimitStmt() const { + return cast_or_null(SubExprs[LIMIT]); + } + const DeclStmt *getLocalLoopIndexStmt() const { + return cast(SubExprs[LOCALLOOPINDEX]); + } + + SourceLocation getBeginLoc() const LLVM_READONLY; + SourceLocation getEndLoc() const LLVM_READONLY; + + // Iterators + child_range children() { return child_range(&SubExprs[0], &SubExprs[END]); } +}; + /// CilkForStmt - This represents a '_Cilk_for(init;cond;inc)' stmt. class CilkForStmt : public Stmt { SourceLocation CilkForLoc; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 9dcfada5c0d4..1fa1ceec7d1f 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1387,8 +1387,6 @@ def err_cilk_for_missing_increment: Error< "missing loop increment expression in 'cilk_for'">; def err_cilk_for_missing_semi: Error< "expected ';' in 'cilk_for'">; -def err_cilk_for_forrange_loop_not_supported: Error< - "'cilk_for' not supported on for-range loops">; def err_cilk_for_foreach_loop_not_supported: Error< "'cilk_for' not supported on for-each loops">; def err_pragma_cilk_invalid_option : Error< @@ -1401,6 +1399,9 @@ def warn_cilk_for_following_grainsize: Warning< def warn_pragma_cilk_grainsize_equals: Warning< "'#pragma cilk grainsize' no longer requires '='">, InGroup; +def warn_cilk_for_forrange_loop_experimental: Warning< + "'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!">, + InGroup; def error_hyperobject_arguments: Error< "hyperobject must have 0 or 2 callbacks">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2aeaecf9cd90..b4a3c9311c56 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10919,7 +10919,12 @@ def err_hyperobject_struct_assign : Error< def no_reducer_array : Warning< "array of reducer not implemented">, InGroup; -} // end of Cilk category + +// cilk for_range +def err_cilk_for_range_end_minus_begin : Error< + "Cannot determine length with '__end - __begin'. Please use a random access iterator.">; +} +// end of Cilk category let CategoryName = "OpenMP Issue" in { // OpenMP support. diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 5841f03e1052..9ccf121590a6 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -223,6 +223,7 @@ def CilkSpawnStmt : StmtNode; def CilkSpawnExpr : StmtNode; def CilkForStmt : StmtNode; def CilkScopeStmt : StmtNode; +def CilkForRangeStmt: StmtNode; // OpenMP Directives. def OMPCanonicalLoop : StmtNode; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1baa6a679262..aec20bc01cc5 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5332,7 +5332,13 @@ class Sema final { SourceLocation RParenLoc, Stmt *Body, DeclStmt *LoopVar = nullptr, Expr *OgCond = nullptr, Expr *OgInc = nullptr); - + StmtResult ActOnCilkForRangeStmt(Scope *S, SourceLocation ForLoc, + Stmt *InitStmt, Stmt *First, + SourceLocation ColonLoc, Expr *Range, + SourceLocation RParenLoc, + BuildForRangeKind Kind); + StmtResult BuildCilkForRangeStmt(CXXForRangeStmt *S); + StmtResult FinishCilkForRangeStmt(Stmt *S, Stmt *B); StmtResult BuildCilkForStmt(SourceLocation CilkForLoc, SourceLocation LParenLoc, Stmt *Init, Expr *Cond, Expr *Inc, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index b5a0cfadc2bd..70eea8051865 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -2021,6 +2021,7 @@ enum StmtCode { STMT_CILKSYNC, STMT_CILKFOR, STMT_CILKSCOPE, + STMT_CILKFORRANGE, }; /// The kinds of designators that can occur in a diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp index 0d6fc848f739..53fd05636e86 100644 --- a/clang/lib/AST/StmtCXX.cpp +++ b/clang/lib/AST/StmtCXX.cpp @@ -11,7 +11,8 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtCXX.h" - +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCilk.h" #include "clang/AST/ASTContext.h" using namespace clang; @@ -126,3 +127,63 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args) std::copy(Args.ParamMoves.begin(), Args.ParamMoves.end(), const_cast(getParamMoves().data())); } + +/// Constructor for the CilkForRangeStmt +CilkForRangeStmt::CilkForRangeStmt(const ASTContext &C, + CXXForRangeStmt *ForRange, + VarDecl *LoopIndex, DeclStmt *LocalLoopIndex, + DeclStmt *Limit, Expr *Cond, Expr *Inc, + DeclStmt *LoopIndexStmt) + : Stmt(CilkForRangeStmtClass) { + SubExprs[FORRANGE] = ForRange; + setLoopIndex(C, LoopIndex); + SubExprs[LOCALLOOPINDEX] = LocalLoopIndex; + SubExprs[COND] = Cond; + SubExprs[INC] = Inc; + SubExprs[LOOPINDEXSTMT] = LoopIndexStmt; + SubExprs[LIMIT] = Limit; +} + +/// Gets the LOOPINDEX as a VarDecl. +/// LOOPINDEX is stored as a DeclStmt for the purpose of clean manipulation. +VarDecl *CilkForRangeStmt::getLoopIndex() const { + if (!SubExprs[LOOPINDEX]) + return nullptr; + + DeclStmt *DS = cast(SubExprs[LOOPINDEX]); + return cast(DS->getSingleDecl()); +} + +void CilkForRangeStmt::setLoopIndex(const ASTContext &C, VarDecl *V) { + if (!V) { + SubExprs[LOOPINDEX] = nullptr; + return; + } + + SourceRange VarRange = V->getSourceRange(); + SubExprs[LOOPINDEX] = + new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); +} + +VarDecl *CilkForRangeStmt::getLocalLoopIndex() { + Decl *LV = cast(getLocalLoopIndexStmt())->getSingleDecl(); + assert(LV && "No local loop index in CilkForRangeStmt"); + return cast(LV); +} + +const VarDecl *CilkForRangeStmt::getLocalLoopIndex() const { + return const_cast(this)->getLocalLoopIndex(); +} + +/// returns the FORRANGE stmt embedded in the CilkForRange. (May be null.) +CXXForRangeStmt *CilkForRangeStmt::getCXXForRangeStmt() const { + return cast_or_null(SubExprs[FORRANGE]); +} + +SourceLocation CilkForRangeStmt::getBeginLoc() const { + return getCXXForRangeStmt()->getBeginLoc(); +} + +SourceLocation CilkForRangeStmt::getEndLoc() const { + return getCXXForRangeStmt()->getEndLoc(); +} \ No newline at end of file diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index b52ea5512b6b..750049ce1cd1 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2816,6 +2816,24 @@ void StmtPrinter::VisitCilkScopeStmt(CilkScopeStmt *Node) { } } +void StmtPrinter::VisitCilkForRangeStmt(CilkForRangeStmt *Node) { + Indent() << "_Cilk_for ("; + + if (Node->getCXXForRangeStmt()->getInit()) + PrintInitStmt(Node->getCXXForRangeStmt()->getInit(), 5); + + PrintingPolicy SubPolicy(Policy); + SubPolicy.SuppressInitializers = true; + Node->getCXXForRangeStmt()->getLoopVariable()->print(OS, SubPolicy, + IndentLevel); + + OS << " : "; + PrintExpr(Node->getCXXForRangeStmt()->getRangeInit()); + OS << ")"; + + PrintControlledStmt(Node->getCXXForRangeStmt()->getBody()); +} + //===----------------------------------------------------------------------===// // Stmt method implementations //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 1d22e7e4eb49..ee564921e1bf 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2294,6 +2294,10 @@ void StmtProfiler::VisitCilkScopeStmt(const CilkScopeStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCilkForRangeStmt(const CilkForRangeStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } diff --git a/clang/lib/CodeGen/CGCilk.cpp b/clang/lib/CodeGen/CGCilk.cpp index c29d82d7a9f0..4391185b8d7f 100644 --- a/clang/lib/CodeGen/CGCilk.cpp +++ b/clang/lib/CodeGen/CGCilk.cpp @@ -18,6 +18,7 @@ using namespace clang; using namespace CodeGen; +using namespace std; CodeGenFunction::IsSpawnedScope::IsSpawnedScope(CodeGenFunction *CGF) : CGF(CGF), OldIsSpawned(CGF->IsSpawned), @@ -491,6 +492,293 @@ void CodeGenFunction::EmitCilkScopeStmt(const CilkScopeStmt &S) { WithinCilkScope = false; } +void +CodeGenFunction::EmitCilkForRangeStmt(const CilkForRangeStmt &S, + ArrayRef ForAttrs) { + JumpDest LoopExit = getJumpDestInCurrentScope("pfor.end"); + + // Setup the sync region + PushSyncRegion(); + llvm::Instruction *SyncRegion = EmitSyncRegionStart(); + CurSyncRegion->setSyncRegionStart(SyncRegion); + + llvm::BasicBlock *TempInvokeDest = createBasicBlock("temp.invoke.dest"); + + LexicalScope ForScope(*this, S.getSourceRange()); + + // Get the ForRange stmt which has all the important semantics. + const CXXForRangeStmt &ForRange = *S.getCXXForRangeStmt(); + + // Evaluate the first part before the loop. + if (ForRange.getInit()) + EmitStmt(ForRange.getInit()); + + llvm::BasicBlock *ExitBlock = LoopExit.getBlock(); + + // We will now emit a difference variable instead of beginstmt and endstmt + // 1. add difference variable and emit it + // 2. make loop condition depend on the difference variable instead + // 3. finally, don't mutate beginstmt and instead do begin=begin+inductionvar + + EmitStmt(ForRange.getRangeStmt()); + EmitStmt(ForRange.getBeginStmt()); + EmitStmt(ForRange.getEndStmt()); + EmitStmt(S.getLoopIndexStmt()); + EmitStmt(S.getLimitStmt()); + + // Start the loop with a block that tests the condition. If there's an + // increment, the continue scope will be overwritten later. + JumpDest Continue = getJumpDestInCurrentScope("pfor.cond"); + llvm::BasicBlock *CondBlock = Continue.getBlock(); + EmitBlock(CondBlock); + Expr::EvalResult Result; + bool CondIsConstInt = S.getCond()->EvaluateAsInt(Result, getContext()); + + LoopStack.setSpawnStrategy(LoopAttributes::DAC); + const SourceRange &R = S.getSourceRange(); + LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, + SourceLocToDebugLoc(R.getBegin()), + SourceLocToDebugLoc(R.getEnd()), + checkIfLoopMustProgress(CondIsConstInt)); + + const Expr *Inc = S.getInc(); + assert(Inc && "_Cilk_for range loop has no increment"); + Continue = getJumpDestInCurrentScope("pfor.inc"); + + // Ensure that the _Cilk_for loop iterations are synced on exit from the loop. + EHStack.pushCleanup(NormalCleanup, SyncRegion); + + // Create a cleanup scope for the condition variable cleanups. + LexicalScope ConditionScope(*this, S.getSourceRange()); + + // Variables to store the old alloca insert point. + llvm::AssertingVH OldAllocaInsertPt; + // Variables to store the old EH state. + llvm::BasicBlock *OldEHResumeBlock; + llvm::Value *OldExceptionSlot; + llvm::AllocaInst *OldEHSelectorSlot; + Address OldNormalCleanupDest = Address::invalid(); + + const VarDecl *LocalLoopIndex = S.getLocalLoopIndex(); + RValue LocalLoopIndexInitRV; + llvm::BasicBlock *DetachBlock; + llvm::BasicBlock *ForBodyEntry; + llvm::BasicBlock *ForBody; + llvm::DetachInst *Detach; + { + // FIXME: Figure out if there is a way to support condition variables in + // Cilk. + // + // // If the for statement has a condition scope, emit the local variable + // // declaration. + // if (S.getConditionVariable()) { + // EmitAutoVarDecl(*S.getConditionVariable()); + // } + + // If there are any cleanups between here and the loop-exit scope, + // create a block to stage a loop exit along. + if (ForScope.requiresCleanups()) + ExitBlock = createBasicBlock("pfor.cond.cleanup"); + + // As long as the condition is true, iterate the loop. + DetachBlock = createBasicBlock("pfor.detach"); + // Emit extra entry block for detached body, to ensure that this detached + // entry block has just one predecessor. + ForBodyEntry = createBasicBlock("pfor.body.entry"); + ForBody = createBasicBlock("pfor.body"); + + EmitBranch(DetachBlock); + + EmitBlockAfterUses(DetachBlock); + + // Get the value of the loop index before we emit the detach. + // This avoids a race condition on cilk_loopindex with -O0. + // Note that the ordinary cilk_for code emits the value of the loop var; + // that does not work for us, since our loop var may be any complicated + // type, and emitting to temp RV only works for simple types. + if (LocalLoopIndex) { + LocalLoopIndexInitRV = EmitAnyExprToTemp(LocalLoopIndex->getInit()); + } + + Detach = + Builder.CreateDetach(ForBodyEntry, Continue.getBlock(), SyncRegion); + // Save the old alloca insert point. + OldAllocaInsertPt = AllocaInsertPt; + // Save the old EH state. + OldEHResumeBlock = EHResumeBlock; + OldExceptionSlot = ExceptionSlot; + OldEHSelectorSlot = EHSelectorSlot; + OldNormalCleanupDest = NormalCleanupDest; + + // Create a new alloca insert point. + llvm::Value *Undef = llvm::UndefValue::get(Int32Ty); + AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "", ForBodyEntry); + + // Push a cleanup to make sure any exceptional exit from the loop is + // terminated by a detached.rethrow. + EHStack.pushCleanup( + static_cast(EHCleanup | LifetimeMarker | TaskExit), + SyncRegion, TempInvokeDest); + + // Set up nested EH state. + EHResumeBlock = nullptr; + ExceptionSlot = nullptr; + EHSelectorSlot = nullptr; + NormalCleanupDest = Address::invalid(); + + EmitBlock(ForBodyEntry); + } + + RunCleanupsScope DetachCleanupsScope(*this); + + // Set up a nested sync region for the loop body, and ensure it has an + // implicit sync. + PushSyncRegion()->addImplicitSync(); + + // Store the blocks to use for break and continue. + JumpDest Preattach = getJumpDestInCurrentScope("pfor.preattach"); + BreakContinueStack.push_back(BreakContinue(Preattach, Preattach)); + + // Inside the detached block, create the loop index, setting its value to + // the saved initialization value. This avoids a race condition. + if (LocalLoopIndex) { + AutoVarEmission LVEmission = EmitAutoVarAlloca(*LocalLoopIndex); + QualType type = LocalLoopIndex->getType(); + Address Loc = LVEmission.getObjectAddress(*this); + LValue LV = MakeAddrLValue(Loc, type); + LV.setNonGC(true); + EmitStoreThroughLValue(LocalLoopIndexInitRV, LV, true); + EmitAutoVarCleanups(LVEmission); + } + // Emit the loop var stmt, which will use the local loop index emitted above. + EmitStmt(ForRange.getLoopVarStmt()); + + Builder.CreateBr(ForBody); + + EmitBlock(ForBody); + + incrementProfileCounter(&S); + + { + // Create a separate cleanup scope for the body, in case it is not + // a compound statement. + RunCleanupsScope BodyScope(*this); + + SyncedScopeRAII SyncedScp(*this); + if (isa(ForRange.getBody())) + ScopeIsSynced = true; + EmitStmt(ForRange.getBody()); + + if (HaveInsertPoint()) + Builder.CreateBr(Preattach.getBlock()); + } + + // Finish detached body and emit the reattach. + { + EmitBlock(Preattach.getBlock()); + // The design of the exception-handling mechanism means we need to cleanup + // the scope before popping the sync region. + DetachCleanupsScope.ForceCleanup(); + PopSyncRegion(); + // Pop the detached.rethrow cleanup. + PopCleanupBlock(); + Builder.CreateReattach(Continue.getBlock(), SyncRegion); + } + + // Restore CGF state after detached region. + llvm::BasicBlock *NestedEHResumeBlock; + { + // Restore the alloca insertion point. + llvm::Instruction *Ptr = AllocaInsertPt; + AllocaInsertPt = OldAllocaInsertPt; + Ptr->eraseFromParent(); + + // Restore the EH state. + NestedEHResumeBlock = EHResumeBlock; + EHResumeBlock = OldEHResumeBlock; + ExceptionSlot = OldExceptionSlot; + EHSelectorSlot = OldEHSelectorSlot; + NormalCleanupDest = OldNormalCleanupDest; + } + + // An invocation of the detached.rethrow intrinsic marks the end of an + // exceptional return from the parallel-loop body. That invoke needs a valid + // landinpad as its unwind destination. We create that unwind destination + // here. + llvm::BasicBlock *InvokeDest = nullptr; + if (!TempInvokeDest->use_empty()) { + InvokeDest = getInvokeDest(); + if (InvokeDest) + TempInvokeDest->replaceAllUsesWith(InvokeDest); + else { + InvokeDest = TempInvokeDest; + EmitTrivialLandingPad(*this, TempInvokeDest); + } + } + + // If invocations in the parallel task led to the creation of EHResumeBlock, + // we need to create for outside the task. In particular, the new + // EHResumeBlock must use an ExceptionSlot and EHSelectorSlot allocated + // outside of the task. + if (NestedEHResumeBlock) { + if (!NestedEHResumeBlock->use_empty()) { + // Translate the nested EHResumeBlock into an appropriate EHResumeBlock in + // the outer scope. + NestedEHResumeBlock->replaceAllUsesWith(getEHResumeBlock( + isa(NestedEHResumeBlock->getTerminator()))); + } + delete NestedEHResumeBlock; + } + + // Emit the increment next. + EmitBlockAfterUses(Continue.getBlock()); + EmitStmt(Inc); + + { + // If the detached-rethrow handler is used, add an unwind destination to the + // detach. + if (InvokeDest) { + CGBuilderTy::InsertPoint SavedIP = Builder.saveIP(); + Builder.SetInsertPoint(DetachBlock); + // Create the new detach instruction. + llvm::DetachInst *NewDetach = Builder.CreateDetach( + ForBodyEntry, Continue.getBlock(), InvokeDest, SyncRegion); + // Remove the old detach. + Detach->eraseFromParent(); + Detach = NewDetach; + Builder.restoreIP(SavedIP); + } + } + + BreakContinueStack.pop_back(); + + ConditionScope.ForceCleanup(); + + EmitStopPoint(&S); + + // C99 6.8.5p2/p4: The first substatement is executed if the expression + // compares unequal to 0. The condition must be a scalar type. + llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond()); + Builder.CreateCondBr(BoolCondVal, CondBlock, ExitBlock, + createProfileWeightsForLoop( + S.getCond(), getProfileCount(ForRange.getBody()))); + + if (ExitBlock != LoopExit.getBlock()) { + EmitBlock(ExitBlock); + EmitBranchThroughCleanup(LoopExit); + } + + ForScope.ForceCleanup(); + + LoopStack.pop(); + // Emit the fall-through block. + EmitBlock(LoopExit.getBlock(), true); + PopSyncRegion(); + + if (TempInvokeDest->use_empty()) + delete TempInvokeDest; +} + static const Stmt *IgnoreImplicitAndCleanups(const Stmt *S) { const Stmt *Current = S; if (auto *E = dyn_cast_or_null(S)) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 4bbc726b35f8..657f35e492ff 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -179,6 +179,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::CilkScopeStmtClass: EmitCilkScopeStmt(cast(*S)); break; + case Stmt::CilkForRangeStmtClass: + EmitCilkForRangeStmt(cast(*S), Attrs); + break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast(*S)); break; diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e3c88f93e156..1eee058ff906 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3919,6 +3919,8 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitCilkSyncStmt(const CilkSyncStmt &S); void EmitCilkForStmt(const CilkForStmt &S, ArrayRef Attrs = std::nullopt); + void EmitCilkForRangeStmt(const CilkForRangeStmt &S, + ArrayRef Attrs = std::nullopt); LValue EmitCilkSpawnExprLValue(const CilkSpawnExpr *E); void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S); diff --git a/clang/lib/Parse/ParseCilk.cpp b/clang/lib/Parse/ParseCilk.cpp index 4bdf98f98f81..dfb87daa91d4 100644 --- a/clang/lib/Parse/ParseCilk.cpp +++ b/clang/lib/Parse/ParseCilk.cpp @@ -151,6 +151,15 @@ struct MisleadingIndentationChecker { /// cilk_for-statement: /// '_Cilk_for' '(' expr ';' expr ';' expr ')' statement /// '_Cilk_for' '(' declaration expr ';' expr ';' expr ')' statement +/// [C++0x] '_Cilk_for' +/// '(' for-range-declaration ':' for-range-initializer ')' +/// statement +/// +/// [C++0x] for-range-declaration: +/// [C++0x] attribute-specifier-seq[opt] type-specifier-seq declarator +/// [C++0x] for-range-initializer: +/// [C++0x] expression +/// [C++0x] braced-init-list StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { assert(Tok.is(tok::kw__Cilk_for) && "Not a _Cilk_for stmt!"); SourceLocation ForLoc = ConsumeToken(); // eat the '_Cilk_for'. @@ -229,12 +238,12 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { ForRangeInfo.RangeExpr = ParseExpression(); Diag(Loc, diag::err_for_range_identifier) - << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) - ? FixItHint::CreateInsertion(Loc, "auto &&") - : FixItHint()); + << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) + ? FixItHint::CreateInsertion(Loc, "auto &&") + : FixItHint()); - ForRangeInfo.LoopVar = - Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs); + ForRangeInfo.LoopVar = Actions.ActOnCXXForRangeIdentifier( + getCurScope(), Loc, Name, attrs); } else if (isForInitDeclaration()) { // _Cilk_for (int X = 4; ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -258,9 +267,9 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { MightBeForRangeStmt ? &ForRangeInfo : nullptr); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); if (ForRangeInfo.ParsedForRangeDecl()) { - Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_for_range : diag::ext_for_range); - + Diag(ForRangeInfo.ColonLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_for_range + : diag::ext_for_range); ForRangeInfo.LoopVar = FirstPart; FirstPart = StmtResult(); } else if (Tok.is(tok::semi)) { // for (int x = 4; @@ -428,21 +437,21 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { // // We need to perform most of the semantic analysis for a C++0x for-range // // statememt before parsing the body, in order to be able to deduce the type // // of an auto-typed loop variable. - // StmtResult ForRangeStmt; + StmtResult ForRangeStmt; // StmtResult ForEachStmt; // TODO: Extend _Cilk_for to support these. if (ForRangeInfo.ParsedForRangeDecl()) { - Diag(ForLoc, diag::err_cilk_for_forrange_loop_not_supported); - // ExprResult CorrectedRange = - // Actions.CorrectDelayedTyposInExpr(ForRangeInfo.RangeExpr.get()); - // ForRangeStmt = Actions.ActOnCXXForRangeStmt( - // getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(), - // ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(), - // T.getCloseLocation(), Sema::BFRK_Build); - - // Similarly, we need to do the semantic analysis for a for-range - // statement immediately in order to close over temporaries correctly. + Diag(ForLoc, diag::warn_cilk_for_forrange_loop_experimental); + ExprResult CorrectedRange = + Actions.CorrectDelayedTyposInExpr(ForRangeInfo.RangeExpr.get()); + ForRangeStmt = Actions.ActOnCilkForRangeStmt( + getCurScope(), ForLoc, FirstPart.get(), ForRangeInfo.LoopVar.get(), + ForRangeInfo.ColonLoc, CorrectedRange.get(), T.getCloseLocation(), + Sema::BFRK_Build); + + // Similarly, we need to do the semantic analysis for a for-range + // statement immediately in order to close over temporaries correctly. } else if (ForEach) { Diag(ForLoc, diag::err_cilk_for_foreach_loop_not_supported); // ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc, @@ -498,8 +507,8 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { // if (ForEach) // return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(), Body.get()); - // if (ForRangeInfo.ParsedForRangeDecl()) - // return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get()); + if (ForRangeInfo.ParsedForRangeDecl()) + return Actions.FinishCilkForRangeStmt(ForRangeStmt.get(), Body.get()); return Actions.ActOnCilkForStmt(ForLoc, T.getOpenLocation(), FirstPart.get(), nullptr, Sema::ConditionResult(), nullptr, diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 003e1153a8f7..6df3d6d43573 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1300,6 +1300,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::VAArgExprClass: case Expr::CXXParenListInitExprClass: case Expr::CilkSpawnExprClass: + case Stmt::CilkForRangeStmtClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index dd5e54f57ba7..feb95d953035 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -4028,6 +4028,184 @@ Sema::ActOnCilkForStmt(SourceLocation CilkForLoc, SourceLocation LParenLoc, OgInc, CilkForLoc, LParenLoc, RParenLoc); } +StmtResult Sema::FinishCilkForRangeStmt(Stmt *S, Stmt *B) { + if (!S || !B) + return StmtError(); + + CilkForRangeStmt *CilkForRange = cast(S); + + StmtResult ForRange = + FinishCXXForRangeStmt(CilkForRange->getCXXForRangeStmt(), B); + if (ForRange.isInvalid()) + return StmtError(); + CilkForRange->setForRange(ForRange.get()); + + CXXForRangeStmt *CXXForRange = cast(ForRange.get()); + + if (isa(CXXForRange->getBody())) { + Diag(CXXForRange->getForLoc(), diag::warn_empty_cilk_for_body); + getCurCompoundScope().setHasEmptyLoopBodies(); + } + + SearchForReturnInStmt(*this, CXXForRange->getBody()); + + if (BreakContinueFinder(*this, CXXForRange->getBody()).BreakFound()) + Diag(CXXForRange->getForLoc(), diag::err_cilk_for_cannot_break); + + VarDecl *BeginVar = + cast(CXXForRange->getBeginStmt()->getSingleDecl()); + QualType BeginType = BeginVar->getType(); + const QualType BeginRefNonRefType = BeginType.getNonReferenceType(); + ExprResult BeginRef = BuildDeclRefExpr(BeginVar, BeginRefNonRefType, + VK_LValue, CXXForRange->getColonLoc()); + if (BeginRef.isInvalid()) + return StmtError(); + + VarDecl *LoopIndex = CilkForRange->getLocalLoopIndex(); + QualType LoopIndexType = LoopIndex->getType(); + const QualType LoopIndexRefNonRefType = LoopIndexType.getNonReferenceType(); + ExprResult LoopIndexRef = BuildDeclRefExpr( + LoopIndex, LoopIndexRefNonRefType, VK_LValue, CXXForRange->getColonLoc()); + if (LoopIndexRef.isInvalid()) + return StmtError(); + + VarDecl *LoopVar = CXXForRange->getLoopVariable(); + SourceLocation LoopVarLoc = LoopVar->getBeginLoc(); + ExprResult NewLoopVarInit = ActOnBinOp(getCurScope(), LoopVarLoc, tok::plus, + BeginRef.get(), LoopIndexRef.get()); + if (NewLoopVarInit.isInvalid()) + return StmtError(); + + ExprResult DerefExpr = + ActOnUnaryOp(getCurScope(), LoopVarLoc, tok::star, NewLoopVarInit.get()); + if (DerefExpr.isInvalid()) { + Diag(LoopVarLoc, diag::note_for_range_invalid_iterator) + << LoopVarLoc << 1 << NewLoopVarInit.get()->getType(); + return StmtError(); + } + + AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false); + + return CilkForRange; +} + +StmtResult Sema::ActOnCilkForRangeStmt(Scope *S, SourceLocation ForLoc, + Stmt *InitStmt, Stmt *First, + SourceLocation ColonLoc, Expr *Range, + SourceLocation RParenLoc, + BuildForRangeKind Kind) { + // We wrap the CXXForRange, in an attempt to reduce code copying. + SourceLocation EmptyCoawaitLoc; + StmtResult ForRangeStmt = + ActOnCXXForRangeStmt(S, ForLoc, EmptyCoawaitLoc, InitStmt, First, + ColonLoc, Range, RParenLoc, Kind); + + if (ForRangeStmt.isInvalid()) + return ForRangeStmt; + + CXXForRangeStmt *ForRange = cast_or_null(ForRangeStmt.get()); + + return BuildCilkForRangeStmt(ForRange); +} + +StmtResult Sema::BuildCilkForRangeStmt(CXXForRangeStmt *ForRange) { + Scope *S = getCurScope(); + + // 1. Build an induction variable of type difference_type + // 2. Create an end - begin stmt, the end value of the induction variable. + + VarDecl *BeginVar = cast(ForRange->getBeginStmt()->getSingleDecl()); + QualType BeginType = BeginVar->getType(); + const QualType BeginRefNonRefType = BeginType.getNonReferenceType(); + ExprResult BeginRef = BuildDeclRefExpr(BeginVar, BeginRefNonRefType, + VK_LValue, ForRange->getColonLoc()); + if (BeginRef.isInvalid()) { + return StmtError(); + } + + VarDecl *EndVar = cast(ForRange->getEndStmt()->getSingleDecl()); + QualType EndType = EndVar->getType(); + const QualType EndRefNonRefType = EndType.getNonReferenceType(); + ExprResult EndRef = BuildDeclRefExpr(EndVar, EndRefNonRefType, VK_LValue, + ForRange->getColonLoc()); + if (EndRef.isInvalid()) { + return StmtError(); + } + ExprResult LimitExpr = ActOnBinOp(S, ForRange->getColonLoc(), tok::minus, + EndRef.get(), BeginRef.get()); + if (LimitExpr.isInvalid()) { + // CilkForRange currently only supports random access iterators. + Diag(ForRange->getForLoc(), diag::err_cilk_for_range_end_minus_begin); + return StmtError(); + } + SourceLocation RangeLoc = ForRange->getBeginLoc(); + VarDecl *Limit = + BuildForRangeVarDecl(*this, RangeLoc, LimitExpr.get()->getType(), + std::string("__cilk_looplimit")); + AddInitializerToDecl(Limit, LimitExpr.get(), + /*DirectInit=*/false); + FinalizeDeclaration(Limit); + CurContext->addHiddenDecl(Limit); + + DeclGroupPtrTy LimitGroup = + BuildDeclaratorGroup(MutableArrayRef((Decl **)&Limit, 1)); + StmtResult LimitStmt = ActOnDeclStmt(LimitGroup, RangeLoc, RangeLoc); + if (LimitStmt.isInvalid()) + return StmtError(); + + VarDecl *LoopIndex = + BuildForRangeVarDecl(*this, RangeLoc, LimitExpr.get()->getType(), + std::string("__cilk_loopindex")); + AddInitializerToDecl(LoopIndex, ActOnIntegerConstant(RangeLoc, 0).get(), + /*DirectInit=*/false); + FinalizeDeclaration(LoopIndex); + CurContext->addHiddenDecl(LoopIndex); + + DeclGroupPtrTy LoopIndexGroup = + BuildDeclaratorGroup(MutableArrayRef((Decl **)&LoopIndex, 1)); + StmtResult LoopIndexStmt = ActOnDeclStmt(LoopIndexGroup, RangeLoc, RangeLoc); + if (LoopIndexStmt.isInvalid()) + return StmtError(); + + ExprResult LimitRef = + BuildDeclRefExpr(Limit, Limit->getType(), VK_LValue, RangeLoc); + ExprResult LoopIndexRef = + BuildDeclRefExpr(LoopIndex, LoopIndex->getType(), VK_LValue, RangeLoc); + ExprResult Cond; + Cond = ActOnBinOp(S, RangeLoc, tok::exclaimequal, LoopIndexRef.get(), + LimitRef.get()); + if (Cond.isInvalid()) + return StmtError(); + + VarDecl *LocalLoopIndex = + BuildForRangeVarDecl(*this, RangeLoc, LimitExpr.get()->getType(), + std::string("__local_loopindex")); + AddInitializerToDecl(LocalLoopIndex, LoopIndexRef.get(), + /*DirectInit=*/false); + FinalizeDeclaration(LocalLoopIndex); + CurContext->addHiddenDecl(LocalLoopIndex); + + DeclGroupPtrTy LocalLoopIndexGroup = BuildDeclaratorGroup( + MutableArrayRef((Decl **)&LocalLoopIndex, 1)); + StmtResult LocalLoopIndexStmt = + ActOnDeclStmt(LocalLoopIndexGroup, RangeLoc, RangeLoc); + if (LocalLoopIndexStmt.isInvalid()) + return StmtError(); + + // Create a new increment operation on the new beginning variable, and add it + // to the existing increment operation. + SourceLocation IncLoc = RangeLoc; + ExprResult NewInc = + ActOnUnaryOp(S, IncLoc, tok::plusplus, LoopIndexRef.get()); + if (NewInc.isInvalid()) + return StmtError(); + + return new (Context) CilkForRangeStmt( + Context, ForRange, LoopIndex, cast(LocalLoopIndexStmt.get()), + cast(LimitStmt.get()), Cond.get(), NewInc.get(), + cast(LoopIndexStmt.get())); +} + /// Determine whether the given expression might be move-eligible or /// copy-elidable in either a (co_)return statement or throw expression, /// without considering function return type, if applicable. diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 848364830054..8e2e257f13cf 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -88,7 +88,8 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, .Default("clang loop"); if ((PragmaName == "cilk") && - (St->getStmtClass() != Stmt::CilkForStmtClass)) { + (St->getStmtClass() != Stmt::CilkForStmtClass && + St->getStmtClass() != Stmt::CilkForRangeStmtClass)) { S.Diag(St->getBeginLoc(), diag::err_pragma_cilk_precedes_noncilk) << "#pragma cilk"; return nullptr; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 117de409d76d..04f5047430d1 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1475,6 +1475,22 @@ class TreeTransform { RParenLoc, Body, cast_or_null(LoopVar), OgCond, OgInc); } + /// Build a new Cilk for range statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildCilkForRangeStmt(Stmt *ForRange) { + // We don't reconstruct the for range from its constituent parts, + // but rather let the CXXForRange do the rebuild, and then recompute + // all our fields by building it again. + return getSema().BuildCilkForRangeStmt( + cast_or_null(ForRange)); + } + StmtResult FinishCilkForRangeStmt(Stmt *ForRange, Stmt *Body) { + return getSema().FinishCilkForRangeStmt(ForRange, Body); + } + + /// Build a new goto statement. /// /// By default, performs semantic analysis to build the new statement. @@ -15692,6 +15708,22 @@ TreeTransform::TransformCilkForStmt(CilkForStmt *S) { Body.get(), OgCond.get(), OgInc.get()); } +template +StmtResult +TreeTransform::TransformCilkForRangeStmt(CilkForRangeStmt *S) { + StmtResult ForRange = getDerived().TransformStmt(S->getCXXForRangeStmt()); + if (ForRange.isInvalid()) + return StmtError(); + + StmtResult CilkForRange = + getDerived().RebuildCilkForRangeStmt(ForRange.get()); + if (CilkForRange.isInvalid()) + return StmtError(); + + return getDerived().FinishCilkForRangeStmt( + CilkForRange.get(), cast(ForRange.get())->getBody()); +} + } // end namespace clang #endif // LLVM_CLANG_LIB_SEMA_TREETRANSFORM_H diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index d9474b4d674c..3252a5c01ae6 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2819,6 +2819,11 @@ void ASTStmtReader::VisitCilkForStmt(CilkForStmt *S) { S->setRParenLoc(readSourceLocation()); } +void ASTStmtReader::VisitCilkForRangeStmt(CilkForRangeStmt *S) { + VisitStmt(S); + S->setForRange(Record.readSubStmt()); +} + //===----------------------------------------------------------------------===// // ASTReader Implementation //===----------------------------------------------------------------------===// @@ -3043,6 +3048,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) CilkScopeStmt(Empty); break; + case STMT_CILKFORRANGE: + S = new (Context) CilkForRangeStmt(Empty); + break; + case EXPR_PREDEFINED: S = PredefinedExpr::CreateEmpty( Context, diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 73264e4ce0e8..623990cc672a 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2318,6 +2318,12 @@ void ASTStmtWriter::VisitCilkForStmt(CilkForStmt *S) { Code = serialization::STMT_CILKFOR; } +void ASTStmtWriter::VisitCilkForRangeStmt(CilkForRangeStmt *S) { + VisitStmt(S); + Record.AddStmt(S->getCXXForRangeStmt()); + Code = serialization::STMT_CILKFORRANGE; +} + //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements. //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 744be0720a7d..354dd94d81c4 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1855,6 +1855,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::WhileStmtClass: case Expr::MSDependentExistsStmtClass: case Stmt::CilkForStmtClass: + case Stmt::CilkForRangeStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ImplicitValueInitExprClass: // These nodes are shared in the CFG and would case caching out. diff --git a/clang/test/Cilk/cilkforrange-ir.cpp b/clang/test/Cilk/cilkforrange-ir.cpp new file mode 100644 index 000000000000..b290ed2d8aca --- /dev/null +++ b/clang/test/Cilk/cilkforrange-ir.cpp @@ -0,0 +1,263 @@ +// RUN: %clang_cc1 %s -std=c++11 -triple x86_64-unknown-linux-gnu -fopencilk -ftapir=none -verify -S -emit-llvm -o - | FileCheck %s +// +// useful command: +// ./clang++ -std=c++11 -fopencilk -ftapir=none -S -emit-llvm ../opencilk-project/clang/test/Cilk/cilkforrange-ir.cpp +// cat cilkforrange-ir.ll | grep Z2upN1 -C 50 + +namespace X { +struct C { + C(); + struct It { + int value; + int operator-(It &); + It operator+(int); + It operator++(); + It operator--(); + int &operator*(); + bool operator!=(It &); + }; + It begin(); + It end(); +}; + +} // namespace X + +void bar(int i); + +void iterate(X::C c) { + _Cilk_for(int x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + bar(x); +} + +// CHECK-LABEL: define void @_Z7iterateN1X1CE( + +// CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 +// CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() +// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 +// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORCOND:.+]] + +// CHECK: [[PFORCOND]]: +// CHECK-NEXT: br label %[[PFORDETACH:.+]] + +// CHECK: [[PFORDETACH]]: +// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] + +// CHECK: [[DETACHED]]: +// CHECK-NEXT: %__local_loopindex = alloca i32, align 4 +// CHECK-NEXT: %[[X:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) +// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, i32* %[[ELEM]], align 4 +// CHECK-NEXT: store i32 %[[ELEMVAL]], i32* %[[X]], align 4 + +// CHECK: [[PFORINC]]: +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 +// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] +// CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] + +// CHECK: [[PFORCONDCLEANUP]]: +// CHECK-NEXT: sync within %[[SYNCREG]] + +void iterate_ref(X::C c) { + _Cilk_for(int &x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + bar(x); +} + +// CHECK-LABEL: define void @_Z11iterate_refN1X1CE( + +// CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 +// CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() +// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 +// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORCOND:.+]] + +// CHECK: [[PFORCOND]]: +// CHECK-NEXT: br label %[[PFORDETACH:.+]] + +// CHECK: [[PFORDETACH]]: +// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] + +// CHECK: [[DETACHED]]: +// CHECK-NEXT: %__local_loopindex = alloca i32, align 4 +// CHECK-NEXT: %[[X:.+]] = alloca i32*, align 8 +// CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) +// CHECK-NEXT: store i32* %[[ELEM]], i32** %[[X]], align 8 + +// CHECK: [[PFORINC]]: +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 +// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] +// CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] + +// CHECK: [[PFORCONDCLEANUP]]: +// CHECK-NEXT: sync within %[[SYNCREG]] + +void iterate_auto(X::C c) { + _Cilk_for(auto x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + bar(x); +} + +// CHECK-LABEL: define void @_Z12iterate_autoN1X1CE( + +// CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 +// CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() +// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 +// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORCOND:.+]] + +// CHECK: [[PFORCOND]]: +// CHECK-NEXT: br label %[[PFORDETACH:.+]] + +// CHECK: [[PFORDETACH]]: +// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] + +// CHECK: [[DETACHED]]: +// CHECK-NEXT: %__local_loopindex = alloca i32, align 4 +// CHECK-NEXT: %[[X:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) +// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, i32* %[[ELEM]], align 4 +// CHECK-NEXT: store i32 %[[ELEMVAL]], i32* %[[X]], align 4 + +// CHECK: [[PFORINC]]: +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 +// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] +// CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] + +// CHECK: [[PFORCONDCLEANUP]]: +// CHECK-NEXT: sync within %[[SYNCREG]] + +void iterate_autoref(X::C c) { + _Cilk_for(auto &x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + bar(x); +} + +// CHECK-LABEL: define void @_Z15iterate_autorefN1X1CE( + +// CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 +// CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() +// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 +// CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 +// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORCOND:.+]] + +// CHECK: [[PFORCOND]]: +// CHECK-NEXT: br label %[[PFORDETACH:.+]] + +// CHECK: [[PFORDETACH]]: +// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] + +// CHECK: [[DETACHED]]: +// CHECK-NEXT: %__local_loopindex = alloca i32, align 4 +// CHECK-NEXT: %[[X:.+]] = alloca i32*, align 8 +// CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 +// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) +// CHECK-NEXT: store i32* %[[ELEM]], i32** %[[X]], align 8 + +// CHECK: [[PFORINC]]: +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 +// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] +// CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] + +// CHECK: [[PFORCONDCLEANUP]]: +// CHECK-NEXT: sync within %[[SYNCREG]] \ No newline at end of file diff --git a/clang/test/Cilk/rangelooptest.cpp b/clang/test/Cilk/rangelooptest.cpp new file mode 100644 index 000000000000..c5dcb6200363 --- /dev/null +++ b/clang/test/Cilk/rangelooptest.cpp @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -std=c++17 -verify -verify-ignore-unexpected=note %s + +namespace StdMock { +template +struct Vector { + T *arr; + Vector(int n) { + // malloc + } + struct It { + T value; + int operator-(It &); + It operator+(int); + It operator++(); + It operator--(); + T &operator*(); + bool operator!=(It &); + }; + It begin(); + It end(); + T &operator[](int i) { + return arr[i]; + } +}; +template +struct Set { + T *set; + Set(int n) { + // malloc + } + struct It { + T value; + It operator++(); + It operator--(); + T &operator*(); + bool operator!=(It &); + }; + It begin(); + It end(); +}; +struct Empty {}; +template +struct Pair { + T first; + U second; +}; +} // namespace StdMock + +int foo(int n); + +int Cilk_for_range_tests(int n) { + StdMock::Vector v(n); + for (int i = 0; i < n; i++) + v[i] = i; + + _Cilk_for(auto x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto &x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(StdMock::Empty x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-error {{no viable conversion from 'int' to 'StdMock::Empty'}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + + // Pairs are aggregate types, which initially had a bug. Assert that they work + StdMock::Vector> vp(n); + for (int i = 0; i < n; i++) { + vp[i] = {i, i + 1}; + } + _Cilk_for(auto p : vp) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + continue; + _Cilk_for(auto &p : vp) { // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + continue; + } + + int a[5]; + _Cilk_for(int x : a) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + continue; + + StdMock::Set s(n); + _Cilk_for(int x : s); // expected-error {{Cannot determine length with '__end - __begin'. Please use a random access iterator.}} expected-error {{invalid operands to binary expression ('StdMock::Set::It' and 'StdMock::Set::It')}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + + // Check for return statements, which cannot appear anywhere in the body of a + // _Cilk_for loop. + _Cilk_for(int i : v) return 7; // expected-error{{cannot return}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + for (int j = 1; j < i; ++j) + return 7; // expected-error{{cannot return}} + + // Check for illegal break statements, which cannot bind to the scope of a + // _Cilk_for loop, but can bind to loops nested within. + _Cilk_for(int i : v) break; // expected-error{{cannot break}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + for (int j = 1; j < i; ++j) + break; + + return 0; +} + +int range_pragma_tests(int n) { + StdMock::Vector v(n); + for (int i = 0; i < n; i++) + v[i] = i; + +#pragma clang loop unroll_count(4) + _Cilk_for(auto i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + foo(i); + +#pragma cilk grainsize(4) + _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + foo(i); + +#pragma cilk grainsize 4 + _Cilk_for(auto i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + foo(i); + +#pragma cilk grainsize = 4 // expected-warning{{'#pragma cilk grainsize' no longer requires '='}} + _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + foo(i); + + return 0; +} + +int range_scope_tests(int n) { + StdMock::Vector v(n); + for (int i = 0; i < n; i++) + v[i] = i; + int A[5]; + _Cilk_for(int i : v) { // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + int A[5]; + A[i % 5] = i; + } + for (int i : v) { + A[i % 5] = i % 5; + } + return 0; +} \ No newline at end of file diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 88b2caf65e90..2b0c2e680336 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6115,8 +6115,9 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("CilkForStmt"); case CXCursor_CilkScopeStmt: return cxstring::createRef("CilkScopeStmt"); + case CXCursor_CilkForRangeStmt: + return cxstring::createRef("CilkForRangeStmt"); } - llvm_unreachable("Unhandled CXCursorKind"); } diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index e206ac746bb4..e02943a475ef 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -312,6 +312,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CilkScopeStmtClass: K = CXCursor_CilkScopeStmt; break; + + case Stmt::CilkForRangeStmtClass: + K = CXCursor_CilkForRangeStmt; + break; case Stmt::ArrayTypeTraitExprClass: case Stmt::AsTypeExprClass: From 5d642f28c90c24baf6c1e477ea5c6c18e5cbbfdd Mon Sep 17 00:00:00 2001 From: Elie Cuevas Date: Fri, 1 Nov 2024 19:38:27 -0400 Subject: [PATCH 2/5] remove unneeded namespace --- clang/lib/CodeGen/CGCilk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGCilk.cpp b/clang/lib/CodeGen/CGCilk.cpp index 4391185b8d7f..cb5ade7e02c1 100644 --- a/clang/lib/CodeGen/CGCilk.cpp +++ b/clang/lib/CodeGen/CGCilk.cpp @@ -18,7 +18,6 @@ using namespace clang; using namespace CodeGen; -using namespace std; CodeGenFunction::IsSpawnedScope::IsSpawnedScope(CodeGenFunction *CGF) : CGF(CGF), OldIsSpawned(CGF->IsSpawned), @@ -495,6 +494,7 @@ void CodeGenFunction::EmitCilkScopeStmt(const CilkScopeStmt &S) { void CodeGenFunction::EmitCilkForRangeStmt(const CilkForRangeStmt &S, ArrayRef ForAttrs) { + JumpDest LoopExit = getJumpDestInCurrentScope("pfor.end"); // Setup the sync region From 466b16346d174e48d1e787fbf40407c346143dc8 Mon Sep 17 00:00:00 2001 From: Elie Cuevas Date: Sun, 17 Nov 2024 15:30:19 -0500 Subject: [PATCH 3/5] possibly fixed empty container problem --- clang/lib/CodeGen/CGCilk.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CGCilk.cpp b/clang/lib/CodeGen/CGCilk.cpp index cb5ade7e02c1..a447b98bb3fe 100644 --- a/clang/lib/CodeGen/CGCilk.cpp +++ b/clang/lib/CodeGen/CGCilk.cpp @@ -15,9 +15,11 @@ #include "CGCleanup.h" #include "clang/AST/ExprCilk.h" #include "clang/AST/StmtCilk.h" +#include using namespace clang; using namespace CodeGen; +using namespace std; CodeGenFunction::IsSpawnedScope::IsSpawnedScope(CodeGenFunction *CGF) : CGF(CGF), OldIsSpawned(CGF->IsSpawned), @@ -530,16 +532,27 @@ CodeGenFunction::EmitCilkForRangeStmt(const CilkForRangeStmt &S, // increment, the continue scope will be overwritten later. JumpDest Continue = getJumpDestInCurrentScope("pfor.cond"); llvm::BasicBlock *CondBlock = Continue.getBlock(); + + // TODO: need to check condition and then get to pfor.end + llvm::BasicBlock *InitialEntryBlock = createBasicBlock("pfor.initial.entry"); + EmitBlock(InitialEntryBlock); + llvm::Value *InitialBoolCondVal = EvaluateExprAsBool(S.getCond()); + // llvm::MDNode *Weights = + // createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody())); + + // Do we need weights? + Builder.CreateCondBr(InitialBoolCondVal, Continue.getBlock(), LoopExit.getBlock()); + + + EmitBlock(CondBlock); Expr::EvalResult Result; - bool CondIsConstInt = S.getCond()->EvaluateAsInt(Result, getContext()); LoopStack.setSpawnStrategy(LoopAttributes::DAC); const SourceRange &R = S.getSourceRange(); LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, SourceLocToDebugLoc(R.getBegin()), - SourceLocToDebugLoc(R.getEnd()), - checkIfLoopMustProgress(CondIsConstInt)); + SourceLocToDebugLoc(R.getEnd())); const Expr *Inc = S.getInc(); assert(Inc && "_Cilk_for range loop has no increment"); From b32e07f9326fb60648a821c3555723200ff8728b Mon Sep 17 00:00:00 2001 From: Elie Cuevas Date: Wed, 18 Dec 2024 15:11:00 -0500 Subject: [PATCH 4/5] fixed forrangelooptest --- .../clang/Basic/DiagnosticParseKinds.td | 2 +- clang/lib/CodeGen/CGCilk.cpp | 4 --- clang/lib/Sema/SemaStmtAttr.cpp | 2 +- clang/test/Cilk/rangelooptest.cpp | 36 ++++++++++--------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 1fa1ceec7d1f..82b491f5c1a5 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1400,7 +1400,7 @@ def warn_pragma_cilk_grainsize_equals: Warning< "'#pragma cilk grainsize' no longer requires '='">, InGroup; def warn_cilk_for_forrange_loop_experimental: Warning< - "'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!">, + "'cilk_for' support for for-range loops is currently EXPERIMENTAL only!">, InGroup; def error_hyperobject_arguments: Error< "hyperobject must have 0 or 2 callbacks">; diff --git a/clang/lib/CodeGen/CGCilk.cpp b/clang/lib/CodeGen/CGCilk.cpp index a447b98bb3fe..9737b7f08d6a 100644 --- a/clang/lib/CodeGen/CGCilk.cpp +++ b/clang/lib/CodeGen/CGCilk.cpp @@ -533,14 +533,10 @@ CodeGenFunction::EmitCilkForRangeStmt(const CilkForRangeStmt &S, JumpDest Continue = getJumpDestInCurrentScope("pfor.cond"); llvm::BasicBlock *CondBlock = Continue.getBlock(); - // TODO: need to check condition and then get to pfor.end llvm::BasicBlock *InitialEntryBlock = createBasicBlock("pfor.initial.entry"); EmitBlock(InitialEntryBlock); llvm::Value *InitialBoolCondVal = EvaluateExprAsBool(S.getCond()); - // llvm::MDNode *Weights = - // createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody())); - // Do we need weights? Builder.CreateCondBr(InitialBoolCondVal, Continue.getBlock(), LoopExit.getBlock()); diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 8e2e257f13cf..ef303b8b1f5b 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -98,7 +98,7 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, // This could be handled automatically by adding a Subjects definition in // Attr.td, but that would make the diagnostic behavior worse in this case // because the user spells this attribute as a pragma. - if (!isa(St)) { + if (!isa(St)) { std::string Pragma = "#pragma " + std::string(PragmaName); S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; return nullptr; diff --git a/clang/test/Cilk/rangelooptest.cpp b/clang/test/Cilk/rangelooptest.cpp index c5dcb6200363..9e3c2507d870 100644 --- a/clang/test/Cilk/rangelooptest.cpp +++ b/clang/test/Cilk/rangelooptest.cpp @@ -53,40 +53,42 @@ int Cilk_for_range_tests(int n) { for (int i = 0; i < n; i++) v[i] = i; - _Cilk_for(auto x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} - _Cilk_for(auto &x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} - _Cilk_for(int x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} - _Cilk_for(StdMock::Empty x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{Cilk for loop has empty body}} expected-error {{no viable conversion from 'int' to 'StdMock::Empty'}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{'cilk_for' loop has empty body}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto &x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{'cilk_for' loop has empty body}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{'cilk_for' loop has empty body}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(StdMock::Empty x : v); // expected-warning {{range-based for loop has empty body}} expected-warning {{'cilk_for' loop has empty body}} expected-error {{no viable conversion from 'int' to 'StdMock::Empty'}} expected-error {{no viable conversion from 'int' to 'StdMock::Empty'}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + // in line above, the "no viable conversion" error comes twice because of the new CodeGen structure of cilk_for_range loops, which now adds an extra check to the initial condition before entering the loop ("pfor.initial.entry") + // Pairs are aggregate types, which initially had a bug. Assert that they work StdMock::Vector> vp(n); for (int i = 0; i < n; i++) { vp[i] = {i, i + 1}; } - _Cilk_for(auto p : vp) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto p : vp) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} continue; - _Cilk_for(auto &p : vp) { // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto &p : vp) { // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} continue; } int a[5]; - _Cilk_for(int x : a) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int x : a) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} continue; StdMock::Set s(n); - _Cilk_for(int x : s); // expected-error {{Cannot determine length with '__end - __begin'. Please use a random access iterator.}} expected-error {{invalid operands to binary expression ('StdMock::Set::It' and 'StdMock::Set::It')}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int x : s); // expected-error {{Cannot determine length with '__end - __begin'. Please use a random access iterator.}} expected-error {{invalid operands to binary expression ('It' and 'It')}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} // Check for return statements, which cannot appear anywhere in the body of a // _Cilk_for loop. - _Cilk_for(int i : v) return 7; // expected-error{{cannot return}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} - _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) return 7; // expected-error{{cannot return}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} for (int j = 1; j < i; ++j) return 7; // expected-error{{cannot return}} // Check for illegal break statements, which cannot bind to the scope of a // _Cilk_for loop, but can bind to loops nested within. - _Cilk_for(int i : v) break; // expected-error{{cannot break}} expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} - _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) break; // expected-error{{cannot break}} expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} for (int j = 1; j < i; ++j) break; @@ -99,19 +101,19 @@ int range_pragma_tests(int n) { v[i] = i; #pragma clang loop unroll_count(4) - _Cilk_for(auto i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} foo(i); #pragma cilk grainsize(4) - _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} foo(i); #pragma cilk grainsize 4 - _Cilk_for(auto i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} foo(i); #pragma cilk grainsize = 4 // expected-warning{{'#pragma cilk grainsize' no longer requires '='}} - _Cilk_for(int i : v) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} foo(i); return 0; @@ -122,7 +124,7 @@ int range_scope_tests(int n) { for (int i = 0; i < n; i++) v[i] = i; int A[5]; - _Cilk_for(int i : v) { // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int i : v) { // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} int A[5]; A[i % 5] = i; } From 5e1d1280615d1dc568998cfe233d25c40ec3811f Mon Sep 17 00:00:00 2001 From: Elie Cuevas Date: Wed, 18 Dec 2024 17:28:12 -0500 Subject: [PATCH 5/5] fixed ir test --- clang/test/Cilk/cilkforrange-ir.cpp | 273 +++++++++++++++------------- 1 file changed, 149 insertions(+), 124 deletions(-) diff --git a/clang/test/Cilk/cilkforrange-ir.cpp b/clang/test/Cilk/cilkforrange-ir.cpp index b290ed2d8aca..4edf0ebe92d1 100644 --- a/clang/test/Cilk/cilkforrange-ir.cpp +++ b/clang/test/Cilk/cilkforrange-ir.cpp @@ -25,59 +25,65 @@ struct C { void bar(int i); void iterate(X::C c) { - _Cilk_for(int x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int x : c) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} bar(x); } -// CHECK-LABEL: define void @_Z7iterateN1X1CE( +// CHECK-LABEL: @_Z7iterateN1X1CE( // CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 // CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() -// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[RANGE:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 -// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) -// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 -// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) -// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 -// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) -// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 -// CHECK-NEXT: br label %[[PFORCOND:.+]] +// CHECK-NEXT: store ptr %[[C]], ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], ptr %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], ptr %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CALL:.+]] = call noundef i32 @_ZN1X1C2ItmiERS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[END]], ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CALL]], ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORINITIALENTRY:.+]] + +// CHECK: [[PFORINITIALENTRY]]: +// CHECK-NEXT: %[[FIRSTINDEX:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[LASTINDEX:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COMPARISON:.+]] = icmp ne i32 %[[FIRSTINDEX]], %[[LASTINDEX]] +// CHECK-NEXT: br i1 %[[COMPARISON]], label %[[PFORCOND:.+]], label %[[PFOREND:.+]] // CHECK: [[PFORCOND]]: // CHECK-NEXT: br label %[[PFORDETACH:.+]] // CHECK: [[PFORDETACH]]: -// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] +// CHECK-NEXT: %[[INITITER:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[PFORBODYENTRY:.+]], label %[[PFORINC:.+]] -// CHECK: [[DETACHED]]: +// CHECK: [[PFORBODYENTRY]]: // CHECK-NEXT: %__local_loopindex = alloca i32, align 4 // CHECK-NEXT: %[[X:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 -// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) -// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 -// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) -// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, i32* %[[ELEM]], align 4 -// CHECK-NEXT: store i32 %[[ELEMVAL]], i32* %[[X]], align 4 +// CHECK-NEXT: store i32 %[[INITITER]], ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]], i32 noundef %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], ptr %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN1X1C2ItdeEv(ptr noundef nonnull align 4 dereferenceable(4) %[[ITER]]) +// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, ptr %[[ELEM]], align 4 +// CHECK-NEXT: store i32 %[[ELEMVAL]], ptr %[[X]], align 4 // CHECK: [[PFORINC]]: -// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 // CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 -// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: store i32 %[[INC]], ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 // CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] // CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] @@ -85,58 +91,64 @@ void iterate(X::C c) { // CHECK-NEXT: sync within %[[SYNCREG]] void iterate_ref(X::C c) { - _Cilk_for(int &x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(int &x : c) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} bar(x); } -// CHECK-LABEL: define void @_Z11iterate_refN1X1CE( +// CHECK-LABEL: @_Z11iterate_refN1X1CE( // CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 // CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() -// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[RANGE:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 -// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) -// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 -// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) -// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 -// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) -// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 -// CHECK-NEXT: br label %[[PFORCOND:.+]] +// CHECK-NEXT: store ptr %[[C]], ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], ptr %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], ptr %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call noundef i32 @_ZN1X1C2ItmiERS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[END]], ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORINITIALENTRY:.+]] + +// CHECK: [[PFORINITIALENTRY]]: +// CHECK-NEXT: %[[FIRSTINDEX:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[LASTINDEX:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COMPARISON:.+]] = icmp ne i32 %[[FIRSTINDEX]], %[[LASTINDEX]] +// CHECK-NEXT: br i1 %[[COMPARISON]], label %[[PFORCOND:.+]], label %[[PFOREND:.+]] // CHECK: [[PFORCOND]]: // CHECK-NEXT: br label %[[PFORDETACH:.+]] // CHECK: [[PFORDETACH]]: -// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] +// CHECK-NEXT: %[[INITITER:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[PFORBODYENTRY:.+]], label %[[PFORINC:.+]] -// CHECK: [[DETACHED]]: +// CHECK: [[PFORBODYENTRY]]: // CHECK-NEXT: %__local_loopindex = alloca i32, align 4 -// CHECK-NEXT: %[[X:.+]] = alloca i32*, align 8 +// CHECK-NEXT: %[[X:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 -// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) -// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 -// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) -// CHECK-NEXT: store i32* %[[ELEM]], i32** %[[X]], align 8 +// CHECK-NEXT: store i32 %[[INITITER]], ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]], i32 noundef %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], ptr %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN1X1C2ItdeEv(ptr noundef nonnull align 4 dereferenceable(4) %[[ITER]]) +// CHECK-NEXT: store ptr %[[ELEM]], ptr %[[X]], align 8 // CHECK: [[PFORINC]]: -// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 // CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 -// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: store i32 %[[INC]], ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 // CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] // CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] @@ -144,59 +156,65 @@ void iterate_ref(X::C c) { // CHECK-NEXT: sync within %[[SYNCREG]] void iterate_auto(X::C c) { - _Cilk_for(auto x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto x : c) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} bar(x); } -// CHECK-LABEL: define void @_Z12iterate_autoN1X1CE( +// CHECK-LABEL: @_Z12iterate_autoN1X1CE( // CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 // CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() -// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[RANGE:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 -// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) -// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 -// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) -// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 -// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) -// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 -// CHECK-NEXT: br label %[[PFORCOND:.+]] +// CHECK-NEXT: store ptr %[[C]], ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], ptr %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], ptr %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call noundef i32 @_ZN1X1C2ItmiERS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[END]], ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORINITIALENTRY:.+]] + +// CHECK: [[PFORINITIALENTRY]]: +// CHECK-NEXT: %[[FIRSTINDEX:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[LASTINDEX:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COMPARISON:.+]] = icmp ne i32 %[[FIRSTINDEX]], %[[LASTINDEX]] +// CHECK-NEXT: br i1 %[[COMPARISON]], label %[[PFORCOND:.+]], label %[[PFOREND:.+]] // CHECK: [[PFORCOND]]: // CHECK-NEXT: br label %[[PFORDETACH:.+]] // CHECK: [[PFORDETACH]]: -// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] +// CHECK-NEXT: %[[INITITER:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[PFORBODYENTRY:.+]], label %[[PFORINC:.+]] -// CHECK: [[DETACHED]]: +// CHECK: [[PFORBODYENTRY]]: // CHECK-NEXT: %__local_loopindex = alloca i32, align 4 // CHECK-NEXT: %[[X:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 -// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) -// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 -// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) -// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, i32* %[[ELEM]], align 4 -// CHECK-NEXT: store i32 %[[ELEMVAL]], i32* %[[X]], align 4 +// CHECK-NEXT: store i32 %[[INITITER]], ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]], i32 noundef %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], ptr %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN1X1C2ItdeEv(ptr noundef nonnull align 4 dereferenceable(4) %[[ITER]]) +// CHECK-NEXT: %[[ELEMVAL:.+]] = load i32, ptr %[[ELEM]], align 4 +// CHECK-NEXT: store i32 %[[ELEMVAL]], ptr %[[X]], align 4 // CHECK: [[PFORINC]]: -// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 // CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 -// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: store i32 %[[INC]], ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 // CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] // CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]] @@ -204,58 +222,65 @@ void iterate_auto(X::C c) { // CHECK-NEXT: sync within %[[SYNCREG]] void iterate_autoref(X::C c) { - _Cilk_for(auto &x : c) // expected-warning {{'_Cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} + _Cilk_for(auto &x : c) // expected-warning {{'cilk_for' support for for-range loops is currently EXPERIMENTAL only!}} bar(x); } -// CHECK-LABEL: define void @_Z15iterate_autorefN1X1CE( +// CHECK-LABEL: @_Z15iterate_autorefN1X1CE( // CHECK: %[[C:.+]] = alloca %"struct.X::C", align 1 // CHECK-NEXT: %syncreg = call token @llvm.syncregion.start() -// CHECK-NEXT: %[[RANGE:.+]] = alloca %"struct.X::C"*, align 8 +// CHECK-NEXT: %[[RANGE:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[BEGIN:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[END:.+]] = alloca %"struct.X::C::It", align 4 // CHECK-NEXT: %[[CILKLOOPINDEX:.+]] = alloca i32, align 4 // CHECK-NEXT: %[[CILKLOOPLIMIT:.+]] = alloca i32, align 4 -// CHECK-NEXT: store %"struct.X::C"* %[[C]], %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[CONTAINER:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(%"struct.X::C"* %[[CONTAINER]]) -// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[BEGIN]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[BEGINCALL]], i32* %[[BEGINCOERCE]], align 4 -// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load %"struct.X::C"*, %"struct.X::C"** %[[RANGE]], align 8 -// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(%"struct.X::C"* %[[CONTAINERAGAIN]]) -// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[END]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ENDCALL]], i32* %[[ENDCOERCE]], align 4 -// CHECK-NEXT: store i32 0, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call i32 @_ZN1X1C2ItmiERS1_(%"struct.X::C::It"* %[[END]], %"struct.X::C::It"* dereferenceable(4) %[[BEGIN]]) -// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], i32* %[[CILKLOOPLIMIT]], align 4 -// CHECK-NEXT: br label %[[PFORCOND:.+]] +// CHECK-NEXT: store ptr %[[C]], ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[CONTAINER:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[BEGINCALL:.+]] = call i32 @_ZN1X1C5beginEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINER]]) +// CHECK-NEXT: %[[BEGINCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[BEGIN]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[BEGINCALL]], ptr %[[BEGINCOERCE]], align 4 +// CHECK-NEXT: %[[CONTAINERAGAIN:.+]] = load ptr, ptr %[[RANGE]], align 8 +// CHECK-NEXT: %[[ENDCALL:.+]] = call i32 @_ZN1X1C3endEv(ptr noundef nonnull align 1 dereferenceable(1) %[[CONTAINERAGAIN]]) +// CHECK-NEXT: %[[ENDCOERCE:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[END]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ENDCALL]], ptr %[[ENDCOERCE]], align 4 +// CHECK-NEXT: store i32 0, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONTAINERLENGTH:.+]] = call noundef i32 @_ZN1X1C2ItmiERS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[END]], ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]]) +// CHECK-NEXT: store i32 %[[CONTAINERLENGTH]], ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: br label %[[PFORINITIALENTRY:.+]] + +// CHECK: [[PFORINITIALENTRY]]: +// CHECK-NEXT: %[[FIRSTINDEX:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[LASTINDEX:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: %[[COMPARISON:.+]] = icmp ne i32 %[[FIRSTINDEX]], %[[LASTINDEX]] +// CHECK-NEXT: br i1 %[[COMPARISON]], label %[[PFORCOND:.+]], label %[[PFOREND:.+]] + // CHECK: [[PFORCOND]]: // CHECK-NEXT: br label %[[PFORDETACH:.+]] // CHECK: [[PFORDETACH]]: -// CHECK-NEXT: %[[INITITER:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[DETACHED:.+]], label %[[PFORINC:.+]] +// CHECK-NEXT: %[[INITITER:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: detach within %[[SYNCREG:.+]], label %[[PFORBODYENTRY:.+]], label %[[PFORINC:.+]] -// CHECK: [[DETACHED]]: +// CHECK: [[PFORBODYENTRY]]: // CHECK-NEXT: %__local_loopindex = alloca i32, align 4 -// CHECK-NEXT: %[[X:.+]] = alloca i32*, align 8 +// CHECK-NEXT: %[[X:.+]] = alloca ptr, align 8 // CHECK-NEXT: %[[ITER:.+]] = alloca %"struct.X::C::It", align 4 -// CHECK-NEXT: store i32 %[[INITITER]], i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, i32* %__local_loopindex, align 4 -// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(%"struct.X::C::It"* %[[BEGIN]], i32 %[[LOOPINDEXCOPY]]) -// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", %"struct.X::C::It"* %[[ITER]], i32 0, i32 0 -// CHECK-NEXT: store i32 %[[ITERREF]], i32* %[[ITER2]], align 4 -// CHECK-NEXT: %[[ELEM:.+]] = call dereferenceable(4) i32* @_ZN1X1C2ItdeEv(%"struct.X::C::It"* %[[ITER]]) -// CHECK-NEXT: store i32* %[[ELEM]], i32** %[[X]], align 8 +// CHECK-NEXT: store i32 %[[INITITER]], ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[LOOPINDEXCOPY:.+]] = load i32, ptr %__local_loopindex, align 4 +// CHECK-NEXT: %[[ITERREF:.+]] = call i32 @_ZN1X1C2ItplEi(ptr noundef nonnull align 4 dereferenceable(4) %[[BEGIN]], i32 noundef %[[LOOPINDEXCOPY]]) +// CHECK-NEXT: %[[ITER2:.+]] = getelementptr inbounds %"struct.X::C::It", ptr %[[ITER]], i32 0, i32 0 +// CHECK-NEXT: store i32 %[[ITERREF]], ptr %[[ITER2]], align 4 +// CHECK-NEXT: %[[ELEM:.+]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN1X1C2ItdeEv(ptr noundef nonnull align 4 dereferenceable(4) %[[ITER]]) +// CHECK-NEXT: store ptr %[[ELEM]], ptr %[[X]], align 8 // CHECK: [[PFORINC]]: -// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[INCBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 // CHECK-NEXT: %[[INC:.+]] = add nsw i32 %[[INCBEGIN]], 1 -// CHECK-NEXT: store i32 %[[INC]], i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, i32* %[[CILKLOOPINDEX]], align 4 -// CHECK-NEXT: %[[CONDEND:.+]] = load i32, i32* %[[CILKLOOPLIMIT]], align 4 +// CHECK-NEXT: store i32 %[[INC]], ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDBEGIN:.+]] = load i32, ptr %[[CILKLOOPINDEX]], align 4 +// CHECK-NEXT: %[[CONDEND:.+]] = load i32, ptr %[[CILKLOOPLIMIT]], align 4 // CHECK-NEXT: %[[COND:.+]] = icmp ne i32 %[[CONDBEGIN]], %[[CONDEND]] // CHECK-NEXT: br i1 %[[COND]], label %{{.+}}, label %[[PFORCONDCLEANUP:.+]], !llvm.loop ![[LOOPMD:.+]]