Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1382,7 +1382,7 @@ /// Extra information which affects how the function is called, like /// regparm and the calling convention. - unsigned ExtInfo : 10; + unsigned ExtInfo : 11; /// Used only by FunctionProtoType, put here to pack with the /// other bitfields. @@ -2927,19 +2927,23 @@ // * AST read and write // * Codegen class ExtInfo { - // Feel free to rearrange or add bits, but if you go over 10, + // Feel free to rearrange or add bits, but if you go over 11, // you'll need to adjust both the Bits field below and // Type::FunctionTypeBitfields. - // | CC |noreturn|produces|regparm| - // |0 .. 4| 5 | 6 | 7 .. 9| + // | CC |noreturn|produces|nocallersavedregs|regparm| + // |0 .. 4| 5 | 6 | 7 |8 .. 10| // // regparm is either 0 (no regparm attribute) or the regparm value+1. enum { CallConvMask = 0x1F }; enum { NoReturnMask = 0x20 }; enum { ProducesResultMask = 0x40 }; - enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask), - RegParmOffset = 7 }; // Assumed to be the last field + enum { NoCallerSavedRegsMask = 0x80 }; + enum { + RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask | + NoCallerSavedRegsMask), + RegParmOffset = 7 + }; // Assumed to be the last field uint16_t Bits; @@ -2950,13 +2954,13 @@ public: // 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) { - assert((!hasRegParm || regParm < 7) && "Invalid regparm value"); - Bits = ((unsigned) cc) | - (noReturn ? NoReturnMask : 0) | - (producesResult ? ProducesResultMask : 0) | - (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0); + ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, + 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); } // Constructor with all defaults. Use when for example creating a @@ -2969,6 +2973,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; @@ -3002,6 +3007,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/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1812,6 +1812,13 @@ let Documentation = [AnyX86InterruptDocs]; } +def AnyX86NoCallerSavedRegisters : InheritableAttr, + TargetSpecificAttr { + let Spellings = [GCC<"no_caller_saved_registers">]; + let Subjects = SubjectList<[FunctionLike], WarnDiag, "ExpectedFunction">; + let Documentation = [AnyX86NoCallerSavedRegistersDocs]; +} + def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr { let Spellings = [GNU<"force_align_arg_pointer">]; // Technically, this appertains to a FunctionDecl, but the target-specific Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2657,6 +2657,40 @@ }]; } +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 except for +registers used for passing parameters to the function or returning parameters +from the function. +The compiler saves and restores any modified registers that were not used for +passing or returning arguments to the function. + +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. + +Note that 'no_caller_saved_registers' attribute is not a calling convention. +In fact, it only overrides the decision of which registers should be saved by +the caller, but not how the parameters are passed from the caller to the callee. + +For example: + + .. code-block:: c + + __attribute__ ((no_caller_saved_registers, fastcall)) + void f (int arg1, int arg2) { + ... + } + + In this case parameters 'arg1' and 'arg2' will be passed in registers. + In this case, on 32-bit x86 targets, the function 'f' will use ECX and EDX as + register parameters. However, it will not assume any scratch registers and + should save and restore any modified registers except for ECX and EDX. + }]; +} + def SwiftCallDocs : Documentation { let Category = DocCatVariable; let Content = [{ Index: include/clang/CodeGen/CGFunctionInfo.h =================================================================== --- include/clang/CodeGen/CGFunctionInfo.h +++ include/clang/CodeGen/CGFunctionInfo.h @@ -461,7 +461,7 @@ unsigned EffectiveCallingConvention : 8; /// The clang::CallingConv that this was originally created with. - unsigned ASTCallingConvention : 8; + unsigned ASTCallingConvention : 6; /// Whether this is an instance method. unsigned InstanceMethod : 1; @@ -475,10 +475,16 @@ /// 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; + /// Unused bits, available for future use. + unsigned : 1; + RequiredArgs Required; /// The struct representing all arguments passed in memory. Only used when @@ -560,6 +566,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 { @@ -583,10 +592,9 @@ unsigned getRegParm() const { return RegParm; } FunctionType::ExtInfo getExtInfo() const { - return FunctionType::ExtInfo(isNoReturn(), - getHasRegParm(), getRegParm(), - getASTCallingConvention(), - isReturnsRetained()); + return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(), + getASTCallingConvention(), isReturnsRetained(), + isNoCallerSavedRegs()); } CanQualType getReturnType() const { return getArgsBuffer()[0].type; } @@ -623,6 +631,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 +657,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/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3150,6 +3150,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: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -7917,6 +7917,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 @@ -750,6 +750,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/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -788,6 +788,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(); @@ -1815,6 +1816,8 @@ RetAttrs.addAttribute(llvm::Attribute::NoAlias); if (TargetDecl->hasAttr()) RetAttrs.addAttribute(llvm::Attribute::NonNull); + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute("no_caller_saved_registers"); HasOptnone = TargetDecl->hasAttr(); if (auto *AllocSize = TargetDecl->getAttr()) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2957,6 +2957,12 @@ RequiresAdjustment = true; } + if (OldTypeInfo.getNoCallerSavedRegs() != + NewTypeInfo.getNoCallerSavedRegs()) { + NewTypeInfo = NewTypeInfo.withNoCallerSavedRegs(true); + RequiresAdjustment = true; + } + if (RequiresAdjustment) { const FunctionType *AdjustedType = New->getType()->getAs(); AdjustedType = Context.adjustFunctionType(AdjustedType, NewTypeInfo); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1927,25 +1927,43 @@ Attr.getAttributeSpellingListIndex())); } -static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { +static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (hasDeclarator(D)) return; - if (S.CheckNoReturnAttr(attr)) return; + if (S.CheckNoReturnAttr(Attr)) + return; if (!isa(D)) { - S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; return; } - D->addAttr(::new (S.Context) - NoReturnAttr(attr.getRange(), S.Context, - attr.getAttributeSpellingListIndex())); + D->addAttr(::new (S.Context) NoReturnAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } -bool Sema::CheckNoReturnAttr(const AttributeList &attr) { - if (!checkAttributeNumArgs(*this, attr, 0)) { - attr.setInvalid(); +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(); + return true; + } + + return false; +} + +bool Sema::CheckNoCallerSavedRegsAttr(const AttributeList &Attr) { + if (!checkAttributeNumArgs(*this, Attr, 0)) { + Attr.setInvalid(); return true; } @@ -6426,6 +6444,9 @@ case AttributeList::AT_TypeTagForDatatype: handleTypeTagForDatatypeAttr(S, D, Attr); break; + case AttributeList::AT_AnyX86NoCallerSavedRegisters: + handleNoCallerSavedRegsAttr(S, D, Attr); + break; case AttributeList::AT_RenderScriptKernel: handleSimpleAttribute(S, D, Attr); break; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -121,6 +121,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. @@ -6371,6 +6372,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/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -5755,13 +5755,13 @@ } 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); } @@ -5773,9 +5773,10 @@ /*hasregparm*/ Record[2], /*regparm*/ Record[3], static_cast(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/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -255,6 +255,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; @@ -839,6 +840,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: 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" } Index: test/SemaCXX/attr-non-x86-no_caller_saved_registers.cpp =================================================================== --- test/SemaCXX/attr-non-x86-no_caller_saved_registers.cpp +++ test/SemaCXX/attr-non-x86-no_caller_saved_registers.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c++11 -triple armv7-unknown-linux-gnueabi -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple mips-unknown-linux -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple mipsel-unknown-linux -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple thumbv7-unknown-linux-gnueabi -fsyntax-only -verify %s + +struct a { + int __attribute__((no_caller_saved_registers)) b; // expected-warning {{'no_caller_saved_registers' only applies to function types; type here is 'int'}} expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + static void foo(int *a) __attribute__((no_caller_saved_registers)) {} // expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} +}; + +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 *){} // expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + + [[gnu::no_caller_saved_registers]] void foo2(int *) {} // expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + +typedef __attribute__((no_caller_saved_registers)) void (*foo3)(int *); // expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + +typedef void (*foo5)(int *); + +int (*foo4)(double a, __attribute__((no_caller_saved_registers)) float b); // expected-warning {{'no_caller_saved_registers' only applies to function types; type here is 'float'}} expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + +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); + foo3 func = foo2; + func(&argc); + foo5 __attribute__((no_caller_saved_registers)) func2 = foo2; // expected-warning {{unknown attribute 'no_caller_saved_registers' ignored}} + return 0; +} 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,33 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple i386-unknown-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-pc-win32 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -triple i386-pc-win32 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -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 *){} + + [[gnu::no_caller_saved_registers]] void foo2(int *) {} + +typedef __attribute__((no_caller_saved_registers)) void (*foo3)(int *); + +typedef void (*foo5)(int *); + +int (*foo4)(double a, __attribute__((no_caller_saved_registers)) float b); // expected-warning {{'no_caller_saved_registers' only applies to function types; type here is 'float'}} expected-warning {{'no_caller_saved_registers' attribute only applies to functions}} + +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); + foo3 func = foo2; + func(&argc); + foo5 __attribute__((no_caller_saved_registers)) func2 = foo2; + return 0; +}