Add pnaclcall convention to Native Client targets.
Because PNaCl bitcode must be target-independent, it uses some different bitcode representations from other targets (e.g. byval and sret for structures). This means that without additional type information, it cannot meet some native ABI requirements for some targets (e.g. passing structures containing unions by value on x86-64). To allow generation of code which uses the correct native ABIs, we also support triples such as x86_64-nacl, which uses target-dependent IR (as opposed to le32-nacl, which uses byval and sret). To allow interoperation between the two types of code, this patch adds a calling convention attribute to be used in code compiled with the target-dependent triple, which will generate code using the le32-style bitcode. This calling convention does not need to be explicitly supported in the backend because it determines bitcode representation rather than native conventions (the backend just needs to undersand how to handle byval and sret for the Native Client OS). This patch implements __attribute__((pnaclcall)) to generate calls in bitcode according to the le32 bitcode conventions, an attribute which is accepted by any Native Client target, but issues a warning otherwise. llvm-svn: 166065
This commit is contained in:
parent
0e8bf254b5
commit
a202096dc0
|
@ -2597,6 +2597,7 @@ enum CXCallingConv {
|
||||||
CXCallingConv_X86Pascal = 5,
|
CXCallingConv_X86Pascal = 5,
|
||||||
CXCallingConv_AAPCS = 6,
|
CXCallingConv_AAPCS = 6,
|
||||||
CXCallingConv_AAPCS_VFP = 7,
|
CXCallingConv_AAPCS_VFP = 7,
|
||||||
|
CXCallingConv_PnaclCall = 8,
|
||||||
|
|
||||||
CXCallingConv_Invalid = 100,
|
CXCallingConv_Invalid = 100,
|
||||||
CXCallingConv_Unexposed = 200
|
CXCallingConv_Unexposed = 200
|
||||||
|
|
|
@ -1269,7 +1269,7 @@ protected:
|
||||||
|
|
||||||
/// Extra information which affects how the function is called, like
|
/// Extra information which affects how the function is called, like
|
||||||
/// regparm and the calling convention.
|
/// regparm and the calling convention.
|
||||||
unsigned ExtInfo : 8;
|
unsigned ExtInfo : 9;
|
||||||
|
|
||||||
/// TypeQuals - Used only by FunctionProtoType, put here to pack with the
|
/// TypeQuals - Used only by FunctionProtoType, put here to pack with the
|
||||||
/// other bitfields.
|
/// other bitfields.
|
||||||
|
@ -2598,19 +2598,19 @@ class FunctionType : public Type {
|
||||||
// * AST read and write
|
// * AST read and write
|
||||||
// * Codegen
|
// * Codegen
|
||||||
class ExtInfo {
|
class ExtInfo {
|
||||||
// Feel free to rearrange or add bits, but if you go over 8,
|
// Feel free to rearrange or add bits, but if you go over 9,
|
||||||
// you'll need to adjust both the Bits field below and
|
// you'll need to adjust both the Bits field below and
|
||||||
// Type::FunctionTypeBitfields.
|
// Type::FunctionTypeBitfields.
|
||||||
|
|
||||||
// | CC |noreturn|produces|regparm|
|
// | CC |noreturn|produces|regparm|
|
||||||
// |0 .. 2| 3 | 4 | 5 .. 7|
|
// |0 .. 3| 4 | 5 | 6 .. 8|
|
||||||
//
|
//
|
||||||
// regparm is either 0 (no regparm attribute) or the regparm value+1.
|
// regparm is either 0 (no regparm attribute) or the regparm value+1.
|
||||||
enum { CallConvMask = 0x7 };
|
enum { CallConvMask = 0xF };
|
||||||
enum { NoReturnMask = 0x8 };
|
enum { NoReturnMask = 0x10 };
|
||||||
enum { ProducesResultMask = 0x10 };
|
enum { ProducesResultMask = 0x20 };
|
||||||
enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask),
|
enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask),
|
||||||
RegParmOffset = 5 }; // Assumed to be the last field
|
RegParmOffset = 6 }; // Assumed to be the last field
|
||||||
|
|
||||||
uint16_t Bits;
|
uint16_t Bits;
|
||||||
|
|
||||||
|
@ -3322,7 +3322,8 @@ public:
|
||||||
attr_fastcall,
|
attr_fastcall,
|
||||||
attr_stdcall,
|
attr_stdcall,
|
||||||
attr_thiscall,
|
attr_thiscall,
|
||||||
attr_pascal
|
attr_pascal,
|
||||||
|
attr_pnaclcall
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -561,6 +561,10 @@ def Packed : InheritableAttr {
|
||||||
let Spellings = [GNU<"packed">];
|
let Spellings = [GNU<"packed">];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def PnaclCall : InheritableAttr {
|
||||||
|
let Spellings = [GNU<"pnaclcall">];
|
||||||
|
}
|
||||||
|
|
||||||
def Pcs : InheritableAttr {
|
def Pcs : InheritableAttr {
|
||||||
let Spellings = [GNU<"pcs">];
|
let Spellings = [GNU<"pcs">];
|
||||||
let Args = [EnumArgument<"PCS", "PCSType",
|
let Args = [EnumArgument<"PCS", "PCSType",
|
||||||
|
|
|
@ -176,17 +176,18 @@ namespace clang {
|
||||||
ICIS_ListInit ///< Direct list-initialization.
|
ICIS_ListInit ///< Direct list-initialization.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief CallingConv - Specifies the calling convention that a function uses.
|
/// \brief CallingConv - Specifies the calling convention that a function uses.
|
||||||
enum CallingConv {
|
enum CallingConv {
|
||||||
CC_Default,
|
CC_Default,
|
||||||
CC_C, // __attribute__((cdecl))
|
CC_C, // __attribute__((cdecl))
|
||||||
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, // __attribute__((pcs("aapcs")))
|
||||||
CC_AAPCS_VFP // __attribute__((pcs("aapcs-vfp")))
|
CC_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp")))
|
||||||
};
|
CC_PnaclCall // __attribute__((pnaclcall))
|
||||||
|
};
|
||||||
|
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
||||||
|
|
|
@ -920,6 +920,7 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
|
||||||
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: return set("cc", "aapcs");
|
||||||
case CC_AAPCS_VFP: return set("cc", "aapcs_vfp");
|
case CC_AAPCS_VFP: return set("cc", "aapcs_vfp");
|
||||||
|
case CC_PnaclCall: return set("cc", "pnaclcall");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1542,6 +1542,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
|
||||||
case CC_X86Pascal: return "pascal";
|
case CC_X86Pascal: return "pascal";
|
||||||
case CC_AAPCS: return "aapcs";
|
case CC_AAPCS: return "aapcs";
|
||||||
case CC_AAPCS_VFP: return "aapcs-vfp";
|
case CC_AAPCS_VFP: return "aapcs-vfp";
|
||||||
|
case CC_PnaclCall: return "pnaclcall";
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm_unreachable("Invalid calling convention.");
|
llvm_unreachable("Invalid calling convention.");
|
||||||
|
|
|
@ -647,6 +647,9 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
|
||||||
case CC_AAPCS_VFP:
|
case CC_AAPCS_VFP:
|
||||||
OS << " __attribute__((pcs(\"aapcs-vfp\")))";
|
OS << " __attribute__((pcs(\"aapcs-vfp\")))";
|
||||||
break;
|
break;
|
||||||
|
case CC_PnaclCall:
|
||||||
|
OS << " __attribute__((pnaclcall))";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (Info.getNoReturn())
|
if (Info.getNoReturn())
|
||||||
OS << " __attribute__((noreturn))";
|
OS << " __attribute__((noreturn))";
|
||||||
|
@ -1166,6 +1169,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
|
||||||
OS << ')';
|
OS << ')';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AttributedType::attr_pnaclcall: OS << "pnaclcall"; break;
|
||||||
}
|
}
|
||||||
OS << "))";
|
OS << "))";
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,6 +626,11 @@ class NaClTargetInfo : public OSTargetInfo<Target> {
|
||||||
this->DescriptionString = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"
|
this->DescriptionString = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"
|
||||||
"f32:32:32-f64:64:64-p:32:32:32-v128:32:32";
|
"f32:32:32-f64:64:64-p:32:32:32-v128:32:32";
|
||||||
}
|
}
|
||||||
|
virtual typename Target::CallingConvCheckResult checkCallingConvention(
|
||||||
|
CallingConv CC) const {
|
||||||
|
return CC == CC_PnaclCall ? Target::CCCR_OK :
|
||||||
|
Target::checkCallingConvention(CC);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace.
|
} // end anonymous namespace.
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,9 @@ static CallingConv getCallingConventionForDecl(const Decl *D) {
|
||||||
if (PcsAttr *PCS = D->getAttr<PcsAttr>())
|
if (PcsAttr *PCS = D->getAttr<PcsAttr>())
|
||||||
return (PCS->getPCS() == PcsAttr::AAPCS ? CC_AAPCS : CC_AAPCS_VFP);
|
return (PCS->getPCS() == PcsAttr::AAPCS ? CC_AAPCS : CC_AAPCS_VFP);
|
||||||
|
|
||||||
|
if (D->hasAttr<PnaclCallAttr>())
|
||||||
|
return CC_PnaclCall;
|
||||||
|
|
||||||
return CC_C;
|
return CC_C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2536,6 +2536,39 @@ llvm::Value *WinX86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
return AddrTyped;
|
return AddrTyped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NaClX86_64ABIInfo : public ABIInfo {
|
||||||
|
public:
|
||||||
|
NaClX86_64ABIInfo(CodeGen::CodeGenTypes &CGT, bool HasAVX)
|
||||||
|
: ABIInfo(CGT), PInfo(CGT), NInfo(CGT, HasAVX) {}
|
||||||
|
virtual void computeInfo(CGFunctionInfo &FI) const;
|
||||||
|
virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
|
CodeGenFunction &CGF) const;
|
||||||
|
private:
|
||||||
|
PNaClABIInfo PInfo; // Used for generating calls with pnaclcall callingconv.
|
||||||
|
X86_64ABIInfo NInfo; // Used for everything else.
|
||||||
|
};
|
||||||
|
|
||||||
|
class NaClX86_64TargetCodeGenInfo : public TargetCodeGenInfo {
|
||||||
|
public:
|
||||||
|
NaClX86_64TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, bool HasAVX)
|
||||||
|
: TargetCodeGenInfo(new NaClX86_64ABIInfo(CGT, HasAVX)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void NaClX86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
||||||
|
if (FI.getASTCallingConvention() == CC_PnaclCall)
|
||||||
|
PInfo.computeInfo(FI);
|
||||||
|
else
|
||||||
|
NInfo.computeInfo(FI);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *NaClX86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
|
CodeGenFunction &CGF) const {
|
||||||
|
// Always use the native convention; calling pnacl-style varargs functions
|
||||||
|
// is unuspported.
|
||||||
|
return NInfo.EmitVAArg(VAListAddr, Ty, CGF);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// PowerPC-32
|
// PowerPC-32
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -3268,6 +3301,38 @@ llvm::Value *ARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
return AddrTyped;
|
return AddrTyped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NaClARMABIInfo : public ABIInfo {
|
||||||
|
public:
|
||||||
|
NaClARMABIInfo(CodeGen::CodeGenTypes &CGT, ARMABIInfo::ABIKind Kind)
|
||||||
|
: ABIInfo(CGT), PInfo(CGT), NInfo(CGT, Kind) {}
|
||||||
|
virtual void computeInfo(CGFunctionInfo &FI) const;
|
||||||
|
virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
|
CodeGenFunction &CGF) const;
|
||||||
|
private:
|
||||||
|
PNaClABIInfo PInfo; // Used for generating calls with pnaclcall callingconv.
|
||||||
|
ARMABIInfo NInfo; // Used for everything else.
|
||||||
|
};
|
||||||
|
|
||||||
|
class NaClARMTargetCodeGenInfo : public TargetCodeGenInfo {
|
||||||
|
public:
|
||||||
|
NaClARMTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, ARMABIInfo::ABIKind Kind)
|
||||||
|
: TargetCodeGenInfo(new NaClARMABIInfo(CGT, Kind)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void NaClARMABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
||||||
|
if (FI.getASTCallingConvention() == CC_PnaclCall)
|
||||||
|
PInfo.computeInfo(FI);
|
||||||
|
else
|
||||||
|
static_cast<const ABIInfo&>(NInfo).computeInfo(FI);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *NaClARMABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty,
|
||||||
|
CodeGenFunction &CGF) const {
|
||||||
|
// Always use the native convention; calling pnacl-style varargs functions
|
||||||
|
// is unsupported.
|
||||||
|
return static_cast<const ABIInfo&>(NInfo).EmitVAArg(VAListAddr, Ty, CGF);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// NVPTX ABI Implementation
|
// NVPTX ABI Implementation
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -4077,7 +4142,14 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
|
||||||
else if (CodeGenOpts.FloatABI == "hard")
|
else if (CodeGenOpts.FloatABI == "hard")
|
||||||
Kind = ARMABIInfo::AAPCS_VFP;
|
Kind = ARMABIInfo::AAPCS_VFP;
|
||||||
|
|
||||||
return *(TheTargetCodeGenInfo = new ARMTargetCodeGenInfo(Types, Kind));
|
switch (Triple.getOS()) {
|
||||||
|
case llvm::Triple::NativeClient:
|
||||||
|
return *(TheTargetCodeGenInfo =
|
||||||
|
new NaClARMTargetCodeGenInfo(Types, Kind));
|
||||||
|
default:
|
||||||
|
return *(TheTargetCodeGenInfo =
|
||||||
|
new ARMTargetCodeGenInfo(Types, Kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case llvm::Triple::ppc:
|
case llvm::Triple::ppc:
|
||||||
|
@ -4143,6 +4215,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
|
||||||
case llvm::Triple::MinGW32:
|
case llvm::Triple::MinGW32:
|
||||||
case llvm::Triple::Cygwin:
|
case llvm::Triple::Cygwin:
|
||||||
return *(TheTargetCodeGenInfo = new WinX86_64TargetCodeGenInfo(Types));
|
return *(TheTargetCodeGenInfo = new WinX86_64TargetCodeGenInfo(Types));
|
||||||
|
case llvm::Triple::NativeClient:
|
||||||
|
return *(TheTargetCodeGenInfo = new NaClX86_64TargetCodeGenInfo(Types, HasAVX));
|
||||||
default:
|
default:
|
||||||
return *(TheTargetCodeGenInfo = new X86_64TargetCodeGenInfo(Types,
|
return *(TheTargetCodeGenInfo = new X86_64TargetCodeGenInfo(Types,
|
||||||
HasAVX));
|
HasAVX));
|
||||||
|
|
|
@ -3582,7 +3582,12 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
D->addAttr(::new (S.Context) PcsAttr(Attr.getRange(), S.Context, PCS));
|
D->addAttr(::new (S.Context) PcsAttr(Attr.getRange(), S.Context, PCS));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
case AttributeList::AT_PnaclCall:
|
||||||
|
D->addAttr(::new (S.Context) PnaclCallAttr(Attr.getRange(), S.Context));
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("unexpected attribute kind");
|
llvm_unreachable("unexpected attribute kind");
|
||||||
}
|
}
|
||||||
|
@ -3635,6 +3640,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) {
|
||||||
Diag(attr.getLoc(), diag::err_invalid_pcs);
|
Diag(attr.getLoc(), diag::err_invalid_pcs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case AttributeList::AT_PnaclCall: CC = CC_PnaclCall; break;
|
||||||
default: llvm_unreachable("unexpected attribute kind");
|
default: llvm_unreachable("unexpected attribute kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4396,6 +4402,7 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
|
||||||
case AttributeList::AT_ThisCall:
|
case AttributeList::AT_ThisCall:
|
||||||
case AttributeList::AT_Pascal:
|
case AttributeList::AT_Pascal:
|
||||||
case AttributeList::AT_Pcs:
|
case AttributeList::AT_Pcs:
|
||||||
|
case AttributeList::AT_PnaclCall:
|
||||||
handleCallConvAttr(S, D, Attr);
|
handleCallConvAttr(S, D, Attr);
|
||||||
break;
|
break;
|
||||||
case AttributeList::AT_OpenCLKernel:
|
case AttributeList::AT_OpenCLKernel:
|
||||||
|
|
|
@ -105,7 +105,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
|
||||||
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 \
|
case AttributeList::AT_Pcs: \
|
||||||
|
case AttributeList::AT_PnaclCall \
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/// An object which stores processing state for the entire
|
/// An object which stores processing state for the entire
|
||||||
|
@ -3004,6 +3005,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
|
||||||
return AttributeList::AT_Pascal;
|
return AttributeList::AT_Pascal;
|
||||||
case AttributedType::attr_pcs:
|
case AttributedType::attr_pcs:
|
||||||
return AttributeList::AT_Pcs;
|
return AttributeList::AT_Pcs;
|
||||||
|
case AttributedType::attr_pnaclcall:
|
||||||
|
return AttributeList::AT_PnaclCall;
|
||||||
}
|
}
|
||||||
llvm_unreachable("unexpected attribute kind!");
|
llvm_unreachable("unexpected attribute kind!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
// RUN: %clang_cc1 -triple armv7-unknown-nacl-gnueabi \
|
||||||
|
// RUN: -ffreestanding -mfloat-abi hard -target-cpu cortex-a8 \
|
||||||
|
// RUN: -emit-llvm -w -o - %s | FileCheck %s
|
||||||
|
|
||||||
|
// Test that functions with pnaclcall attribute generate portable bitcode
|
||||||
|
// like the le32 arch target
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
} s1;
|
||||||
|
// CHECK: define i32 @f48(%struct.s1* byval %s)
|
||||||
|
int __attribute__((pnaclcall)) f48(s1 s) { return s.a; }
|
||||||
|
|
||||||
|
// CHECK: define void @f49(%struct.s1* noalias sret %agg.result)
|
||||||
|
s1 __attribute__((pnaclcall)) f49() { s1 s; s.a = s.b = 1; return s; }
|
||||||
|
|
||||||
|
union simple_union {
|
||||||
|
int a;
|
||||||
|
char b;
|
||||||
|
};
|
||||||
|
// Unions should be passed as byval structs
|
||||||
|
// CHECK: define void @f50(%union.simple_union* byval %s)
|
||||||
|
void __attribute__((pnaclcall)) f50(union simple_union s) {}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int b4 : 4;
|
||||||
|
int b3 : 3;
|
||||||
|
int b8 : 8;
|
||||||
|
} bitfield1;
|
||||||
|
// Bitfields should be passed as byval structs
|
||||||
|
// CHECK: define void @f51(%struct.bitfield1* byval %bf1)
|
||||||
|
void __attribute__((pnaclcall)) f51(bitfield1 bf1) {}
|
|
@ -90,3 +90,31 @@ void f9122143()
|
||||||
{
|
{
|
||||||
func(ss);
|
func(ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
} s1;
|
||||||
|
// CHECK: define i32 @f48(%struct.s1* byval %s)
|
||||||
|
int __attribute__((pnaclcall)) f48(s1 s) { return s.a; }
|
||||||
|
|
||||||
|
// CHECK: define void @f49(%struct.s1* noalias sret %agg.result)
|
||||||
|
s1 __attribute__((pnaclcall)) f49() { s1 s; s.a = s.b = 1; return s; }
|
||||||
|
|
||||||
|
union simple_union {
|
||||||
|
int a;
|
||||||
|
char b;
|
||||||
|
};
|
||||||
|
// Unions should be passed as byval structs
|
||||||
|
// CHECK: define void @f50(%union.simple_union* byval %s)
|
||||||
|
void __attribute__((pnaclcall)) f50(union simple_union s) {}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int b4 : 4;
|
||||||
|
int b3 : 3;
|
||||||
|
int b8 : 8;
|
||||||
|
} bitfield1;
|
||||||
|
// Bitfields should be passed as byval structs
|
||||||
|
// CHECK: define void @f51(%struct.bitfield1* byval %bf1)
|
||||||
|
void __attribute__((pnaclcall)) f51(bitfield1 bf1) {}
|
||||||
|
|
|
@ -53,3 +53,4 @@ void __attribute__((cdecl)) ctest3() {}
|
||||||
typedef __attribute__((stdcall)) void (*PROC)();
|
typedef __attribute__((stdcall)) void (*PROC)();
|
||||||
PROC __attribute__((cdecl)) ctest4(const char *x) {}
|
PROC __attribute__((cdecl)) ctest4(const char *x) {}
|
||||||
|
|
||||||
|
void __attribute__((pnaclcall)) pnaclfunc(float *a) {} // expected-warning {{calling convention 'pnaclcall' ignored for this target}}
|
||||||
|
|
|
@ -464,6 +464,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) {
|
||||||
TCALLINGCONV(X86Pascal);
|
TCALLINGCONV(X86Pascal);
|
||||||
TCALLINGCONV(AAPCS);
|
TCALLINGCONV(AAPCS);
|
||||||
TCALLINGCONV(AAPCS_VFP);
|
TCALLINGCONV(AAPCS_VFP);
|
||||||
|
TCALLINGCONV(PnaclCall);
|
||||||
}
|
}
|
||||||
#undef TCALLINGCONV
|
#undef TCALLINGCONV
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue