Implement ARM pcs attribute. Basically it's another way of calling convention selection (AAPCS or

AAPCS+VFP), similar to fastcall / stdcall / whatevercall seen on x86.

In particular, all library functions should always be AAPCS regardless of floating point ABI used.

llvm-svn: 129534
This commit is contained in:
Anton Korobeynikov 2011-04-14 20:06:49 +00:00
parent fdc33cbecd
commit 231e875b5c
14 changed files with 119 additions and 11 deletions

View File

@ -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<const Type*, Qualifiers> 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,

View File

@ -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"];
}

View File

@ -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<

View File

@ -213,6 +213,7 @@ public:
AT_ownership_takes, // Clang-specific.
AT_packed,
AT_pascal,
AT_pcs, // ARM specific
AT_pure,
AT_regparm,
AT_section,

View File

@ -911,6 +911,8 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
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");
}
}

View File

@ -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;

View File

@ -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.");

View File

@ -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<FunctionType>()->getCallConv() == CC_AAPCS ?
"\"aapcs\"" : "\"aapcs-vfp\"");
S += ")";
break;
}
}
S += "))";
}

View File

@ -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<PascalAttr>())
return CC_X86Pascal;
if (PcsAttr *PCS = D->getAttr<PcsAttr>())
return (PCS->getPCS() == PcsAttr::AAPCS ? CC_AAPCS : CC_AAPCS_VFP);
return CC_C;
}

View File

@ -2271,26 +2271,32 @@ 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:
if (DefaultCC != llvm::CallingConv::ARM_AAPCS_VFP)
FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS_VFP);
break;
}

View File

@ -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);
}

View File

@ -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<StringLiteral>(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<StringLiteral>(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:

View File

@ -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();

View File

@ -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();
}