Skip to content

Commit

Permalink
[CUDA][HIP] make trivial ctor/dtor host device (#72394)
Browse files Browse the repository at this point in the history
Make trivial ctor/dtor implicitly host device functions so that they can
be used to initialize file-scope
device variables to match nvcc behavior.

Fixes: #72261

Fixes: SWDEV-432412
  • Loading branch information
yxsamliu committed Nov 16, 2023
1 parent ea84897 commit 27e6e4a
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 8 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13453,6 +13453,10 @@ class Sema final {
void maybeAddCUDAHostDeviceAttrs(FunctionDecl *FD,
const LookupResult &Previous);

/// May add implicit CUDAHostAttr and CUDADeviceAttr attributes to a
/// trivial cotr/dtor that does not have host and device attributes.
void maybeAddCUDAHostDeviceAttrsToTrivialCtorDtor(FunctionDecl *FD);

/// May add implicit CUDAConstantAttr attribute to VD, depending on VD
/// and current compilation settings.
void MaybeAddCUDAConstantAttr(VarDecl *VD);
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaCUDA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,22 @@ void Sema::maybeAddCUDAHostDeviceAttrs(FunctionDecl *NewD,
NewD->addAttr(CUDADeviceAttr::CreateImplicit(Context));
}

// If a trivial ctor/dtor has no host/device
// attributes, make it implicitly host device function.
void Sema::maybeAddCUDAHostDeviceAttrsToTrivialCtorDtor(FunctionDecl *FD) {
bool IsTrivialCtor = false;
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
IsTrivialCtor = isEmptyCudaConstructor(SourceLocation(), CD);
bool IsTrivialDtor = false;
if (auto *DD = dyn_cast<CXXDestructorDecl>(FD))
IsTrivialDtor = isEmptyCudaDestructor(SourceLocation(), DD);
if ((IsTrivialCtor || IsTrivialDtor) && !FD->hasAttr<CUDAHostAttr>() &&
!FD->hasAttr<CUDADeviceAttr>()) {
FD->addAttr(CUDAHostAttr::CreateImplicit(Context));
FD->addAttr(CUDADeviceAttr::CreateImplicit(Context));
}
}

// TODO: `__constant__` memory may be a limited resource for certain targets.
// A safeguard may be needed at the end of compilation pipeline if
// `__constant__` memory usage goes beyond limit.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16232,6 +16232,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
if (FD && !FD->isDeleted())
checkTypeSupport(FD->getType(), FD->getLocation(), FD);

if (LangOpts.CUDA)
maybeAddCUDAHostDeviceAttrsToTrivialCtorDtor(FD);

return dcl;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCUDA/call-host-fn-from-device.cu
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extern "C" void host_fn() {}
struct Dummy {};

struct S {
S() {}
S() { static int nontrivial_ctor = 1; }
// expected-note@-1 2 {{'S' declared here}}
~S() { host_fn(); }
// expected-note@-1 {{'~S' declared here}}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCUDA/default-ctor.cu
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ __device__ void fd() {
InD ind;
InH inh; // expected-error{{no matching constructor for initialization of 'InH'}}
InHD inhd;
Out out; // expected-error{{no matching constructor for initialization of 'Out'}}
Out out;
OutD outd;
OutH outh; // expected-error{{no matching constructor for initialization of 'OutH'}}
OutHD outhd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Test 1: collision between two bases

struct A1_with_host_ctor {
A1_with_host_ctor() {}
A1_with_host_ctor() { static int nontrivial_ctor = 1; }
};

struct B1_with_device_ctor {
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCUDA/implicit-member-target-collision.cu
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Test 1: collision between two bases

struct A1_with_host_ctor {
A1_with_host_ctor() {}
A1_with_host_ctor() { static int nontrivial_ctor = 1; }
};

struct B1_with_device_ctor {
Expand Down
4 changes: 2 additions & 2 deletions clang/test/SemaCUDA/implicit-member-target-inherited.cu
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Test 1: infer inherited default ctor to be host.

struct A1_with_host_ctor {
A1_with_host_ctor() {}
A1_with_host_ctor() { static int nontrivial_ctor = 1; }
};
// expected-note@-3 {{candidate constructor (the implicit copy constructor) not viable}}
// expected-note@-4 {{candidate constructor (the implicit move constructor) not viable}}
Expand Down Expand Up @@ -83,7 +83,7 @@ void hostfoo3() {
// Test 4: infer inherited default ctor from a field, not a base

struct A4_with_host_ctor {
A4_with_host_ctor() {}
A4_with_host_ctor() { static int nontrivial_ctor = 1; }
};

struct B4_with_inherited_host_ctor : A4_with_host_ctor{
Expand Down
4 changes: 2 additions & 2 deletions clang/test/SemaCUDA/implicit-member-target.cu
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Test 1: infer default ctor to be host.

struct A1_with_host_ctor {
A1_with_host_ctor() {}
A1_with_host_ctor() { static int nontrivial_ctor = 1; }
};

// The implicit default constructor is inferred to be host because it only needs
Expand Down Expand Up @@ -75,7 +75,7 @@ void hostfoo3() {
// Test 4: infer default ctor from a field, not a base

struct A4_with_host_ctor {
A4_with_host_ctor() {}
A4_with_host_ctor() { static int nontrivial_ctor = 1; }
};

struct B4_with_implicit_default_ctor {
Expand Down
40 changes: 40 additions & 0 deletions clang/test/SemaCUDA/trivial-ctor-dtor.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify %s
// RUN: %clang_cc1 -isystem %S/Inputs -fcuda-is-device -fsyntax-only -verify %s

#include <cuda.h>

// Check trivial ctor/dtor
struct A {
int x;
A() {}
~A() {}
};

__device__ A a;

// Check trivial ctor/dtor of template class
template<typename T>
struct TA {
T x;
TA() {}
~TA() {}
};

__device__ TA<int> ta;

// Check non-trivial ctor/dtor in parent template class
template<typename T>
struct TB {
T x;
TB() { static int nontrivial_ctor = 1; }
~TB() {}
};

template<typename T>
struct TC : TB<T> {
T x;
TC() {}
~TC() {}
};

__device__ TC<int> tc; //expected-error {{dynamic initialization is not supported for __device__, __constant__, __shared__, and __managed__ variables}}

0 comments on commit 27e6e4a

Please sign in to comment.