r282880 - [CUDA] Make lambdas inherit __host__ and __device__ attributes from the scope in which they're created.
Justin Lebar via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 30 10:14:53 PDT 2016
Author: jlebar
Date: Fri Sep 30 12:14:53 2016
New Revision: 282880
URL: http://llvm.org/viewvc/llvm-project?rev=282880&view=rev
Log:
[CUDA] Make lambdas inherit __host__ and __device__ attributes from the scope in which they're created.
Summary: NVCC compat. Fixes bug 30567.
Reviewers: tra
Subscribers: cfe-commits, rnk
Differential Revision: https://reviews.llvm.org/D25105
Added:
cfe/trunk/test/SemaCUDA/implicit-device-lambda-hd.cu
cfe/trunk/test/SemaCUDA/implicit-device-lambda.cu
Modified:
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Sema/SemaCUDA.cpp
cfe/trunk/lib/Sema/SemaLambda.cpp
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=282880&r1=282879&r2=282880&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Sep 30 12:14:53 2016
@@ -9264,6 +9264,14 @@ public:
/// an error otherwise.
bool CheckCUDAVLA(SourceLocation Loc);
+ /// Set __device__ or __host__ __device__ attributes on the given lambda
+ /// operator() method.
+ ///
+ /// CUDA lambdas declared inside __device__ or __global__ functions inherit
+ /// the __device__ attribute. Similarly, lambdas inside __host__ __device__
+ /// functions become __host__ __device__ themselves.
+ void CUDASetLambdaAttrs(CXXMethodDecl *Method);
+
/// Finds a function in \p Matches with highest calling priority
/// from \p Caller context and erases all functions with lower
/// calling priority.
Modified: cfe/trunk/lib/Sema/SemaCUDA.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCUDA.cpp?rev=282880&r1=282879&r2=282880&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCUDA.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCUDA.cpp Fri Sep 30 12:14:53 2016
@@ -559,3 +559,22 @@ bool Sema::CheckCUDAVLA(SourceLocation L
}
return true;
}
+
+void Sema::CUDASetLambdaAttrs(CXXMethodDecl *Method) {
+ if (Method->hasAttr<CUDAHostAttr>() || Method->hasAttr<CUDADeviceAttr>())
+ return;
+ FunctionDecl *CurFn = dyn_cast<FunctionDecl>(CurContext);
+ if (!CurFn)
+ return;
+ CUDAFunctionTarget Target = IdentifyCUDATarget(CurFn);
+ if (Target == CFT_Global || Target == CFT_Device) {
+ Method->addAttr(CUDADeviceAttr::CreateImplicit(Context));
+ } else if (Target == CFT_HostDevice) {
+ Method->addAttr(CUDADeviceAttr::CreateImplicit(Context));
+ Method->addAttr(CUDAHostAttr::CreateImplicit(Context));
+ }
+
+ // TODO: nvcc doesn't allow you to specify __host__ or __device__ attributes
+ // on lambdas in all contexts -- we should emit a compatibility warning where
+ // we're more permissive.
+}
Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=282880&r1=282879&r2=282880&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Fri Sep 30 12:14:53 2016
@@ -886,7 +886,12 @@ void Sema::ActOnStartOfLambdaDefinition(
// Attributes on the lambda apply to the method.
ProcessDeclAttributes(CurScope, Method, ParamInfo);
-
+
+ // CUDA lambdas get implicit attributes based on the scope in which they're
+ // declared.
+ if (getLangOpts().CUDA)
+ CUDASetLambdaAttrs(Method);
+
// Introduce the function call operator as the current declaration context.
PushDeclContext(CurScope, Method);
Added: cfe/trunk/test/SemaCUDA/implicit-device-lambda-hd.cu
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCUDA/implicit-device-lambda-hd.cu?rev=282880&view=auto
==============================================================================
--- cfe/trunk/test/SemaCUDA/implicit-device-lambda-hd.cu (added)
+++ cfe/trunk/test/SemaCUDA/implicit-device-lambda-hd.cu Fri Sep 30 12:14:53 2016
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++11 -fcuda-is-device -verify -verify-ignore-unexpected=note \
+// RUN: -S -o /dev/null %s
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -verify-ignore-unexpected=note \
+// RUN: -DHOST -S -o /dev/null %s
+#include "Inputs/cuda.h"
+
+__host__ __device__ void hd_fn() {
+ auto f1 = [&] {};
+ f1(); // implicitly __host__ __device__
+
+ auto f2 = [&] __device__ {};
+ f2();
+#ifdef HOST
+ // expected-error at -2 {{reference to __device__ function}}
+#endif
+
+ auto f3 = [&] __host__ {};
+ f3();
+#ifndef HOST
+ // expected-error at -2 {{reference to __host__ function}}
+#endif
+
+ auto f4 = [&] __host__ __device__ {};
+ f4();
+}
+
+
Added: cfe/trunk/test/SemaCUDA/implicit-device-lambda.cu
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCUDA/implicit-device-lambda.cu?rev=282880&view=auto
==============================================================================
--- cfe/trunk/test/SemaCUDA/implicit-device-lambda.cu (added)
+++ cfe/trunk/test/SemaCUDA/implicit-device-lambda.cu Fri Sep 30 12:14:53 2016
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -std=c++11 -fcuda-is-device -verify -fsyntax-only -verify-ignore-unexpected=note %s
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -verify-ignore-unexpected=note %s
+
+#include "Inputs/cuda.h"
+
+__device__ void device_fn() {
+ auto f1 = [&] {};
+ f1(); // implicitly __device__
+
+ auto f2 = [&] __device__ {};
+ f2();
+
+ auto f3 = [&] __host__ {};
+ f3(); // expected-error {{no matching function}}
+
+ auto f4 = [&] __host__ __device__ {};
+ f4();
+
+ // Now do it all again with '()'s in the lambda declarations: This is a
+ // different parse path.
+ auto g1 = [&]() {};
+ g1(); // implicitly __device__
+
+ auto g2 = [&]() __device__ {};
+ g2();
+
+ auto g3 = [&]() __host__ {};
+ g3(); // expected-error {{no matching function}}
+
+ auto g4 = [&]() __host__ __device__ {};
+ g4();
+
+ // Once more, with the '()'s in a different place.
+ auto h1 = [&]() {};
+ h1(); // implicitly __device__
+
+ auto h2 = [&] __device__ () {};
+ h2();
+
+ auto h3 = [&] __host__ () {};
+ h3(); // expected-error {{no matching function}}
+
+ auto h4 = [&] __host__ __device__ () {};
+ h4();
+}
+
+// Behaves identically to device_fn.
+__global__ void kernel_fn() {
+ auto f1 = [&] {};
+ f1(); // implicitly __device__
+
+ auto f2 = [&] __device__ {};
+ f2();
+
+ auto f3 = [&] __host__ {};
+ f3(); // expected-error {{no matching function}}
+
+ auto f4 = [&] __host__ __device__ {};
+ f4();
+
+ // No need to re-test all the parser contortions we test in the device
+ // function.
+}
+
+__host__ void host_fn() {
+ auto f1 = [&] {};
+ f1(); // implicitly __host__ (i.e., no magic)
+
+ auto f2 = [&] __device__ {};
+ f2(); // expected-error {{no matching function}}
+
+ auto f3 = [&] __host__ {};
+ f3();
+
+ auto f4 = [&] __host__ __device__ {};
+ f4();
+}
+
+// The special treatment above only applies to lambdas.
+__device__ void foo() {
+ struct X {
+ void foo() {}
+ };
+ X x;
+ x.foo(); // expected-error {{reference to __host__ function 'foo' in __device__ function}}
+}
More information about the cfe-commits
mailing list