From 82bfa19fe3be324b13fdbcda46304b52c500f0d4 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Tue, 2 Oct 2012 14:26:08 +0000 Subject: [PATCH] Allowing individual targets to determine whether a given calling convention is allowed or ignored with warning. This allows for correct name mangling for x64 targets on Windows, which in turn allows for linking against the Win32 APIs. Fixes PR13782 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@165015 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 13 +------- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ include/clang/Basic/Specifiers.h | 13 ++++++++ include/clang/Basic/TargetInfo.h | 29 ++++++++++++++++ lib/Basic/Targets.cpp | 26 +++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 7 ++++ lib/Sema/SemaType.cpp | 6 ++-- test/CodeGen/microsoft-call-conv-x64.c | 39 ++++++++++++++++++++++ test/CodeGen/microsoft-call-conv.c | 2 +- test/CodeGen/stdcall-fastcall.c | 2 +- test/CodeGenCXX/mangle-ms.cpp | 2 ++ test/Sema/MicrosoftCompatibility-x64.c | 8 +++++ test/Sema/MicrosoftCompatibility.c | 8 ++++- test/Sema/callingconv.c | 7 ++-- test/Sema/stdcall-fastcall-x64.c | 20 +++++++++++ test/Sema/stdcall-fastcall.c | 1 - 16 files changed, 163 insertions(+), 22 deletions(-) create mode 100644 test/CodeGen/microsoft-call-conv-x64.c create mode 100644 test/Sema/MicrosoftCompatibility-x64.c create mode 100644 test/Sema/stdcall-fastcall-x64.c diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 6a35ac6b56..7b903b315c 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -20,6 +20,7 @@ #include "clang/Basic/Linkage.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/Visibility.h" +#include "clang/Basic/Specifiers.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateName.h" #include "llvm/Support/type_traits.h" @@ -490,18 +491,6 @@ private: static const uint32_t AddressSpaceShift = 8; }; -/// CallingConv - Specifies the calling convention that a function uses. -enum CallingConv { - CC_Default, - CC_C, // __attribute__((cdecl)) - CC_X86StdCall, // __attribute__((stdcall)) - CC_X86FastCall, // __attribute__((fastcall)) - CC_X86ThisCall, // __attribute__((thiscall)) - CC_X86Pascal, // __attribute__((pascal)) - CC_AAPCS, // __attribute__((pcs("aapcs"))) - CC_AAPCS_VFP // __attribute__((pcs("aapcs-vfp"))) -}; - /// A std::pair-like structure for storing a qualified type split /// into its local qualifiers and its locally-unqualified type. struct SplitQualType { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 648fe0b94a..811be4e1b6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1799,6 +1799,8 @@ def err_attribute_vecreturn_only_pod_record : Error< def err_cconv_change : Error< "function declared '%0' here was previously declared " "%select{'%2'|without calling convention}1">; +def warn_cconv_ignored : Warning< + "calling convention %0 ignored for this target">, InGroup; def err_cconv_knr : Error< "function with no prototype cannot use %0 calling convention">; def err_cconv_varargs : Error< diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 31b38d9ad9..a5ca521a20 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -175,6 +175,19 @@ namespace clang { ICIS_CopyInit, ///< Copy initialization. ICIS_ListInit ///< Direct list-initialization. }; + + /// \brief CallingConv - Specifies the calling convention that a function uses. + enum CallingConv { + CC_Default, + CC_C, // __attribute__((cdecl)) + CC_X86StdCall, // __attribute__((stdcall)) + CC_X86FastCall, // __attribute__((fastcall)) + CC_X86ThisCall, // __attribute__((thiscall)) + CC_X86Pascal, // __attribute__((pascal)) + CC_AAPCS, // __attribute__((pcs("aapcs"))) + CC_AAPCS_VFP // __attribute__((pcs("aapcs-vfp"))) + }; + } // end namespace clang #endif // LLVM_CLANG_BASIC_SPECIFIERS_H diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 54d49e6fa5..fac4667bb8 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -24,6 +24,7 @@ #include "llvm/Support/DataTypes.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/VersionTuple.h" +#include "clang/Basic/Specifiers.h" #include #include #include @@ -712,6 +713,34 @@ public: bool isBigEndian() const { return BigEndian; } + /// \brief Gets the default calling convention for the given target and + /// declaration context. + virtual CallingConv getDefaultCallingConv() const { + // Not all targets will specify an explicit calling convention that we can + // express. This will always do the right thing, even though it's not + // an explicit calling convention. + return CC_Default; + } + + enum CallingConvCheckResult { + CCCR_OK, + CCCR_Warning + }; + + /// \brief Determines whether a given calling convention is valid for the + /// target. A calling convention can either be accepted, produce a warning + /// and be substituted with the default calling convention, or (someday) + /// produce an error (such as using thiscall on a non-instance function). + virtual CallingConvCheckResult checkCallingConvention(CallingConv CC) const { + switch (CC) { + default: + return CCCR_Warning; + case CC_C: + case CC_Default: + return CCCR_OK; + } + } + protected: virtual uint64_t getPointerWidthV(unsigned AddrSpace) const { return PointerWidth; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 201913774c..6469c46574 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -1673,6 +1673,19 @@ public: } llvm_unreachable("Unhandled CPU kind"); } + + virtual CallingConvCheckResult checkCallingConvention(CallingConv CC) const { + // We accept all non-ARM calling conventions + return (CC == CC_X86ThisCall || + CC == CC_X86FastCall || + CC == CC_X86StdCall || + CC == CC_C || + CC == CC_X86Pascal) ? CCCR_OK : CCCR_Warning; + } + + virtual CallingConv getDefaultCallingConv() const { + return CC_C; + } }; void X86TargetInfo::getDefaultFeatures(llvm::StringMap &Features) const { @@ -2708,6 +2721,15 @@ public: if (RegNo == 1) return 1; return -1; } + + virtual CallingConvCheckResult checkCallingConvention(CallingConv CC) const { + return TargetInfo::checkCallingConvention(CC); + } + + virtual CallingConv getDefaultCallingConv() const { + return CC_Default; + } + }; } // end anonymous namespace @@ -3167,6 +3189,10 @@ public: // FIXME: Is this really right? return ""; } + + virtual CallingConvCheckResult checkCallingConvention(CallingConv CC) const { + return (CC == CC_AAPCS || CC == CC_AAPCS_VFP) ? CCCR_OK : CCCR_Warning; + } }; const char * const ARMTargetInfo::GCCRegNames[] = { diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index b09ec08651..5671a0fd21 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3638,6 +3638,13 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { default: llvm_unreachable("unexpected attribute kind"); } + const TargetInfo &TI = Context.getTargetInfo(); + TargetInfo::CallingConvCheckResult A = TI.checkCallingConvention(CC); + if (A == TargetInfo::CCCR_Warning) { + Diag(attr.getLoc(), diag::warn_cconv_ignored) << attr.getName(); + CC = TI.getDefaultCallingConv(); + } + return false; } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 243ae12d2a..a3af981c37 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -3886,14 +3886,14 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } + // Delay if the type didn't work out to a function. + if (!unwrapped.isFunctionType()) return false; + // Otherwise, a calling convention. CallingConv CC; if (S.CheckCallingConvAttr(attr, CC)) return true; - // Delay if the type didn't work out to a function. - if (!unwrapped.isFunctionType()) return false; - const FunctionType *fn = unwrapped.get(); CallingConv CCOld = fn->getCallConv(); if (S.Context.getCanonicalCallConv(CC) == diff --git a/test/CodeGen/microsoft-call-conv-x64.c b/test/CodeGen/microsoft-call-conv-x64.c new file mode 100644 index 0000000000..9a0aa59207 --- /dev/null +++ b/test/CodeGen/microsoft-call-conv-x64.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-pc-win32 -emit-llvm < %s | FileCheck %s + +void __fastcall f1(void); +void __stdcall f2(void); +void __fastcall f4(void) { +// CHECK: define void @f4() + f1(); +// CHECK: call void @f1() +} +void __stdcall f5(void) { +// CHECK: define void @f5() + f2(); +// CHECK: call void @f2() +} + +// PR5280 +void (__fastcall *pf1)(void) = f1; +void (__stdcall *pf2)(void) = f2; +void (__fastcall *pf4)(void) = f4; +void (__stdcall *pf5)(void) = f5; + +int main(void) { + f4(); f5(); + // CHECK: call void @f4() + // CHECK: call void @f5() + pf1(); pf2(); pf4(); pf5(); + // CHECK: call void %{{.*}}() + // CHECK: call void %{{.*}}() + // CHECK: call void %{{.*}}() + // CHECK: call void %{{.*}}() + return 0; +} + +// PR7117 +void __stdcall f7(foo) int foo; {} +void f8(void) { + f7(0); + // CHECK: call void @f7(i32 0) +} diff --git a/test/CodeGen/microsoft-call-conv.c b/test/CodeGen/microsoft-call-conv.c index 390c3be05e..64d10fb4f4 100644 --- a/test/CodeGen/microsoft-call-conv.c +++ b/test/CodeGen/microsoft-call-conv.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-pc-linux -emit-llvm < %s | FileCheck %s void __fastcall f1(void); void __stdcall f2(void); diff --git a/test/CodeGen/stdcall-fastcall.c b/test/CodeGen/stdcall-fastcall.c index 3de7b6727b..2832e91123 100644 --- a/test/CodeGen/stdcall-fastcall.c +++ b/test/CodeGen/stdcall-fastcall.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm < %s | FileCheck %s void __attribute__((fastcall)) f1(void); void __attribute__((stdcall)) f2(void); diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp index 6964581f92..0edb4b4339 100644 --- a/test/CodeGenCXX/mangle-ms.cpp +++ b/test/CodeGenCXX/mangle-ms.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fms-extensions -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fms-compatibility -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s // CHECK: @"\01?a@@3HA" // CHECK: @"\01?b@N@@3HA" @@ -109,6 +110,7 @@ bool __fastcall beta(long long a, wchar_t b) throw(signed char, unsigned char) { } // CHECK: @"\01?alpha@@YGXMN@Z" +// X64: @"\01?alpha@@YAXMN@Z" // Make sure tag-type mangling works. void gamma(class foo, struct bar, union baz, enum quux) {} diff --git a/test/Sema/MicrosoftCompatibility-x64.c b/test/Sema/MicrosoftCompatibility-x64.c new file mode 100644 index 0000000000..bf595af699 --- /dev/null +++ b/test/Sema/MicrosoftCompatibility-x64.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -fsyntax-only -Wno-unused-value -Wmicrosoft -verify -fms-compatibility -triple x86_64-pc-win32 +int __stdcall f(void); /* expected-warning {{calling convention '__stdcall' ignored for this target}} */ + +/* This should compile without warning because __stdcall is treated +as __cdecl in MS compatibility mode for x64 compiles*/ +int __cdecl f(void) { + return 0; +} diff --git a/test/Sema/MicrosoftCompatibility.c b/test/Sema/MicrosoftCompatibility.c index 6b137a60c4..be13949ddd 100644 --- a/test/Sema/MicrosoftCompatibility.c +++ b/test/Sema/MicrosoftCompatibility.c @@ -18,4 +18,10 @@ __declspec(noreturn) void f6( void ) { __declspec(align(32768)) struct S1 { int a; } s; /* expected-error {{requested alignment must be 8192 bytes or smaller}} */ struct __declspec(aligned) S2 {}; /* expected-warning {{unknown __declspec attribute 'aligned' ignored}} */ -struct __declspec(appdomain) S3 {}; /* expected-warning {{__declspec attribute 'appdomain' is not supported}} */ \ No newline at end of file +struct __declspec(appdomain) S3 {}; /* expected-warning {{__declspec attribute 'appdomain' is not supported}} */ + +int __stdcall f(void); /* expected-note {{previous declaration is here}} */ + +int __cdecl f(void) { /* expected-error {{function declared 'cdecl' here was previously declared 'stdcall'}} */ + return 0; +} diff --git a/test/Sema/callingconv.c b/test/Sema/callingconv.c index 6c844a3733..76b5f2d0b9 100644 --- a/test/Sema/callingconv.c +++ b/test/Sema/callingconv.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify +// RUN: %clang_cc1 %s -fsyntax-only -triple i386-unknown-unknown -verify void __attribute__((fastcall)) foo(float *a) { } @@ -40,8 +40,9 @@ int __attribute__((pcs("aapcs", "aapcs"))) pcs1(void); // expected-error {{attri int __attribute__((pcs())) pcs2(void); // expected-error {{attribute takes one argument}} int __attribute__((pcs(pcs1))) pcs3(void); // expected-error {{attribute takes one argument}} int __attribute__((pcs(0))) pcs4(void); // expected-error {{'pcs' attribute requires parameter 1 to be a string}} -int __attribute__((pcs("aapcs"))) pcs5(void); // no-error -int __attribute__((pcs("aapcs-vfp"))) pcs6(void); // no-error +/* These are ignored because the target is i386 and not ARM */ +int __attribute__((pcs("aapcs"))) pcs5(void); // expected-warning {{calling convention 'pcs' ignored for this target}} +int __attribute__((pcs("aapcs-vfp"))) pcs6(void); // expected-warning {{calling convention 'pcs' ignored for this target}} int __attribute__((pcs("foo"))) pcs7(void); // expected-error {{Invalid PCS type}} // PR6361 diff --git a/test/Sema/stdcall-fastcall-x64.c b/test/Sema/stdcall-fastcall-x64.c new file mode 100644 index 0000000000..ca1995e401 --- /dev/null +++ b/test/Sema/stdcall-fastcall-x64.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-pc-linux-gnu %s + +// CC qualifier can be applied only to functions +int __attribute__((stdcall)) var1; // expected-warning{{'stdcall' only applies to function types; type here is 'int'}} +int __attribute__((fastcall)) var2; // expected-warning{{'fastcall' only applies to function types; type here is 'int'}} + +// Different CC qualifiers are not compatible +void __attribute__((stdcall, fastcall)) foo3(void); // expected-warning{{calling convention 'stdcall' ignored for this target}} expected-warning {{calling convention 'fastcall' ignored for this target}} +void __attribute__((stdcall)) foo4(); // expected-warning{{calling convention 'stdcall' ignored for this target}} +void __attribute__((fastcall)) foo4(void); // expected-warning {{calling convention 'fastcall' ignored for this target}} + +// rdar://8876096 +void rdar8876096foo1(int i, int j) __attribute__((fastcall, cdecl)); // expected-warning{{calling convention 'fastcall' ignored for this target}} +void rdar8876096foo2(int i, int j) __attribute__((fastcall, stdcall)); // expected-warning{{calling convention 'stdcall' ignored for this target}} expected-warning {{calling convention 'fastcall' ignored for this target}} +void rdar8876096foo3(int i, int j) __attribute__((fastcall, regparm(2))); // expected-warning {{calling convention 'fastcall' ignored for this target}} +void rdar8876096foo4(int i, int j) __attribute__((stdcall, cdecl)); // expected-warning{{calling convention 'stdcall' ignored for this target}} +void rdar8876096foo5(int i, int j) __attribute__((stdcall, fastcall)); // expected-warning{{calling convention 'stdcall' ignored for this target}} expected-warning {{calling convention 'fastcall' ignored for this target}} +void rdar8876096foo6(int i, int j) __attribute__((cdecl, fastcall)); // expected-warning {{calling convention 'fastcall' ignored for this target}} +void rdar8876096foo7(int i, int j) __attribute__((cdecl, stdcall)); // expected-warning{{calling convention 'stdcall' ignored for this target}} +void rdar8876096foo8(int i, int j) __attribute__((regparm(2), fastcall)); // expected-warning {{calling convention 'fastcall' ignored for this target}} diff --git a/test/Sema/stdcall-fastcall.c b/test/Sema/stdcall-fastcall.c index eeacf94eb4..dea1fc5e7a 100644 --- a/test/Sema/stdcall-fastcall.c +++ b/test/Sema/stdcall-fastcall.c @@ -1,4 +1,3 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-pc-linux-gnu %s // RUN: %clang_cc1 -fsyntax-only -verify -triple i686-apple-darwin10 %s // CC qualifier can be applied only to functions -- 2.40.0