[clang] 6b104ea - Implement Lambda Conversion Operators for All CCs for MSVC.

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 5 07:26:02 PST 2020


Author: Erich Keane
Date: 2020-11-05T07:25:44-08:00
New Revision: 6b104ea4b4630c2fa841c6d5f7c7a69b08d31979

URL: https://github.com/llvm/llvm-project/commit/6b104ea4b4630c2fa841c6d5f7c7a69b08d31979
DIFF: https://github.com/llvm/llvm-project/commit/6b104ea4b4630c2fa841c6d5f7c7a69b08d31979.diff

LOG: Implement Lambda Conversion Operators for All CCs for MSVC.

As described here:
https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623

In order to allow Lambdas to be used with traditional Win32 APIs, they
emit a conversion function for (what Raymond Chen claims is all) a
number of the calling conventions.  Through experimentation, we
discovered that the list isn't quite 'all'.

This patch implements this by taking the list of conversions that MSVC
emits (across 'all' architectures, I don't see any CCs on ARM), then
emits them if they are supported by the current target.

However, we also add 3 other options (which may be duplicates):
free-function, member-function, and operator() calling conventions.  We
do this because we have an extension where we generate both free and
member for these cases so th at people specifying a calling convention
on the lambda will have the expected behavior when specifying one of
those two.

MSVC doesn't seem to permit specifying calling-convention on lambdas,
but we do, so we need to make sure those are emitted as well. We do this
so that clang-only conventions are supported if the user specifies them.

Differential Revision: https://reviews.llvm.org/D90634

Added: 
    

Modified: 
    clang/lib/Sema/SemaLambda.cpp
    clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 8242e89eb111..9e45709551c6 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1272,6 +1272,34 @@ static void repeatForLambdaConversionFunctionCallingConvs(
       CallOpProto.isVariadic(), /*IsCXXMethod=*/true);
   CallingConv CallOpCC = CallOpProto.getCallConv();
 
+  /// Implement emitting a version of the operator for many of the calling
+  /// conventions for MSVC, as described here:
+  /// https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623.
+  /// Experimentally, we determined that cdecl, stdcall, fastcall, and
+  /// vectorcall are generated by MSVC when it is supported by the target.
+  /// Additionally, we are ensuring that the default-free/default-member and
+  /// call-operator calling convention are generated as well.
+  /// NOTE: We intentionally generate a 'thiscall' on Win32 implicitly from the
+  /// 'member default', despite MSVC not doing so. We do this in order to ensure
+  /// that someone who intentionally places 'thiscall' on the lambda call
+  /// operator will still get that overload, since we don't have the a way of
+  /// detecting the attribute by the time we get here.
+  if (S.getLangOpts().MSVCCompat) {
+    CallingConv Convs[] = {
+        CC_C,        CC_X86StdCall, CC_X86FastCall, CC_X86VectorCall,
+        DefaultFree, DefaultMember, CallOpCC};
+    llvm::sort(Convs);
+    llvm::iterator_range<CallingConv *> Range(
+        std::begin(Convs), std::unique(std::begin(Convs), std::end(Convs)));
+    const TargetInfo &TI = S.getASTContext().getTargetInfo();
+
+    for (CallingConv C : Range) {
+      if (TI.checkCallingConvention(C) == TargetInfo::CCCR_OK)
+        F(C);
+    }
+    return;
+  }
+
   if (CallOpCC == DefaultMember && DefaultMember != DefaultFree) {
     F(DefaultFree);
     F(DefaultMember);
@@ -1475,9 +1503,6 @@ static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
 /// C++11 [expr.prim.lambda]p6. Note that in most cases, this should emit only a
 /// single pointer conversion. In the event that the default calling convention
 /// for free and member functions is 
diff erent, it will emit both conventions.
-/// FIXME: Implement emitting a version of the operator for EVERY calling
-/// convention for MSVC, as described here:
-/// https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623.
 static void addFunctionPointerConversions(Sema &S, SourceRange IntroducerRange,
                                           CXXRecordDecl *Class,
                                           CXXMethodDecl *CallOperator) {

diff  --git a/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp b/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp
index c2618925de15..cbb90e1f6078 100644
--- a/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp
+++ b/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,LIN64
 // RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -DCC="__attribute__((vectorcall))" | FileCheck %s --check-prefixes=CHECK,VECCALL
-// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-windows-pc -DWIN32 | FileCheck %s --check-prefixes=WIN32
+// RUN: %clang_cc1 -emit-llvm %s -o - -fms-compatibility -triple=i386-windows-pc -DWIN32 | FileCheck %s --check-prefixes=WIN32
 
 #ifndef CC
 #define CC
@@ -10,20 +10,36 @@ void usage() {
   auto lambda = [](int i, float f, double d) CC { return i + f + d; };
 
   double (*CC fp)(int, float, double) = lambda;
-  fp(0, 1.1, 2.2);
 #ifdef WIN32
   double (*__attribute__((thiscall)) fp2)(int, float, double) = lambda;
+  double (*__attribute__((stdcall)) fp3)(int, float, double) = lambda;
+  double (*__attribute__((fastcall)) fp4)(int, float, double) = lambda;
+  double (*__attribute__((vectorcall)) fp5)(int, float, double) = lambda;
+#endif // WIN32
+  fp(0, 1.1, 2.2);
+#ifdef WIN32
   fp2(0, 1.1, 2.2);
+  fp3(0, 1.1, 2.2);
+  fp4(0, 1.1, 2.2);
+  fp5(0, 1.1, 2.2);
 #endif // WIN32
+
+  auto x = +lambda;
 }
 
-// void usage function, calls convrsion operator.
+// void usage function, calls conversion operator.
 // LIN64: define void @_Z5usagev()
 // VECCALL: define void @_Z5usagev()
 // WIN32: define dso_local void @"?usage@@YAXXZ"()
 // CHECK: call double (i32, float, double)* @"_ZZ5usagevENK3$_0cvPFdifdEEv"
 // WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6A?A?<auto>@@HMN at ZXZ"
 // WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6E?A?<auto>@@HMN at ZXZ"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6G?A?<auto>@@HMN at ZXZ"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6I?A?<auto>@@HMN at ZXZ"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6Q?A?<auto>@@HMN at ZXZ"
+// Operator+ calls 'default' calling convention.
+// CHECK: call double (i32, float, double)* @"_ZZ5usagevENK3$_0cvPFdifdEEv"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6A?A?<auto>@@HMN at ZXZ"
 //
 // Conversion operator, returns __invoke.
 // CHECK: define internal double (i32, float, double)* @"_ZZ5usagevENK3$_0cvPFdifdEEv"
@@ -32,6 +48,12 @@ void usage() {
 // WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CA?A?<auto>@@HMN at Z"
 // WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6E?A?<auto>@@HMN at ZXZ"
 // WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CE?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6G?A?<auto>@@HMN at ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CG?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6I?A?<auto>@@HMN at ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CI?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6Q?A?<auto>@@HMN at ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CQ?A?<auto>@@HMN at Z"
 //
 // __invoke function, calls operator(). Win32 should call both.
 // LIN64: define internal double @"_ZZ5usagevEN3$_08__invokeEifd"
@@ -42,3 +64,9 @@ void usage() {
 // WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"
 // WIN32: define internal x86_thiscallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CE?A?<auto>@@HMN at Z"
 // WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_stdcallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CG?A?<auto>@@HMN at Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_fastcallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CI?A?<auto>@@HMN at Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_vectorcallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CQ?A?<auto>@@HMN at Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"


        


More information about the cfe-commits mailing list