]> granicus.if.org Git - clang/commitdiff
On 32 bit windows, mangle stdcall and fastcall decls in clang.
authorRafael Espindola <rafael.espindola@gmail.com>
Wed, 16 Oct 2013 01:40:34 +0000 (01:40 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Wed, 16 Oct 2013 01:40:34 +0000 (01:40 +0000)
This removes the dependency on the llvm mangler doing it for us. In isolation,
the benefit is that the testing of what mangling is applied is all in one place:
(C, C++) X (Itanium, Microsoft) are all handled by clang.

This also gives me hope that in the future the llvm mangler (and llvm-ar) will
not depend on TargetMachine.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192762 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Mangle.h
lib/AST/ItaniumMangle.cpp
lib/AST/Mangle.cpp
lib/AST/MicrosoftMangle.cpp
test/CodeGen/mangle-windows-rtd.c [new file with mode: 0644]
test/CodeGen/mangle-windows.c [new file with mode: 0644]
test/CodeGenCXX/mangle-windows.cpp [new file with mode: 0644]

index 76d099f938a7fc80817cc3eeb7df79f43499804a..d2e5325447436e9c48d721dda14a7fe7c8c9802d 100644 (file)
@@ -108,10 +108,12 @@ public:
   /// @name Mangler Entry Points
   /// @{
 
-  virtual bool shouldMangleDeclName(const NamedDecl *D) = 0;
+  bool shouldMangleDeclName(const NamedDecl *D);
+  virtual bool shouldMangleCXXName(const NamedDecl *D) = 0;
 
   // FIXME: consider replacing raw_ostream & with something like SmallString &.
-  virtual void mangleName(const NamedDecl *D, raw_ostream &) = 0;
+  void mangleName(const NamedDecl *D, raw_ostream &);
+  virtual void mangleCXXName(const NamedDecl *D, raw_ostream &) = 0;
   virtual void mangleThunk(const CXXMethodDecl *MD,
                           const ThunkInfo &Thunk,
                           raw_ostream &) = 0;
index fe8575d5d34b997f2ed00862068b02a118a24a5f..0d26c9e163ed0f34320d13a21dc3b1f8c35b6c64 100644 (file)
@@ -125,8 +125,8 @@ public:
   /// @name Mangler Entry Points
   /// @{
 
-  bool shouldMangleDeclName(const NamedDecl *D);
-  void mangleName(const NamedDecl *D, raw_ostream &);
+  bool shouldMangleCXXName(const NamedDecl *D);
+  void mangleCXXName(const NamedDecl *D, raw_ostream &);
   void mangleThunk(const CXXMethodDecl *MD,
                    const ThunkInfo &Thunk,
                    raw_ostream &);
@@ -384,16 +384,7 @@ private:
 
 }
 
