Skip to content

Commit

Permalink
Add support for attribute 'noescape'.
Browse files Browse the repository at this point in the history
The attribute informs the compiler that the annotated pointer parameter
of a function cannot escape and enables IRGen to attach attribute
'nocapture' to parameters that are annotated with the attribute. That is
the only optimization that currently takes advantage of 'noescape', but
there are other optimizations that will be added later that improves
IRGen for ObjC blocks.

This recommits r313722, which was reverted in r313725 because clang
couldn't build compiler-rt. It failed to build because there were
function declarations that were missing 'noescape'. That has been fixed
in r313929.

rdar://problem/19886775

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@313945 91177308-0d34-0410-b5e6-96231b3b80d8
(cherry picked from commit d6b1e92)

Conflicts:
include/clang/Basic/Attr.td
lib/Sema/SemaDeclAttr.cpp
  • Loading branch information
ahatanaka committed Sep 22, 2017
1 parent 7bf83a7 commit 92631d7
Show file tree
Hide file tree
Showing 17 changed files with 217 additions and 37 deletions.
27 changes: 24 additions & 3 deletions include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2394,9 +2394,30 @@ class ASTContext : public RefCountedBase<ASTContext> {

QualType mergeObjCGCQualifiers(QualType, QualType);

bool doFunctionTypesMatchOnExtParameterInfos(
const FunctionProtoType *FromFunctionType,
const FunctionProtoType *ToFunctionType);
/// This function merges the ExtParameterInfo lists of two functions. It
/// returns true if the lists are compatible. The merged list is returned in
/// NewParamInfos.
///
/// \param FirstFnType The type of the first function.
///
/// \param SecondFnType The type of the second function.
///
/// \param CanUseFirst This flag is set to true if the first function's
/// ExtParameterInfo list can be used as the composite list of
/// ExtParameterInfo.
///
/// \param CanUseSecond This flag is set to true if the second function's
/// ExtParameterInfo list can be used as the composite list of
/// ExtParameterInfo.
///
/// \param NewParamInfos The composite list of ExtParameterInfo. The list is
/// empty if none of the flags are set.
///
bool mergeExtParameterInfo(
const FunctionProtoType *FirstFnType,
const FunctionProtoType *SecondFnType,
bool &CanUseFirst, bool &CanUseSecond,
SmallVectorImpl<FunctionProtoType::ExtParameterInfo> &NewParamInfos);

void ResetObjCLayout(const ObjCContainerDecl *CD);

Expand Down
14 changes: 14 additions & 0 deletions include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -3152,6 +3152,7 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode {
ABIMask = 0x0F,
IsConsumed = 0x10,
HasPassObjSize = 0x20,
IsNoEscape = 0x40,
};
unsigned char Data;

Expand Down Expand Up @@ -3192,6 +3193,19 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode {
return Copy;
}

bool isNoEscape() const {
return Data & IsNoEscape;
}

ExtParameterInfo withIsNoEscape(bool NoEscape) const {
ExtParameterInfo Copy = *this;
if (NoEscape)
Copy.Data |= IsNoEscape;
else
Copy.Data &= ~IsNoEscape;
return Copy;
}

unsigned char getOpaqueValue() const { return Data; }
static ExtParameterInfo getFromOpaqueValue(unsigned char data) {
ExtParameterInfo result;
Expand Down
8 changes: 4 additions & 4 deletions include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1390,10 +1390,10 @@ def ObjCKindOf : TypeAttr {
let Documentation = [Undocumented];
}

def NoEscape : InheritableAttr {
let Spellings = [GCC<"noescape">];
let Subjects = SubjectList<[ParmVar], WarnDiag, "ExpectedParameter">;
let Documentation = [Undocumented];
def NoEscape : Attr {
let Spellings = [GNU<"noescape">, CXX11<"clang", "noescape">];
let Subjects = SubjectList<[ParmVar]>;
let Documentation = [NoEscapeDocs];
}

def AssumeAligned : InheritableAttr {
Expand Down
41 changes: 41 additions & 0 deletions include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,47 @@ members, and static locals.
}];
}

def NoEscapeDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
``noescape`` placed on a function parameter of a pointer type is used to inform
the compiler that the pointer cannot escape: that is, no reference to the object
the pointer points to that is derived from the parameter value will survive
after the function returns. Users are responsible for making sure parameters
annotated with ``noescape`` do not actuallly escape.

For example:

.. code-block:: c
int *gp;

void nonescapingFunc(__attribute__((noescape)) int *p) {
*p += 100; // OK.
}

void escapingFunc(__attribute__((noescape)) int *p) {
gp = p; // Not OK.
}

Additionally, when the parameter is a `block pointer
<https://clang.llvm.org/docs/BlockLanguageSpec.html>`, the same restriction
applies to copies of the block. For example:

typedef void (^BlockTy)();
BlockTy g0, g1;

void nonescapingFunc(__attribute__((noescape)) BlockTy block) {
block(); // OK.
}

void escapingFunc(__attribute__((noescape)) BlockTy block) {
g0 = block; // Not OK.
g1 = Block_copy(block); // Not OK either.
}

}];
}

def CarriesDependencyDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def GNUStringLiteralOperatorTemplate :
DiagGroup<"gnu-string-literal-operator-template">;
def UndefinedVarTemplate : DiagGroup<"undefined-var-template">;
def UndefinedFuncTemplate : DiagGroup<"undefined-func-template">;
def MissingNoEscape : DiagGroup<"missing-noescape">;

def DeleteIncomplete : DiagGroup<"delete-incomplete">;
def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor">;
Expand Down
5 changes: 5 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,11 @@ def err_conflicting_overriding_cc_attributes : Error<
"virtual function %0 has different calling convention attributes "
"%diff{($) than the function it overrides (which has calling convention $)|"
"than the function it overrides}1,2">;
def warn_overriding_method_missing_noescape : Warning<
"parameter of overriding method should be annotated with "
"__attribute__((noescape))">, InGroup<MissingNoEscape>;
def note_overridden_marked_noescape : Note<
"parameter of overridden method is annotated with __attribute__((noescape))">;

def err_covariant_return_inaccessible_base : Error<
"invalid covariant return for virtual function: %1 is a "
Expand Down
64 changes: 49 additions & 15 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7957,9 +7957,17 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,
if (lproto->getTypeQuals() != rproto->getTypeQuals())
return QualType();

if (!doFunctionTypesMatchOnExtParameterInfos(rproto, lproto))
SmallVector<FunctionProtoType::ExtParameterInfo, 4> newParamInfos;
bool canUseLeft, canUseRight;
if (!mergeExtParameterInfo(lproto, rproto, canUseLeft, canUseRight,
newParamInfos))
return QualType();

if (!canUseLeft)
allLTypes = false;
if (!canUseRight)
allRTypes = false;

// Check parameter type compatibility
SmallVector<QualType, 10> types;
for (unsigned i = 0, n = lproto->getNumParams(); i < n; i++) {
Expand Down Expand Up @@ -7990,6 +7998,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,

FunctionProtoType::ExtProtoInfo EPI = lproto->getExtProtoInfo();
EPI.ExtInfo = einfo;
EPI.ExtParameterInfos =
newParamInfos.empty() ? nullptr : newParamInfos.data();
return getFunctionType(retType, types, EPI);
}

Expand Down Expand Up @@ -8349,26 +8359,50 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
llvm_unreachable("Invalid Type::Class!");
}

