Index: lib/CodeGen/CGStmt.cpp =================================================================== --- lib/CodeGen/CGStmt.cpp +++ lib/CodeGen/CGStmt.cpp @@ -2028,20 +2028,29 @@ return SlotLV; } +static void InitVLACaptures(CodeGenFunction &CGF, const CapturedStmt &S) { + for (CapturedStmt::const_capture_iterator I = S.capture_begin(), + E = S.capture_end(); + I != E; ++I) { + if (I->capturesVariable()) { + QualType QTy = I->getCapturedVar()->getType(); + if (QTy->isVariablyModifiedType()) { + CGF.EmitVariablyModifiedType(QTy); + } + } + } +} + /// Generate an outlined function for the body of a CapturedStmt, store any /// captured variables into the captured struct, and call the outlined function. llvm::Function * CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K) { - const CapturedDecl *CD = S.getCapturedDecl(); - const RecordDecl *RD = S.getCapturedRecordDecl(); - assert(CD->hasBody() && "missing CapturedDecl body"); - LValue CapStruct = InitCapturedStruct(*this, S); // Emit the CapturedDecl CodeGenFunction CGF(CGM, true); CGF.CapturedStmtInfo = new CGCapturedStmtInfo(S, K); - llvm::Function *F = CGF.GenerateCapturedStmtFunction(CD, RD, S.getLocStart()); + llvm::Function *F = CGF.GenerateCapturedStmtFunction(S); delete CGF.CapturedStmtInfo; // Emit call to the helper function. @@ -2058,11 +2067,13 @@ /// Creates the outlined function for a CapturedStmt. llvm::Function * -CodeGenFunction::GenerateCapturedStmtFunction(const CapturedDecl *CD, - const RecordDecl *RD, - SourceLocation Loc) { +CodeGenFunction::GenerateCapturedStmtFunction(const CapturedStmt &S) { assert(CapturedStmtInfo && "CapturedStmtInfo should be set when generating the captured function"); + const CapturedDecl *CD = S.getCapturedDecl(); + const RecordDecl *RD = S.getCapturedRecordDecl(); + SourceLocation Loc = S.getLocStart(); + assert(CD->hasBody() && "missing CapturedDecl body"); // Build the argument list. ASTContext &Ctx = CGM.getContext(); @@ -2085,12 +2096,14 @@ StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, CD->getLocation(), CD->getBody()->getLocStart()); - // Set the context parameter in CapturedStmtInfo. llvm::Value *DeclPtr = LocalDeclMap[CD->getContextParam()]; assert(DeclPtr && "missing context parameter for CapturedStmt"); CapturedStmtInfo->setContextValue(Builder.CreateLoad(DeclPtr)); + // Initialize variable-length arrays. + InitVLACaptures(*this, S); + // If 'this' is captured, load it into CXXThisValue. if (CapturedStmtInfo->isCXXThisExprCaptured()) { FieldDecl *FD = CapturedStmtInfo->getThisFieldDecl(); Index: lib/CodeGen/CGStmtOpenMP.cpp =================================================================== --- lib/CodeGen/CGStmtOpenMP.cpp +++ lib/CodeGen/CGStmtOpenMP.cpp @@ -32,8 +32,7 @@ CodeGenFunction CGF(CGM, true); CGCapturedStmtInfo CGInfo(*CS, CS->getCapturedRegionKind()); CGF.CapturedStmtInfo = &CGInfo; - OutlinedFn = CGF.GenerateCapturedStmtFunction( - CS->getCapturedDecl(), CS->getCapturedRecordDecl(), CS->getLocStart()); + OutlinedFn = CGF.GenerateCapturedStmtFunction(*CS); } // Build call __kmpc_fork_call(loc, 1, microtask, captured_struct/*context*/) Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1895,9 +1895,7 @@ const ArrayRef &Attrs = None); llvm::Function *EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K); - llvm::Function *GenerateCapturedStmtFunction(const CapturedDecl *CD, - const RecordDecl *RD, - SourceLocation Loc); + llvm::Function *GenerateCapturedStmtFunction(const CapturedStmt &S); llvm::Value *GenerateCapturedStmtArgument(const CapturedStmt &S); void EmitOMPParallelDirective(const OMPParallelDirective &S); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -11518,7 +11518,7 @@ } // Prohibit variably-modified types; they're difficult to deal with. - if (Var->getType()->isVariablyModifiedType()) { + if (Var->getType()->isVariablyModifiedType() && (IsBlock || IsLambda)) { if (Diagnose) { if (IsBlock) S.Diag(Loc, diag::err_ref_vm_type); @@ -12005,8 +12005,107 @@ // certain types of variables (unnamed, variably modified types etc.) // so check for eligibility. if (!isVariableCapturable(CSI, Var, ExprLoc, BuildAndDiagnose, *this)) - return true; - + return true; + + // Try to capture variable-length arrays types. + if (Var->getType()->isVariablyModifiedType()) { + // We're going to walk down into the type and look for VLA + // expressions. + QualType QTy = Var->getType(); + if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) + QTy = PVD->getOriginalType(); + do { + const Type *Ty = QTy.getTypePtr(); + switch (Ty->getTypeClass()) { +#define TYPE(Class, Base) +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.def" + QTy = QualType(); + break; + // These types are never variably-modified. + case Type::Builtin: + case Type::Complex: + case Type::Vector: + case Type::ExtVector: + case Type::Record: + case Type::Enum: + case Type::Elaborated: + case Type::TemplateSpecialization: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + llvm_unreachable("type class is never variably-modified!"); + case Type::Adjusted: + QTy = cast(Ty)->getOriginalType(); + break; + case Type::Decayed: + QTy = cast(Ty)->getPointeeType(); + break; + case Type::Pointer: + QTy = cast(Ty)->getPointeeType(); + break; + case Type::BlockPointer: + QTy = cast(Ty)->getPointeeType(); + break; + case Type::LValueReference: + case Type::RValueReference: + QTy = cast(Ty)->getPointeeType(); + break; + case Type::MemberPointer: + QTy = cast(Ty)->getPointeeType(); + break; + case Type::ConstantArray: + case Type::IncompleteArray: + // Losing element qualification here is fine. + QTy = cast(Ty)->getElementType(); + break; + case Type::VariableArray: { + // Losing element qualification here is fine. + const VariableArrayType *Vat = cast(Ty); + + // Unknown size indication requires no size computation. + // Otherwise, evaluate and record it. + if (Expr *Size = Vat->getSizeExpr()) { + MarkDeclarationsReferencedInExpr(Size); + } + QTy = Vat->getElementType(); + break; + } + case Type::FunctionProto: + case Type::FunctionNoProto: + QTy = cast(Ty)->getReturnType(); + break; + case Type::Paren: + case Type::TypeOf: + case Type::UnaryTransform: + case Type::Attributed: + case Type::SubstTemplateTypeParm: + case Type::PackExpansion: + // Keep walking after single level desugaring. + QTy = QTy.getSingleStepDesugaredType(getASTContext()); + break; + case Type::Typedef: + QTy = cast(Ty)->desugar(); + break; + case Type::Decltype: + QTy = cast(Ty)->desugar(); + break; + case Type::Auto: + QTy = cast(Ty)->getDeducedType(); + break; + case Type::TypeOfExpr: + QTy = cast(Ty)->getUnderlyingExpr()->getType(); + break; + case Type::Atomic: + QTy = cast(Ty)->getValueType(); + break; + } + } while (!QTy.isNull() && QTy->isVariablyModifiedType()); + } + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { // No capture-default, and this is not an explicit capture // so cannot capture this variable. Index: test/CodeGen/captured-statements-nested.c =================================================================== --- test/CodeGen/captured-statements-nested.c +++ test/CodeGen/captured-statements-nested.c @@ -8,11 +8,12 @@ char c; }; -void test_nest_captured_stmt(int param) { +void test_nest_captured_stmt(int param, int size, int param_arr[size]) { int w; - // CHECK1: %struct.anon{{.*}} = type { i32*, i32* } - // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32**, i32* } - // CHECK1: [[T:%struct.anon.*]] = type { i32*, i32*, %struct.A*, i32**, i32* } + int arr[param][size]; + // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32*, i32**, i32* } + // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32**, i32*, i32*, i32**, i32* } + // CHECK1: [[T:%struct.anon.*]] = type { i32*, i32*, %struct.A*, i32**, i32*, i32*, i32**, i32* } #pragma clang __debug captured { int x; @@ -26,6 +27,8 @@ *y = param; z.b = 0.1f; z.c = 'c'; + param_arr[size - 1] = 2; + arr[10][z.a] = 12; // CHECK1: define internal void @__captured_stmt{{.*}}([[T]] // @@ -59,6 +62,29 @@ // CHECK1-NEXT: load %struct.A** // CHECK1-NEXT: getelementptr inbounds %struct.A* // CHECK1-NEXT: store i8 99 + // + // CHECK1: [[SIZE_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 5 + // CHECK1-NEXT: [[SIZE_ADDR:%.*]] = load i32** [[SIZE_ADDR_REF]] + // CHECK1-NEXT: [[SIZE:%.*]] = load i32* [[SIZE_ADDR]] + // CHECK1-NEXT: [[SIZE_MINUS_1:%.*]] = sub nsw i32 [[SIZE]], 1 + // CHECK1-NEXT: [[PARAM_ARR_IDX:%.*]] = {{.*}} [[SIZE_MINUS_1]] + // CHECK1-NEXT: [[PARAM_ARR_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 6 + // CHECK1-NEXT: [[PARAM_ARR_ADDR:%.*]] = load i32*** [[PARAM_ARR_ADDR_REF]] + // CHECK1-NEXT: [[PARAM_ARR:%.*]] = load i32** [[PARAM_ARR_ADDR]] + // CHECK1-NEXT: [[PARAM_ARR_SIZE_MINUS_1_ADDR:%.*]] = getelementptr inbounds i32* [[PARAM_ARR]], i{{.*}} [[PARAM_ARR_IDX]] + // CHECK1-NEXT: store i32 2, i32* [[PARAM_ARR_SIZE_MINUS_1_ADDR]] + // + // CHECK1: [[Z_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1-NEXT: [[Z_ADDR:%.*]] = load %struct.A** [[Z_ADDR_REF]] + // CHECK1-NEXT: [[Z_A_ADDR:%.*]] = getelementptr inbounds %struct.A* [[Z_ADDR]], i32 0, i32 0 + // CHECK1-NEXT: [[Z_A:%.*]] = load i32* [[Z_A_ADDR]] + // CHECK1-NEXT: [[ARR_IDX_2:%.*]] = {{.*}} [[Z_A]] + // CHECK1-NEXT: [[ARR_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 7 + // CHECK1-NEXT: [[ARR_ADDR:%.*]] = load i32** [[ARR_ADDR_REF]] + // CHECK1-NEXT: [[ARR_IDX_1:%.*]] = mul {{.*}} 10 + // CHECK1-NEXT: [[ARR_10_ADDR:%.*]] = getelementptr inbounds i32* [[ARR_ADDR]], i{{.*}} [[ARR_IDX_1]] + // CHECK1-NEXT: [[ARR_10_Z_A_ADDR:%.*]] = getelementptr inbounds i32* [[ARR_10_ADDR]], i{{.*}} [[ARR_IDX_2]] + // CHECK1-NEXT: store i32 12, i32* [[ARR_10_Z_A_ADDR]] } } } Index: test/CodeGen/captured-statements.c =================================================================== --- test/CodeGen/captured-statements.c +++ test/CodeGen/captured-statements.c @@ -48,11 +48,12 @@ // CHECK-2: %i = alloca i32 // Capture array -void test3() { +void test3(int size) { int arr[] = {1, 2, 3, 4, 5}; + int vla_arr[size]; #pragma clang __debug captured { - arr[2] = arr[1]; + arr[2] = vla_arr[size - 1]; } // CHECK-3: test3 // CHECK-3: alloca [5 x i32]