Skip to content

Commit

Permalink
Distinguish __block variables that are captured by escaping blocks
Browse files Browse the repository at this point in the history
from those that aren't.

This patch changes the way __block variables that aren't captured by
escaping blocks are handled:

- Since non-escaping blocks on the stack never get copied to the heap
  (see https://reviews.llvm.org/D49303), Sema shouldn't error out when
  the type of a non-escaping __block variable doesn't have an accessible
  copy constructor.

- IRGen doesn't have to use the specialized byref structure (see
  https://clang.llvm.org/docs/Block-ABI-Apple.html#id8) for a
  non-escaping __block variable anymore. Instead IRGen can emit the
  variable as a normal variable and copy the reference to the block
  literal. Byref copy/dispose helpers aren't needed either.

rdar://problem/39352313

Differential Revision: https://reviews.llvm.org/D51564

llvm-svn: 341754
  • Loading branch information
ahatanaka committed Sep 8, 2018
1 parent 7af5e33 commit 2e00b98
Show file tree
Hide file tree
Showing 30 changed files with 335 additions and 82 deletions.
23 changes: 23 additions & 0 deletions clang/include/clang/AST/Decl.h
Expand Up @@ -965,6 +965,8 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
/// Defines kind of the ImplicitParamDecl: 'this', 'self', 'vtt', '_cmd' or
/// something else.
unsigned ImplicitParamKind : 3;

unsigned EscapingByref : 1;
};

union {
Expand Down Expand Up @@ -1407,6 +1409,19 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
NonParmVarDeclBits.PreviousDeclInSameBlockScope = Same;
}

/// Indicates the capture is a __block variable that is captured by a block
/// that can potentially escape (a block for which BlockDecl::doesNotEscape
/// returns false).
bool isEscapingByref() const;

/// Indicates the capture is a __block variable that is never captured by an
/// escaping block.
bool isNonEscapingByref() const;

void setEscapingByref() {
NonParmVarDeclBits.EscapingByref = true;
}

