Skip to content

Commit 496c05f

Browse files
Vedant2005goyalvgvassilev
authored andcommitted
Fixes bug in zero initialization of custom structs on GPU
1 parent f5cd601 commit 496c05f

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

include/clad/Differentiator/Differentiator.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,18 +232,40 @@ CUDA_HOST_DEVICE auto back(TapeType& of) -> decltype(of.back()) {
232232

233233
template <class T> CUDA_HOST_DEVICE void zero_init(T& t);
234234

235+
#ifndef __has_builtin
236+
#define __has_builtin(x) 0
237+
#endif
235238
template <class T,
236239
typename std::enable_if<!is_range<T>::value, int>::type = 0>
237240
CUDA_HOST_DEVICE void zero_impl(volatile T& t) {
241+
#if defined(__CUDACC__)
242+
static_assert(std::is_trivially_destructible<T>::value,
243+
"Clad device fallback zero_init requires trivially "
244+
"destructible types.");
245+
#endif
238246
// Fill an array with zeros.
239247
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
240248
unsigned char tmp[sizeof(T)] = {};
249+
250+
#if __has_builtin(__builtin_memcpy)
251+
__builtin_memcpy(const_cast<T*>(&t), tmp, sizeof(T));
252+
#elif defined(__CUDACC__)
253+
// Fallback for the devices that don't have __builtin_memcpy.
254+
// Transfers the zero with a loop. Unlike memcpyt, this does not create the
255+
// object in the destination region of storage and language semantics can't
256+
// be fully preserved
257+
volatile unsigned char* byte_ptr =
258+
reinterpret_cast<volatile unsigned char*>(const_cast<T*>(&t));
259+
for (std::size_t i = 0; i < sizeof(T); ++i)
260+
byte_ptr[i] = 0;
261+
#else
241262
// Transfer the zeros with the magic function memcpy which can implicitly
242263
// create objects in the destination region of storage immediately prior to
243264
// copying the sequence of characters to the destination [27.5.1(3)].
244265
// (C++ has deprecated the volatile qualifiers. However, we drop them here
245266
// to make sure things still work with codebases which still have them)
246267
std::memcpy(const_cast<T*>(&t), tmp, sizeof(T));
268+
#endif
247269
}
248270

249271
template <class T, typename std::enable_if<is_range<T>::value, int>::type = 0>

test/CUDA/ZeroInitDevice.cu

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// RUN: %cladclang_cuda -I%S/../../include --cuda-path=%cudapath \
2+
// RUN: --cuda-gpu-arch=%cudaarch %cudaldflags -oZeroInitDevice.out \
3+
// RUN: -Xclang -verify %s 2>&1 | %filecheck %s
4+
//
5+
// RUN: %cudarun ./ZeroInitDevice.out | %filecheck_exec %s
6+
//
7+
// RUN: %cladclang_cuda -I%S/../../include --cuda-path=%cudapath \
8+
// RUN: --cuda-gpu-arch=%cudaarch -fsyntax-only %s -DEXPECT_DIAG \
9+
// RUN: -Xclang -verify=device -Xclang -verify-ignore-unexpected=note
10+
//
11+
// REQUIRES: cuda-runtime
12+
//
13+
// expected-no-diagnostics
14+
15+
#include <iostream>
16+
#include "clad/Differentiator/Differentiator.h"
17+
#include <cuda.h>
18+
19+
struct CustomStruct {
20+
double x;
21+
double y;
22+
};
23+
24+
//Non trivial ctor will fallback to use of loop
25+
struct CustomStruct1{
26+
double x;
27+
double y;
28+
__host__ __device__ CustomStruct1(double x, double y){
29+
this->x=x;
30+
this->y=y;
31+
}
32+
};
33+
__host__ __device__ double dummy_func(double x) { return x * 3.0; }
34+
35+
// CHECK: void dummy_func_grad(double x, double *_d_x) {
36+
// CHECK-NEXT: *_d_x += 1 * 3.;
37+
// CHECK-NEXT: }
38+
39+
__global__ void zero_init_device(double* out) {
40+
CustomStruct s;
41+
s.x = 10.0;
42+
s.y = 20.0;
43+
44+
clad::zero_init(s);
45+
46+
out[0] = s.x;
47+
out[1] = s.y;
48+
}
49+
50+
__global__ void zero_init_device1(double* out1){
51+
CustomStruct1 s1(1.0,2.0);
52+
53+
clad::zero_init(s1);
54+
55+
out1[0]=s1.x;
56+
out1[1]=s1.y;
57+
}
58+
59+
#ifdef EXPECT_DIAG
60+
struct CustomStruct2 {
61+
double x;
62+
__host__ __device__ CustomStruct2() : x(0) {}
63+
__host__ __device__ ~CustomStruct2() {}
64+
};
65+
66+
__global__ void test_diag() {
67+
CustomStruct2 s;
68+
clad::zero_init(s); // device-error@* {{Clad device fallback zero_init requires trivially destructible types}}
69+
}
70+
#endif
71+
72+
int main() {
73+
auto grad = clad::gradient(dummy_func, "x");
74+
75+
double* d_out;
76+
cudaMalloc(&d_out, 2*sizeof(double));
77+
78+
zero_init_device<<<1, 1>>>(d_out);
79+
cudaDeviceSynchronize();
80+
81+
double h_out[2];
82+
cudaMemcpy(h_out, d_out, 2*sizeof(double), cudaMemcpyDeviceToHost);
83+
84+
std::cout << "s.x: " << h_out[0] << std::endl;
85+
// CHECK-EXEC: s.x: 0
86+
std::cout << "s.y: " << h_out[1] << std::endl;
87+
// CHECK-EXEC-NEXT: s.y: 0
88+
89+
double* d_out1;
90+
cudaMalloc(&d_out1,2*sizeof(double));
91+
92+
zero_init_device1<<<1,1>>>(d_out1);
93+
cudaDeviceSynchronize();
94+
95+
double h_out1[2];
96+
cudaMemcpy(h_out1, d_out1, 2*sizeof(double), cudaMemcpyDeviceToHost);
97+
98+
std::cout << "s1.x: " << h_out1[0] << std::endl;
99+
// CHECK-EXEC: s1.x: 0
100+
std::cout << "s1.y: " << h_out1[1] << std::endl;
101+
// CHECK-EXEC-NEXT: s1.y: 0
102+
103+
cudaFree(d_out);
104+
cudaFree(d_out1);
105+
return 0;
106+
}

0 commit comments

Comments
 (0)