Improved capturing variable-length array types in CapturedStmt.

An updated implemnentation of VLA types capturing based on previously committed solution for Lambdas.
This version captures the whole VLA type instead of particular variables which are part of VLA size expression and allows to use previusly calculated size of VLA type in captured regions. Required for OpenMP.
Differential Revision: http://reviews.llvm.org/D5099

llvm-svn: 220850
This commit is contained in:
Alexey Bataev 2014-10-29 12:21:55 +00:00
parent 77af3812ec
commit 330de03083
12 changed files with 149 additions and 88 deletions

View File

@ -3218,6 +3218,13 @@ public:
/// 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

View File

@ -1982,15 +1982,18 @@ public:
/// @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<VarDecl *, 1, VariableCaptureKind> VarAndKind;
llvm::PointerIntPair<VarDecl *, 2, VariableCaptureKind> VarAndKind;
SourceLocation Loc;
public:
@ -2012,6 +2015,10 @@ public:
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;
}
}
@ -2026,13 +2033,20 @@ public:
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;

View File

@ -1918,3 +1918,11 @@ def LoopHint : Attr {
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];
}

View File

@ -3283,6 +3283,11 @@ bool FieldDecl::isAnonymousStructOrUnion() const {
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<Expr *>(InitStorage.getPointer());
@ -3325,7 +3330,8 @@ SourceRange FieldDecl::getSourceRange() const {
}
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 @@ bool RecordDecl::isLambda() const {
return false;
}
bool RecordDecl::isCapturedRecord() const {
return hasAttr<CapturedRecordAttr>();
}
void RecordDecl::setCapturedRecord() {
addAttr(CapturedRecordAttr::CreateImplicit(getASTContext()));
}
RecordDecl::field_iterator RecordDecl::field_begin() const {
if (hasExternalLexicalStorage() && !LoadedFieldsFromExternalStorage)
LoadFieldsFromExternalStorage();

View File

@ -2102,46 +2102,35 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
}
}
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<ParmVarDecl>(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 @@ CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K) {
llvm::Value *
CodeGenFunction::GenerateCapturedStmtArgument(const CapturedStmt &S) {
LValue CapStruct = InitCapturedStruct(*this, S);
LValue CapStruct = InitCapturedStruct(S);
return CapStruct.getAddress();
}
@ -2198,14 +2187,21 @@ CodeGenFunction::GenerateCapturedStmtFunction(const CapturedStmt &S) {
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();
}

View File

@ -193,7 +193,7 @@ public:
I != E; ++I, ++Field) {
if (I->capturesThis())
CXXThisFieldDecl = *Field;
else
else if (I->capturesVariable())
CaptureFields[I->getCapturedVar()] = *Field;
}
}
@ -2002,6 +2002,7 @@ public:
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
ArrayRef<const Attr *> 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);

View File

@ -95,8 +95,14 @@ FunctionScopeInfo::WeakObjectProfileTy::getBaseInfo(const Expr *E) {
}
bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const {
RecordDecl *RD = nullptr;
if (auto *LSI = dyn_cast<LambdaScopeInfo>(this))
for (auto *FD : LSI->Lambda->fields()) {
RD = LSI->Lambda;
else if (auto CRSI = dyn_cast<CapturedRegionScopeInfo>(this))
RD = CRSI->TheRecordDecl;
if (RD)
for (auto *FD : RD->fields()) {
if (FD->hasCapturedVLAType() && FD->getCapturedVLAType() == VAT)
return true;
}

View File

@ -12219,29 +12219,29 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc,
// Unknown size indication requires no size computation.
// Otherwise, evaluate and record it.
if (auto Size = VAT->getSizeExpr()) {
if (auto LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
if (!LSI->isVLATypeCaptured(VAT)) {
if (!CSI->isVLATypeCaptured(VAT)) {
RecordDecl *CapRecord = nullptr;
if (auto LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
CapRecord = LSI->Lambda;
} else if (auto CRSI = dyn_cast<CapturedRegionScopeInfo>(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();

View File

@ -3385,6 +3385,7 @@ Sema::CreateCapturedStmtRecordDecl(CapturedDecl *&CD, SourceLocation Loc,
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 @@ static void buildCapturedStmtCaptureList(
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() &&

View File

@ -307,7 +307,7 @@ void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
// 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);

View File

@ -11,9 +11,9 @@ struct A {
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 @@ void test_nest_captured_stmt(int param, int size, int param_arr[size]) {
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]]* {{.*}}, 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]]* {{.*}}, i32 0, i32 0
// CHECK1-NEXT: load i32**
// CHECK1-NEXT: store i32 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]]* {{.*}}, 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]+}} 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 @@ void test_nest_captured_stmt(int param, int size, int param_arr[size]) {
// 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 @@ void test_nest_block() {
// 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 @@ void test_nest_block() {
}
// 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: 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 {{.*}}, i32 0, i32 8
// CHECK2-NEXT: [[Val:%[0-9a-z_]*]] = load i32* [[C]]
// CHECK2-NEXT: store i32 [[Val]], i32* [[CapC]]
// 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*

View File

@ -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 @@ void test3(int size) {
}
// 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
}