/// Retrieve the variable declaration from which this variable could
/// be instantiated, if it is an instantiation (rather than a non-template).
VarDecl *getTemplateInstantiationPattern() const;
Expand Down Expand Up @@ -3858,6 +3873,14 @@ class BlockDecl : public Decl, public DeclContext {
/// variable.
bool isByRef() const { return VariableAndFlags.getInt() & flag_isByRef; }

bool isEscapingByref() const {
return getVariable()->isEscapingByref();
}

bool isNonEscapingByref() const {
return getVariable()->isNonEscapingByref();
}

/// Whether this is a nested capture, i.e. the variable captured
/// is not from outside the immediately enclosing function/block.
bool isNested() const { return VariableAndFlags.getInt() & flag_isNested; }
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/Sema/ScopeInfo.h
Expand Up @@ -31,6 +31,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
Expand Down Expand Up @@ -202,6 +203,12 @@ class FunctionScopeInfo {
/// function.
SmallVector<CompoundScopeInfo, 4> CompoundScopes;

/// The set of blocks that are introduced in this function.
llvm::SmallPtrSet<const BlockDecl *, 1> Blocks;

/// The set of __block variables that are introduced in this function.
llvm::TinyPtrVector<VarDecl *> ByrefBlockVars;

/// A list of PartialDiagnostics created but delayed within the
/// current function scope. These diagnostics are vetted for reachability
/// prior to being emitted.
Expand Down Expand Up @@ -426,6 +433,16 @@ class FunctionScopeInfo {
(HasBranchProtectedScope && HasBranchIntoScope));
}

// Add a block introduced in this function.
void addBlock(const BlockDecl *BD) {
Blocks.insert(BD);
}

// Add a __block variable introduced in this function.
void addByrefBlockVar(VarDecl *VD) {
ByrefBlockVars.push_back(VD);
}

bool isCoroutine() const { return !FirstCoroutineStmtLoc.isInvalid(); }

void setFirstCoroutineStmt(SourceLocation Loc, StringRef Keyword) {
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/Decl.cpp
Expand Up @@ -2361,6 +2361,14 @@ static DeclT *getDefinitionOrSelf(DeclT *D) {
return D;
}

bool VarDecl::isEscapingByref() const {
return hasAttr<BlocksAttr>() && NonParmVarDeclBits.EscapingByref;
}

bool VarDecl::isNonEscapingByref() const {
return hasAttr<BlocksAttr>() && !NonParmVarDeclBits.EscapingByref;
}

VarDecl *VarDecl::getTemplateInstantiationPattern() const {
// If it's a variable template specialization, find the template or partial
// specialization from which it was instantiated.
Expand Down
28 changes: 19 additions & 9 deletions clang/lib/CodeGen/CGBlocks.cpp
Expand Up @@ -493,7 +493,11 @@ static QualType getCaptureFieldType(const CodeGenFunction &CGF,
return CGF.BlockInfo->getCapture(VD).fieldType();
if (auto *FD = CGF.LambdaCaptureFields.lookup(VD))
return FD->getType();
return VD->getType();
// If the captured variable is a non-escaping __block variable, the field
// type is the reference type. If the variable is a __block variable that
// already has a reference type, the field type is the variable's type.
return VD->isNonEscapingByref() ?
CGF.getContext().getLValueReferenceType(VD->getType()) : VD->getType();
}

/// Compute the layout of the given block. Attempts to lay the block
Expand Down Expand Up @@ -549,7 +553,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
for (const auto &CI : block->captures()) {
const VarDecl *variable = CI.getVariable();

if (CI.isByRef()) {
if (CI.isEscapingByref()) {
// We have to copy/dispose of the __block reference.
info.NeedsCopyDispose = true;

Expand Down Expand Up @@ -1032,7 +1036,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// The lambda capture in a lambda's conversion-to-block-pointer is
// special; we'll simply emit it directly.
src = Address::invalid();
} else if (CI.isByRef()) {
} else if (CI.isEscapingByref()) {
if (BlockInfo && CI.isNested()) {
// We need to use the capture from the enclosing block.
const CGBlockInfo::Capture &enclosingCapture =
Expand Down Expand Up @@ -1060,7 +1064,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// the block field. There's no need to chase the forwarding
// pointer at this point, since we're building something that will
// live a shorter life than the stack byref anyway.
if (CI.isByRef()) {
if (CI.isEscapingByref()) {
// Get a void* that points to the byref struct.
llvm::Value *byrefPointer;
if (CI.isNested())
Expand Down Expand Up @@ -1279,8 +1283,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E,
return EmitCall(FnInfo, Callee, ReturnValue, Args);
}

Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
bool isByRef) {
Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable) {
assert(BlockInfo && "evaluating block ref without block information?");
const CGBlockInfo::Capture &capture = BlockInfo->getCapture(variable);

Expand All @@ -1291,7 +1294,7 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
Builder.CreateStructGEP(LoadBlockStruct(), capture.getIndex(),
capture.getOffset(), "block.capture.addr");

if (isByRef) {
if (variable->isEscapingByref()) {
// addr should be a void** right now. Load, then cast the result
// to byref*.

Expand All @@ -1305,6 +1308,10 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
variable->getName());
}

assert((!variable->isNonEscapingByref() ||
capture.fieldType()->isReferenceType()) &&
"the capture field of a non-escaping variable should have a "
"reference type");
if (capture.fieldType()->isReferenceType())
addr = EmitLoadOfReference(MakeAddrLValue(addr, capture.fieldType()));

Expand Down Expand Up @@ -1656,7 +1663,7 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags());
}
BlockFieldFlags Flags;
if (CI.isByRef()) {
if (CI.isEscapingByref()) {
Flags = BLOCK_FIELD_IS_BYREF;
if (T.isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
Expand Down Expand Up @@ -2102,7 +2109,7 @@ getBlockFieldFlagsForObjCObjectPointer(const BlockDecl::Capture &CI,
static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
const LangOptions &LangOpts) {
if (CI.isByRef()) {
if (CI.isEscapingByref()) {
BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF;
if (T.isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
Expand Down Expand Up @@ -2564,6 +2571,9 @@ BlockByrefHelpers *
CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType,
const AutoVarEmission &emission) {
const VarDecl &var = *emission.Variable;
assert(var.isEscapingByref() &&
"only escaping __block variables need byref helpers");

QualType type = var.getType();

auto &byrefInfo = getBlockByrefInfo(&var);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGClass.cpp
Expand Up @@ -2839,7 +2839,7 @@ void CodeGenFunction::EmitLambdaBlockInvokeBody() {
CallArgList CallArgs;

QualType ThisType = getContext().getPointerType(getContext().getRecordType(Lambda));
Address ThisPtr = GetAddrOfBlockDecl(variable, false);
Address ThisPtr = GetAddrOfBlockDecl(variable);
CallArgs.add(RValue::get(ThisPtr.getPointer()), ThisType);

// Add the rest of the parameters.
Expand Down
19 changes: 10 additions & 9 deletions clang/lib/CodeGen/CGDecl.cpp
Expand Up @@ -1212,8 +1212,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {

AutoVarEmission emission(D);

bool isByRef = D.hasAttr<BlocksAttr>();
emission.IsByRef = isByRef;
bool isEscapingByRef = D.isEscapingByref();
emission.IsEscapingByRef = isEscapingByRef;

CharUnits alignment = getContext().getDeclAlign(&D);

Expand Down Expand Up @@ -1252,8 +1252,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
// in OpenCL.
if ((!getLangOpts().OpenCL ||
Ty.getAddressSpace() == LangAS::opencl_constant) &&
(CGM.getCodeGenOpts().MergeAllConstants && !NRVO && !isByRef &&
CGM.isTypeConstant(Ty, true))) {
(CGM.getCodeGenOpts().MergeAllConstants && !NRVO &&
!isEscapingByRef && CGM.isTypeConstant(Ty, true))) {
EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage);

// Signal this condition to later callbacks.
Expand Down Expand Up @@ -1305,7 +1305,7 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
} else {
CharUnits allocaAlignment;
llvm::Type *allocaTy;
if (isByRef) {
if (isEscapingByRef) {
auto &byrefInfo = getBlockByrefInfo(&D);
allocaTy = byrefInfo.Type;
allocaAlignment = byrefInfo.ByrefAlignment;
Expand Down Expand Up @@ -1505,7 +1505,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
}

// Initialize the structure of a __block variable.
if (emission.IsByRef)
if (emission.IsEscapingByRef)
emitByrefStructureInit(emission);

// Initialize the variable here if it doesn't have a initializer and it is a
Expand All @@ -1515,7 +1515,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
type.isNonTrivialToPrimitiveDefaultInitialize() ==
QualType::PDIK_Struct) {
LValue Dst = MakeAddrLValue(emission.getAllocatedAddress(), type);
if (emission.IsByRef)
if (emission.IsEscapingByRef)
drillIntoBlockVariable(*this, Dst, &D);
defaultInitNonTrivialCStructVar(Dst);
return;
Expand All @@ -1527,7 +1527,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
// Check whether this is a byref variable that's potentially
// captured and moved by its own initializer. If so, we'll need to
// emit the initializer first, then copy into the variable.
bool capturedByInit = emission.IsByRef && isCapturedBy(D, Init);
bool capturedByInit = emission.IsEscapingByRef && isCapturedBy(D, Init);

Address Loc =
capturedByInit ? emission.Addr : emission.getObjectAddress(*this);
Expand Down Expand Up @@ -1721,7 +1721,8 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) {
// If this is a block variable, call _Block_object_destroy
// (on the unforwarded address). Don't enter this cleanup if we're in pure-GC
// mode.
if (emission.IsByRef && CGM.getLangOpts().getGC() != LangOptions::GCOnly) {
if (emission.IsEscapingByRef &&
CGM.getLangOpts().getGC() != LangOptions::GCOnly) {
BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF;
if (emission.Variable->getType().isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -2486,7 +2486,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
}

assert(isa<BlockDecl>(CurCodeDecl));
Address addr = GetAddrOfBlockDecl(VD, VD->hasAttr<BlocksAttr>());
Address addr = GetAddrOfBlockDecl(VD);
return MakeAddrLValue(addr, T, AlignmentSource::Decl);
}
}
Expand Down Expand Up @@ -2538,7 +2538,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
}

// Drill into block byref variables.
bool isBlockByref = VD->hasAttr<BlocksAttr>();
bool isBlockByref = VD->isEscapingByref();
if (isBlockByref) {
addr = emitBlockByrefAddress(addr, VD);
}
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/CodeGen/CodeGenFunction.h
Expand Up @@ -1787,7 +1787,7 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *ptr);

Address LoadBlockStruct();
Address GetAddrOfBlockDecl(const VarDecl *var, bool ByRef);
Address GetAddrOfBlockDecl(const VarDecl *var);

/// BuildBlockByrefAddress - Computes the location of the
/// data in a variable which is declared as __block.
Expand Down Expand Up @@ -2683,8 +2683,9 @@ class CodeGenFunction : public CodeGenTypeCache {

llvm::Value *NRVOFlag;

/// True if the variable is a __block variable.
bool IsByRef;
/// True if the variable is a __block variable that is captured by an
/// escaping block.
bool IsEscapingByRef;

/// True if the variable is of aggregate type and has a constant
/// initializer.
Expand All @@ -2704,7 +2705,7 @@ class CodeGenFunction : public CodeGenTypeCache {

AutoVarEmission(const VarDecl &variable)
: Variable(&variable), Addr(Address::invalid()), NRVOFlag(nullptr),
IsByRef(false), IsConstantAggregate(false),
IsEscapingByRef(false), IsConstantAggregate(false),
SizeForLifetimeMarkers(nullptr), AllocaAddr(Address::invalid()) {}

bool wasEmittedAsGlobal() const { return !Addr.isValid(); }
Expand Down Expand Up @@ -2734,7 +2735,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Note that this does not chase the forwarding pointer for
/// __block decls.
Address getObjectAddress(CodeGenFunction &CGF) const {
if (!IsByRef) return Addr;
if (!IsEscapingByRef) return Addr;

return CGF.emitBlockByrefAddress(Addr, Variable, /*forward*/ false);
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/ScopeInfo.cpp
Expand Up @@ -54,6 +54,8 @@ void FunctionScopeInfo::Clear() {
PossiblyUnreachableDiags.clear();
WeakObjectUses.clear();
ModifiedNonNullParams.clear();
Blocks.clear();
ByrefBlockVars.clear();
}

static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) {
Expand Down

0 comments on commit 2e00b98

Please sign in to comment.