bool ASTContext::doFunctionTypesMatchOnExtParameterInfos(
const FunctionProtoType *firstFnType,
const FunctionProtoType *secondFnType) {
bool ASTContext::mergeExtParameterInfo(
const FunctionProtoType *FirstFnType, const FunctionProtoType *SecondFnType,
bool &CanUseFirst, bool &CanUseSecond,
SmallVectorImpl<FunctionProtoType::ExtParameterInfo> &NewParamInfos) {
assert(NewParamInfos.empty() && "param info list not empty");
CanUseFirst = CanUseSecond = true;
bool FirstHasInfo = FirstFnType->hasExtParameterInfos();
bool SecondHasInfo = SecondFnType->hasExtParameterInfos();

// Fast path: if the first type doesn't have ext parameter infos,
// we match if and only if they second type also doesn't have them.
if (!firstFnType->hasExtParameterInfos())
return !secondFnType->hasExtParameterInfos();
// we match if and only if the second type also doesn't have them.
if (!FirstHasInfo && !SecondHasInfo)
return true;

// Otherwise, we can only match if the second type has them.
if (!secondFnType->hasExtParameterInfos())
return false;
bool NeedParamInfo = false;
size_t E = FirstHasInfo ? FirstFnType->getExtParameterInfos().size()
: SecondFnType->getExtParameterInfos().size();

auto firstEPI = firstFnType->getExtParameterInfos();
auto secondEPI = secondFnType->getExtParameterInfos();
assert(firstEPI.size() == secondEPI.size());
for (size_t I = 0; I < E; ++I) {
FunctionProtoType::ExtParameterInfo FirstParam, SecondParam;
if (FirstHasInfo)
FirstParam = FirstFnType->getExtParameterInfo(I);
if (SecondHasInfo)
SecondParam = SecondFnType->getExtParameterInfo(I);

for (size_t i = 0, n = firstEPI.size(); i != n; ++i) {
if (firstEPI[i] != secondEPI[i])
// Cannot merge unless everything except the noescape flag matches.
if (FirstParam.withIsNoEscape(false) != SecondParam.withIsNoEscape(false))
return false;

bool FirstNoEscape = FirstParam.isNoEscape();
bool SecondNoEscape = SecondParam.isNoEscape();
bool IsNoEscape = FirstNoEscape && SecondNoEscape;
NewParamInfos.push_back(FirstParam.withIsNoEscape(IsNoEscape));
if (NewParamInfos.back().getOpaqueValue())
NeedParamInfo = true;
if (FirstNoEscape != IsNoEscape)
CanUseFirst = false;
if (SecondNoEscape != IsNoEscape)
CanUseSecond = false;
}

if (!NeedParamInfo)
NewParamInfos.clear();

return true;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2586,6 +2586,9 @@ CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) {

if (PI.isConsumed())
mangleVendorQualifier("ns_consumed");

if (PI.isNoEscape())
mangleVendorQualifier("noescape");
}

// <type> ::= <function-type>
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,8 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,

auto EPI = T->getExtParameterInfo(i);
if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) ";
if (EPI.isNoEscape())
OS << "__attribute__((noescape)) ";
auto ABI = EPI.getABI();
if (ABI != ParameterABI::Ordinary)
OS << "__attribute__((" << getParameterABISpelling(ABI) << ")) ";
Expand Down
9 changes: 8 additions & 1 deletion lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,15 @@ const CGFunctionInfo &
CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD,
QualType receiverType) {
SmallVector<CanQualType, 16> argTys;
SmallVector<FunctionProtoType::ExtParameterInfo, 4> extParamInfos(2);
argTys.push_back(Context.getCanonicalParamType(receiverType));
argTys.push_back(Context.getCanonicalParamType(Context.getObjCSelType()));
// FIXME: Kill copy?
for (const auto *I : MD->parameters()) {
argTys.push_back(Context.getCanonicalParamType(I->getType()));
auto extParamInfo = FunctionProtoType::ExtParameterInfo().withIsNoEscape(
I->hasAttr<NoEscapeAttr>());
extParamInfos.push_back(extParamInfo);
}

FunctionType::ExtInfo einfo;
Expand All @@ -475,7 +479,7 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD,

return arrangeLLVMFunctionInfo(
GetReturnType(MD->getReturnType()), /*instanceMethod=*/false,
/*chainCall=*/false, argTys, einfo, {}, required);
/*chainCall=*/false, argTys, einfo, extParamInfos, required);
}

const CGFunctionInfo &
Expand Down Expand Up @@ -2092,6 +2096,9 @@ void CodeGenModule::ConstructAttributeList(
break;
}

if (FI.getExtParameterInfo(ArgNo).isNoEscape())
Attrs.addAttribute(llvm::Attribute::NoCapture);

if (Attrs.hasAttributes()) {
unsigned FirstIRArg, NumIRArgs;
std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo);
Expand Down
16 changes: 6 additions & 10 deletions lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1518,23 +1518,19 @@ static void handleReturnsNonNullAttr(Sema &S, Decl *D,
}

static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D);
if (!PD)
if (D->isInvalidDecl())
return;

