LLVM AMDGPU 后端代码分析研究(4)AMDGPUAtomicOptimizer

1. 介绍

在计算机并行计算领域,并行计算模型常分为以下四种情况:

  • 单指令单数据 (SISD)
  • 单指令多数据 (SIMD)
  • 多指令单数据 (MISD)
  • 多指令多数据 (MIMD)

在GPU shader编程中,处理器会自动把shader转换为并行执行,如Pixel shader, 只需对一个像素点处理,GPU会对所有的像素做相同处理,这是一种隐式的SIMD,用户是无法控制的,在最新的DX12/Vulkan 图形API中,都加强了用户层代码对底层功能的控制能力,HLSL SM6.0 提供了一系列Wave操作,Vulkan也提供了类型概念Invacation , 详见文献[3,4], 这类接口使用户层编写一些并行程序更加容易,如FFT(快速傅里叶变换),并行快排算法,但想要高效的使用这些接口,需要理解GPU如何执行的.

假设我们的芯片是一个SIMD32的架构,下图中每一个小方块代表一个lane, 上面有不同的数据,
在这里插入图片描述
在芯片上运行下面这段代码

instruction 1;

----------
if (condition)
{
	color =  vec4(1.0,1.0,1.0,0.0);   //  a
}
else {
	if (condition2)
		color =  vec4(1.0,1.0,1.0,0.0);  // b
	else
		color =  vec4(1.0,1.0,1.0,0.0);  // c
}
----------
   
instruction 2;

上面的代码片段分为三个部分:if-else 语句前一条,if-else, if-else语句前后一条. SIMD 上不同的数据会根据条件是否满足,相应执行a,b,c 路径,而GPU SIMD 是lockstep的执行模式,即虽然 a 路径的 线程执行结束后直接到达 instruction 2前, 但是它们不会继续往下执行,必须等待b,c路径的线程也执行到instruction 2前,所有的线程才能继续执行下去。如果代码中有很多的嵌套if-else, 先执行完的线程会等待很多时钟周期等待其他线程,如果SIMD 出现了这种情况,会对GPU 性能有很大的浪费。divergence 也就是指这种情况,SIMD遇到了if-else 分支,线程出现了分叉的情况。

对于Divergence的分析与优化,用户层代码不需要考虑,主要是通过Compiler的来优化,从参考文献1已经提出解决方案。在此,先提前提出几个问题帮助理解这个Pass:

  • 如何用静态分析技术,判断某种指令在该条件分支是否会造成divergence? 反之,该指令会让线程汇聚(Uniform)
  1. 学术答案:根据论文1 和论文2 可知,
THEOREM 3.1. A variable v ∈ P is divergent if, and only if, one of these conditions hold:
	1. v = tid. 
	2. v is defined by the atomic increment, e.g: atomic(v, vx).
	3. v is data dependent on some divergent variable.   
	4. v is sync dependent on some divergent variable.
  1. 工业界答案:根据AMD源码可知

 1. 传参,看寄存器类型
 2. Load 指令,具体看地址空间
 3. atomic 指令
 4. intrinsic指令 :AMDGPUSearchableTables.td
 5. 排除部分InlineAsm 的所有call指令
 6. InvokeInst 指令

bool GCNTTIImpl::isSourceOfDivergence(const Value *V) const {
  if (const Argument *A = dyn_cast<Argument>(V))
    return !isArgPassedInSGPR(A);

  if (const LoadInst *Load = dyn_cast<LoadInst>(V))
    return Load->getPointerAddressSpace() == AMDGPUAS::PRIVATE_ADDRESS ||
           Load->getPointerAddressSpace() == AMDGPUAS::FLAT_ADDRESS;

  if (isa<AtomicRMWInst>(V) || isa<AtomicCmpXchgInst>(V))
    return true;

  if (const IntrinsicInst *Intrinsic = dyn_cast<IntrinsicInst>(V))
    return AMDGPU::isIntrinsicSourceOfDivergence(Intrinsic->getIntrinsicID());

  if (const CallInst *CI = dyn_cast<CallInst>(V)) {
    if (CI->isInlineAsm())
      return isInlineAsmSourceOfDivergence(CI);
    return true;
  }

  if (isa<InvokeInst>(V))
    return true;

  return false;
}

2. Divergence 算法伪码


GPUDivergenceAnalysis()
{
    // S1: Collect some Known info from hardware view.
    DenseSet<const Value *> DivergentValues;
	DenseSet<const Value *> UniformOverrides;
    for (auto &I : instructions(F)) {
		if (isSourceOfDivergence(I))
            DivergentValues.insert(I);
        else if(isAlwaysUniform(I))
			UniformOverrides.insert(I);
    }

	// S2. Deduce unknown information
    
}

5. 参考文献

  1. https://homepages.dcc.ufmg.br/~fernando/publications/papers/divergence.pdf
  2. https://hal.inria.fr/hal-00909072/document
  3. https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/divergence-optimizations/
  4. https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12#shading-language-intrinsics
  5. https://gpuopen.com/wp-content/uploads/2017/07/GDC2017-Wave-Programming-D3D12-Vulkan.pdf
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
污点分析是一种静态程序分析技术,用于确定程序中哪些变量可以被恶意输入或其他安全漏洞所利用。LLVM是一个广泛使用的编译器基础设施,可以用于实现污点分析。下面是一个简单的LLVM Pass,它实现了简单的污点分析。 首先,我们需要定义一个Pass类,该类继承自llvm::FunctionPass。然后,我们需要在runOnFunction函数中实现我们的污点分析逻辑。在这个例子中,我们将通过检查函数的参数和指令来确定哪些变量是受污染的。 ```c++ #include "llvm/IR/Function.h" #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/Instructions.h" using namespace llvm; namespace { struct TaintAnalysis : public FunctionPass { static char ID; TaintAnalysis() : FunctionPass(ID) {} bool runOnFunction(Function &F) override { // 遍历函数的所有基本块 for (auto &BB : F) { // 遍历基本块的所有指令 for (auto &I : BB) { // 如果指令是一个存储指令 if (auto *SI = dyn_cast<StoreInst>(&I)) { // 如果存储指令的源操作数是一个指针类型 if (auto *Ptr = dyn_cast<PointerType>(SI->getOperand(1)->getType())) { // 如果指针指向的类型是整数类型 if (auto *IntTy = dyn_cast<IntegerType>(Ptr->getElementType())) { // 如果整数类型的位宽为8 if (IntTy->getBitWidth() == 8) { // 输出受污染的指针值和存储的值 errs() << "Tainted pointer value: " << *SI->getOperand(1) << "\n"; errs() << "Tainted value: " << *SI->getOperand(0) << "\n"; } } } } } } return false; } }; } char TaintAnalysis::ID = 0; static RegisterPass<TaintAnalysis> X("taint-analysis", "Taint Analysis Pass"); ``` 我们在runOnFunction函数中遍历函数的所有基本块和指令。我们检查每个存储指令,以确定它是否存储了一个指向整数类型的指针,并且该整数类型具有8位的位宽。如果是的话,我们输出受污染的指针值和存储的值。 最后,我们将该Pass注册到LLVM中,以便在编译时运行。我们使用static RegisterPass来注册我们的Pass,并将其命名为“taint-analysis”。 现在,我们可以使用LLVM编译器运行我们的Pass,以便对C或C++程序进行污点分析。例如,假设我们有以下C程序: ```c++ #include <stdio.h> void foo(int *ptr) { int x = *ptr; printf("The value of x is: %d\n", x); } int main() { int y = 42; foo(&y); return 0; } ``` 我们可以使用以下命令编译程序并运行我们的Pass: ``` clang -Xclang -load -Xclang MyPass.so -c test.c ``` 这将生成一个名为“test.o”的目标文件,并使用我们的Pass进行污点分析。如果程序中存在受污染的指针,我们的Pass将输出它们的值。在这个例子中,我们应该得到以下输出: ``` Tainted pointer value: i32* %ptr Tainted value: i32 42 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值