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_X86StdCall, // __attribute__((stdcall))
CC_X86FastCall, // __attribute__((fastcall)) CC_X86FastCall, // __attribute__((fastcall))
CC_X86ThisCall, // __attribute__((thiscall)) 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; typedef std::pair<const Type*, Qualifiers> SplitQualType;
@ -2864,9 +2866,10 @@ public:
// Enumerated operand (string or keyword). // Enumerated operand (string or keyword).
attr_objc_gc, attr_objc_gc,
attr_pcs,
FirstEnumOperandKind = attr_objc_gc, FirstEnumOperandKind = attr_objc_gc,
LastEnumOperandKind = attr_objc_gc, LastEnumOperandKind = attr_pcs,
// No operand. // No operand.
attr_noreturn, attr_noreturn,

View File

@ -416,6 +416,13 @@ def Packed : InheritableAttr {
let Spellings = ["packed"]; let Spellings = ["packed"];
} }
def Pcs : InheritableAttr {
let Spellings = ["pcs"];
let Args = [EnumArgument<"PCS", "PCSType",
["aapcs", "aapcs-vfp"],
["AAPCS", "AAPCS_VFP"]>];
}
def Pure : InheritableAttr { def Pure : InheritableAttr {
let Spellings = ["pure"]; 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) " def err_regparm_mismatch : Error<"function declared with with regparm(%0) "
"attribute was previously declared " "attribute was previously declared "
"%plural{0:without the regparm|:with the regparm(%1)}1 attribute">; "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">;
def err_invalid_pcs : Error<"Invalid PCS type">;
// Availability attribute // Availability attribute
def warn_availability_unknown_platform : Warning< def warn_availability_unknown_platform : Warning<

View File

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

View File

@ -911,6 +911,8 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
case CC_X86StdCall: return set("cc", "x86_stdcall"); case CC_X86StdCall: return set("cc", "x86_stdcall");
case CC_X86ThisCall: return set("cc", "x86_thiscall"); case CC_X86ThisCall: return set("cc", "x86_thiscall");
case CC_X86Pascal: return set("cc", "x86_pascal"); 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) if (CC == CC_Default)
CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C; CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C;
switch (CC) { switch (CC) {
default:
assert(0 && "Unsupported CC for mangling");
case CC_Default: case CC_Default:
case CC_C: Out << 'A'; break; case CC_C: Out << 'A'; break;
case CC_X86Pascal: Out << 'C'; 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_X86FastCall: return "fastcall";
case CC_X86ThisCall: return "thiscall"; case CC_X86ThisCall: return "thiscall";
case CC_X86Pascal: return "pascal"; case CC_X86Pascal: return "pascal";
case CC_AAPCS: return "aapcs";
case CC_AAPCS_VFP: return "aapcs-vfp";
} }
llvm_unreachable("Invalid calling convention."); llvm_unreachable("Invalid calling convention.");

View File

@ -400,6 +400,12 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T,
case CC_X86Pascal: case CC_X86Pascal:
S += " __attribute__((pascal))"; S += " __attribute__((pascal))";
break; break;
case CC_AAPCS:
S += " __attribute__((pcs(\"aapcs\")))";
break;
case CC_AAPCS_VFP:
S += " __attribute__((pcs(\"aapcs-vfp\")))";
break;
} }
if (Info.getNoReturn()) if (Info.getNoReturn())
S += " __attribute__((noreturn))"; S += " __attribute__((noreturn))";
@ -851,6 +857,16 @@ void TypePrinter::printAttributed(const AttributedType *T,
case AttributedType::attr_stdcall: S += "stdcall"; break; case AttributedType::attr_stdcall: S += "stdcall"; break;
case AttributedType::attr_thiscall: S += "thiscall"; break; case AttributedType::attr_thiscall: S += "thiscall"; break;
case AttributedType::attr_pascal: S += "pascal"; 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 += "))"; S += "))";
} }

View File

@ -36,6 +36,8 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) {
case CC_X86StdCall: return llvm::CallingConv::X86_StdCall; case CC_X86StdCall: return llvm::CallingConv::X86_StdCall;
case CC_X86FastCall: return llvm::CallingConv::X86_FastCall; case CC_X86FastCall: return llvm::CallingConv::X86_FastCall;
case CC_X86ThisCall: return llvm::CallingConv::X86_ThisCall; 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 // TODO: add support for CC_X86Pascal to llvm
} }
} }
@ -104,6 +106,9 @@ static CallingConv getCallingConventionForDecl(const Decl *D) {
if (D->hasAttr<PascalAttr>()) if (D->hasAttr<PascalAttr>())
return CC_X86Pascal; return CC_X86Pascal;
if (PcsAttr *PCS = D->getAttr<PcsAttr>())
return (PCS->getPCS() == PcsAttr::AAPCS ? CC_AAPCS : CC_AAPCS_VFP);
return CC_C; return CC_C;
} }