// noescape only applies to pointer types.
QualType T = PD->getType();
if (!T->isAnyPointerType() && !T->isBlockPointerType() &&
!T->isReferenceType() && !T->isArrayType() &&
!T->isMemberPointerType()) {
S.Diag(Attr.getLoc(), diag::warn_attribute_noescape_non_pointer)
<< T;
QualType T = cast<ParmVarDecl>(D)->getType();
if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only)
<< Attr.getName() << Attr.getRange() << 0;
return;
}

D->addAttr(::new (S.Context) NoEscapeAttr(
Attr.getRange(), S.Context,
Attr.getAttributeSpellingListIndex()));
Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex()));
}

static void handleAssumeAlignedAttr(Sema &S, Decl *D,
Expand Down
17 changes: 15 additions & 2 deletions lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14071,8 +14071,21 @@ void Sema::DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock) {

bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
const CXXMethodDecl *Old) {
const FunctionType *NewFT = New->getType()->getAs<FunctionType>();
const FunctionType *OldFT = Old->getType()->getAs<FunctionType>();
const auto *NewFT = New->getType()->getAs<FunctionProtoType>();
const auto *OldFT = Old->getType()->getAs<FunctionProtoType>();

if (OldFT->hasExtParameterInfos()) {
for (unsigned I = 0, E = OldFT->getNumParams(); I != E; ++I)
// A parameter of the overriding method should be annotated with noescape
// if the corresponding parameter of the overridden method is annotated.
if (OldFT->getExtParameterInfo(I).isNoEscape() &&
!NewFT->getExtParameterInfo(I).isNoEscape()) {
Diag(New->getParamDecl(I)->getLocation(),
diag::warn_overriding_method_missing_noescape);
Diag(Old->getParamDecl(I)->getLocation(),
diag::note_overridden_marked_noescape);
}
}

CallingConv NewCC = NewFT->getCallConv(), OldCC = OldFT->getCallConv();

Expand Down
8 changes: 8 additions & 0 deletions lib/Sema/SemaDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ void Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
Diag(newDecl->getLocation(), diag::warn_nsconsumed_attribute_mismatch);
Diag(oldDecl->getLocation(), diag::note_previous_decl) << "parameter";
}

// A parameter of the overriding method should be annotated with noescape
// if the corresponding parameter of the overridden method is annotated.
if (oldDecl->hasAttr<NoEscapeAttr>() && !newDecl->hasAttr<NoEscapeAttr>()) {
Diag(newDecl->getLocation(),
diag::warn_overriding_method_missing_noescape);
Diag(oldDecl->getLocation(), diag::note_overridden_marked_noescape);
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,23 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
.getTypePtr());
Changed = true;
}

// Convert FromFPT's ExtParameterInfo if necessary. The conversion is valid
// only if the ExtParameterInfo lists of the two function prototypes can be
// merged and the merged list is identical to ToFPT's ExtParameterInfo list.
SmallVector<FunctionProtoType::ExtParameterInfo, 4> NewParamInfos;
bool CanUseToFPT, CanUseFromFPT;
if (Context.mergeExtParameterInfo(ToFPT, FromFPT, CanUseToFPT,
CanUseFromFPT, NewParamInfos) &&
CanUseToFPT && !CanUseFromFPT) {
FunctionProtoType::ExtProtoInfo ExtInfo = FromFPT->getExtProtoInfo();
ExtInfo.ExtParameterInfos =
NewParamInfos.empty() ? nullptr : NewParamInfos.data();
QualType QT = Context.getFunctionType(FromFPT->getReturnType(),
FromFPT->getParamTypes(), ExtInfo);
FromFn = QT->getAs<FunctionType>();
Changed = true;
}
}

if (!Changed)
Expand Down Expand Up @@ -2663,8 +2680,12 @@ bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType,
// Argument types are too different. Abort.
return false;
}
if (!Context.doFunctionTypesMatchOnExtParameterInfos(FromFunctionType,
ToFunctionType))

SmallVector<FunctionProtoType::ExtParameterInfo, 4> NewParamInfos;
bool CanUseToFPT, CanUseFromFPT;
if (!Context.mergeExtParameterInfo(ToFunctionType, FromFunctionType,
CanUseToFPT, CanUseFromFPT,
NewParamInfos))
return false;

ConvertedType = ToType;
Expand Down
Loading

0 comments on commit 92631d7

Please sign in to comment.