Skip to content

Commit 7ca116c

Browse files
author
Justin Lebar
committedSep 30, 2016
[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 llvm-svn: 282880
1 parent 0fad0ba commit 7ca116c

File tree

5 files changed

+146
-1
lines changed

5 files changed

+146
-1
lines changed
 

‎clang/include/clang/Sema/Sema.h

+8
Original file line numberDiff line numberDiff line change
@@ -9264,6 +9264,14 @@ class Sema {
92649264
/// an error otherwise.
92659265
bool CheckCUDAVLA(SourceLocation Loc);
92669266

9267+
/// Set __device__ or __host__ __device__ attributes on the given lambda
9268+
/// operator() method.
9269+
///
9270+
/// CUDA lambdas declared inside __device__ or __global__ functions inherit
9271+
/// the __device__ attribute. Similarly, lambdas inside __host__ __device__
9272+
/// functions become __host__ __device__ themselves.
9273+
void CUDASetLambdaAttrs(CXXMethodDecl *Method);
9274+
92679275
/// Finds a function in \p Matches with highest calling priority
92689276
/// from \p Caller context and erases all functions with lower
92699277
/// calling priority.

‎clang/lib/Sema/SemaCUDA.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,22 @@ bool Sema::CheckCUDAVLA(SourceLocation Loc) {
559559
}
560560
return true;
561561
}
562+
563+
void Sema::CUDASetLambdaAttrs(CXXMethodDecl *Method) {
564+
if (Method->hasAttr<CUDAHostAttr>() || Method->hasAttr<CUDADeviceAttr>())
565+
return;
566+
FunctionDecl *CurFn = dyn_cast<FunctionDecl>(CurContext);
567+
if (!CurFn)
568+
return;
569+
CUDAFunctionTarget Target = IdentifyCUDATarget(CurFn);
570+
if (Target == CFT_Global || Target == CFT_Device) {
571+
Method->addAttr(CUDADeviceAttr::CreateImplicit(Context));
572+
} else if (Target == CFT_HostDevice) {
573+
Method->addAttr(CUDADeviceAttr::CreateImplicit(Context));
574+
Method->addAttr(CUDAHostAttr::CreateImplicit(Context));
575+
}
576+
577+
// TODO: nvcc doesn't allow you to specify __host__ or __device__ attributes
578+
// on lambdas in all contexts -- we should emit a compatibility warning where
579+
// we're more permissive.
580+
}

‎clang/lib/Sema/SemaLambda.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,12 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
886886

887887
// Attributes on the lambda apply to the method.
888888
ProcessDeclAttributes(CurScope, Method, ParamInfo);
889-
889+
890+
// CUDA lambdas get implicit attributes based on the scope in which they're
891+
// declared.
892+
if (getLangOpts().CUDA)
893+
CUDASetLambdaAttrs(Method);
894+
890895
// Introduce the function call operator as the current declaration context.
891896
PushDeclContext(CurScope, Method);
892897

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -std=c++11 -fcuda-is-device -verify -verify-ignore-unexpected=note \
2+
// RUN: -S -o /dev/null %s
3+
// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -verify-ignore-unexpected=note \
4+
// RUN: -DHOST -S -o /dev/null %s
5+
#include "Inputs/cuda.h"
6+
7+
__host__ __device__ void hd_fn() {
8+
auto f1 = [&] {};
9+
f1(); // implicitly __host__ __device__
10+
11+
auto f2 = [&] __device__ {};
12+
f2();
13+
#ifdef HOST
14+
// expected-error@-2 {{reference to __device__ function}}
15+
#endif
16+
17+
auto f3 = [&] __host__ {};
18+
f3();
19+
#ifndef HOST
20+
// expected-error@-2 {{reference to __host__ function}}
21+
#endif
22+
23+
auto f4 = [&] __host__ __device__ {};
24+
f4();
25+
}
26+
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_cc1 -std=c++11 -fcuda-is-device -verify -fsyntax-only -verify-ignore-unexpected=note %s
2+
// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -verify-ignore-unexpected=note %s
3+
4+
#include "Inputs/cuda.h"
5+
6+
__device__ void device_fn() {
7+
auto f1 = [&] {};
8+
f1(); // implicitly __device__
9+
10+
auto f2 = [&] __device__ {};
11+
f2();
12+
13+
auto f3 = [&] __host__ {};
14+
f3(); // expected-error {{no matching function}}
15+
16+
auto f4 = [&] __host__ __device__ {};
17+
f4();
18+
19+
// Now do it all again with '()'s in the lambda declarations: This is a
20+
// different parse path.
21+
auto g1 = [&]() {};
22+
g1(); // implicitly __device__
23+
24+
auto g2 = [&]() __device__ {};
25+
g2();
26+
27+
auto g3 = [&]() __host__ {};
28+
g3(); // expected-error {{no matching function}}
29+
30+
auto g4 = [&]() __host__ __device__ {};
31+
g4();
32+
33+
// Once more, with the '()'s in a different place.
34+
auto h1 = [&]() {};
35+
h1(); // implicitly __device__
36+
37+
auto h2 = [&] __device__ () {};
38+
h2();
39+
40+
auto h3 = [&] __host__ () {};
41+
h3(); // expected-error {{no matching function}}
42+
43+
auto h4 = [&] __host__ __device__ () {};
44+
h4();
45+
}
46+
47+
// Behaves identically to device_fn.
48+
__global__ void kernel_fn() {
49+
auto f1 = [&] {};
50+
f1(); // implicitly __device__
51+
52+
auto f2 = [&] __device__ {};
53+
f2();
54+
55+
auto f3 = [&] __host__ {};
56+
f3(); // expected-error {{no matching function}}
57+
58+
auto f4 = [&] __host__ __device__ {};
59+
f4();
60+
61+
// No need to re-test all the parser contortions we test in the device
62+
// function.
63+
}
64+
65+
__host__ void host_fn() {
66+
auto f1 = [&] {};
67+
f1(); // implicitly __host__ (i.e., no magic)
68+
69+
auto f2 = [&] __device__ {};
70+
f2(); // expected-error {{no matching function}}
71+
72+
auto f3 = [&] __host__ {};
73+
f3();
74+
75+
auto f4 = [&] __host__ __device__ {};
76+
f4();
77+
}
78+
79+
// The special treatment above only applies to lambdas.
80+
__device__ void foo() {
81+
struct X {
82+
void foo() {}
83+
};
84+
X x;
85+
x.foo(); // expected-error {{reference to __host__ function 'foo' in __device__ function}}
86+
}

0 commit comments

Comments
 (0)
Please sign in to comment.