-bool ItaniumMangleContextImpl::shouldMangleDeclName(const NamedDecl *D) {
-  // In C, functions with no attributes never need to be mangled. Fastpath them.
-  if (!getASTContext().getLangOpts().CPlusPlus && !D->hasAttrs())
-    return false;
-
-  // Any decl can be declared with __asm("foo") on it, and this takes precedence
-  // over all other naming in the .o file.
-  if (D->hasAttr<AsmLabelAttr>())
-    return true;
-
+bool ItaniumMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) {
   const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
   if (FD) {
     LanguageLinkage L = FD->getLanguageLinkage();
@@ -440,26 +431,6 @@ bool ItaniumMangleContextImpl::shouldMangleDeclName(const NamedDecl *D) {
 }
 
 void CXXNameMangler::mangle(const NamedDecl *D, StringRef Prefix) {
-  // Any decl can be declared with __asm("foo") on it, and this takes precedence
-  // over all other naming in the .o file.
-  if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
-    // If we have an asm name, then we use it as the mangling.
-
-    // Adding the prefix can cause problems when one file has a "foo" and
-    // another has a "\01foo". That is known to happen on ELF with the
-    // tricks normally used for producing aliases (PR9177). Fortunately the
-    // llvm mangler on ELF is a nop, so we can just avoid adding the \01
-    // marker.  We also avoid adding the marker if this is an alias for an
-    // LLVM intrinsic.
-    StringRef UserLabelPrefix =
-      getASTContext().getTargetInfo().getUserLabelPrefix();
-    if (!UserLabelPrefix.empty() && !ALA->getLabel().startswith("llvm."))
-      Out << '\01';  // LLVM IR Marker for __asm("foo")
-
-    Out << ALA->getLabel();
-    return;
-  }
-
   // <mangled-name> ::= _Z <encoding>
   //            ::= <data name>
   //            ::= <special-name>
@@ -3632,8 +3603,8 @@ void CXXNameMangler::addSubstitution(uintptr_t Ptr) {
 /// and this routine will return false. In this case, the caller should just
 /// emit the identifier of the declaration (\c D->getIdentifier()) as its
 /// name.
-void ItaniumMangleContextImpl::mangleName(const NamedDecl *D,
-                                          raw_ostream &Out) {
+void ItaniumMangleContextImpl::mangleCXXName(const NamedDecl *D,
+                                             raw_ostream &Out) {
   assert((isa<FunctionDecl>(D) || isa<VarDecl>(D)) &&
           "Invalid mangleName() call, argument is not a variable or function!");
   assert(!isa<CXXConstructorDecl>(D) && !isa<CXXDestructorDecl>(D) &&
index 07c84b2eb4c7459f77a9a475b496c0d3661a83e2..231ef036d829100c4f659110fc42158050a7fabc 100644 (file)
@@ -10,6 +10,7 @@
 // Implements generic name mangling support for blocks and Objective-C.
 //
 //===----------------------------------------------------------------------===//
+#include "clang/AST/Attr.h"
 #include "clang/AST/Mangle.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -19,6 +20,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/Basic/ABI.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
@@ -47,6 +49,130 @@ static void mangleFunctionBlock(MangleContext &Context,
 
 void MangleContext::anchor() { }
 
+enum StdOrFastCC {
+  SOF_OTHER,
+  SOF_FAST,
+  SOF_STD
+};
+
+static bool isExternC(const NamedDecl *ND) {
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND))
+    return FD->isExternC();
+  return cast<VarDecl>(ND)->isExternC();
+}
+
+static StdOrFastCC getStdOrFastCallMangling(const ASTContext &Context,
+                                            const NamedDecl *ND) {
+  const TargetInfo &TI = Context.getTargetInfo();
+  llvm::Triple Triple = TI.getTriple();
+  if (!Triple.isOSWindows() || Triple.getArch() != llvm::Triple::x86)
+    return SOF_OTHER;
+
+  if (Context.getLangOpts().CPlusPlus && !isExternC(ND) &&
+      TI.getCXXABI() == TargetCXXABI::Microsoft)
+    return SOF_OTHER;
+
+  const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
+  if (!FD)
+    return SOF_OTHER;
+  QualType T = FD->getType();
+
+  const FunctionType *FT = T->castAs<FunctionType>();
+
+  CallingConv CC = FT->getCallConv();
+  switch (CC) {
+  default:
+    return SOF_OTHER;
+  case CC_X86FastCall:
+    return SOF_FAST;
+  case CC_X86StdCall:
+    return SOF_STD;
+  }
+}
+
+bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
+  const ASTContext &ASTContext = getASTContext();
+
+  StdOrFastCC CC = getStdOrFastCallMangling(ASTContext, D);
+  if (CC != SOF_OTHER)
+    return true;
+
+  // In C, functions with no attributes never need to be mangled. Fastpath them.
+  if (!getASTContext().getLangOpts().CPlusPlus && !D->hasAttrs())
+    return false;
+
+  // Any decl can be declared with __asm("foo") on it, and this takes precedence
+  // over all other naming in the .o file.
+  if (D->hasAttr<AsmLabelAttr>())
+    return true;
+
+  return shouldMangleCXXName(D);
+}
+
+void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) {
+  // Any decl can be declared with __asm("foo") on it, and this takes precedence
+  // over all other naming in the .o file.
+  if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
+    // If we have an asm name, then we use it as the mangling.
+
+    // Adding the prefix can cause problems when one file has a "foo" and
+    // another has a "\01foo". That is known to happen on ELF with the
+    // tricks normally used for producing aliases (PR9177). Fortunately the
+    // llvm mangler on ELF is a nop, so we can just avoid adding the \01
+    // marker.  We also avoid adding the marker if this is an alias for an
+    // LLVM intrinsic.
+    StringRef UserLabelPrefix =
+        getASTContext().getTargetInfo().getUserLabelPrefix();
+    if (!UserLabelPrefix.empty() && !ALA->getLabel().startswith("llvm."))
+      Out << '\01'; // LLVM IR Marker for __asm("foo")
+
+    Out << ALA->getLabel();
+    return;
+  }
+
+  const ASTContext &ASTContext = getASTContext();
+  StdOrFastCC CC = getStdOrFastCallMangling(ASTContext, D);
+  bool MCXX = shouldMangleCXXName(D);
+  const TargetInfo &TI = Context.getTargetInfo();
+  if (CC == SOF_OTHER || (MCXX && TI.getCXXABI() == TargetCXXABI::Microsoft)) {
+    mangleCXXName(D, Out);
+    return;
+  }
+
+  Out << '\01';
+  if (CC == SOF_STD)
+    Out << '_';
+  else
+    Out << '@';
+
+  if (!MCXX)
+    Out << D->getIdentifier()->getName();
+  else
+    mangleCXXName(D, Out);
+
+  const FunctionDecl *FD = cast<FunctionDecl>(D);
+  const FunctionType *FT = FD->getType()->castAs<FunctionType>();
+  const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT);
+  Out << '@';
+  if (!Proto) {
+    Out << '0';
+    return;
+  }
+  assert(!Proto->isVariadic());
+  unsigned ArgWords = 0;
+  if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
+    if (!MD->isStatic())
+      ++ArgWords;
+  for (FunctionProtoType::arg_type_iterator Arg = Proto->arg_type_begin(),
+         ArgEnd = Proto->arg_type_end();
+       Arg != ArgEnd; ++Arg) {
+    QualType AT = *Arg;
+    // Size should be aligned to DWORD boundary
+    ArgWords += llvm::RoundUpToAlignment(ASTContext.getTypeSize(AT), 32) / 32;
+  }
+  Out << 4 * ArgWords;
+}
+
 void MangleContext::mangleGlobalBlock(const BlockDecl *BD,
                                       const NamedDecl *ID,
                                       raw_ostream &Out) {
index 2c37709bcf1797a02ea4fe598304d0878dbabb9a..7194536c5266f699b9d90e913211ec236ba2d6f7 100644 (file)
@@ -179,8 +179,8 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext {
 public:
   MicrosoftMangleContextImpl(ASTContext &Context, DiagnosticsEngine &Diags)
       : MicrosoftMangleContext(Context, Diags) {}
-  virtual bool shouldMangleDeclName(const NamedDecl *D);
-  virtual void mangleName(const NamedDecl *D, raw_ostream &Out);
+  virtual bool shouldMangleCXXName(const NamedDecl *D);
+  virtual void mangleCXXName(const NamedDecl *D, raw_ostream &Out);
   virtual void mangleThunk(const CXXMethodDecl *MD,
                            const ThunkInfo &Thunk,
                            raw_ostream &);
@@ -211,16 +211,7 @@ private:
 
 }
 
-bool MicrosoftMangleContextImpl::shouldMangleDeclName(const NamedDecl *D) {
-  // In C, functions with no attributes never need to be mangled. Fastpath them.
-  if (!getASTContext().getLangOpts().CPlusPlus && !D->hasAttrs())
-    return false;
-
-  // Any decl can be declared with __asm("foo") on it, and this takes precedence
-  // over all other naming in the .o file.
-  if (D->hasAttr<AsmLabelAttr>())
-    return true;
-
+bool MicrosoftMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) {
   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     LanguageLinkage L = FD->getLanguageLinkage();
     // Overloadable functions need mangling.
@@ -281,14 +272,6 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D,
   // default, we emit an asm marker at the start so we get the name right.
   // Callers can override this with a custom prefix.
 
-  // Any decl can be declared with __asm("foo") on it, and this takes precedence
-  // over all other naming in the .o file.
-  if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
-    // If we have an asm name, then we use it as the mangling.
-    Out << '\01' << ALA->getLabel();
-    return;
-  }
-
   // <mangled-name> ::= ? <name> <type-encoding>
   Out << Prefix;
   mangleName(D);
@@ -1845,8 +1828,8 @@ void MicrosoftCXXNameMangler::mangleType(const AtomicType *T,
     << Range;
 }
 
-void MicrosoftMangleContextImpl::mangleName(const NamedDecl *D,
-                                            raw_ostream &Out) {
+void MicrosoftMangleContextImpl::mangleCXXName(const NamedDecl *D,
+                                               raw_ostream &Out) {
   assert((isa<FunctionDecl>(D) || isa<VarDecl>(D)) &&
          "Invalid mangleName() call, argument is not a variable or function!");
   assert(!isa<CXXConstructorDecl>(D) && !isa<CXXDestructorDecl>(D) &&
diff --git a/test/CodeGen/mangle-windows-rtd.c b/test/CodeGen/mangle-windows-rtd.c
new file mode 100644 (file)
index 0000000..fc6f309
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -emit-llvm -mrtd %s -o - -triple=i386-mingw32 | FileCheck %s
+
+void f1(void) {}
+// CHECK: define x86_stdcallcc void @"\01_f1@0"
+
+void __stdcall f2(void) {}
+// CHECK: define x86_stdcallcc void @"\01_f2@0"
+
+void __fastcall f3(void) {}
+// CHECK: define x86_fastcallcc void @"\01@f3@0"
diff --git a/test/CodeGen/mangle-windows.c b/test/CodeGen/mangle-windows.c
new file mode 100644 (file)
index 0000000..6706492
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft \
+// RUN:     -triple=i386-pc-win32 | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-mingw32 | FileCheck %s
+
+void __stdcall f1(void) {}
+// CHECK: define x86_stdcallcc void @"\01_f1@0"
+
+void __fastcall f2(void) {}
+// CHECK: define x86_fastcallcc void @"\01@f2@0"
+
+void __stdcall f3() {}
+// CHECK: define x86_stdcallcc void @"\01_f3@0"
+
+void __fastcall f4(char a) {}
+// CHECK: define x86_fastcallcc void @"\01@f4@4"
+
+void __fastcall f5(short a) {}
+// CHECK: define x86_fastcallcc void @"\01@f5@4"
+
+void __fastcall f6(int a) {}
+// CHECK: define x86_fastcallcc void @"\01@f6@4"
+
+void __fastcall f7(long a) {}
+// CHECK: define x86_fastcallcc void @"\01@f7@4"
+
+void __fastcall f8(long long a) {}
+// CHECK: define x86_fastcallcc void @"\01@f8@8"
+
+void __fastcall f9(long long a, char b, char c, short d) {}
+// CHECK: define x86_fastcallcc void @"\01@f9@20"(i64 %a, i8 signext %b, i8
+// signext %c, i16 signext %d)
+
+void f12(void) {}
+// CHECK: define void @f12(
diff --git a/test/CodeGenCXX/mangle-windows.cpp b/test/CodeGenCXX/mangle-windows.cpp
new file mode 100644 (file)
index 0000000..c087616
--- /dev/null
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft \
+// RUN:     -triple=i386-pc-win32 | FileCheck --check-prefix=WIN %s
+//
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-mingw32 | \
+// RUN:     FileCheck --check-prefix=ITANIUM %s
+
+void __stdcall f1(void) {}
+// WIN: define x86_stdcallcc void @"\01?f1@@YGXXZ"
+// ITANIUM: define x86_stdcallcc void @"\01__Z2f1v@0"
+
+void __fastcall f2(void) {}
+// WIN: define x86_fastcallcc void @"\01?f2@@YIXXZ"
+// ITANIUM: define x86_fastcallcc void @"\01@_Z2f2v@0"
+
+extern "C" void __stdcall f3(void) {}
+// WIN: define x86_stdcallcc void @"\01_f3@0"
+// ITANIUM: define x86_stdcallcc void @"\01_f3@0"
+
+extern "C" void __fastcall f4(void) {}
+// WIN: define x86_fastcallcc void @"\01@f4@0"
+// ITANIUM: define x86_fastcallcc void @"\01@f4@0"
+
+struct Foo {
+  void __stdcall foo();
+  static void __stdcall bar();
+};
+
+void Foo::foo() {}
+// WIN: define x86_stdcallcc void @"\01?foo@Foo@@QAGXXZ"
+// ITANIUM: define x86_stdcallcc void @"\01__ZN3Foo3fooEv@4"
+
+void Foo::bar() {}
+// WIN: define x86_stdcallcc void @"\01?bar@Foo@@SGXXZ"
+// ITANIUM: define x86_stdcallcc void @"\01__ZN3Foo3barEv@0"
+
+// Mostly a test that we don't crash and that the names start with a \01.
+// gcc on mingw produces __Zpp@4
+// cl produces _++@4
+extern "C" void __stdcall operator++(Foo &x) {
+}
+// WIN: define x86_stdcallcc void @"\01??E@YGXAAUFoo@@@Z"
+// ITANIUM: define x86_stdcallcc void @"\01__ZppR3Foo@4"