Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -3218,6 +3218,13 @@ /// function object. bool isLambda() const; + /// \brief Determine whether this record is a record for captured variables in + /// CapturedStmt construct. + bool isCapturedRecord() const; + /// \brief Mark the record as a record for captured variables in CapturedStmt + /// construct. + void setCapturedRecord(); + /// getDefinition - Returns the RecordDecl that actually defines /// this struct/union/class. When determining whether or not a /// struct/union/class is completely defined, one should use this Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -1981,15 +1981,18 @@ /// @endcode class CapturedStmt : public Stmt { public: - /// \brief The different capture forms: by 'this' or by reference, etc. + /// \brief The different capture forms: by 'this', by reference, capture for + /// variable-length array type etc. enum VariableCaptureKind { VCK_This, - VCK_ByRef + VCK_ByRef, + VCK_VLAType, }; - /// \brief Describes the capture of either a variable or 'this'. + /// \brief Describes the capture of either a variable, or 'this', or + /// variable-length array type. class Capture { - llvm::PointerIntPair VarAndKind; + llvm::PointerIntPair VarAndKind; SourceLocation Loc; public: @@ -2011,6 +2014,10 @@ case VCK_ByRef: assert(Var && "capturing by reference must have a variable!"); break; + case VCK_VLAType: + assert(!Var && + "Variable-length array type capture cannot have a variable!"); + break; } } @@ -2025,13 +2032,20 @@ bool capturesThis() const { return getCaptureKind() == VCK_This; } /// \brief Determine whether this capture handles a variable. - bool capturesVariable() const { return getCaptureKind() != VCK_This; } + bool capturesVariable() const { return getCaptureKind() == VCK_ByRef; } + + /// \brief Determine whether this capture handles a variable-length array + /// type. + bool capturesVariableArrayType() const { + return getCaptureKind() == VCK_VLAType; + } /// \brief Retrieve the declaration of the variable being captured. /// - /// This operation is only valid if this capture does not capture 'this'. + /// This operation is only valid if this capture captures a variable. VarDecl *getCapturedVar() const { - assert(!capturesThis() && "No variable available for 'this' capture"); + assert(capturesVariable() && + "No variable available for 'this' or VAT capture"); return VarAndKind.getPointer(); } friend class ASTStmtReader; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1911,3 +1911,11 @@ let Documentation = [LoopHintDocs, UnrollHintDocs]; } + +def CapturedRecord : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3283,6 +3283,11 @@ return false; } +static bool isVLATypeCapturingAllowed(const RecordDecl *RD) { + // Allow variable-length array capturing in Lambdas and CapturedStmts. + return RD->isLambda() || RD->isCapturedRecord(); +} + unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { assert(isBitField() && "not a bitfield"); Expr *BitWidth = static_cast(InitStorage.getPointer()); @@ -3325,7 +3330,8 @@ } void FieldDecl::setCapturedVLAType(const VariableArrayType *VLAType) { - assert(getParent()->isLambda() && "capturing type in non-lambda."); + assert(isVLATypeCapturingAllowed(getParent()) && + "capturing type in non-lambda or captured record."); assert(InitStorage.getInt() == ISK_BitWidthOrNothing && InitStorage.getPointer() == nullptr && "bit width, initializer or captured type already set"); @@ -3559,6 +3565,14 @@ return false; } +bool RecordDecl::isCapturedRecord() const { + return hasAttr(); +} + +void RecordDecl::setCapturedRecord() { + addAttr(CapturedRecordAttr::CreateImplicit(getASTContext())); +} + RecordDecl::field_iterator RecordDecl::field_begin() const { if (hasExternalLexicalStorage() && !LoadedFieldsFromExternalStorage) LoadFieldsFromExternalStorage(); Index: lib/CodeGen/CGStmt.cpp =================================================================== --- lib/CodeGen/CGStmt.cpp +++ lib/CodeGen/CGStmt.cpp @@ -2102,46 +2102,35 @@ } } -static LValue InitCapturedStruct(CodeGenFunction &CGF, const CapturedStmt &S) { +LValue CodeGenFunction::InitCapturedStruct(const CapturedStmt &S) { const RecordDecl *RD = S.getCapturedRecordDecl(); - QualType RecordTy = CGF.getContext().getRecordType(RD); + QualType RecordTy = getContext().getRecordType(RD); // Initialize the captured struct. - LValue SlotLV = CGF.MakeNaturalAlignAddrLValue( - CGF.CreateMemTemp(RecordTy, "agg.captured"), RecordTy); + LValue SlotLV = MakeNaturalAlignAddrLValue( + CreateMemTemp(RecordTy, "agg.captured"), RecordTy); RecordDecl::field_iterator CurField = RD->field_begin(); for (CapturedStmt::capture_init_iterator I = S.capture_init_begin(), E = S.capture_init_end(); I != E; ++I, ++CurField) { - LValue LV = CGF.EmitLValueForFieldInitialization(SlotLV, *CurField); - CGF.EmitInitializerForField(*CurField, LV, *I, None); + LValue LV = EmitLValueForFieldInitialization(SlotLV, *CurField); + if (CurField->hasCapturedVLAType()) { + auto VAT = CurField->getCapturedVLAType(); + EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV); + } else { + EmitInitializerForField(*CurField, LV, *I, None); + } } return SlotLV; } -static void InitVLACaptures(CodeGenFunction &CGF, const CapturedStmt &S) { - for (auto &C : S.captures()) { - if (C.capturesVariable()) { - QualType QTy; - auto VD = C.getCapturedVar(); - if (const ParmVarDecl *PVD = dyn_cast(VD)) - QTy = PVD->getOriginalType(); - else - QTy = VD->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) { - LValue CapStruct = InitCapturedStruct(*this, S); + LValue CapStruct = InitCapturedStruct(S); // Emit the CapturedDecl CodeGenFunction CGF(CGM, true); @@ -2157,7 +2146,7 @@ llvm::Value * CodeGenFunction::GenerateCapturedStmtArgument(const CapturedStmt &S) { - LValue CapStruct = InitCapturedStruct(*this, S); + LValue CapStruct = InitCapturedStruct(S); return CapStruct.getAddress(); } @@ -2198,14 +2187,21 @@ CapturedStmtInfo->setContextValue(Builder.CreateLoad(DeclPtr)); // Initialize variable-length arrays. - InitVLACaptures(*this, S); + LValue Base = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getContextValue(), + Ctx.getTagDeclType(RD)); + for (auto *FD : RD->fields()) { + if (FD->hasCapturedVLAType()) { + auto *ExprArg = EmitLoadOfLValue(EmitLValueForField(Base, FD), + S.getLocStart()).getScalarVal(); + auto VAT = FD->getCapturedVLAType(); + VLASizeMap[VAT->getSizeExpr()] = ExprArg; + } + } // If 'this' is captured, load it into CXXThisValue. if (CapturedStmtInfo->isCXXThisExprCaptured()) { FieldDecl *FD = CapturedStmtInfo->getThisFieldDecl(); - LValue LV = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getContextValue(), - Ctx.getTagDeclType(RD)); - LValue ThisLValue = EmitLValueForField(LV, FD); + LValue ThisLValue = EmitLValueForField(Base, FD); CXXThisValue = EmitLoadOfLValue(ThisLValue, Loc).getScalarVal(); } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -193,7 +193,7 @@ I != E; ++I, ++Field) { if (I->capturesThis()) CXXThisFieldDecl = *Field; - else + else if (I->capturesVariable()) CaptureFields[I->getCapturedVar()] = *Field; } } @@ -2002,6 +2002,7 @@ void EmitCXXForRangeStmt(const CXXForRangeStmt &S, ArrayRef Attrs = None); + LValue InitCapturedStruct(const CapturedStmt &S); llvm::Function *EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K); void GenerateCapturedStmtFunctionProlog(const CapturedStmt &S); llvm::Function *GenerateCapturedStmtFunctionEpilog(const CapturedStmt &S); Index: lib/Sema/ScopeInfo.cpp =================================================================== --- lib/Sema/ScopeInfo.cpp +++ lib/Sema/ScopeInfo.cpp @@ -95,8 +95,14 @@ } bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const { + RecordDecl *RD = nullptr; if (auto *LSI = dyn_cast(this)) - for (auto *FD : LSI->Lambda->fields()) { + RD = LSI->Lambda; + else if (auto CRSI = dyn_cast(this)) + RD = CRSI->TheRecordDecl; + + if (RD) + for (auto *FD : RD->fields()) { if (FD->hasCapturedVLAType() && FD->getCapturedVLAType() == VAT) return true; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -12211,29 +12211,29 @@ // Unknown size indication requires no size computation. // Otherwise, evaluate and record it. if (auto Size = VAT->getSizeExpr()) { - if (auto LSI = dyn_cast(CSI)) { - if (!LSI->isVLATypeCaptured(VAT)) { + if (!CSI->isVLATypeCaptured(VAT)) { + RecordDecl *CapRecord = nullptr; + if (auto LSI = dyn_cast(CSI)) { + CapRecord = LSI->Lambda; + } else if (auto CRSI = dyn_cast(CSI)) { + CapRecord = CRSI->TheRecordDecl; + } + if (CapRecord) { auto ExprLoc = Size->getExprLoc(); auto SizeType = Context.getSizeType(); - auto Lambda = LSI->Lambda; - // Build the non-static data member. auto Field = FieldDecl::Create( - Context, Lambda, ExprLoc, ExprLoc, + Context, CapRecord, ExprLoc, ExprLoc, /*Id*/ nullptr, SizeType, /*TInfo*/ nullptr, /*BW*/ nullptr, /*Mutable*/ false, /*InitStyle*/ ICIS_NoInit); Field->setImplicit(true); Field->setAccess(AS_private); Field->setCapturedVLAType(VAT); - Lambda->addDecl(Field); + CapRecord->addDecl(Field); - LSI->addVLATypeCapture(ExprLoc, SizeType); + CSI->addVLATypeCapture(ExprLoc, SizeType); } - } else { - // Immediately mark all referenced vars for CapturedStatements, - // they all are captured by reference. - MarkDeclarationsReferencedInExpr(Size); } } QTy = VAT->getElementType(); Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -3385,6 +3385,7 @@ else RD = RecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/nullptr); + RD->setCapturedRecord(); DC->addDecl(RD); RD->setImplicit(); RD->startDefinition(); @@ -3408,6 +3409,11 @@ CapturedStmt::VCK_This)); CaptureInits.push_back(Cap->getInitExpr()); continue; + } else if (Cap->isVLATypeCapture()) { + Captures.push_back( + CapturedStmt::Capture(Cap->getLocation(), CapturedStmt::VCK_VLAType)); + CaptureInits.push_back(nullptr); + continue; } assert(Cap->isReferenceCapture() && Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -307,7 +307,7 @@ // Captures for (const auto &I : S->captures()) { - if (I.capturesThis()) + if (I.capturesThis() || I.capturesVariableArrayType()) Writer.AddDeclRef(nullptr, Record); else Writer.AddDeclRef(I.getCapturedVar(), Record); Index: test/CodeGen/captured-statements-nested.c =================================================================== --- test/CodeGen/captured-statements-nested.c +++ test/CodeGen/captured-statements-nested.c @@ -11,9 +11,9 @@ void test_nest_captured_stmt(int param, int size, int param_arr[size]) { int w; int arr[param][size]; - // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i{{.+}}*, i32**, i32* } - // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32**, i32*, i{{.+}}*, i32**, i32* } - // CHECK1: [[T:%struct.anon.*]] = type { i32*, i32*, %struct.A*, i32**, i32*, i{{.+}}*, i32**, i32* } + // CHECK1: %struct.anon{{.*}} = type { [[INT:i.+]]*, [[INT]]*, [[SIZE_TYPE:i.+]], [[INT]]**, [[INT]]*, [[SIZE_TYPE]], [[SIZE_TYPE]], [[INT]]* } + // CHECK1: %struct.anon{{.*}} = type { [[INT]]*, [[INT]]*, [[INT]]**, [[INT]]*, [[SIZE_TYPE]], [[INT]]**, [[INT]]*, [[SIZE_TYPE]], [[SIZE_TYPE]], [[INT]]* } + // CHECK1: [[T:%struct.anon.*]] = type { [[INT]]*, [[INT]]*, %struct.A*, [[INT]]**, [[INT]]*, [[SIZE_TYPE]], [[INT]]**, [[INT]]*, [[SIZE_TYPE]], [[SIZE_TYPE]], [[INT]]* } #pragma clang __debug captured { int x; @@ -31,39 +31,45 @@ arr[10][z.a] = 12; // CHECK1: define internal void @__captured_stmt{{.*}}([[T]] + // CHECK1: [[PARAM_ARR_SIZE_REF:%.+]] = getelementptr inbounds [[T]]* {{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 5 + // CHECK1: [[PARAM_ARR_SIZE:%.+]] = load [[SIZE_TYPE]]* [[PARAM_ARR_SIZE_REF]] + // CHECK1: [[ARR_SIZE1_REF:%.+]] = getelementptr inbounds [[T]]* {{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 8 + // CHECK1: [[ARR_SIZE1:%.+]] = load [[SIZE_TYPE]]* [[ARR_SIZE1_REF]] + // CHECK1: [[ARR_SIZE2_REF:%.+]] = getelementptr inbounds [[T]]* {{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 9 + // CHECK1: [[ARR_SIZE2:%.+]] = load [[SIZE_TYPE]]* [[ARR_SIZE2_REF]] // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 // CHECK1-NEXT: load %struct.A** // CHECK1-NEXT: getelementptr inbounds %struct.A* // CHECK1-NEXT: store i{{.+}} 1 // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 1 - // CHECK1-NEXT: load i32** - // CHECK1-NEXT: store i32 1 - // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 0 - // CHECK1-NEXT: load i32** - // CHECK1-NEXT: store i32 1 - // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 4 - // CHECK1-NEXT: load i32** - // CHECK1-NEXT: load i32* - // CHECK1-NEXT: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 3 - // CHECK1-NEXT: load i32*** - // CHECK1-NEXT: load i32** - // CHECK1-NEXT: store i32 + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 1 + // CHECK1-NEXT: load i{{[0-9]+}}** + // CHECK1-NEXT: store i{{[0-9]+}} 1 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // CHECK1-NEXT: load i{{[0-9]+}}** + // CHECK1-NEXT: store i{{[0-9]+}} 1 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 4 + // CHECK1-NEXT: load i{{[0-9]+}}** + // CHECK1-NEXT: load i{{[0-9]+}}* + // CHECK1-NEXT: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 3 + // CHECK1-NEXT: load i{{[0-9]+}}*** + // CHECK1-NEXT: load i{{[0-9]+}}** + // CHECK1-NEXT: store i{{[0-9]+}} // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 // CHECK1-NEXT: load %struct.A** // CHECK1-NEXT: getelementptr inbounds %struct.A* // CHECK1-NEXT: store float // - // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 // CHECK1-NEXT: load %struct.A** // CHECK1-NEXT: getelementptr inbounds %struct.A* // CHECK1-NEXT: store i8 99 // - // CHECK1: [[SIZE_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i{{.+}} 0, i{{.+}} 5 + // CHECK1: [[SIZE_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i{{.+}} 0, i{{.+}} 7 // CHECK1-DAG: [[SIZE_ADDR:%.*]] = load i{{.+}}** [[SIZE_ADDR_REF]] // CHECK1-DAG: [[SIZE:%.*]] = load i{{.+}}* [[SIZE_ADDR]] // CHECK1-DAG: [[PARAM_ARR_IDX:%.*]] = sub nsw i{{.+}} [[SIZE]], 1 @@ -77,7 +83,7 @@ // CHECK1-DAG: [[Z_ADDR:%.*]] = load %struct.A** [[Z_ADDR_REF]] // CHECK1-DAG: [[Z_A_ADDR:%.*]] = getelementptr inbounds %struct.A* [[Z_ADDR]], i{{.+}} 0, i{{.+}} 0 // CHECK1-DAG: [[ARR_IDX_2:%.*]] = load i{{.+}}* [[Z_A_ADDR]] - // CHECK1-DAG: [[ARR_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i{{.+}} 0, i{{.+}} 7 + // CHECK1-DAG: [[ARR_ADDR_REF:%.*]] = getelementptr inbounds [[T]]* {{.*}}, i{{.+}} 0, i{{.+}} 10 // CHECK1-DAG: [[ARR_ADDR:%.*]] = load i{{.+}}** [[ARR_ADDR_REF]] // CHECK1-DAG: [[ARR_IDX_1:%.*]] = mul {{.*}} 10 // CHECK1-DAG: [[ARR_10_ADDR:%.*]] = getelementptr inbounds i{{.+}}* [[ARR_ADDR]], i{{.*}} [[ARR_IDX_1]] @@ -102,15 +108,15 @@ // CHECK2: define internal void @{{.*}}test_nest_block_block_invoke // - // CHECK2: [[Z:%[0-9a-z_]*]] = alloca i32 + // CHECK2: [[Z:%[0-9a-z_]*]] = alloca i{{[0-9]+}} // CHECK2: alloca %struct.anon{{.*}} // - // CHECK2: store i32 - // CHECK2: store i32* [[Z]] + // CHECK2: store i{{[0-9]+}} + // CHECK2: store i{{[0-9]+}}* [[Z]] // // CHECK2: getelementptr inbounds %struct.anon // CHECK2-NEXT: getelementptr inbounds - // CHECK2-NEXT: store i32* + // CHECK2-NEXT: store i{{[0-9]+}}* // // CHECK2: call void @__captured_stmt @@ -128,22 +134,22 @@ } // CHECK2: alloca %struct.__block_byref_b - // CHECK2-NEXT: [[C:%[0-9a-z_]*]] = alloca i32 + // CHECK2-NEXT: [[C:%[0-9a-z_]*]] = alloca i{{[0-9]+}} // CHECK2-NEXT: alloca %struct.__block_byref_d // // CHECK2: bitcast %struct.__block_byref_b* // CHECK2-NEXT: store i8* // - // CHECK2: [[CapA:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 7 + // CHECK2: [[CapA:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 7 // - // CHECK2: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0 - // CHECK2: load i32** - // CHECK2: load i32* - // CHECK2: store i32 {{.*}}, i32* [[CapA]] - // - // CHECK2: [[CapC:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 8 - // CHECK2-NEXT: [[Val:%[0-9a-z_]*]] = load i32* [[C]] - // CHECK2-NEXT: store i32 [[Val]], i32* [[CapC]] + // CHECK2: getelementptr inbounds %struct.anon{{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // CHECK2: load i{{[0-9]+}}** + // CHECK2: load i{{[0-9]+}}* + // CHECK2: store i{{[0-9]+}} {{.*}}, i{{[0-9]+}}* [[CapA]] + // + // CHECK2: [[CapC:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i{{[0-9]+}} 0, i{{[0-9]+}} 8 + // CHECK2-NEXT: [[Val:%[0-9a-z_]*]] = load i{{[0-9]+}}* [[C]] + // CHECK2-NEXT: store i{{[0-9]+}} [[Val]], i{{[0-9]+}}* [[CapC]] // // CHECK2: bitcast %struct.__block_byref_d* // CHECK2-NEXT: store i8* Index: test/CodeGen/captured-statements.c =================================================================== --- test/CodeGen/captured-statements.c +++ test/CodeGen/captured-statements.c @@ -4,6 +4,8 @@ // RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2 // RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3 +typedef __INTPTR_TYPE__ intptr_t; + int foo(); int global; @@ -61,15 +63,16 @@ } // Capture VLA array -void test4(int size, int vla_arr[size]) { +void test4(intptr_t size, intptr_t vla_arr[size]) { #pragma clang __debug captured { vla_arr[0] = 1; } - // CHECK-3: test4([[INT:i.+]] {{.*}}[[SIZE:%.+]], [[INT]]* - // CHECK-3: store [[INT]] {{.*}}[[SIZE]], [[INT]]* [[SIZE_ADDR:%.+]], + // CHECK-3: test4([[INTPTR_T:i.+]] {{.*}}[[SIZE_ARG:%.+]], [[INTPTR_T]]* + // CHECK-3: store [[INTPTR_T]] {{.*}}[[SIZE_ARG]], [[INTPTR_T]]* [[SIZE_ADDR:%.+]], + // CHECK-3: [[SIZE:%.+]] = load [[INTPTR_T]]* [[SIZE_ADDR]], // CHECK-3: [[REF:%.+]] = getelementptr inbounds - // CHECK-3: store [[INT]]* [[SIZE_ADDR]], [[INT]]** [[REF]] + // CHECK-3: store [[INTPTR_T]] [[SIZE]], [[INTPTR_T]]* [[REF]] // CHECK-3: call void @__captured_stmt }