Hi rjmccall, ABataev,
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 currently
fixed, and the function contains only the body of the captured statement.
- When this is extended for different kinds of captured statements, I imagine
that hooks will be added to customize the function type/parameters, and insert
prologue/epilogue code.
This patch depends on the following uncommitted patches:
- http://llvm-reviews.chandlerc.com/D369 (parsing)
- http://llvm-reviews.chandlerc.com/D370 (AST)
- http://llvm-reviews.chandlerc.com/D433 (Sema)
http://llvm-reviews.chandlerc.com/D640
Files:
include/clang/AST/GlobalDecl.h
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGDecl.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGStmt.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGen/captured-statements.c
test/CodeGenCXX/captured-statements.cpp
Index: include/clang/AST/GlobalDecl.h
===================================================================
--- include/clang/AST/GlobalDecl.h
+++ include/clang/AST/GlobalDecl.h
@@ -41,6 +41,7 @@
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)
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -1408,6 +1408,10 @@
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);
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -535,6 +535,10 @@
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)))
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -1684,6 +1684,9 @@
assert(DMEntry == 0 && "Decl already exists in localdeclmap!");
DMEntry = DeclPtr;
+ if (CapturedStmtInfo && CapturedStmtInfo->isThisParmVarDecl(&D))
+ CapturedStmtInfo->setThisValue(Builder.CreateLoad(DeclPtr));
+
// Emit debug info for param declaration.
if (CGDebugInfo *DI = getDebugInfo()) {
if (CGM.getCodeGenOpts().getDebugInfo()
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -1811,6 +1811,15 @@
LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue,
LambdaTagType);
return EmitLValueForField(LambdaLV, FD);
+ } else if (CapturedStmtInfo) {
+ if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD)) {
+ QualType TagType = getContext().getTagDeclType(FD->getParent());
+ LValue LV
+ = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getThisValue(),
+ TagType);
+
+ return EmitLValueForField(LV, FD);
+ }
}
assert(isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal());
Index: lib/CodeGen/CGStmt.cpp
===================================================================
--- lib/CodeGen/CGStmt.cpp
+++ lib/CodeGen/CGStmt.cpp
@@ -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;
@@ -1738,6 +1739,80 @@
}
}
+/// Generate an outlined function for the body of a CapturedStmt, store any
+/// captured variables into the captured struct, and call the outlined function.
void CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S) {
- llvm_unreachable("not implemented yet");
+ const CapturedDecl *CD = S.getCapturedDecl();
+ const RecordDecl *RD = S.getCapturedRecordDecl();
+ QualType RecordTy = getContext().getRecordType(RD);
+ assert(CD->hasBody() && "missing CapturedDecl body");
+
+ // Initialize the captured struct.
+ AggValueSlot Slot = CreateAggTemp(RecordTy, "agg.captured");
+ LValue SlotLV = MakeAddrLValue(Slot.getAddr(), RecordTy, Slot.getAlignment());
+
+ 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 = EmitLValueForFieldInitialization(SlotLV, *CurField);
+ ArrayRef<VarDecl *> ArrayIndexes;
+ EmitInitializerForField(*CurField, LV, *I, ArrayIndexes);
+ }
+
+ // The function argument is the address of the captured struct.
+ llvm::SmallVector<llvm::Value *, 1> Args;
+ Args.push_back(SlotLV.getAddress());
+
+ // Emit the CapturedDecl
+ CGCapturedStmtInfo CSInfo(S);
+ CodeGenFunction CGF(CGM, true);
+ CGF.CapturedStmtInfo = &CSInfo;
+
+ llvm::Function *F = CGF.GenerateCapturedFunction(CurGD, CD, RD);
+
+ // Emit call to the helper function.
+ EmitCallOrInvoke(F, Args);
+}
+
+/// Creates the outlined function for a CapturedStmt.
+llvm::Function *
+CodeGenFunction::GenerateCapturedFunction(GlobalDecl GD,
+ 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();
+ CurGD = GD;
+
+ // Build the argument list.
+ ASTContext &Ctx = CGM.getContext();
+ QualType ThisTy = Ctx.getPointerType(Ctx.getTagDeclType(RD));
+ FunctionArgList Args;
+ ImplicitParamDecl ThisDecl(const_cast<CapturedDecl*>(CD), SourceLocation(),
+ /*Id=*/0, ThisTy);
+ Args.push_back(&ThisDecl);
+ CapturedStmtInfo->setThisParmVarDecl(&ThisDecl);
+
+ // 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,
+ "__captured_stmt", &CGM.getModule());
+ CGM.SetInternalFunctionAttributes(CD, F, FuncInfo);
+
+ // Generate the function.
+ StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, CD->getBody()->getLocStart());
+ // TODO: lots of code here in GenerateBlockFunction - is any of it needed here?
+ EmitStmt(CD->getBody());
+ FinishFunction(CD->getBodyRBrace());
+
+ return F;
}
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -34,6 +34,7 @@
: CodeGenTypeCache(cgm), CGM(cgm),
Target(CGM.getContext().getTargetInfo()),
Builder(cgm.getModule().getContext()),
+ CapturedStmtInfo(0),
SanitizePerformTypeCheck(CGM.getSanOpts().Null |
CGM.getSanOpts().Alignment |
CGM.getSanOpts().ObjectSize |
@@ -571,6 +572,18 @@
}
}
+ // If CFG is emitting a captured statement and 'this' is captured,
+ // load it into CXXThisValue.
+ if (CapturedStmtInfo && CapturedStmtInfo->isCXXThisExprCaptured()) {
+ FieldDecl *FD = CapturedStmtInfo->getThisFieldDecl();
+ QualType TagType = getContext().getTagDeclType(FD->getParent());
+ LValue LV = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getThisValue(),
+ TagType);
+ LValue ThisLValue = EmitLValueForField(LV, FD);
+
+ CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal();
+ }
+
// If any of the arguments have a variably modified type, make sure to
// emit the type size.
for (FunctionArgList::const_iterator i = Args.begin(), e = Args.end();
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -606,6 +606,60 @@
/// 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)
+ : ThisValue(0), CXXThisFieldDecl(0), ThisParmVarDecl(0) {
+
+ RecordDecl::field_iterator Field =
+ S.getCapturedRecordDecl()->field_begin();
+ for (CapturedStmt::capture_iterator I = S.capture_begin(),
+ E = S.capture_end();
+ I != E; ++I, ++Field) {
+ if (I->capturesThis())
+ CXXThisFieldDecl = *Field;
+ else
+ CaptureFields[I->getCapturedVar()] = *Field;
+ }
+ }
+
+ void setThisValue(llvm::Value *V) { ThisValue = V; }
+ llvm::Value *getThisValue() 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; }
+
+ bool isThisParmVarDecl(const VarDecl *V) const {
+ return V == ThisParmVarDecl;
+ }
+
+ void setThisParmVarDecl(VarDecl *V) {
+ ThisParmVarDecl = V;
+ }
+
+ private:
+ /// \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;
+
+ /// \brief The captured record parameter to the helper function.
+ VarDecl *ThisParmVarDecl;
+ };
+ CGCapturedStmtInfo *CapturedStmtInfo;
+
/// BoundsChecking - Emit run-time bounds checks. Higher values mean
/// potentially higher performance penalties.
unsigned char BoundsChecking;
@@ -2133,7 +2187,6 @@
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);
@@ -2149,6 +2202,11 @@
void EmitCXXTryStmt(const CXXTryStmt &S);
void EmitCXXForRangeStmt(const CXXForRangeStmt &S);
+ void EmitCapturedStmt(const CapturedStmt &S);
+ llvm::Function *GenerateCapturedFunction(GlobalDecl GD,
+ const CapturedDecl *CD,
+ const RecordDecl *RD);
+
//===--------------------------------------------------------------------===//
// LValue Expression Emission
//===--------------------------------------------------------------------===//
Index: test/CodeGen/captured-statements.c
===================================================================
--- /dev/null
+++ test/CodeGen/captured-statements.c
@@ -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
Index: test/CodeGenCXX/captured-statements.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/captured-statements.cpp
@@ -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*
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits