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/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1669,6 +1669,13 @@ let Documentation = [AnyX86InterruptDocs]; } +def AnyX86NoCallerSavedRegisters : InheritableAttr, + TargetSpecificAttr { + let Spellings = [GNU<"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 @@ -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/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/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: 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/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()) RetAttrs.addAttribute(llvm::Attribute::NonNull); + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute("no_caller_saved_registers"); HasAnyX86InterruptAttr = TargetDecl->hasAttr(); HasOptnone = TargetDecl->hasAttr(); 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(); 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(S, D, Attr); break; 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/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(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 @@ -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: 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-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; +}