diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index e35ceb8527a1..00e4a3c88cd4 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2590,7 +2590,11 @@ enum CXCursorKind { */ CXCursor_CilkForStmt = 289, - CXCursor_LastStmt = CXCursor_CilkForStmt, + /** A _Cilk_for range statement. + */ + CXCursor_CilkForRangeStmt = 290, + + 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 56b507fc641f..96000dd1fe29 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2669,6 +2669,7 @@ DEF_TRAVERSE_STMT(CilkSpawnStmt, {}) DEF_TRAVERSE_STMT(CilkSpawnExpr, {}) DEF_TRAVERSE_STMT(CilkSyncStmt, {}) DEF_TRAVERSE_STMT(CilkForStmt, {}) +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 6f10b70b98b2..9f2b3c5ad54e 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 { @@ -84,6 +85,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 5a094c7acfec..c054058153e6 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1221,8 +1221,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< @@ -1235,6 +1233,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; // OpenMP support. def warn_pragma_omp_ignored : Warning< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1a04adef8e49..06fa958a190a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9519,7 +9519,13 @@ def note_exits_cilk_for : Note< "jump exits the scope of a '_Cilk_for'">; def err_jump_out_of_cilk_for : Error< "cannot jump out of '_Cilk_for' statement">; -} // end of Cilk category + +// Cilk For Range errors + +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 9acb503579b5..c7820efc4aa1 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -215,6 +215,7 @@ def CilkSyncStmt : StmtNode; def CilkSpawnStmt : StmtNode; def CilkSpawnExpr : StmtNode; def CilkForStmt : StmtNode; +def CilkForRangeStmt: StmtNode; // OpenMP Directives. def OMPExecutableDirective : StmtNode; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a26bfa7820d3..72e9d0be9639 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12093,6 +12093,13 @@ class Sema final { ConstructorDestructor, BuiltinFunction }; + 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); }; /// RAII object that enters a new expression evaluation context. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 0c08bedc0137..6e68983e0b07 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1877,6 +1877,7 @@ namespace serialization { EXPR_CILKSPAWN, STMT_CILKSYNC, STMT_CILKFOR, + 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 060d090fc06a..190c37ff1084 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; @@ -125,3 +126,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(); +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index b6b679b806f3..5542181ad65b 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2562,6 +2562,24 @@ void StmtPrinter::VisitCilkForStmt(CilkForStmt *Node) { Indent() << "}"; } +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 0d8be5709fb9..144d8bb0deda 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1982,6 +1982,10 @@ void StmtProfiler::VisitCilkForStmt(const CilkForStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCilkForRangeStmt(const CilkForRangeStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitCilkSpawnStmt(const CilkSpawnStmt *S) { VisitStmt(S); } diff --git a/clang/lib/CodeGen/CGCilk.cpp b/clang/lib/CodeGen/CGCilk.cpp index 7abc0235f2b4..c2d02bb17b0b 100644 --- a/clang/lib/CodeGen/CGCilk.cpp +++ b/clang/lib/CodeGen/CGCilk.cpp @@ -817,3 +817,287 @@ void CodeGenFunction::EmitCilkForStmt(const CilkForStmt &S, if (TempInvokeDest->use_empty()) delete TempInvokeDest; } + +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); + + LoopStack.setSpawnStrategy(LoopAttributes::DAC); + const SourceRange &R = S.getSourceRange(); + LoopStack.push(CondBlock, CGM.getContext(), ForAttrs, + SourceLocToDebugLoc(R.getBegin()), + SourceLocToDebugLoc(R.getEnd())); + + 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; +} diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index c01f746baca2..68cb17726a40 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -165,6 +165,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::CilkForStmtClass: EmitCilkForStmt(cast(*S), Attrs); 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 145367ad9386..35acdcdbb3d4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3441,6 +3441,8 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitCilkSyncStmt(const CilkSyncStmt &S); void EmitCilkForStmt(const CilkForStmt &S, ArrayRef Attrs = None); + void EmitCilkForRangeStmt(const CilkForRangeStmt &S, + ArrayRef Attrs = None); 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 8945205e0751..dd65dec83ed1 100644 --- a/clang/lib/Parse/ParseCilk.cpp +++ b/clang/lib/Parse/ParseCilk.cpp @@ -57,9 +57,19 @@ StmtResult Parser::ParseCilkSpawnStatement() { /// 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'. + SourceLocation ForLoc = ConsumeToken(); // eat the '_Cilk_for'. // SourceLocation CoawaitLoc; // if (Tok.is(tok::kw_co_await)) @@ -71,8 +81,8 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { return StmtError(); } - bool C99orCXXorObjC = getLangOpts().C99 || getLangOpts().CPlusPlus || - getLangOpts().ObjC; + bool C99orCXXorObjC = + getLangOpts().C99 || getLangOpts().CPlusPlus || getLangOpts().ObjC; // A _Cilk_for statement is a block. Start the loop scope. // @@ -97,11 +107,11 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { ExprResult Value; - bool ForEach = false, ForRange = false; + bool ForEach = false; StmtResult FirstPart; Sema::ConditionResult SecondPart; ExprResult Collection; - ForRangeInit ForRangeInit; + ForRangeInfo ForRangeInfo; FullExprArg ThirdPart(Actions); if (Tok.is(tok::code_completion)) { @@ -113,8 +123,10 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); + SourceLocation EmptyInitStmtSemiLoc; + // Parse the first part of the for specifier. - if (Tok.is(tok::semi)) { // _Cilk_for (; + if (Tok.is(tok::semi)) { // _Cilk_for (; ProhibitAttributes(attrs); // We disallow this syntax for now. Diag(Tok, diag::err_cilk_for_missing_control_variable) << ";"; @@ -126,23 +138,22 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { SourceLocation Loc = ConsumeToken(); MaybeParseCXX11Attributes(attrs); - ForRangeInit.ColonLoc = ConsumeToken(); + ForRangeInfo.ColonLoc = ConsumeToken(); if (Tok.is(tok::l_brace)) - ForRangeInit.RangeExpr = ParseBraceInitializer(); + ForRangeInfo.RangeExpr = ParseBraceInitializer(); else - ForRangeInit.RangeExpr = ParseExpression(); + ForRangeInfo.RangeExpr = ParseExpression(); Diag(Loc, diag::err_for_range_identifier) - << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) - ? FixItHint::CreateInsertion(Loc, "auto &&") - : FixItHint()); - - FirstPart = Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, - attrs, attrs.Range.getEnd()); - ForRange = true; - } else if (isForInitDeclaration()) { // _Cilk_for (int X = 4; + << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) + ? FixItHint::CreateInsertion(Loc, "auto &&") + : FixItHint()); + + ForRangeInfo.LoopVar = Actions.ActOnCXXForRangeIdentifier( + getCurScope(), Loc, Name, attrs, attrs.Range.getEnd()); + } else if (isForInitDeclaration()) { // _Cilk_for (int X = 4; // Parse declaration, which eats the ';'. - if (!C99orCXXorObjC) // Use of C99-style for loops in C90 mode? + if (!C99orCXXorObjC) // Use of C99-style for loops in C90 mode? Diag(Tok, diag::ext_c99_variable_decl_in_for_loop); // In C++0x, "for (T NS:a" might not be a typo for :: @@ -152,14 +163,15 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy DG = ParseSimpleDeclaration( DeclaratorContext::ForContext, DeclEnd, attrs, false, - MightBeForRangeStmt ? &ForRangeInit : nullptr); + MightBeForRangeStmt ? &ForRangeInfo : nullptr); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); - if (ForRangeInit.ParsedForRangeDecl()) { - Diag(ForRangeInit.ColonLoc, getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_for_range : diag::ext_for_range); - - ForRange = true; - } else if (Tok.is(tok::semi)) { // for (int x = 4; + if (ForRangeInfo.ParsedForRangeDecl()) { + 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; ConsumeToken(); } else if ((ForEach = isTokIdentifier_in())) { Actions.ActOnForEachDeclStmt(DG); @@ -185,8 +197,16 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { if (!Value.isInvalid()) { if (ForEach) FirstPart = Actions.ActOnForEachLValueExpr(Value.get()); - else - FirstPart = Actions.ActOnExprStmt(Value); + else { + // We already know this is not an init-statement within a for loop, so + // if we are parsing a C++11 range-based for loop, we should treat this + // expression statement as being a discarded value expression because + // we will err below. This way we do not warn on an unused expression + // that was an error in the first place, like with: for (expr : expr); + bool IsRangeBasedFor = + getLangOpts().CPlusPlus11 && !ForEach && Tok.is(tok::colon); + FirstPart = Actions.ActOnExprStmt(Value, !IsRangeBasedFor); + } } if (Tok.is(tok::semi)) { @@ -200,11 +220,12 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { return StmtError(); } Collection = ParseExpression(); - } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::colon) && FirstPart.get()) { + } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::colon) && + FirstPart.get()) { // User tried to write the reasonable, but ill-formed, for-range-statement // for (expr : expr) { ... } Diag(Tok, diag::err_for_range_expected_decl) - << FirstPart.get()->getSourceRange(); + << FirstPart.get()->getSourceRange(); SkipUntil(tok::r_paren, StopBeforeMatch); SecondPart = Sema::ConditionError(); } else { @@ -221,21 +242,41 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { // Parse the second part of the for specifier. getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope); - if (!ForEach && !ForRange && !SecondPart.isInvalid()) { + if (!ForEach && !ForRangeInfo.ParsedForRangeDecl() && + !SecondPart.isInvalid()) { // Parse the second part of the for specifier. - if (Tok.is(tok::semi)) { // for (...;; + if (Tok.is(tok::semi)) { // for (...;; // no second part. Diag(Tok, diag::err_cilk_for_missing_condition) - << FirstPart.get()->getSourceRange(); + << FirstPart.get()->getSourceRange(); } else if (Tok.is(tok::r_paren)) { // missing both semicolons. Diag(Tok, diag::err_cilk_for_missing_condition) - << FirstPart.get()->getSourceRange(); + << FirstPart.get()->getSourceRange(); } else { - if (getLangOpts().CPlusPlus) + if (getLangOpts().CPlusPlus) { + // C++2a: We've parsed an init-statement; we might have a + // for-range-declaration next. + bool MightBeForRangeStmt = !ForRangeInfo.ParsedForRangeDecl(); + ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); SecondPart = ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean); - else { + + if (ForRangeInfo.ParsedForRangeDecl()) { + Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc() + : ForRangeInfo.ColonLoc, + getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_for_range_init_stmt + : diag::ext_for_range_init_stmt) + << (FirstPart.get() ? FirstPart.get()->getSourceRange() + : SourceRange()); + if (EmptyInitStmtSemiLoc.isValid()) { + Diag(EmptyInitStmtSemiLoc, diag::warn_empty_init_statement) + << /*for-loop*/ 2 + << FixItHint::CreateRemoval(EmptyInitStmtSemiLoc); + } + } + } else { ExprResult SecondExpr = ParseExpression(); if (SecondExpr.isInvalid()) SecondPart = Sema::ConditionError(); @@ -246,27 +287,29 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { } } - if (Tok.isNot(tok::semi)) { - if (!SecondPart.isInvalid()) - Diag(Tok, diag::err_expected_semi_for); - else - // Skip until semicolon or rparen, don't consume it. - SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch); - } + if (!ForEach && !ForRangeInfo.ParsedForRangeDecl()) { + if (Tok.isNot(tok::semi)) { + if (!SecondPart.isInvalid()) + Diag(Tok, diag::err_expected_semi_for); + else + // Skip until semicolon or rparen, don't consume it. + SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch); + } - if (Tok.is(tok::semi)) { - ConsumeToken(); - } + if (Tok.is(tok::semi)) { + ConsumeToken(); + } - // Parse the third part of the _Cilk_for specifier. - if (Tok.isNot(tok::r_paren)) { // for (...;...;) - ExprResult Third = ParseExpression(); - // FIXME: The C++11 standard doesn't actually say that this is a - // discarded-value expression, but it clearly should be. - ThirdPart = Actions.MakeFullDiscardedValueExpr(Third.get()); - } else - Diag(Tok, diag::err_cilk_for_missing_increment) - << FirstPart.get()->getSourceRange(); + // Parse the third part of the _Cilk_for specifier. + if (Tok.isNot(tok::r_paren)) { // for (...;...;) + ExprResult Third = ParseExpression(); + // FIXME: The C++11 standard doesn't actually say that this is a + // discarded-value expression, but it clearly should be. + ThirdPart = Actions.MakeFullDiscardedValueExpr(Third.get()); + } else + Diag(Tok, diag::err_cilk_for_missing_increment) + << FirstPart.get()->getSourceRange(); + } } // Match the ')'. T.consumeClose(); @@ -278,25 +321,23 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { // CoawaitLoc = SourceLocation(); // } - // // 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; + // We need to perform most of the semantic analysis for a C++0x for-range + // statement before parsing the body, in order to be able to deduce the type + // of an auto-typed loop variable. + StmtResult ForRangeStmt; // StmtResult ForEachStmt; - // TODO: Extend _Cilk_for to support these. - if (ForRange) { - Diag(ForLoc, diag::err_cilk_for_forrange_loop_not_supported); - // ExprResult CorrectedRange = - // Actions.CorrectDelayedTyposInExpr(ForRangeInit.RangeExpr.get()); - // ForRangeStmt = Actions.ActOnCXXForRangeStmt( - // getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(), - // ForRangeInit.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. + if (ForRangeInfo.ParsedForRangeDecl()) { + 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, @@ -349,8 +390,8 @@ StmtResult Parser::ParseCilkForStatement(SourceLocation *TrailingElseLoc) { // return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(), // Body.get()); - // if (ForRange) - // 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 5e926046ae59..24b27c07b9d4 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1483,6 +1483,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::CilkSpawnStmtClass: case Stmt::CilkSyncStmtClass: case Stmt::CilkForStmtClass: + case Stmt::CilkForRangeStmtClass: return canSubStmtsThrow(*this, S); case Stmt::DeclStmtClass: { diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 04cbf8dfa6d1..6d6db35a2b3d 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3496,6 +3496,184 @@ static void SearchForReturnInStmt(Sema &Self, Stmt *S) { } } +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())); +} + StmtResult Sema::ActOnCilkForStmt(SourceLocation CilkForLoc, SourceLocation LParenLoc, Stmt *First, DeclStmt *Limit, ConditionResult InitCond, diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index a6f84605431c..0522db45e6d4 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -87,9 +87,10 @@ 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"; + << "#pragma cilk"; return nullptr; } @@ -97,7 +98,8 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, St->getStmtClass() != Stmt::ForStmtClass && St->getStmtClass() != Stmt::CXXForRangeStmtClass && St->getStmtClass() != Stmt::WhileStmtClass && - St->getStmtClass() != Stmt::CilkForStmtClass) { + St->getStmtClass() != Stmt::CilkForStmtClass && + St->getStmtClass() != Stmt::CilkForRangeStmtClass) { std::string Pragma = "#pragma " + std::string(PragmaName); S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; return nullptr; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ab49046140c2..93a7f801b69f 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1368,6 +1368,21 @@ class TreeTransform { RParenLoc, Body, LoopVar); } + /// 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. @@ -13861,6 +13876,22 @@ TreeTransform::TransformCilkForStmt(CilkForStmt *S) { LoopVar, Body.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 d484b6ed24de..54b3d9cb8a0d 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2573,6 +2573,11 @@ void ASTStmtReader::VisitCilkForStmt(CilkForStmt *S) { S->setRParenLoc(readSourceLocation()); } +void ASTStmtReader::VisitCilkForRangeStmt(CilkForRangeStmt *S) { + VisitStmt(S); + S->setForRange(Record.readSubStmt()); +} + //===----------------------------------------------------------------------===// // ASTReader Implementation //===----------------------------------------------------------------------===// @@ -2788,6 +2793,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) CilkForStmt(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 e0c4b3119c59..f71903e4e19e 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2025,6 +2025,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/test/Cilk/cilkforrange-ir.cpp b/clang/test/Cilk/cilkforrange-ir.cpp new file mode 100644 index 000000000000..1f7ac22f576d --- /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]] diff --git a/clang/test/Cilk/rangelooptest.cpp b/clang/test/Cilk/rangelooptest.cpp new file mode 100644 index 000000000000..17d0fd11bdb7 --- /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; +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 245334779199..edda06583776 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5584,6 +5584,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("CilkSyncStmt"); case CXCursor_CilkForStmt: return cxstring::createRef("CilkForStmt"); + 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 2c8c114efcf3..3e9754437ca8 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -266,6 +266,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_CilkForStmt; break; + case Stmt::CilkForRangeStmtClass: + K = CXCursor_CilkForRangeStmt; + break; + case Stmt::ArrayTypeTraitExprClass: case Stmt::AsTypeExprClass: case Stmt::AtomicExprClass: