通过Clang / CUDA解析CUDA关键字__shared__

时间:2016-01-12 08:33:54

标签: cuda clang llvm-clang llvm-ir

由于可以使用Clang进行CUDA编译,我有兴趣研究通过clang到中间表示(IR)的cuda代码(.cu文件)转换。

Clang的CUDA编译需要某些CUDA库。那么在CUDA程序中解析关键字__shared__是由Clang还是由CUDA编译器完成的?从我最初的搜索开始,我相信转换是由CUDA而不是Clang完成的。这种理解是否正确?

1 个答案:

答案 0 :(得分:3)

当clang编译CUDA代码时,不涉及Nvidia NVCC编译器。

__shared__或更准确__attribute__((shared)) 是一个属性clang知道。如果clang遇到用shared属性标记的变量,它将做两件事:

  1. 变量将具有静态链接。这意味着变量的定义从内核函数移动到模块范围。
  2. 变量将放在地址空间3中,地址空间3定义为共享内存地址空间。
  3. 用clang编译这个小程序:

    __global__ void foo(int* tmp)
    {
      __shared__ int vec[32];
      vec[threadIdx.x] = tmp[threadIdx.x];
      tmp[threadIdx.y] = vec[threadIdx.y];
    }
    
    int main()
    {
      int* tmp;
      foo<<<1, 1>>>(tmp);
      return tmp[0];
    }
    

    产生以下IR:

      ; ModuleID = 'sm.cu'
      target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
      target triple = "nvptx64-unknown-unknown"
    
      @vec= internal unnamed_addr addrspace(3) global [32 x i32] zeroinitializer, align 4
    
      ; Function Attrs: nounwind readnone
      declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() #0
    
      ; Function Attrs: nounwind readnone
      declare i32 @llvm.nvvm.read.ptx.sreg.tid.y() #0
    
      define ptx_kernel void @__pacxx_kernel0(i32 addrspace(1)* %tmp) {
        %1 = tail call spir_func i32 @llvm.nvvm.read.ptx.sreg.tid.x() #1
        %2 = zext i32 %1 to i64
        %3 = getelementptr i32, i32 addrspace(1)* %tmp, i64 %2
        %4 = load i32, i32 addrspace(1)* %3, align 4
        %5 = getelementptr [32 x i32], [32 x i32] addrspace(3)* @vec, i64 0, i64 %2
        store i32 %4, i32 addrspace(3)* %5, align 4
        %6 = tail call spir_func i32 @llvm.nvvm.read.ptx.sreg.tid.y() #1
        %7 = zext i32 %6 to i64
        %8 = getelementptr [32 x i32], [32 x i32] addrspace(3)* @vec, i64 0, i64 %7
        %9 = load i32, i32 addrspace(3)* %8, align 4
        %10 = getelementptr i32, i32 addrspace(1)* %tmp, i64 %7
        store i32 %9, i32 addrspace(1)* %10, align 4
        ret void
      }
    

    您可以看到变量vec在模块内部具有静态(但内部)链接,并位于地址空间3中。

    Clang遵循NVVM IR规范,可以找到here。但是,NVVM IR是为LLVM 3.4指定的,如果使用较新的LLVM / Clang版本生成的IR,则可能会遇到问题。然而,LLVM的NVPTX后端没有这个限制,可以毫无问题地生成PTX代码。 Clang(在较新的版本中)将像NVCC一样构建一个胖箱。在旧版本的Clang中,您必须自己构建可执行文件,并使用CUDAIsDevice命令行标志编译程序的设备部分。

    PTX代码可用于通过将其与CUDA API链接来对GPU进行编程。

    修改 由于问题出现在这里定义__shared__属性的地方是: 在clang标题中host_defines.h包含在CUDA工具包中。在host_defines.h(来自CUDA 7.5)中,您可以看到:

      192 #define __shared__ \
      193         __location__(shared)
    

    __location__(这是另一个宏定义)扩展为__annotate__

       85 #define __annotate__(a) \
       86         __attribute__((a))
       87 #define __location__(a) \
       88         __annotate__(a)
    

    正如我在答案的第一部分所写的那样扩展到__attribute__。因此__shared__已扩展为__attribute__((shared))