diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1b94a1d83fe0..6154f0e624a8 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -360,7 +360,9 @@ enum CallingConv { CC_X86StdCall, // __attribute__((stdcall)) CC_X86FastCall, // __attribute__((fastcall)) CC_X86ThisCall, // __attribute__((thiscall)) - CC_X86Pascal // __attribute__((pascal)) + CC_X86Pascal, // __attribute__((pascal)) + CC_AAPCS, // __attribute__((pcs("aapcs"))) + CC_AAPCS_VFP // __attribute__((pcs("aapcs-vfp"))) }; typedef std::pair SplitQualType; @@ -2864,9 +2866,10 @@ public: // Enumerated operand (string or keyword). attr_objc_gc, + attr_pcs, FirstEnumOperandKind = attr_objc_gc, - LastEnumOperandKind = attr_objc_gc, + LastEnumOperandKind = attr_pcs, // No operand. attr_noreturn, diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 308315cb32fe..51ef7df47dc8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -416,6 +416,13 @@ def Packed : InheritableAttr { let Spellings = ["packed"]; } +def Pcs : InheritableAttr { + let Spellings = ["pcs"]; + let Args = [EnumArgument<"PCS", "PCSType", + ["aapcs", "aapcs-vfp"], + ["AAPCS", "AAPCS_VFP"]>]; +} + def Pure : InheritableAttr { let Spellings = ["pure"]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9d0c2c4f2484..90945d7bd40e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1151,6 +1151,7 @@ def err_cconv_varargs : Error< def err_regparm_mismatch : Error<"function declared with with regparm(%0) " "attribute was previously declared " "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">; +def err_invalid_pcs : Error<"Invalid PCS type">; // Availability attribute def warn_availability_unknown_platform : Warning< diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index fa2f94379eb3..ed2295554e35 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -213,6 +213,7 @@ public: AT_ownership_takes, // Clang-specific. AT_packed, AT_pascal, + AT_pcs, // ARM specific AT_pure, AT_regparm, AT_section, diff --git a/clang/lib/AST/DumpXML.cpp b/clang/lib/AST/DumpXML.cpp index 93ae1e37c9ce..8355b2d901c3 100644 --- a/clang/lib/AST/DumpXML.cpp +++ b/clang/lib/AST/DumpXML.cpp @@ -911,6 +911,8 @@ struct XMLDumper : public XMLDeclVisitor, case CC_X86StdCall: return set("cc", "x86_stdcall"); case CC_X86ThisCall: return set("cc", "x86_thiscall"); case CC_X86Pascal: return set("cc", "x86_pascal"); + case CC_AAPCS: return set("cc", "aapcs"); + case CC_AAPCS_VFP: return set("cc", "aapcs_vfp"); } } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 5b4dc5a1913d..445d975625ba 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -874,6 +874,8 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(const FunctionType *T, if (CC == CC_Default) CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C; switch (CC) { + default: + assert(0 && "Unsupported CC for mangling"); case CC_Default: case CC_C: Out << 'A'; break; case CC_X86Pascal: Out << 'C'; break; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index b0726e427564..62e17f044887 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1169,6 +1169,8 @@ llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_X86FastCall: return "fastcall"; case CC_X86ThisCall: return "thiscall"; case CC_X86Pascal: return "pascal"; + case CC_AAPCS: return "aapcs"; + case CC_AAPCS_VFP: return "aapcs-vfp"; } llvm_unreachable("Invalid calling convention."); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index e287c46fa974..79fbcaa434e7 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -400,6 +400,12 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, case CC_X86Pascal: S += " __attribute__((pascal))"; break; + case CC_AAPCS: + S += " __attribute__((pcs(\"aapcs\")))"; + break; + case CC_AAPCS_VFP: + S += " __attribute__((pcs(\"aapcs-vfp\")))"; + break; } if (Info.getNoReturn()) S += " __attribute__((noreturn))"; @@ -851,6 +857,16 @@ void TypePrinter::printAttributed(const AttributedType *T, case AttributedType::attr_stdcall: S += "stdcall"; break; case AttributedType::attr_thiscall: S += "thiscall"; break; case AttributedType::attr_pascal: S += "pascal"; break; + case AttributedType::attr_pcs: { + S += "pcs("; + QualType t = T->getEquivalentType(); + while (!t->isFunctionType()) + t = t->getPointeeType(); + S += (t->getAs()->getCallConv() == CC_AAPCS ? + "\"aapcs\"" : "\"aapcs-vfp\""); + S += ")"; + break; + } } S += "))"; } diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0ba6789367f4..dfe9049add11 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -36,6 +36,8 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_X86StdCall: return llvm::CallingConv::X86_StdCall; case CC_X86FastCall: return llvm::CallingConv::X86_FastCall; case CC_X86ThisCall: return llvm::CallingConv::X86_ThisCall; + case CC_AAPCS: return llvm::CallingConv::ARM_AAPCS; + case CC_AAPCS_VFP: return llvm::CallingConv::ARM_AAPCS_VFP; // TODO: add support for CC_X86Pascal to llvm } } @@ -104,6 +106,9 @@ static CallingConv getCallingConventionForDecl(const Decl *D) { if (D->hasAttr()) return CC_X86Pascal; + if (PcsAttr *PCS = D->getAttr()) + return (PCS->getPCS() == PcsAttr::AAPCS ? CC_AAPCS : CC_AAPCS_VFP); + return CC_C; } diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 5db9a8e640ba..cab60ee5b58a 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -2271,27 +2271,33 @@ void ARMABIInfo::computeInfo(CGFunctionInfo &FI) const { it != ie; ++it) it->info = classifyArgumentType(it->type); - const llvm::Triple &Triple(getContext().Target.getTriple()); + // Always honor user-specified calling convention. + if (FI.getCallingConvention() != llvm::CallingConv::C) + return; + + // Calling convention as default by an ABI. llvm::CallingConv::ID DefaultCC; - if (Triple.getEnvironmentName() == "gnueabi" || - Triple.getEnvironmentName() == "eabi") + llvm::StringRef Env = getContext().Target.getTriple().getEnvironmentName(); + if (Env == "gnueabi" || Env == "eabi") DefaultCC = llvm::CallingConv::ARM_AAPCS; else DefaultCC = llvm::CallingConv::ARM_APCS; + // If user did not ask for specific calling convention explicitly (e.g. via + // pcs attribute), set effective calling convention if it's different than ABI + // default. switch (getABIKind()) { case APCS: if (DefaultCC != llvm::CallingConv::ARM_APCS) FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_APCS); break; - case AAPCS: if (DefaultCC != llvm::CallingConv::ARM_AAPCS) FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS); break; - case AAPCS_VFP: - FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS_VFP); + if (DefaultCC != llvm::CallingConv::ARM_AAPCS_VFP) + FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS_VFP); break; } } diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp index 792ab4ee8be1..983c0e0c289c 100644 --- a/clang/lib/Sema/AttributeList.cpp +++ b/clang/lib/Sema/AttributeList.cpp @@ -201,5 +201,6 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("nocommon", AT_nocommon) .Case("opencl_kernel_function", AT_opencl_kernel_function) .Case("uuid", AT_uuid) + .Case("pcs", AT_pcs) .Default(UnknownAttribute); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 6d1a4c86f282..4c72c8390c5a 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2427,6 +2427,30 @@ static void HandleCallConvAttr(Decl *d, const AttributeList &attr, Sema &S) { case AttributeList::AT_pascal: d->addAttr(::new (S.Context) PascalAttr(attr.getLoc(), S.Context)); return; + case AttributeList::AT_pcs: { + Expr *Arg = attr.getArg(0); + StringLiteral *Str = dyn_cast(Arg); + if (Str == 0 || Str->isWide()) { + S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + << "pcs" << 1; + attr.setInvalid(); + return; + } + + llvm::StringRef StrRef = Str->getString(); + PcsAttr::PCSType PCS; + if (StrRef == "aapcs") + PCS = PcsAttr::AAPCS; + else if (StrRef == "aapcs-vfp") + PCS = PcsAttr::AAPCS_VFP; + else { + S.Diag(attr.getLoc(), diag::err_invalid_pcs); + attr.setInvalid(); + return; + } + + d->addAttr(::new (S.Context) PcsAttr(attr.getLoc(), S.Context, PCS)); + } default: llvm_unreachable("unexpected attribute kind"); return; @@ -2442,19 +2466,41 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { if (attr.isInvalid()) return true; - if (attr.getNumArgs() != 0) { + if (attr.getNumArgs() != 0 && + !(attr.getKind() == AttributeList::AT_pcs && attr.getNumArgs() == 1)) { Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; attr.setInvalid(); return true; } - // TODO: diagnose uses of these conventions on the wrong target. + // TODO: diagnose uses of these conventions on the wrong target. Or, better + // move to TargetAttributesSema one day. switch (attr.getKind()) { case AttributeList::AT_cdecl: CC = CC_C; break; case AttributeList::AT_fastcall: CC = CC_X86FastCall; break; case AttributeList::AT_stdcall: CC = CC_X86StdCall; break; case AttributeList::AT_thiscall: CC = CC_X86ThisCall; break; case AttributeList::AT_pascal: CC = CC_X86Pascal; break; + case AttributeList::AT_pcs: { + Expr *Arg = attr.getArg(0); + StringLiteral *Str = dyn_cast(Arg); + if (Str == 0 || Str->isWide()) { + Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + << "pcs" << 1; + attr.setInvalid(); + return true; + } + + llvm::StringRef StrRef = Str->getString(); + if (StrRef == "aapcs") { + CC = CC_AAPCS; + break; + } else if (StrRef == "aapcs-vfp") { + CC = CC_AAPCS_VFP; + break; + } + // FALLS THROUGH + } default: llvm_unreachable("unexpected attribute kind"); return true; } @@ -2882,6 +2928,7 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_fastcall: case AttributeList::AT_thiscall: case AttributeList::AT_pascal: + case AttributeList::AT_pcs: HandleCallConvAttr(D, Attr, S); break; case AttributeList::AT_opencl_kernel_function: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 9c70d250ddc3..d3408fc6acdd 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -119,7 +119,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_stdcall: \ case AttributeList::AT_thiscall: \ case AttributeList::AT_pascal: \ - case AttributeList::AT_regparm + case AttributeList::AT_regparm: \ + case AttributeList::AT_pcs \ namespace { /// An object which stores processing state for the entire @@ -2244,6 +2245,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_thiscall; case AttributedType::attr_pascal: return AttributeList::AT_pascal; + case AttributedType::attr_pcs: + return AttributeList::AT_pcs; } llvm_unreachable("unexpected attribute kind!"); return AttributeList::Kind(); diff --git a/clang/test/CodeGen/arm-pcs.c b/clang/test/CodeGen/arm-pcs.c new file mode 100644 index 000000000000..d722f84cebd7 --- /dev/null +++ b/clang/test/CodeGen/arm-pcs.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm-none-linux-gnueabi -emit-llvm -w -o - < %s | FileCheck %s +typedef int __attribute__((pcs("aapcs"))) (*aapcs_fn)(void); +typedef int __attribute__((pcs("aapcs-vfp"))) (*aapcs_vfp_fn)(void); + +aapcs_fn bar; + +int foo(aapcs_vfp_fn baz) { +// CHECK: define i32 @foo +// CHECK: call arm_aapcscc +// CHECK: call arm_aapcs_vfpcc + return bar() + baz(); +}