View File

@ -2271,27 +2271,33 @@ void ARMABIInfo::computeInfo(CGFunctionInfo &FI) const {
it != ie; ++it) it != ie; ++it)
it->info = classifyArgumentType(it->type); 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; llvm::CallingConv::ID DefaultCC;
if (Triple.getEnvironmentName() == "gnueabi" || llvm::StringRef Env = getContext().Target.getTriple().getEnvironmentName();
Triple.getEnvironmentName() == "eabi") if (Env == "gnueabi" || Env == "eabi")
DefaultCC = llvm::CallingConv::ARM_AAPCS; DefaultCC = llvm::CallingConv::ARM_AAPCS;
else else
DefaultCC = llvm::CallingConv::ARM_APCS; 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()) { switch (getABIKind()) {
case APCS: case APCS:
if (DefaultCC != llvm::CallingConv::ARM_APCS) if (DefaultCC != llvm::CallingConv::ARM_APCS)
FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_APCS); FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_APCS);
break; break;
case AAPCS: case AAPCS:
if (DefaultCC != llvm::CallingConv::ARM_AAPCS) if (DefaultCC != llvm::CallingConv::ARM_AAPCS)
FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS); FI.setEffectiveCallingConvention(llvm::CallingConv::ARM_AAPCS);
break; break;
case AAPCS_VFP: 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; break;
} }
} }

View File

@ -201,5 +201,6 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
.Case("nocommon", AT_nocommon) .Case("nocommon", AT_nocommon)
.Case("opencl_kernel_function", AT_opencl_kernel_function) .Case("opencl_kernel_function", AT_opencl_kernel_function)
.Case("uuid", AT_uuid) .Case("uuid", AT_uuid)
.Case("pcs", AT_pcs)
.Default(UnknownAttribute); .Default(UnknownAttribute);
} }

View File

@ -2427,6 +2427,30 @@ static void HandleCallConvAttr(Decl *d, const AttributeList &attr, Sema &S) {
case AttributeList::AT_pascal: case AttributeList::AT_pascal:
d->addAttr(::new (S.Context) PascalAttr(attr.getLoc(), S.Context)); d->addAttr(::new (S.Context) PascalAttr(attr.getLoc(), S.Context));
return; 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: default:
llvm_unreachable("unexpected attribute kind"); llvm_unreachable("unexpected attribute kind");
return; return;
@ -2442,19 +2466,41 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) {
if (attr.isInvalid()) if (attr.isInvalid())
return true; 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; Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0;
attr.setInvalid(); attr.setInvalid();
return true; 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()) { switch (attr.getKind()) {
case AttributeList::AT_cdecl: CC = CC_C; break; case AttributeList::AT_cdecl: CC = CC_C; break;
case AttributeList::AT_fastcall: CC = CC_X86FastCall; break; case AttributeList::AT_fastcall: CC = CC_X86FastCall; break;
case AttributeList::AT_stdcall: CC = CC_X86StdCall; break; case AttributeList::AT_stdcall: CC = CC_X86StdCall; break;
case AttributeList::AT_thiscall: CC = CC_X86ThisCall; break; case AttributeList::AT_thiscall: CC = CC_X86ThisCall; break;
case AttributeList::AT_pascal: CC = CC_X86Pascal; 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; 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_fastcall:
case AttributeList::AT_thiscall: case AttributeList::AT_thiscall:
case AttributeList::AT_pascal: case AttributeList::AT_pascal:
case AttributeList::AT_pcs:
HandleCallConvAttr(D, Attr, S); HandleCallConvAttr(D, Attr, S);
break; break;
case AttributeList::AT_opencl_kernel_function: 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_stdcall: \
case AttributeList::AT_thiscall: \ case AttributeList::AT_thiscall: \
case AttributeList::AT_pascal: \ case AttributeList::AT_pascal: \
case AttributeList::AT_regparm case AttributeList::AT_regparm: \
case AttributeList::AT_pcs \
namespace { namespace {
/// An object which stores processing state for the entire /// An object which stores processing state for the entire
@ -2244,6 +2245,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
return AttributeList::AT_thiscall; return AttributeList::AT_thiscall;
case AttributedType::attr_pascal: case AttributedType::attr_pascal:
return AttributeList::AT_pascal; return AttributeList::AT_pascal;
case AttributedType::attr_pcs:
return AttributeList::AT_pcs;
} }
llvm_unreachable("unexpected attribute kind!"); llvm_unreachable("unexpected attribute kind!");
return AttributeList::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();
}