// Based on clang/test/CodeGenCUDA/device-stub.cu (incubator). // Create a dummy GPU binary file for registration. // RUN: echo -n "GPU binary would be here." > %t // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-cir %s -x cuda \ // RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t -o %t.cir // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR // RUN: %clang_cc1 -triple x86_64-linux-gnu -fclangir -emit-llvm %s -x cuda \ // RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -x cuda \ // RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-cir %s -x cuda \ // RUN: -target-sdk-version=12.3 -o %t.nogpu.cir // RUN: FileCheck --input-file=%t.nogpu.cir %s --check-prefix=NOGPUBIN // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-cir %s -x hip \ // RUN: -fhip-new-launch-api -fcuda-include-gpubinary %t -o %t.hip.cir // RUN: FileCheck --input-file=%t.hip.cir %s --check-prefix=HIP-CIR // RUN: %clang_cc1 -triple x86_64-linux-gnu -fclangir -emit-llvm %s -x hip \ // RUN: -fhip-new-launch-api -fcuda-include-gpubinary %t -o %t.hip-cir.ll // RUN: FileCheck --input-file=%t.hip-cir.ll %s --check-prefix=HIP-LLVM // // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -x hip \ // RUN: -fhip-new-launch-api -fcuda-include-gpubinary %t -o %t.hip.ll // RUN: FileCheck --input-file=%t.hip.ll %s --check-prefix=HIP-OGCG // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-cir %s -x hip \ // RUN: -fhip-new-launch-api -o %t.nogpu.hip.cir // RUN: FileCheck --input-file=%t.nogpu.hip.cir %s --check-prefix=HIP-NOGPUBIN #include "Inputs/cuda.h" __global__ void kernelfunc(int i, int j, int k) {} void hostfunc(void) { kernelfunc<<<1, 1>>>(1, 1, 1); } // Device-side shadows: exercise the __cudaRegisterVar code path alongside the // existing __cudaRegisterFunction kernel registration. __device__ int a; __constant__ int b; __device__ _BitInt(36) c; // Check module constructor is registered in module attributes. // CIR: cir.global_ctors = [#cir.global_ctor<"__cuda_module_ctor", 65535>] // Check runtime function declarations. // CIR: cir.func private @atexit(!cir.ptr>) -> !s32i // CIR: cir.func private @__cudaUnregisterFatBinary(!cir.ptr>) // Check the module destructor body: load handle and call UnregisterFatBinary. // CIR: cir.func internal private @__cuda_module_dtor() // CIR-NEXT: %[[HANDLE_ADDR:.*]] = cir.get_global @__cuda_gpubin_handle // CIR-NEXT: %[[HANDLE:.*]] = cir.load %[[HANDLE_ADDR]] // CIR-NEXT: cir.call @__cudaUnregisterFatBinary(%[[HANDLE]]) // CIR-NEXT: cir.return // CIR: cir.func private @__cudaRegisterFatBinaryEnd(!cir.ptr>) // __cudaRegisterVar runtime declaration and per-variable name strings for // device shadows. These are emitted between __cudaRegisterFatBinaryEnd and // __cudaRegisterFunction; relative order is not significant. // CIR-DAG: cir.func private @__cudaRegisterVar(!cir.ptr>, !cir.ptr, !cir.ptr, !cir.ptr, !s32i, !u64i, !s32i, !s32i) // CIR-DAG: cir.global "private" constant cir_private @".stra" = #cir.const_array<"a" : !cir.array, trailing_zeros> // CIR-DAG: cir.global "private" constant cir_private @".strb" = #cir.const_array<"b" : !cir.array, trailing_zeros> // CIR-DAG: cir.global "private" constant cir_private @".strc" = #cir.const_array<"c" : !cir.array, trailing_zeros> // Check the __cudaRegisterFunction runtime declaration: // int __cudaRegisterFunction(void**, void*, void*, void*, int, // void*, void*, void*, void*, void*) // CIR: cir.func private @__cudaRegisterFunction(!cir.ptr>, !cir.ptr, !cir.ptr, !cir.ptr, !s32i, !cir.ptr, !cir.ptr, !cir.ptr, !cir.ptr, !cir.ptr) -> !s32i // Check the device-side name string for kernelfunc (mangled, null-terminated). // CIR: cir.global "private" constant cir_private @".str_Z10kernelfunciii" = #cir.const_array<"_Z10kernelfunciii" : !cir.array, trailing_zeros> : !cir.array // Check __cuda_register_globals body: __cudaRegisterFunction for each kernel, // then __cudaRegisterVar for each device shadow. // CIR: cir.func internal private @__cuda_register_globals(%[[FATBIN:.*]]: !cir.ptr> // CIR-NEXT: %[[NULL:.*]] = cir.const #cir.ptr : !cir.ptr // CIR-NEXT: %[[STR_ADDR:.*]] = cir.get_global @".str_Z10kernelfunciii" // CIR-NEXT: %[[DEVICE_FUNC:.*]] = cir.cast bitcast %[[STR_ADDR]] // CIR-NEXT: %[[HOST_FUNC_RAW:.*]] = cir.get_global @{{.*}}kernelfunc{{.*}} // CIR-NEXT: %[[HOST_FUNC:.*]] = cir.cast bitcast %[[HOST_FUNC_RAW]] // CIR-NEXT: %[[THREAD_LIMIT:.*]] = cir.const #cir.int<-1> : !s32i // CIR-NEXT: cir.call @__cudaRegisterFunction(%{{.*}}, %[[HOST_FUNC]], %[[DEVICE_FUNC]], %[[DEVICE_FUNC]], %[[THREAD_LIMIT]], %[[NULL]], %[[NULL]], %[[NULL]], %[[NULL]], %[[NULL]]) // Registration for __device__ int a (constant=0): // CIR: %[[#NAMEA_RAW:]] = cir.get_global @".stra" // CIR-NEXT: %[[#NAMEA:]] = cir.cast bitcast %[[#NAMEA_RAW]] // CIR-NEXT: %[[#HOSTA_RAW:]] = cir.get_global @a // CIR-NEXT: %[[#HOSTA:]] = cir.cast bitcast %[[#HOSTA_RAW]] // CIR-NEXT: %[[#EXTA:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[#SZA:]] = cir.const #cir.int<4> : !u64i // CIR-NEXT: %[[#CONA:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[#NORMA:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: cir.call @__cudaRegisterVar(%[[FATBIN]], %[[#HOSTA]], %[[#NAMEA]], %[[#NAMEA]], %[[#EXTA]], %[[#SZA]], %[[#CONA]], %[[#NORMA]]) // Registration for __constant__ int b (constant=1): // CIR: %[[#NAMEB_RAW:]] = cir.get_global @".strb" // CIR-NEXT: %[[#NAMEB:]] = cir.cast bitcast %[[#NAMEB_RAW]] // CIR-NEXT: %[[#HOSTB_RAW:]] = cir.get_global @b // CIR-NEXT: %[[#HOSTB:]] = cir.cast bitcast %[[#HOSTB_RAW]] // CIR-NEXT: %[[#EXTB:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[#SZB:]] = cir.const #cir.int<4> : !u64i // CIR-NEXT: %[[#CONB:]] = cir.const #cir.int<1> : !s32i // CIR-NEXT: %[[#NORMB:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: cir.call @__cudaRegisterVar(%[[FATBIN]], %[[#HOSTB]], %[[#NAMEB]], %[[#NAMEB]], %[[#EXTB]], %[[#SZB]], %[[#CONB]], %[[#NORMB]]) // Registration for __device__ _BitInt(36) // CIR: %[[#NAMEC_RAW:]] = cir.get_global @".strc" // CIR-NEXT: %[[#NAMEC:]] = cir.cast bitcast %[[#NAMEC_RAW]] // CIR-NEXT: %[[#HOSTC_RAW:]] = cir.get_global @c // CIR-NEXT: %[[#HOSTC:]] = cir.cast bitcast %[[#HOSTC_RAW]] // CIR-NEXT: %[[#EXTC:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[#SZC:]] = cir.const #cir.int<8> : !u64i // CIR-NEXT: %[[#CONC:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[#NORMC:]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: cir.call @__cudaRegisterVar(%[[FATBIN]], %[[#HOSTC]], %[[#NAMEC]], %[[#NAMEC]], %[[#EXTC]], %[[#SZC]], %[[#CONC]], %[[#NORMC]]) // CIR-NEXT: cir.return // CIR: cir.global "private" constant cir_private @__cuda_fatbin_str = #cir.const_array<"GPU binary would be here." : !cir.array> : !cir.array {alignment = 8 : i64, section = ".nv_fatbin"} // Check the fatbin wrapper struct: { magic, version, ptr to fatbin, null }, with section. // CIR: cir.global constant cir_private @__cuda_fatbin_wrapper = #cir.const_record<{ // CIR-SAME: #cir.int<1180844977> : !s32i, // CIR-SAME: #cir.int<1> : !s32i, // CIR-SAME: #cir.global_view<@__cuda_fatbin_str> : !cir.ptr, // CIR-SAME: #cir.ptr : !cir.ptr // CIR-SAME: }> : !rec_anon_struct {section = ".nvFatBinSegment"} // Check the GPU binary handle global. // CIR: cir.global "private" internal @__cuda_gpubin_handle = #cir.ptr : !cir.ptr> // CIR: cir.func private @__cudaRegisterFatBinary(!cir.ptr) -> !cir.ptr> // Check the module constructor body: register fatbin, store handle, // call __cuda_register_globals, call RegisterFatBinaryEnd (CUDA >= 10.1), // then register dtor with atexit. // CIR: cir.func internal private @__cuda_module_ctor() // CIR-NEXT: %[[WRAPPER:.*]] = cir.get_global @__cuda_fatbin_wrapper // CIR-NEXT: %[[VOID_PTR:.*]] = cir.cast bitcast %[[WRAPPER]] // CIR-NEXT: %[[RET:.*]] = cir.call @__cudaRegisterFatBinary(%[[VOID_PTR]]) // CIR-NEXT: %[[HANDLE_ADDR:.*]] = cir.get_global @__cuda_gpubin_handle // CIR-NEXT: cir.store %[[RET]], %[[HANDLE_ADDR]] // CIR-NEXT: cir.call @__cuda_register_globals(%[[RET]]) // CIR-NEXT: cir.call @__cudaRegisterFatBinaryEnd(%[[RET]]) // CIR-NEXT: %[[DTOR_PTR:.*]] = cir.get_global @__cuda_module_dtor // CIR-NEXT: {{.*}} = cir.call @atexit(%[[DTOR_PTR]]) // CIR-NEXT: cir.return // OGCG: constant [25 x i8] c"GPU binary would be here.", section ".nv_fatbin", align 8 // OGCG: @__cuda_fatbin_wrapper = internal constant { i32, i32, ptr, ptr } { i32 1180844977, i32 1, ptr @{{.*}}, ptr null }, section ".nvFatBinSegment" // OGCG: @__cuda_gpubin_handle = internal global ptr null // OGCG: @llvm.global_ctors = appending global {{.*}}@__cuda_module_ctor // OGCG: define internal void @__cuda_register_globals(ptr %[[#OGFATBIN:]]) // OGCG: call{{.*}}__cudaRegisterFunction(ptr %[[#OGFATBIN]], {{.*}}kernelfunc{{.*}} // OGCG: call void @__cudaRegisterVar(ptr %[[#OGFATBIN]], ptr @a, {{.*}}, {{.*}}, i32 0, i64 4, i32 0, i32 0) // OGCG: call void @__cudaRegisterVar(ptr %[[#OGFATBIN]], ptr @b, {{.*}}, {{.*}}, i32 0, i64 4, i32 1, i32 0) // OGCG: call void @__cudaRegisterVar(ptr %[[#OGFATBIN]], ptr @c, {{.*}}, {{.*}}, i32 0, i64 8, i32 0, i32 0) // OGCG: ret void // OGCG: define internal void @__cuda_module_ctor // OGCG: call{{.*}}__cudaRegisterFatBinary(ptr @__cuda_fatbin_wrapper) // OGCG: store ptr %{{.*}}, ptr @__cuda_gpubin_handle // OGCG-NEXT: call void @__cuda_register_globals // OGCG: call i32 @atexit(ptr @__cuda_module_dtor) // OGCG: define internal void @__cuda_module_dtor // OGCG: load ptr, ptr @__cuda_gpubin_handle // OGCG: call void @__cudaUnregisterFatBinary // LLVM: constant [25 x i8] c"GPU binary would be here.", section ".nv_fatbin", align 8 // LLVM: @__cuda_fatbin_wrapper = {{.*}}constant { i32, i32, ptr, ptr } { i32 1180844977, i32 1, ptr @{{.*}}, ptr null }, section ".nvFatBinSegment" // LLVM: @__cuda_gpubin_handle = internal global ptr null // LLVM: @llvm.global_ctors = appending global {{.*}}@__cuda_module_ctor // LLVM: define internal void @__cuda_module_dtor // LLVM: load ptr, ptr @__cuda_gpubin_handle // LLVM: call void @__cudaUnregisterFatBinary // LLVM: define internal void @__cuda_register_globals(ptr %[[#FATBIN:]]) // LLVM: call{{.*}}@__cudaRegisterFunction(ptr %[[#FATBIN]], ptr @{{.*}}kernelfunc{{.*}}, ptr @{{.*}}, ptr @{{.*}}, i32 -1, ptr null, ptr null, ptr null, ptr null, ptr null) // LLVM: call void @__cudaRegisterVar(ptr %[[#FATBIN]], ptr @a, ptr @.stra, ptr @.stra, i32 0, i64 4, i32 0, i32 0) // LLVM: call void @__cudaRegisterVar(ptr %[[#FATBIN]], ptr @b, ptr @.strb, ptr @.strb, i32 0, i64 4, i32 1, i32 0) // LLVM: call void @__cudaRegisterVar(ptr %[[#FATBIN]], ptr @c, ptr @.strc, ptr @.strc, i32 0, i64 8, i32 0, i32 0) // LLVM: ret void // LLVM: define internal void @__cuda_module_ctor // LLVM: call{{.*}}@__cudaRegisterFatBinary(ptr @__cuda_fatbin_wrapper) // LLVM: store ptr %{{.*}}, ptr @__cuda_gpubin_handle // LLVM-NEXT: call void @__cuda_register_globals // LLVM: call i32 @atexit(ptr @__cuda_module_dtor) // No GPU binary — no registration infrastructure at all. // NOGPUBIN-NOT: fatbin // NOGPUBIN-NOT: gpubin // NOGPUBIN-NOT: __cuda_register_globals // NOGPUBIN-NOT: __cuda_module_ctor // NOGPUBIN-NOT: __cuda_module_dtor // ============================================================================= // HIP host-side registration (`buildCUDAModuleCtor` / `buildHIPModuleDtor` / // `buildCUDARegisterGlobalFunctions` HIP arms in CIR LoweringPrepare). // ============================================================================= // HIP module ctor is registered with the default global-ctor priority. // HIP-CIR: cir.global_ctors = [#cir.global_ctor<"__hip_module_ctor", 65535>] // Runtime function decls. // HIP-CIR: cir.func private @atexit(!cir.ptr>) -> !s32i // HIP-CIR: cir.func private @__hipUnregisterFatBinary(!cir.ptr>) // Module dtor: only unregister when the handle is non-null, then null it out. // Reuses the SSA value loaded in the entry block for the unregister call. // HIP-CIR: cir.func internal private @__hip_module_dtor() // HIP-CIR: %[[DH0:.*]] = cir.get_global @__hip_gpubin_handle // HIP-CIR: %[[H0:.*]] = cir.load %[[DH0]] // HIP-CIR: %[[NULL0:.*]] = cir.const #cir.ptr // HIP-CIR: %[[NE:.*]] = cir.cmp ne %[[H0]], %[[NULL0]] // HIP-CIR: cir.brcond %[[NE]] ^bb1, ^bb2 // HIP-CIR: ^bb1: // HIP-CIR: cir.call @__hipUnregisterFatBinary(%[[H0]]) // HIP-CIR: %[[DH1:.*]] = cir.get_global @__hip_gpubin_handle // HIP-CIR: cir.store %[[NULL0]], %[[DH1]] // HIP-CIR: cir.br ^bb2 // HIP-CIR: ^bb2: // HIP-CIR: cir.return // __hipRegisterFunction runtime declaration. // HIP-CIR: cir.func private @__hipRegisterFunction(!cir.ptr>, !cir.ptr, !cir.ptr, !cir.ptr, !s32i, !cir.ptr, !cir.ptr, !cir.ptr, !cir.ptr, !cir.ptr) -> !s32i // __hip_register_globals: under -fhip-new-launch-api the host-side argument is // the kernel-handle GlobalOp shadow (e.g. @_Z10kernelfunciii) — not the // device-stub function pointer that the CUDA arm uses. // HIP-CIR: cir.global "private" constant cir_private @".str_Z10kernelfunciii" = #cir.const_array<"_Z10kernelfunciii" : !cir.array, trailing_zeros> : !cir.array // HIP-CIR: cir.func internal private @__hip_register_globals(%[[FATBIN:.*]]: !cir.ptr> // HIP-CIR: %[[NULL1:.*]] = cir.const #cir.ptr : !cir.ptr // HIP-CIR: %[[STR_ADDR:.*]] = cir.get_global @".str_Z10kernelfunciii" // HIP-CIR: %[[DEVICE_FUNC:.*]] = cir.cast bitcast %[[STR_ADDR]] // HIP-CIR: %[[KH:.*]] = cir.get_global @_Z10kernelfunciii : !cir.ptr>> // HIP-CIR: %[[HOST_FUNC:.*]] = cir.cast bitcast %[[KH]] // HIP-CIR: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1> : !s32i // HIP-CIR: cir.call @__hipRegisterFunction(%[[FATBIN]], %[[HOST_FUNC]], %[[DEVICE_FUNC]], %[[DEVICE_FUNC]], %[[MINUS_ONE]], %[[NULL1]], %[[NULL1]], %[[NULL1]], %[[NULL1]], %[[NULL1]]) // HIP-CIR: cir.return // Fatbin string + wrapper live in the HIP-specific sections; magic // 0x48495046 = 1212764230. // HIP-CIR: cir.global "private" constant cir_private @__hip_fatbin_str = #cir.const_array<"GPU binary would be here." : !cir.array> : !cir.array {alignment = 8 : i64, section = ".hip_fatbin"} // HIP-CIR: cir.global constant cir_private @__hip_fatbin_wrapper = #cir.const_record<{ // HIP-CIR-SAME: #cir.int<1212764230> : !s32i, // HIP-CIR-SAME: #cir.int<1> : !s32i, // HIP-CIR-SAME: #cir.global_view<@__hip_fatbin_str> : !cir.ptr, // HIP-CIR-SAME: #cir.ptr : !cir.ptr // HIP-CIR-SAME: }> : !rec_anon_struct {section = ".hipFatBinSegment"} // HIP-CIR: cir.global "private" internal @__hip_gpubin_handle = #cir.ptr : !cir.ptr> // HIP-CIR: cir.func private @__hipRegisterFatBinary(!cir.ptr) -> !cir.ptr> // Module ctor: guard registration on a null handle, register globals from the // (possibly newly-stored) handle, then atexit(__hip_module_dtor). // HIP-CIR: cir.func internal private @__hip_module_ctor() // HIP-CIR: %[[GHA:.*]] = cir.get_global @__hip_gpubin_handle // HIP-CIR: %[[H:.*]] = cir.load %[[GHA]] // HIP-CIR: %[[NULLPTR:.*]] = cir.const #cir.ptr // HIP-CIR: %[[EQ:.*]] = cir.cmp eq %[[H]], %[[NULLPTR]] // HIP-CIR: cir.brcond %[[EQ]] ^bb1, ^bb2 // HIP-CIR: ^bb1: // HIP-CIR: %[[WRAPPER:.*]] = cir.get_global @__hip_fatbin_wrapper // HIP-CIR: %[[VOID_PTR:.*]] = cir.cast bitcast %[[WRAPPER]] // HIP-CIR: %[[REG:.*]] = cir.call @__hipRegisterFatBinary(%[[VOID_PTR]]) // HIP-CIR: %[[GHA2:.*]] = cir.get_global @__hip_gpubin_handle // HIP-CIR: cir.store %[[REG]], %[[GHA2]] // HIP-CIR: cir.br ^bb2 // HIP-CIR: ^bb2: // HIP-CIR: %[[GHA3:.*]] = cir.get_global @__hip_gpubin_handle // HIP-CIR: %[[H2:.*]] = cir.load %[[GHA3]] // HIP-CIR: cir.call @__hip_register_globals(%[[H2]]) // HIP-CIR: %[[DTOR_PTR:.*]] = cir.get_global @__hip_module_dtor // HIP-CIR: {{.*}} = cir.call @atexit(%[[DTOR_PTR]]) // HIP-CIR: cir.return // HIP-CIR: cir.global constant external @_Z10kernelfunciii = #cir.global_view<@_Z25__device_stub__kernelfunciii> : !cir.ptr> {alignment = 8 : i64} // HIP OGCG cross-check (LLVM IR matches what OG codegen emits for HIP). // HIP-OGCG: @{{.*}} = private constant [25 x i8] c"GPU binary would be here.", section ".hip_fatbin" // HIP-OGCG: @__hip_fatbin_wrapper = internal constant { i32, i32, ptr, ptr } { i32 1212764230, i32 1, ptr @{{.*}}, ptr null }, section ".hipFatBinSegment" // HIP-OGCG: @__hip_gpubin_handle = internal global ptr null // HIP-OGCG: @llvm.global_ctors = appending global {{.*}}@__hip_module_ctor // HIP-OGCG: define internal void @__hip_module_ctor() // HIP-OGCG: load ptr, ptr @__hip_gpubin_handle // HIP-OGCG: icmp eq ptr {{.*}}, null // HIP-OGCG: call ptr @__hipRegisterFatBinary(ptr @__hip_fatbin_wrapper) // HIP-OGCG: store ptr {{.*}}, ptr @__hip_gpubin_handle // HIP-OGCG: call void @__hip_register_globals( // HIP-OGCG: call i32 @atexit(ptr @__hip_module_dtor) // HIP-OGCG: ret void // HIP-OGCG: define internal void @__hip_module_dtor() // HIP-OGCG: load ptr, ptr @__hip_gpubin_handle // HIP-OGCG: icmp ne ptr {{.*}}, null // HIP-OGCG: call void @__hipUnregisterFatBinary // HIP-OGCG: store ptr null, ptr @__hip_gpubin_handle // HIP LLVM lowering cross-check. // HIP-LLVM: @{{.*}} = private constant [25 x i8] c"GPU binary would be here.", section ".hip_fatbin", align 8 // HIP-LLVM: @__hip_fatbin_wrapper = {{.*}}constant { i32, i32, ptr, ptr } { i32 1212764230, i32 1, ptr @{{.*}}, ptr null }, section ".hipFatBinSegment" // HIP-LLVM: @__hip_gpubin_handle = internal global ptr null // HIP-LLVM: @_Z10kernelfunciii = constant ptr @_Z25__device_stub__kernelfunciii, align 8 // HIP-LLVM: @llvm.global_ctors = appending global {{.*}}@__hip_module_ctor // HIP-LLVM: define internal void @__hip_module_dtor() // HIP-LLVM: load ptr, ptr @__hip_gpubin_handle // HIP-LLVM: icmp ne ptr {{.*}}, null // HIP-LLVM: br i1 {{.*}}, label %{{.*}}, label %{{.*}} // HIP-LLVM: call void @__hipUnregisterFatBinary(ptr {{.*}}) // HIP-LLVM: store ptr null, ptr @__hip_gpubin_handle // HIP-LLVM: ret void // HIP-LLVM: define internal void @__hip_register_globals(ptr %[[FATBIN:.*]]) // HIP-LLVM: call{{.*}}@__hipRegisterFunction(ptr %[[FATBIN]], ptr @_Z10kernelfunciii, ptr @{{.*}}, ptr @{{.*}}, i32 -1, ptr null, ptr null, ptr null, ptr null, ptr null) // HIP-LLVM: ret void // HIP-LLVM: define internal void @__hip_module_ctor() // HIP-LLVM: load ptr, ptr @__hip_gpubin_handle // HIP-LLVM: icmp eq ptr {{.*}}, null // HIP-LLVM: br i1 {{.*}}, label %{{.*}}, label %{{.*}} // HIP-LLVM: call ptr @__hipRegisterFatBinary(ptr @__hip_fatbin_wrapper) // HIP-LLVM: store ptr {{.*}}, ptr @__hip_gpubin_handle // HIP-LLVM: load ptr, ptr @__hip_gpubin_handle // HIP-LLVM: call void @__hip_register_globals(ptr {{.*}}) // HIP-LLVM: call i32 @atexit(ptr @__hip_module_dtor) // HIP-LLVM: ret void // No GPU binary: no fatbin, no handle, no registration scaffolding. // HIP-NOGPUBIN-NOT: __hip_fatbin // HIP-NOGPUBIN-NOT: __hip_gpubin_handle // HIP-NOGPUBIN-NOT: __hip_register_globals // HIP-NOGPUBIN-NOT: __hip_module_ctor // HIP-NOGPUBIN-NOT: __hip_module_dtor