/// 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
/// @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:
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;
}
}
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;
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];
+}
+
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());
}
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");
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();
}
}
-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);
llvm::Value *
CodeGenFunction::GenerateCapturedStmtArgument(const CapturedStmt &S) {
- LValue CapStruct = InitCapturedStruct(*this, S);
+ LValue CapStruct = InitCapturedStruct(S);
return CapStruct.getAddress();
}
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();
}
I != E; ++I, ++Field) {
if (I->capturesThis())
CXXThisFieldDecl = *Field;
- else
+ else if (I->capturesVariable())
CaptureFields[I->getCapturedVar()] = *Field;
}
}
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);
}
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;
}
// 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();
else
RD = RecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/nullptr);
+ RD->setCapturedRecord();
DC->addDecl(RD);
RD->setImplicit();
RD->startDefinition();
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() &&
// 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);
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;
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
// 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]]
// 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
}
// 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*
// 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;
}
// 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
}