forked from OSchip/llvm-project
CodeGen for CapturedStmts
EmitCapturedStmt creates a captured struct containing all of the captured variables, and then emits a call to the outlined function. This is similar in principle to EmitBlockLiteral. GenerateCapturedFunction actually produces the outlined function. It is based on GenerateBlockFunction, but is much simpler. The function type is determined by the parameters that are in the CapturedDecl. Some changes have been added to this patch that were reviewed as part of the serialization patch and moving the parameters to the captured decl. Differential Revision: http://llvm-reviews.chandlerc.com/D640 llvm-svn: 181536
This commit is contained in:
parent
00681dc1f0
commit
3b4c30b7e7
|
@ -41,6 +41,7 @@ public:
|
|||
GlobalDecl(const VarDecl *D) { Init(D);}
|
||||
GlobalDecl(const FunctionDecl *D) { Init(D); }
|
||||
GlobalDecl(const BlockDecl *D) { Init(D); }
|
||||
GlobalDecl(const CapturedDecl *D) { Init(D); }
|
||||
GlobalDecl(const ObjCMethodDecl *D) { Init(D); }
|
||||
|
||||
GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type)
|
||||
|
|
|
@ -4848,8 +4848,6 @@ let CategoryName = "Lambda Issue" in {
|
|||
"duration">;
|
||||
def err_this_capture : Error<
|
||||
"'this' cannot be %select{implicitly |}0captured in this context">;
|
||||
def err_lambda_capture_block : Error<
|
||||
"__block variable %0 cannot be captured in a lambda expression">;
|
||||
def err_lambda_capture_anonymous_var : Error<
|
||||
"unnamed variable cannot be implicitly captured in a lambda expression">;
|
||||
def err_lambda_capture_vm_type : Error<
|
||||
|
@ -4893,6 +4891,9 @@ let CategoryName = "Lambda Issue" in {
|
|||
|
||||
def err_return_in_captured_stmt : Error<
|
||||
"cannot return from %0">;
|
||||
def err_capture_block_variable : Error<
|
||||
"__block variable %0 cannot be captured in a "
|
||||
"%select{lambda expression|captured statement}1">;
|
||||
|
||||
def err_operator_arrow_circular : Error<
|
||||
"circular pointer delegation detected">;
|
||||
|
|
|
@ -1419,6 +1419,10 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
|
|||
NameStream.flush();
|
||||
Out << Name.size() << Name;
|
||||
return;
|
||||
} else if (isa<CapturedDecl>(DC)) {
|
||||
// Skip CapturedDecl context.
|
||||
manglePrefix(getEffectiveParentContext(DC), NoFunction);
|
||||
return;
|
||||
}
|
||||
|
||||
const NamedDecl *ND = cast<NamedDecl>(DC);
|
||||
|
|
|
@ -541,6 +541,10 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC,
|
|||
Context.mangleBlock(BD, Out);
|
||||
Out << '@';
|
||||
return manglePostfix(DC->getParent(), NoFunction);
|
||||
} else if (isa<CapturedDecl>(DC)) {
|
||||
// Skip CapturedDecl context.
|
||||
manglePostfix(DC->getParent(), NoFunction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NoFunction && (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC)))
|
||||
|
|
|
@ -1793,6 +1793,13 @@ static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF,
|
|||
return CGF.MakeAddrLValue(V, E->getType(), Alignment);
|
||||
}
|
||||
|
||||
static LValue EmitCapturedFieldLValue(CodeGenFunction &CGF, const FieldDecl *FD,
|
||||
llvm::Value *ThisValue) {
|
||||
QualType TagType = CGF.getContext().getTagDeclType(FD->getParent());
|
||||
LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType);
|
||||
return CGF.EmitLValueForField(LV, FD);
|
||||
}
|
||||
|
||||
LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
||||
const NamedDecl *ND = E->getDecl();
|
||||
CharUnits Alignment = getContext().getDeclAlign(ND);
|
||||
|
@ -1844,10 +1851,11 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
|||
// Use special handling for lambdas.
|
||||
if (!V) {
|
||||
if (FieldDecl *FD = LambdaCaptureFields.lookup(VD)) {
|
||||
QualType LambdaTagType = getContext().getTagDeclType(FD->getParent());
|
||||
LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue,
|
||||
LambdaTagType);
|
||||
return EmitLValueForField(LambdaLV, FD);
|
||||
return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue);
|
||||
} else if (CapturedStmtInfo) {
|
||||
if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD))
|
||||
return EmitCapturedFieldLValue(*this, FD,
|
||||
CapturedStmtInfo->getContextValue());
|
||||
}
|
||||
|
||||
assert(isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal());
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
using namespace clang;
|
||||
using namespace CodeGen;
|
||||
|
||||
|
@ -137,7 +138,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
|
|||
case Stmt::GCCAsmStmtClass: // Intentional fall-through.
|
||||
case Stmt::MSAsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break;
|
||||
case Stmt::CapturedStmtClass:
|
||||
EmitCapturedStmt(cast<CapturedStmt>(*S));
|
||||
EmitCapturedStmt(cast<CapturedStmt>(*S), CR_Default);
|
||||
break;
|
||||
case Stmt::ObjCAtTryStmtClass:
|
||||
EmitObjCAtTryStmt(cast<ObjCAtTryStmt>(*S));
|
||||
|
@ -1750,6 +1751,94 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
|
|||
}
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S) {
|
||||
llvm_unreachable("not implemented yet");
|
||||
static LValue InitCapturedStruct(CodeGenFunction &CGF, const CapturedStmt &S) {
|
||||
const RecordDecl *RD = S.getCapturedRecordDecl();
|
||||
QualType RecordTy = CGF.getContext().getRecordType(RD);
|
||||
|
||||
// Initialize the captured struct.
|
||||
LValue SlotLV = CGF.MakeNaturalAlignAddrLValue(
|
||||
CGF.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, ArrayRef<VarDecl *>());
|
||||
}
|
||||
|
||||
return SlotLV;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
delete CGF.CapturedStmtInfo;
|
||||
|
||||
// Emit call to the helper function.
|
||||
EmitCallOrInvoke(F, CapStruct.getAddress());
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
/// Creates the outlined function for a CapturedStmt.
|
||||
llvm::Function *
|
||||
CodeGenFunction::GenerateCapturedStmtFunction(const CapturedDecl *CD,
|
||||
const RecordDecl *RD) {
|
||||
assert(CapturedStmtInfo &&
|
||||
"CapturedStmtInfo should be set when generating the captured function");
|
||||
|
||||
// Check if we should generate debug info for this function.
|
||||
maybeInitializeDebugInfo();
|
||||
|
||||
// Build the argument list.
|
||||
ASTContext &Ctx = CGM.getContext();
|
||||
FunctionArgList Args;
|
||||
Args.append(CD->param_begin(), CD->param_end());
|
||||
|
||||
// Create the function declaration.
|
||||
FunctionType::ExtInfo ExtInfo;
|
||||
const CGFunctionInfo &FuncInfo =
|
||||
CGM.getTypes().arrangeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo,
|
||||
/*IsVariadic=*/false);
|
||||
llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo);
|
||||
|
||||
llvm::Function *F =
|
||||
llvm::Function::Create(FuncLLVMTy, llvm::GlobalValue::InternalLinkage,
|
||||
CapturedStmtInfo->getHelperName(), &CGM.getModule());
|
||||
CGM.SetInternalFunctionAttributes(CD, F, FuncInfo);
|
||||
|
||||
// Generate the function.
|
||||
StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, 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));
|
||||
|
||||
// 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);
|
||||
|
||||
CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal();
|
||||
}
|
||||
|
||||
CapturedStmtInfo->EmitBody(*this, CD->getBody());
|
||||
FinishFunction(CD->getBodyRBrace());
|
||||
|
||||
return F;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ using namespace CodeGen;
|
|||
CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
|
||||
: CodeGenTypeCache(cgm), CGM(cgm), Target(cgm.getTarget()),
|
||||
Builder(cgm.getModule().getContext()),
|
||||
CapturedStmtInfo(0),
|
||||
SanitizePerformTypeCheck(CGM.getSanOpts().Null |
|
||||
CGM.getSanOpts().Alignment |
|
||||
CGM.getSanOpts().ObjectSize |
|
||||
|
@ -1447,3 +1448,5 @@ llvm::Value *CodeGenFunction::EmitFieldAnnotations(const FieldDecl *D,
|
|||
|
||||
return V;
|
||||
}
|
||||
|
||||
CodeGenFunction::CGCapturedStmtInfo::~CGCapturedStmtInfo() { }
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/ABI.h"
|
||||
#include "clang/Basic/CapturedStmt.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/Frontend/CodeGenOptions.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
@ -606,6 +607,65 @@ public:
|
|||
/// we prefer to insert allocas.
|
||||
llvm::AssertingVH<llvm::Instruction> AllocaInsertPt;
|
||||
|
||||
/// \brief API for captured statement code generation.
|
||||
class CGCapturedStmtInfo {
|
||||
public:
|
||||
explicit CGCapturedStmtInfo(const CapturedStmt &S,
|
||||
CapturedRegionKind K = CR_Default)
|
||||
: Kind(K), ThisValue(0), CXXThisFieldDecl(0) {
|
||||
|
||||
RecordDecl::field_iterator Field =
|
||||
S.getCapturedRecordDecl()->field_begin();
|
||||
for (CapturedStmt::const_capture_iterator I = S.capture_begin(),
|
||||
E = S.capture_end();
|
||||
I != E; ++I, ++Field) {
|
||||
if (I->capturesThis())
|
||||
CXXThisFieldDecl = *Field;
|
||||
else
|
||||
CaptureFields[I->getCapturedVar()] = *Field;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~CGCapturedStmtInfo();
|
||||
|
||||
CapturedRegionKind getKind() const { return Kind; }
|
||||
|
||||
void setContextValue(llvm::Value *V) { ThisValue = V; }
|
||||
// \brief Retrieve the value of the context parameter.
|
||||
llvm::Value *getContextValue() const { return ThisValue; }
|
||||
|
||||
/// \brief Lookup the captured field decl for a variable.
|
||||
const FieldDecl *lookup(const VarDecl *VD) const {
|
||||
return CaptureFields.lookup(VD);
|
||||
}
|
||||
|
||||
bool isCXXThisExprCaptured() const { return CXXThisFieldDecl != 0; }
|
||||
FieldDecl *getThisFieldDecl() const { return CXXThisFieldDecl; }
|
||||
|
||||
/// \brief Emit the captured statement body.
|
||||
virtual void EmitBody(CodeGenFunction &CGF, Stmt *S) {
|
||||
CGF.EmitStmt(S);
|
||||
}
|
||||
|
||||
/// \brief Get the name of the capture helper.
|
||||
virtual StringRef getHelperName() const { return "__captured_stmt"; }
|
||||
|
||||
private:
|
||||
/// \brief The kind of captured statement being generated.
|
||||
CapturedRegionKind Kind;
|
||||
|
||||
/// \brief Keep the map between VarDecl and FieldDecl.
|
||||
llvm::SmallDenseMap<const VarDecl *, FieldDecl *> CaptureFields;
|
||||
|
||||
/// \brief The base address of the captured record, passed in as the first
|
||||
/// argument of the parallel region function.
|
||||
llvm::Value *ThisValue;
|
||||
|
||||
/// \brief Captured 'this' type.
|
||||
FieldDecl *CXXThisFieldDecl;
|
||||
};
|
||||
CGCapturedStmtInfo *CapturedStmtInfo;
|
||||
|
||||
/// BoundsChecking - Emit run-time bounds checks. Higher values mean
|
||||
/// potentially higher performance penalties.
|
||||
unsigned char BoundsChecking;
|
||||
|
@ -2188,7 +2248,6 @@ public:
|
|||
void EmitCaseStmt(const CaseStmt &S);
|
||||
void EmitCaseStmtRange(const CaseStmt &S);
|
||||
void EmitAsmStmt(const AsmStmt &S);
|
||||
void EmitCapturedStmt(const CapturedStmt &S);
|
||||
|
||||
void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S);
|
||||
void EmitObjCAtTryStmt(const ObjCAtTryStmt &S);
|
||||
|
@ -2204,6 +2263,10 @@ public:
|
|||
void EmitCXXTryStmt(const CXXTryStmt &S);
|
||||
void EmitCXXForRangeStmt(const CXXForRangeStmt &S);
|
||||
|
||||
llvm::Function *EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K);
|
||||
llvm::Function *GenerateCapturedStmtFunction(const CapturedDecl *CD,
|
||||
const RecordDecl *RD);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// LValue Expression Emission
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
|
|
@ -11212,12 +11212,12 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
|
|||
return true;
|
||||
}
|
||||
}
|
||||
// Lambdas are not allowed to capture __block variables; they don't
|
||||
// support the expected semantics.
|
||||
if (IsLambda && HasBlocksAttr) {
|
||||
// Lambdas and captured statements are not allowed to capture __block
|
||||
// variables; they don't support the expected semantics.
|
||||
if (HasBlocksAttr && (IsLambda || isa<CapturedRegionScopeInfo>(CSI))) {
|
||||
if (BuildAndDiagnose) {
|
||||
Diag(Loc, diag::err_lambda_capture_block)
|
||||
<< Var->getDeclName();
|
||||
Diag(Loc, diag::err_capture_block_variable)
|
||||
<< Var->getDeclName() << !IsLambda;
|
||||
Diag(Var->getLocation(), diag::note_previous_decl)
|
||||
<< Var->getDeclName();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
// RUN: %clang_cc1 -fblocks -emit-llvm %s -o %t
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK1
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK2
|
||||
|
||||
struct A {
|
||||
int a;
|
||||
float b;
|
||||
char c;
|
||||
};
|
||||
|
||||
void test_nest_captured_stmt(int param) {
|
||||
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* }
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
int x;
|
||||
int *y = &w;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
struct A z;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
w = x = z.a = 1;
|
||||
*y = param;
|
||||
z.b = 0.1f;
|
||||
z.c = 'c';
|
||||
|
||||
// CHECK1: define internal void @__captured_stmt{{.*}}([[T]]
|
||||
//
|
||||
// CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2
|
||||
// CHECK1-NEXT: load %struct.A**
|
||||
// CHECK1-NEXT: getelementptr inbounds %struct.A*
|
||||
// CHECK1-NEXT: store i32 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]]* {{.*}}, i32 0, i32 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-NEXT: load %struct.A**
|
||||
// CHECK1-NEXT: getelementptr inbounds %struct.A*
|
||||
// CHECK1-NEXT: store i8 99
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_nest_block() {
|
||||
__block int x;
|
||||
int y;
|
||||
^{
|
||||
int z;
|
||||
x = z;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
z = y; // OK
|
||||
}
|
||||
}();
|
||||
|
||||
// CHECK2: define internal void @{{.*}}test_nest_block_block_invoke
|
||||
//
|
||||
// CHECK2: [[Z:%[0-9a-z_]*]] = alloca i32
|
||||
// CHECK2: alloca %struct.anon{{.*}}
|
||||
//
|
||||
// CHECK2: store i32
|
||||
// CHECK2: store i32* [[Z]]
|
||||
//
|
||||
// CHECK2: getelementptr inbounds %struct.anon
|
||||
// CHECK2-NEXT: getelementptr inbounds
|
||||
// CHECK2-NEXT: store i32*
|
||||
//
|
||||
// CHECK2: call void @__captured_stmt
|
||||
|
||||
int a;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
__block int b;
|
||||
int c;
|
||||
__block int d;
|
||||
^{
|
||||
b = a;
|
||||
b = c;
|
||||
b = d;
|
||||
}();
|
||||
}
|
||||
|
||||
// CHECK2: alloca %struct.__block_byref_b
|
||||
// CHECK2-NEXT: [[C:%[0-9a-z_]*]] = alloca i32
|
||||
// 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: 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: bitcast %struct.__block_byref_d*
|
||||
// CHECK2-NEXT: store i8*
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// RUN: %clang_cc1 -emit-llvm %s -o %t
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-GLOBALS
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3
|
||||
|
||||
int foo();
|
||||
int global;
|
||||
|
||||
// Single statement
|
||||
void test1() {
|
||||
int i = 0;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
i++;
|
||||
}
|
||||
// CHECK-1: %struct.anon = type { i32* }
|
||||
//
|
||||
// CHECK-1: test1
|
||||
// CHECK-1: alloca %struct.anon
|
||||
// CHECK-1: getelementptr inbounds %struct.anon*
|
||||
// CHECK-1: store i32* %i
|
||||
// CHECK-1: call void @[[HelperName:__captured_stmt[0-9]+]]
|
||||
}
|
||||
|
||||
// CHECK-1: define internal void @[[HelperName]](%struct.anon
|
||||
// CHECK-1: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0
|
||||
// CHECK-1: load i32**
|
||||
// CHECK-1: load i32*
|
||||
// CHECK-1: add nsw i32
|
||||
// CHECK-1: store i32
|
||||
|
||||
// Compound statement with local variable
|
||||
void test2(int x) {
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < x; i++)
|
||||
foo();
|
||||
}
|
||||
// CHECK-2: test2
|
||||
// CHECK-2-NOT: %i
|
||||
// CHECK-2: call void @[[HelperName:__captured_stmt[0-9]+]]
|
||||
}
|
||||
|
||||
// CHECK-2: define internal void @[[HelperName]]
|
||||
// CHECK-2-NOT: }
|
||||
// CHECK-2: %i = alloca i32
|
||||
|
||||
// Capture array
|
||||
void test3() {
|
||||
int arr[] = {1, 2, 3, 4, 5};
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
arr[2] = arr[1];
|
||||
}
|
||||
// CHECK-3: test3
|
||||
// CHECK-3: alloca [5 x i32]
|
||||
// CHECK-3: call void @__captured_stmt
|
||||
}
|
||||
|
||||
void dont_capture_global() {
|
||||
static int s;
|
||||
extern int e;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
global++;
|
||||
s++;
|
||||
e++;
|
||||
}
|
||||
|
||||
// CHECK-GLOBALS: %[[Capture:struct\.anon[\.0-9]*]] = type {}
|
||||
// CHECK-GLOBALS: call void @__captured_stmt[[HelperName:[0-9]+]](%[[Capture]]
|
||||
}
|
||||
|
||||
// CHECK-GLOBALS: define internal void @__captured_stmt[[HelperName]]
|
||||
// CHECK-GLOBALS-NOT: ret
|
||||
// CHECK-GLOBALS: load i32* @global
|
||||
// CHECK-GLOBALS: load i32* @
|
||||
// CHECK-GLOBALS: load i32* @e
|
|
@ -0,0 +1,97 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o %t
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3
|
||||
// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-4
|
||||
|
||||
struct Foo {
|
||||
int x;
|
||||
float y;
|
||||
~Foo() {}
|
||||
};
|
||||
|
||||
struct TestClass {
|
||||
int x;
|
||||
|
||||
TestClass() : x(0) {};
|
||||
void MemberFunc() {
|
||||
Foo f;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
f.y = x;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void test1() {
|
||||
TestClass c;
|
||||
c.MemberFunc();
|
||||
// CHECK-1: %[[Capture:struct\.anon[\.0-9]*]] = type { %struct.Foo*, %struct.TestClass* }
|
||||
|
||||
// CHECK-1: define {{.*}} void @_ZN9TestClass10MemberFuncEv
|
||||
// CHECK-1: alloca %struct.anon
|
||||
// CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 0
|
||||
// CHECK-1: store %struct.Foo* %f, %struct.Foo**
|
||||
// CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 1
|
||||
// CHECK-1: call void @[[HelperName:[A-Za-z0-9_]+]](%[[Capture]]*
|
||||
// CHECK-1: call void @_ZN3FooD1Ev
|
||||
// CHECK-1: ret
|
||||
}
|
||||
|
||||
// CHECK-1: define internal void @[[HelperName]]
|
||||
// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 1
|
||||
// CHECK-1: getelementptr inbounds %struct.TestClass* {{[^,]*}}, i32 0, i32 0
|
||||
// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 0
|
||||
|
||||
void test2(int x) {
|
||||
int y = [&]() {
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
x++;
|
||||
}
|
||||
return x;
|
||||
}();
|
||||
|
||||
// CHECK-2: define void @_Z5test2i
|
||||
// CHECK-2: call i32 @[[Lambda:["$\w]+]]
|
||||
//
|
||||
// CHECK-2: define internal i32 @[[Lambda]]
|
||||
// CHECK-2: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]*
|
||||
//
|
||||
// CHECK-2: define internal void @[[HelperName]]
|
||||
// CHECK-2: getelementptr inbounds %[[Capture]]*
|
||||
// CHECK-2: load i32**
|
||||
// CHECK-2: load i32*
|
||||
}
|
||||
|
||||
void test3(int x) {
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
x = [=]() { return x + 1; } ();
|
||||
}
|
||||
|
||||
// CHECK-3: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* }
|
||||
|
||||
// CHECK-3: define void @_Z5test3i(i32 %x)
|
||||
// CHECK-3: store i32*
|
||||
// CHECK-3: call void @{{.*}}__captured_stmt
|
||||
// CHECK-3: ret void
|
||||
}
|
||||
|
||||
void test4() {
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
Foo f;
|
||||
f.x = 5;
|
||||
}
|
||||
// CHECK-4: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* }
|
||||
|
||||
// CHECK-4: define void @_Z5test3i(i32 %x)
|
||||
// CHECK-4: store i32*
|
||||
// CHECK-4: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]*
|
||||
// CHECK-4: ret void
|
||||
//
|
||||
// CHECK-4: define internal void @[[HelperName]]
|
||||
// CHECK-4: store i32 5, i32*
|
||||
// CHECK-4: call void @{{.*}}FooD1Ev(%struct.Foo*
|
||||
}
|
|
@ -49,29 +49,29 @@ void test_nest() {
|
|||
}
|
||||
|
||||
void test_nest_block() {
|
||||
__block int x;
|
||||
__block int x; // expected-note {{'x' declared here}}
|
||||
int y;
|
||||
^{
|
||||
int z;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
x = y; // OK
|
||||
x = y; // expected-error{{__block variable 'x' cannot be captured in a captured statement}}
|
||||
y = z; // expected-error{{variable is not assignable (missing __block type specifier)}}
|
||||
z = y; // OK
|
||||
}
|
||||
}();
|
||||
|
||||
__block int a;
|
||||
__block int a; // expected-note 2 {{'a' declared here}}
|
||||
int b;
|
||||
#pragma clang __debug captured
|
||||
{
|
||||
__block int c;
|
||||
int d;
|
||||
^{
|
||||
a = b; // OK
|
||||
a = c; // OK
|
||||
a = b; // expected-error{{__block variable 'a' cannot be captured in a captured statement}}
|
||||
b = d; // OK - Consistent with block inside a lambda
|
||||
c = a; // OK
|
||||
c = a; // expected-error{{__block variable 'a' cannot be captured in a captured statement}}
|
||||
c = d; // OK
|
||||
d = b; // expected-error{{variable is not assignable (missing __block type specifier)}}
|
||||
}();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue