forked from OSchip/llvm-project
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:
parent
fdc33cbecd
commit
231e875b5c
|
@ -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,
|
||||||
|
|
|
@ -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"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.");
|
||||||
|
|
|
@ -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 += "))";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue