aaboud updated this revision to Diff 66810.
aaboud added a comment.
Made "no_caller_saved_registers" part of function prototype.
This will allow Clang to yell a warning when there is a mismatch between
function pointer and assigned function value.
Thanks to Erich for implementing this addition change.
https://reviews.llvm.org/D22045
Files:
include/clang/AST/Type.h
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/CodeGen/CGFunctionInfo.h
include/clang/Sema/Sema.h
lib/AST/ASTContext.cpp
lib/AST/TypePrinter.cpp
lib/CodeGen/CGCall.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaType.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/CodeGenCXX/attr-x86-no_caller_saved_registers.cpp
test/SemaCXX/attr-x86-no_caller_saved_registers.cpp
Index: lib/Serialization/ASTWriter.cpp
===================================================================
--- lib/Serialization/ASTWriter.cpp
+++ lib/Serialization/ASTWriter.cpp
@@ -221,6 +221,7 @@
// FIXME: need to stabilize encoding of calling convention...
Record.push_back(C.getCC());
Record.push_back(C.getProducesResult());
+ Record.push_back(C.getNoCallerSavedRegs());
if (C.getHasRegParm() || C.getRegParm() || C.getProducesResult())
AbbrevToUse = 0;
@@ -731,6 +732,7 @@
Abv->Add(BitCodeAbbrevOp(0)); // RegParm
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 4)); // CC
Abv->Add(BitCodeAbbrevOp(0)); // ProducesResult
+ Abv->Add(BitCodeAbbrevOp(0)); // NoCallerSavedRegs
// FunctionProtoType
Abv->Add(BitCodeAbbrevOp(0)); // IsVariadic
Abv->Add(BitCodeAbbrevOp(0)); // HasTrailingReturn
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp
+++ lib/Serialization/ASTReader.cpp
@@ -5368,13 +5368,14 @@
}
case TYPE_FUNCTION_NO_PROTO: {
- if (Record.size() != 6) {
+ if (Record.size() != 7) {
Error("incorrect encoding of no-proto function type");
return QualType();
}
QualType ResultType = readType(*Loc.F, Record, Idx);
FunctionType::ExtInfo Info(Record[1], Record[2], Record[3],
- (CallingConv)Record[4], Record[5]);
+ (CallingConv)Record[4], Record[5],
+ Record[6]);
return Context.getFunctionNoProtoType(ResultType, Info);
}
@@ -5386,9 +5387,10 @@
/*hasregparm*/ Record[2],
/*regparm*/ Record[3],
static_cast<CallingConv>(Record[4]),
- /*produces*/ Record[5]);
+ /*produces*/ Record[5],
+ /*nocallersavedregs*/ Record[6]);
- unsigned Idx = 6;
+ unsigned Idx = 7;
EPI.Variadic = Record[Idx++];
EPI.HasTrailingReturn = Record[Idx++];
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -746,6 +746,7 @@
FI->ChainCall = chainCall;
FI->NoReturn = info.getNoReturn();
FI->ReturnsRetained = info.getProducesResult();
+ FI->NoCallerSavedRegs = info.getNoCallerSavedRegs();
FI->Required = required;
FI->HasRegParm = info.getHasRegParm();
FI->RegParm = info.getRegParm();
@@ -1673,6 +1674,8 @@
RetAttrs.addAttribute(llvm::Attribute::NoAlias);
if (TargetDecl->hasAttr<ReturnsNonNullAttr>())
RetAttrs.addAttribute(llvm::Attribute::NonNull);
+ if (TargetDecl->hasAttr<AnyX86NoCallerSavedRegistersAttr>())
+ FuncAttrs.addAttribute("no_caller_saved_registers");
HasAnyX86InterruptAttr = TargetDecl->hasAttr<AnyX86InterruptAttr>();
HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -7564,6 +7564,8 @@
if (lbaseInfo.getProducesResult() != rbaseInfo.getProducesResult())
return QualType();
+ if (lbaseInfo.getNoCallerSavedRegs() != rbaseInfo.getNoCallerSavedRegs())
+ return QualType();
// FIXME: some uses, e.g. conditional exprs, really want this to be 'both'.
bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn();
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -745,6 +745,8 @@
if (Info.getRegParm())
OS << " __attribute__((regparm ("
<< Info.getRegParm() << ")))";
+ if (Info.getNoCallerSavedRegs())
+ OS << "__attribute__((no_caller_saved_registers))";
if (unsigned quals = T->getTypeQuals()) {
OS << ' ';
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -120,6 +120,7 @@
#define FUNCTION_TYPE_ATTRS_CASELIST \
case AttributeList::AT_NoReturn: \
case AttributeList::AT_Regparm: \
+ case AttributeList::AT_AnyX86NoCallerSavedRegisters: \
CALLING_CONV_ATTRS_CASELIST
// Microsoft-specific type qualifiers.
@@ -6143,6 +6144,20 @@
return true;
}
+ if (attr.getKind() == AttributeList::AT_AnyX86NoCallerSavedRegisters) {
+ if (S.CheckNoCallerSavedRegsAttr(attr))
+ return true;
+
+ // Delay if this is not a function type.
+ if (!unwrapped.isFunctionType())
+ return false;
+
+ FunctionType::ExtInfo EI =
+ unwrapped.get()->getExtInfo().withNoCallerSavedRegs(true);
+ type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
+ return true;
+ }
+
if (attr.getKind() == AttributeList::AT_Regparm) {
unsigned value;
if (S.CheckRegparmAttr(attr, value))
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -2880,6 +2880,12 @@
RequiresAdjustment = true;
}
+ if (OldTypeInfo.getNoCallerSavedRegs() !=
+ NewTypeInfo.getNoCallerSavedRegs()) {
+ NewTypeInfo = NewTypeInfo.withNoCallerSavedRegs(true);
+ RequiresAdjustment = true;
+ }
+
if (RequiresAdjustment) {
const FunctionType *AdjustedType = New->getType()->getAs<FunctionType>();
AdjustedType = Context.adjustFunctionType(AdjustedType, NewTypeInfo);
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -1689,6 +1689,15 @@
attr.getAttributeSpellingListIndex()));
}
+static void handleNoCallerSavedRegsAttr(Sema &S, Decl *D,
+ const AttributeList &attr) {
+ if (S.CheckNoCallerSavedRegsAttr(attr))
+ return;
+
+ D->addAttr(::new (S.Context) AnyX86NoCallerSavedRegistersAttr(
+ attr.getRange(), S.Context, attr.getAttributeSpellingListIndex()));
+}
+
bool Sema::CheckNoReturnAttr(const AttributeList &attr) {
if (!checkAttributeNumArgs(*this, attr, 0)) {
attr.setInvalid();
@@ -1698,6 +1707,15 @@
return false;
}
+bool Sema::CheckNoCallerSavedRegsAttr(const AttributeList &attr) {
+ if (!checkAttributeNumArgs(*this, attr, 0)) {
+ attr.setInvalid();
+ return true;
+ }
+
+ return false;
+}
+
static void handleAnalyzerNoReturnAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
@@ -5909,6 +5927,9 @@
case AttributeList::AT_TypeTagForDatatype:
handleTypeTagForDatatypeAttr(S, D, Attr);
break;
+ case AttributeList::AT_AnyX86NoCallerSavedRegisters:
+ handleNoCallerSavedRegsAttr(S, D, Attr);
+ break;
case AttributeList::AT_RenderScriptKernel:
handleSimpleAttribute<RenderScriptKernelAttr>(S, D, Attr);
break;
Index: include/clang/CodeGen/CGFunctionInfo.h
===================================================================
--- include/clang/CodeGen/CGFunctionInfo.h
+++ include/clang/CodeGen/CGFunctionInfo.h
@@ -475,6 +475,9 @@
/// Whether this function is returns-retained.
unsigned ReturnsRetained : 1;
+ /// Whether this function saved caller registers.
+ unsigned NoCallerSavedRegs : 1;
+
/// How many arguments to pass inreg.
unsigned HasRegParm : 1;
unsigned RegParm : 3;
@@ -560,6 +563,9 @@
/// is not always reliable for call sites.
bool isReturnsRetained() const { return ReturnsRetained; }
+ /// Whether this function no longer saves caller registers
+ bool isNoCallerSavedRegs() const { return NoCallerSavedRegs; }
+
/// getASTCallingConvention() - Return the AST-specified calling
/// convention.
CallingConv getASTCallingConvention() const {
@@ -586,7 +592,8 @@
return FunctionType::ExtInfo(isNoReturn(),
getHasRegParm(), getRegParm(),
getASTCallingConvention(),
- isReturnsRetained());
+ isReturnsRetained(),
+ isNoCallerSavedRegs());
}
CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -623,6 +630,7 @@
ID.AddBoolean(ChainCall);
ID.AddBoolean(NoReturn);
ID.AddBoolean(ReturnsRetained);
+ ID.AddBoolean(NoCallerSavedRegs);
ID.AddBoolean(HasRegParm);
ID.AddInteger(RegParm);
ID.AddInteger(Required.getOpaqueData());
@@ -648,6 +656,7 @@
ID.AddBoolean(ChainCall);
ID.AddBoolean(info.getNoReturn());
ID.AddBoolean(info.getProducesResult());
+ ID.AddBoolean(info.getNoCallerSavedRegs());
ID.AddBoolean(info.getHasRegParm());
ID.AddInteger(info.getRegParm());
ID.AddInteger(required.getOpaqueData());
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1377,7 +1377,7 @@
/// Extra information which affects how the function is called, like
/// regparm and the calling convention.
- unsigned ExtInfo : 9;
+ unsigned ExtInfo : 10;
/// Used only by FunctionProtoType, put here to pack with the
/// other bitfields.
@@ -2905,19 +2905,21 @@
// * AST read and write
// * Codegen
class ExtInfo {
- // Feel free to rearrange or add bits, but if you go over 9,
+ // Feel free to rearrange or add bits, but if you go over 10,
// you'll need to adjust both the Bits field below and
// Type::FunctionTypeBitfields.
- // | CC |noreturn|produces|regparm|
- // |0 .. 3| 4 | 5 | 6 .. 8|
+ // | CC |noreturn|produces|nocallersavedregs|regparm|
+ // |0 .. 3| 4 | 5 | 6 | 7 .. 9|
//
// regparm is either 0 (no regparm attribute) or the regparm value+1.
enum { CallConvMask = 0xF };
enum { NoReturnMask = 0x10 };
enum { ProducesResultMask = 0x20 };
- enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask),
- RegParmOffset = 6 }; // Assumed to be the last field
+ enum { NoCallerSavedRegsMask = 0x40 };
+ enum { RegParmMask = ~(CallConvMask | NoReturnMask |
+ ProducesResultMask | NoCallerSavedRegsMask ),
+ RegParmOffset = 7 }; // Assumed to be the last field
uint16_t Bits;
@@ -2929,11 +2931,12 @@
// Constructor with no defaults. Use this when you know that you
// have all the elements (when reading an AST file for example).
ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
- bool producesResult) {
+ bool producesResult, bool noCallerSavedRegs) {
assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
Bits = ((unsigned) cc) |
(noReturn ? NoReturnMask : 0) |
(producesResult ? ProducesResultMask : 0) |
+ (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
(hasRegParm ? ((regParm + 1) << RegParmOffset) : 0);
}
@@ -2947,6 +2950,7 @@
bool getNoReturn() const { return Bits & NoReturnMask; }
bool getProducesResult() const { return Bits & ProducesResultMask; }
+ bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; }
unsigned getRegParm() const {
unsigned RegParm = Bits >> RegParmOffset;
@@ -2980,6 +2984,13 @@
return ExtInfo(Bits & ~ProducesResultMask);
}
+ ExtInfo withNoCallerSavedRegs(bool noCallerSavedRegs) const {
+ if (noCallerSavedRegs)
+ return ExtInfo(Bits | NoCallerSavedRegsMask);
+ else
+ return ExtInfo(Bits & ~NoCallerSavedRegsMask);
+ }
+
ExtInfo withRegParm(unsigned RegParm) const {
assert(RegParm < 7 && "Invalid regparm value");
return ExtInfo((Bits & ~RegParmMask) |
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -3026,6 +3026,7 @@
bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC,
const FunctionDecl *FD = nullptr);
bool CheckNoReturnAttr(const AttributeList &attr);
+ bool CheckNoCallerSavedRegsAttr(const AttributeList &attr);
bool checkStringLiteralArgumentAttr(const AttributeList &Attr,
unsigned ArgNum, StringRef &Str,
SourceLocation *ArgLocation = nullptr);
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2198,6 +2198,20 @@
}];
}
+def AnyX86NoCallerSavedRegistersDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use this attribute to indicate that the specified function has no
+caller-saved registers. That is, all registers are callee-saved.
+The compiler generates proper function entry and exit sequences to
+save and restore any modified registers.
+
+The user can call functions specified with the 'no_caller_saved_registers'
+attribute from an interrupt handler without saving and restoring all
+call clobbered registers.
+ }];
+}
+
def SwiftCallDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1669,6 +1669,13 @@
let Documentation = [AnyX86InterruptDocs];
}
+def AnyX86NoCallerSavedRegisters : InheritableAttr,
+ TargetSpecificAttr<TargetAnyX86> {
+ let Spellings = [GNU<"no_caller_saved_registers">];
+ let Subjects = SubjectList<[FunctionLike], WarnDiag, "ExpectedFunction">;
+ let Documentation = [AnyX86NoCallerSavedRegistersDocs];
+}
+
def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr<TargetX86> {
let Spellings = [GNU<"force_align_arg_pointer">];
// Technically, this appertains to a FunctionDecl, but the target-specific
Index: test/SemaCXX/attr-x86-no_caller_saved_registers.cpp
===================================================================
--- test/SemaCXX/attr-x86-no_caller_saved_registers.cpp
+++ test/SemaCXX/attr-x86-no_caller_saved_registers.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple i386-pc-win32 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 -fsyntax-only -verify %s
+
+struct a {
+ int b __attribute__((no_caller_saved_registers)); // expected-warning {{'no_caller_saved_registers' only applies to function types; type here is 'int'}}
+ static void foo(int *a) __attribute__((no_caller_saved_registers)) {}
+};
+
+struct a test __attribute__((no_caller_saved_registers)); // expected-warning {{'no_caller_saved_registers' only applies to function types; type here is 'struct a'}}
+
+__attribute__((no_caller_saved_registers(999))) void bar(int *) {} // expected-error {{'no_caller_saved_registers' attribute takes no arguments}}
+
+__attribute__((no_caller_saved_registers)) void foo(int *) {}
+
+int main(int argc, char **argv) {
+ void(*fp)(int *) = foo; // expected-error {{cannot initialize a variable of type 'void (*)(int *)' with an lvalue of type 'void (int *)__attribute__((no_caller_saved_registers))'}}
+ a::foo(&argc);
+ foo(&argc);
+ return 0;
+}
Index: test/CodeGenCXX/attr-x86-no_caller_saved_registers.cpp
===================================================================
--- test/CodeGenCXX/attr-x86-no_caller_saved_registers.cpp
+++ test/CodeGenCXX/attr-x86-no_caller_saved_registers.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-win32 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple i386-pc-win32 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 %s -emit-llvm -o - | FileCheck %s
+
+// CHECK: foo{{[^#]*}}#[[ATTRS:[0-9]+]]
+__attribute__((no_caller_saved_registers)) void foo() {}
+namespace S {
+// CHECK: bar{{[^#]*}}#[[ATTRS]]
+__attribute__((no_caller_saved_registers)) void bar(int *a) { foo(); }
+}
+
+struct St {
+ static void baz(int *a) __attribute__((no_caller_saved_registers)) { S::bar(a); }
+};
+
+__attribute((no_caller_saved_registers)) void (*foobar)(void);
+
+// CHECK-LABEL: @main
+int main(int argc, char **argv) {
+ St::baz(&argc);
+ // CHECK: [[FOOBAR:%.+]] = load void ()*, void ()** @{{.*}}foobar{{.*}},
+ // CHECK-NEXT: call void [[FOOBAR]]() #[[ATTRS1:.+]]
+ foobar();
+ return 0;
+}
+
+// CHECK: baz{{[^#]*}}#[[ATTRS]]
+
+// CHECK: attributes #[[ATTRS]] = {
+// CHECK-SAME: "no_caller_saved_registers"
+// CHECK-SAME: }
+// CHECK: attributes #[[ATTRS1]] = { "no_caller_saved_registers" }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits