forked from OSchip/llvm-project
Add nomerge function attribute to clang
This commit is contained in:
parent
f96a7706d9
commit
307e853954
|
@ -1275,6 +1275,11 @@ def FallThrough : StmtAttr {
|
|||
let Documentation = [FallthroughDocs];
|
||||
}
|
||||
|
||||
def NoMerge : StmtAttr {
|
||||
let Spellings = [Clang<"nomerge">];
|
||||
let Documentation = [NoMergeDocs];
|
||||
}
|
||||
|
||||
def FastCall : DeclOrTypeAttr {
|
||||
let Spellings = [GCC<"fastcall">, Keyword<"__fastcall">,
|
||||
Keyword<"_fastcall">];
|
||||
|
|
|
@ -350,6 +350,20 @@ that appears to be capable of returning to its caller.
|
|||
}];
|
||||
}
|
||||
|
||||
def NoMergeDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
If a statement is marked ``nomerge`` and contains call experessions, those call
|
||||
expressions inside the statement will not be merged during optimization. This
|
||||
attribute can be used to prevent the optimizer from obscuring the source
|
||||
location of certain calls. For example, it will prevent tail merging otherwise
|
||||
identical code sequences that raise an exception or terminate the program. Tail
|
||||
merging normally reduces the precision of source location information, making
|
||||
stack traces less useful for debugging. This attribute gives the user control
|
||||
over the tradeoff between code size and debug information precision.
|
||||
}];
|
||||
}
|
||||
|
||||
def AssertCapabilityDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "assert_capability, assert_shared_capability";
|
||||
|
|
|
@ -2762,6 +2762,10 @@ def warn_auto_var_is_id : Warning<
|
|||
InGroup<DiagGroup<"auto-var-id">>;
|
||||
|
||||
// Attributes
|
||||
def warn_nomerge_attribute_ignored_in_stmt: Warning<
|
||||
"%0 attribute is ignored because there exists no call expression inside the "
|
||||
"statement">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
def err_nsobject_attribute : Error<
|
||||
"'NSObject' attribute is for pointer types only">;
|
||||
def err_attributes_are_not_compatible : Error<
|
||||
|
|
|
@ -4822,6 +4822,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
|||
Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex,
|
||||
llvm::Attribute::StrictFP);
|
||||
|
||||
// Add call-site nomerge attribute if exists.
|
||||
if (InNoMergeAttributedStmt)
|
||||
Attrs =
|
||||
Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex,
|
||||
llvm::Attribute::NoMerge);
|
||||
|
||||
// Apply some call-site-specific attributes.
|
||||
// TODO: work this into building the attribute set.
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace CodeGen;
|
||||
|
@ -608,6 +609,13 @@ void CodeGenFunction::EmitLabelStmt(const LabelStmt &S) {
|
|||
}
|
||||
|
||||
void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
|
||||
bool nomerge = false;
|
||||
for (const auto *A: S.getAttrs())
|
||||
if (A->getKind() == attr::NoMerge) {
|
||||
nomerge = true;
|
||||
break;
|
||||
}
|
||||
SaveAndRestore<bool> save_nomerge(InNoMergeAttributedStmt, nomerge);
|
||||
EmitStmt(S.getSubStmt(), S.getAttrs());
|
||||
}
|
||||
|
||||
|
|
|
@ -595,6 +595,9 @@ public:
|
|||
/// region.
|
||||
bool IsInPreservedAIRegion = false;
|
||||
|
||||
/// True if the current statement has nomerge attribute.
|
||||
bool InNoMergeAttributedStmt = false;
|
||||
|
||||
const CodeGen::CGBlockInfo *BlockInfo = nullptr;
|
||||
llvm::Value *BlockPointer = nullptr;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/EvaluatedExprVisitor.h"
|
||||
#include "clang/Sema/SemaInternal.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
|
@ -170,6 +171,45 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
|
|||
return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
|
||||
bool FoundCallExpr = false;
|
||||
|
||||
public:
|
||||
typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
|
||||
|
||||
CallExprFinder(Sema &S, const Stmt* St) : Inherited(S.Context) {
|
||||
Visit(St);
|
||||
}
|
||||
|
||||
bool foundCallExpr() { return FoundCallExpr; }
|
||||
|
||||
void VisitCallExpr(const CallExpr *E) { FoundCallExpr = true; }
|
||||
|
||||
void Visit(const Stmt *St) {
|
||||
if (!St) return;
|
||||
ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
|
||||
SourceRange Range) {
|
||||
NoMergeAttr NMA(S.Context, A);
|
||||
if (S.CheckAttrNoArgs(A))
|
||||
return nullptr;
|
||||
|
||||
CallExprFinder CEF(S, St);
|
||||
|
||||
if (!CEF.foundCallExpr()) {
|
||||
S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt)
|
||||
<< NMA.getSpelling();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ::new (S.Context) NoMergeAttr(S.Context, A);
|
||||
}
|
||||
|
||||
static void
|
||||
CheckForIncompatibleAttributes(Sema &S,
|
||||
const SmallVectorImpl<const Attr *> &Attrs) {
|
||||
|
@ -335,6 +375,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
|
|||
return handleOpenCLUnrollHint(S, St, A, Range);
|
||||
case ParsedAttr::AT_Suppress:
|
||||
return handleSuppressAttr(S, St, A, Range);
|
||||
case ParsedAttr::AT_NoMerge:
|
||||
return handleNoMergeAttr(S, St, A, Range);
|
||||
default:
|
||||
// if we're here, then we parsed a known attribute, but didn't recognize
|
||||
// it as a statement attribute => it is declaration attribute
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// RUN: %clang_cc1 -S -emit-llvm %s -o - | FileCheck %s
|
||||
|
||||
bool bar();
|
||||
void f(bool, bool);
|
||||
|
||||
void foo(int i) {
|
||||
[[clang::nomerge]] bar();
|
||||
[[clang::nomerge]] (i = 4, bar());
|
||||
[[clang::nomerge]] (void)(bar());
|
||||
[[clang::nomerge]] f(bar(), bar());
|
||||
[[clang::nomerge]] [] { bar(); bar(); }(); // nomerge only applies to the anonymous function call
|
||||
[[clang::nomerge]] for (bar(); bar(); bar()) {}
|
||||
bar();
|
||||
}
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR:[0-9]+]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call void @_Z1fbb({{.*}}) #[[NOMERGEATTR]]
|
||||
// CHECK: call void @"_ZZ3fooiENK3$_0clEv"(%class.anon* %ref.tmp) #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]]
|
||||
// CHECK: call zeroext i1 @_Z3barv()
|
||||
// CHECK: attributes #[[NOMERGEATTR]] = { nomerge }
|
|
@ -0,0 +1,17 @@
|
|||
// RUN: %clang_cc1 -verify -fsyntax-only %s
|
||||
|
||||
void bar();
|
||||
|
||||
void foo() {
|
||||
[[clang::nomerge]] bar();
|
||||
[[clang::nomerge(1, 2)]] bar(); // expected-error {{'nomerge' attribute takes no arguments}}
|
||||
int x;
|
||||
[[clang::nomerge]] x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}}
|
||||
|
||||
[[clang::nomerge]] label: bar(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}}
|
||||
|
||||
}
|
||||
|
||||
int f();
|
||||
|
||||
[[clang::nomerge]] static int i = f(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}}
|
Loading…
Reference in New Issue