// REQUIRES: amdgpu-registered-target // RUN: %clang_cc1 -no-opaque-pointers -triple amdgcn-amd-amdhsa -fcuda-is-device -x hip %s \ // RUN: -std=c++17 -O3 -mllvm -amdgpu-internalize-symbols -emit-llvm -o - \ // RUN: | FileCheck -check-prefix=DEV %s // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-linux-gnu -x hip %s \ // RUN: -std=c++17 -O3 -emit-llvm -o - | FileCheck -check-prefix=HOST %s // Negative tests. // RUN: %clang_cc1 -no-opaque-pointers -triple amdgcn-amd-amdhsa -fcuda-is-device -x hip %s \ // RUN: -std=c++17 -O3 -mllvm -amdgpu-internalize-symbols -emit-llvm -o - \ // RUN: | FileCheck -check-prefix=DEV-NEG %s // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-linux-gnu -x hip %s \ // RUN: -std=c++17 -O3 -emit-llvm -o - | FileCheck -check-prefix=HOST-NEG %s #include "Inputs/cuda.h" // DEV-DAG: @v1 __device__ int v1; // DEV-DAG: @v2 __constant__ int v2; // Check device variables used by neither host nor device functioins are not kept. // DEV-NEG-NOT: @_ZL2v3 static __device__ int v3; // Check device variables used by host functions are kept. // DEV-DAG: @u1 __device__ int u1; // DEV-DAG: @u2 __constant__ int u2; // Check host-used static device var is in llvm.compiler.used. // DEV-DAG: @_ZL2u3 static __device__ int u3; // Check device-used static device var is emitted but is not in llvm.compiler.used. // DEV-DAG: @_ZL2u4 static __device__ int u4; // Check device variables with used attribute are always kept. // DEV-DAG: @u5 __device__ __attribute__((used)) int u5; // Test external device variable ODR-used by host code is not emitted or registered. // DEV-NEG-NOT: @ext_var extern __device__ int ext_var; // DEV-DAG: @inline_var = linkonce_odr addrspace(1) externally_initialized global i32 0 __device__ inline int inline_var; template<typename T> using func_t = T (*) (T, T); template <typename T> __device__ T add_func (T x, T y) { return x + y; } // DEV-DAG: @_Z10p_add_funcIiE = linkonce_odr addrspace(1) externally_initialized global i32 (i32, i32)* @_Z8add_funcIiET_S0_S0_ template <typename T> __device__ func_t<T> p_add_func = add_func<T>; // Check non-constant constexpr variables ODR-used by host code only is not emitted. // DEV-NEG-NOT: constexpr_var1a // DEV-NEG-NOT: constexpr_var1b constexpr int constexpr_var1a = 1; inline constexpr int constexpr_var1b = 1; // Check constant constexpr variables ODR-used by host code only. // Non-inline constexpr variable has internal linkage, therefore it is not accessible by host and not kept. // Inline constexpr variable has linkonce_ord linkage, therefore it can be accessed by host and kept. // DEV-NEG-NOT: constexpr_var2a // DEV-DAG: @constexpr_var2b = linkonce_odr addrspace(4) externally_initialized constant i32 2 __constant__ constexpr int constexpr_var2a = 2; inline __constant__ constexpr int constexpr_var2b = 2; void use(func_t<int> p); __host__ __device__ void use(const int *p); // Check static device variable in host function. // DEV-DAG: @_ZZ4fun1vE11static_var1 = addrspace(1) externally_initialized global i32 3 void fun1() { static __device__ int static_var1 = 3; use(&u1); use(&u2); use(&u3); use(&ext_var); use(&inline_var); use(p_add_func<int>); use(&constexpr_var1a); use(&constexpr_var1b); use(&constexpr_var2a); use(&constexpr_var2b); use(&static_var1); } // Check static variable in host device function. // DEV-DAG: @_ZZ4fun2vE11static_var2 = internal addrspace(1) global i32 4 // DEV-DAG: @_ZZ4fun2vE11static_var3 = addrspace(1) global i32 4 __host__ __device__ void fun2() { static int static_var2 = 4; static __device__ int static_var3 = 4; use(&static_var2); use(&static_var3); } __global__ void kern1(int **x) { *x = &u4; fun2(); } // Check static variables of lambda functions. // Lambda functions are implicit host device functions. // Default static variables in lambda functions should be treated // as host variables on host side, therefore should not be forced // to be emitted on device. // DEV-DAG: @_ZZZN21TestStaticVarInLambda3funEvENKUlPcE_clES0_E4var2 = addrspace(1) externally_initialized global i32 5 // DEV-NEG-NOT: @_ZZZN21TestStaticVarInLambda3funEvENKUlPcE_clES0_E4var1 namespace TestStaticVarInLambda { class A { public: A(char *); }; void fun() { (void) [](char *c) { static A var1(c); static __device__ int var2 = 5; (void) var1; (void) var2; }; } } // Check implicit constant variable ODR-used by host code is not emitted. // AST contains instantiation of al<ar>, which triggers AST instantiation // of x::al<ar>::am, which triggers AST instatiation of x::ap<ar>, // which triggers AST instantiation of aw<ar>::c, which has type // ar. ar has base class x which has member ah. x::ah is initialized // with function pointer pointing to ar:as, which returns an object // of type ou. The constexpr aw<ar>::c is an implicit constant variable // which is ODR-used by host function x::ap<ar>. An incorrect implementation // will force aw<ar>::c to be emitted on device side, which will trigger // emit of x::as and further more ctor of ou and variable o. // The ODR-use of aw<ar>::c in x::ap<ar> should be treated as a host variable // instead of device variable. // DEV-NEG-NOT: _ZN16TestConstexprVar1oE namespace TestConstexprVar { char o; class ou { public: ou(char) { __builtin_strlen(&o); } }; template < typename ao > struct aw { static constexpr ao c; }; class x { protected: typedef ou (*y)(const x *); constexpr x(y ag) : ah(ag) {} template < bool * > struct ak; template < typename > struct al { static bool am; static ak< &am > an; }; template < typename ao > static x ap() { (void)aw< ao >::c; return x(nullptr); } y ah; }; template < typename ao > bool x::al< ao >::am(&ap< ao >); class ar : x { public: constexpr ar() : x(as) {} static ou as(const x *) { return 0; } al< ar > av; }; } // Check the exact list of variables to ensure @_ZL2u4 is not among them. // DEV: @llvm.compiler.used = {{[^@]*}} @_Z10p_add_funcIiE // DEV-SAME: {{^[^@]*}} @_ZL2u3 // DEV-SAME: {{^[^@]*}} @_ZZ4fun1vE11static_var1 // DEV-SAME: {{^[^@]*}} @_ZZZN21TestStaticVarInLambda3funEvENKUlPcE_clES0_E4var2 // DEV-SAME: {{^[^@]*}} @constexpr_var2b // DEV-SAME: {{^[^@]*}} @inline_var // DEV-SAME: {{^[^@]*}} @u1 // DEV-SAME: {{^[^@]*}} @u2 // DEV-SAME: {{^[^@]*}} @u5 // DEV-SAME: {{^[^@]*$}} // HOST-DAG: hipRegisterVar{{.*}}@u1 // HOST-DAG: hipRegisterVar{{.*}}@u2 // HOST-DAG: hipRegisterVar{{.*}}@_ZL2u3 // HOST-DAG: hipRegisterVar{{.*}}@constexpr_var2b // HOST-DAG: hipRegisterVar{{.*}}@u5 // HOST-DAG: hipRegisterVar{{.*}}@inline_var // HOST-DAG: hipRegisterVar{{.*}}@_Z10p_add_funcIiE // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZZ4fun1vE11static_var1 // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZZ4fun2vE11static_var2 // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZZ4fun2vE11static_var3 // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZZZN21TestStaticVarInLambda3funEvENKUlPcE_clES0_E4var2 // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZZZN21TestStaticVarInLambda3funEvENKUlPcE_clES0_E4var1 // HOST-NEG-NOT: hipRegisterVar{{.*}}@ext_var // HOST-NEG-NOT: hipRegisterVar{{.*}}@_ZL2u4 // HOST-NEG-NOT: hipRegisterVar{{.*}}@constexpr_var1a // HOST-NEG-NOT: hipRegisterVar{{.*}}@constexpr_var1b // HOST-NEG-NOT: hipRegisterVar{{.*}}@constexpr_var2a