PoCL如何用LLVM Pass把GPU思维“编译”成CPU代码:OpenCL 3.0在CPU上的SPMD魔法

SPMDOpenCL 的核心灵魂:成千上万的工作项(work-item)在同一段内核代码中并行推进,遇到 barrier() 时整齐划一地同步对齐。

这套执行模型在 GPU 上由硬件原生支撑,然而一旦目标平台变为 x86_64、ARM64 或 RISC-V 这类只能单线程串行执行的 CPU,难题便浮出水面——如何让一个 CPU 核心同时模拟 256 个工作项,并在屏障处实现”全员到位”的效果?

这正是 pocl/pocl[1] 必须攻克的核心挑战。

作为目前唯一在 CPU[2] 和 Level Zero GPU[3] 上都通过 Khronos 官方一致性认证的开源 OpenCL 3.0 实现,PoCL 给出的解决方案是:在 LLVM IR 层面,通过编译器 Pass 将工作项的”并行性”折叠成”循环”,并将屏障转化为循环的边界

本文将深入 60 多万行代码,聚焦 lib/llvmopencl/ 这一 PoCL 的”核心引擎”,揭示它如何将一份以 GPU 思维编写的内核,转变为 CPU 也能高效执行的机器码。

快速上手与使用

以下是 Ubuntu 系统上构建并运行 PoCL CPU 驱动的最简流程。更详细的依赖说明、交叉编译、CUDA / Level Zero / Vulkan 后端构建指引,请参阅官方安装指南[4] 及仓库 README.md[5]。

# 1. 安装依赖(LLVM/Clang 必须为最新发布版本)
sudo apt install -y cmake ninja-build python3 
libclang-dev libclang-cpp-dev libllvm-dev 
zlib1g-dev libtinfo-dev libhwloc-dev ocl-icd-libopencl1 ocl-icd-opencl-dev

# 2. 拉取并构建(默认开启 CPU 驱动 + ICD)
git clone https://github.com/pocl/pocl.git && cd pocl
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build -j

# 3. 不安装也可直接使用:将构建目录中的 ICD 注册给 OpenCL Loader
export OCL_ICD_VENDORS=$PWD/build/ocl-vendors
clinfo | head    # 应能看到 "Portable Computing Language" 平台

若仅想体验,发行版的二进制包同样可用(例如 apt install pocl-opencl-icdbrew install poclmamba install pocl-cuda)。

CTS 一致性测试、SPIR-V 支持等细节,可参考构建文档[6] 中的 ENABLE_CONFORMANCEENABLE_SPIRV 等选项。

一、架构总览与设计哲学

1.1 三层结构:Host Runtime / Kernel Compiler / Device Driver

打开仓库根目录,PoCL 的物理结构其实相当简洁,主要由三大模块构成:

PoCL如何用LLVM Pass把GPU思维“编译”成CPU代码:OpenCL 3.0在CPU上的SPMD魔法

第2/6部分

核心架构组件详解

PoCL的代码库围绕三个关键目录组织,各自承担截然不同的职责:

  • lib/CL/:主机运行时(Host Runtime)。每个 clXxx.c 文件对应一个OpenCL API入口点,例如 clEnqueueNDRangeKernel.c[7] 和 clCreateBuffer.c[8]。这些文件的核心工作包括:参数校验、引用计数管理、命令打包,最终将任务委派给设备驱动层。共享的数据结构集中在体量惊人的 pocl_cl.h[9] 头文件中。

  • lib/llvmopencl/:内核编译器(Kernel Compiler)。这是PoCL区别于普通“LLVM调优”项目的灵魂所在。该目录由数十个自定义的LLVM Function/Module Pass组成,负责将Clang生成的OpenCL内核IR转换为“一个工作组就是一个普通函数”的形式,使内核能够以串行循环配合SIMD指令在CPU上执行。

  • lib/CL/devices/:设备驱动(Device Drivers)。CPU、CUDA、Level Zero、Vulkan、Remote、OpenASIP等后端均位于此目录,通过统一的 pocl_devices_ops 函数表接入运行时系统。

整套流程可以抽象为一条“两端窄、中间宽”的处理链路:

clBuildProgram ─► Clang(前端) ─► LLVM IR ─► PoCL Pass 流水线 ─► 设备 ISA / SPIR-V ─► clEnqueueNDRangeKernel
(lib/llvmopencl) (lib/CL/devices/*)

1.2 为何需要专属内核编译器

当OpenCL程序员编写 kernel void k(...) { ... barrier(CLK_LOCAL_MEM_FENCE); ... } 时,其假设是:有 N = local_size_x × y × z 个工作项同时执行,并在 barrier() 处完成同步对齐。

  • GPU通过warp/wave调度机制天然满足这一语义要求;
  • 但CPU环境下,如果朴素地为每个工作项启动一个线程,N值动辄上千,线程调度开销将彻底碾压实际计算。

PoCL的关键洞察在于:屏障切分了内核的控制流——两个屏障之间的代码段对所有工作项而言是“互不干涉的并行执行”,这恰好可以折叠成一个 for (lid=0; lid<local_size; ++lid) 的串行/可向量化循环。这一洞察由Pekka Jääskeläinen等人在2015年的论文中首次提出,PoCL正是其参考实现。

下文我们将跟随IR进入Pass流水线,一路向下探索。

二、Kernel Compiler:让屏障变成循环边界

2.1 核心抽象:BarrierParallelRegion

PoCL首先将OpenCL的 barrier() 调用标准化为一个特殊的LLVM CallInst——pocl.barrier

它由 Barrier[10] 这个继承自 llvm::CallInst 的薄封装类来表达:

// 来源:lib/llvmopencl/Barrier.h
class Barrier : public llvm::CallInst {
public:
// 在BB起始/末尾插入屏障;若已存在则复用
static Barrier *createAtStart(llvm::BasicBlock *BB) {
return create(BB->getFirstInsertionPt());
}
static Barrier *createAtEnd(llvm::BasicBlock *BB) {
return create(BB->getTerminator()->getIterator());
}
// 判定一个循环里是否包含屏障——非常关键的查询
static bool isLoopWithBarrier(llvm::Loop &L) {
for (auto *BB : L.blocks())
for (auto &I : *BB)
if (llvm::isa<Barrier>(&I)) return true;
return false;
}
// 利用LLVM RTTI让 isa<Barrier>() 工作
static bool classof(const llvm::CallInst *C) {
return C->getCalledFunction() &&
C->getCalledFunction()->getName() == BARRIER_FUNCTION_NAME;
}
};

有了 Barrier 之后,PoCL可以执行一个图论操作:将控制流图(CFG)按照“路径上必经一个屏障”的关系切割成若干 ParallelRegion——这是PoCL编译器最核心的概念,定义在 ParallelRegion.h[11] 中。

一个ParallelRegion是一段在所有工作项中无屏障穿越的连通子图,其入口前、出口后都是屏障基本块。一旦切分完成,对于同一个ParallelRegion,所有工作项的执行就彼此独立——这正是后续将其套入工作项循环的合法性基础。

2.2 屏障规范化:CanonicalizeBarriersImplicitLoopBarriers

为了让ParallelRegion的切分干净利落,PoCL首先对IR进行一系列“形整”处理。最重要的Pass包括:

  • CanonicalizeBarriers[12]:确保每个屏障独占一个基本块,且该基本块只包含屏障这一条指令。这把屏障变成了纯粹的“图边界”。
  • ImplicitLoopBarriers[13]:如果某个循环内部包含屏障,按照OpenCL语义,所有工作项必须在循环的每次迭代都进行同步。该Pass在循环前后插入隐式屏障,避免后续ParallelRegion切分时错误地将循环视为可独立并行的区域。
  • ImplicitConditionalBarriers[14]:处理“条件分支两侧路径不一致”的屏障,将其提升到分支前或汇合点,避免发散导致的死锁问题。
  • LoopBarriers[15]:确保带屏障的循环header与latch都各自拥有独立的屏障基本块。

这些看似“啰嗦”的预处理Pass共同维护一个不变量:所有发散的屏障必须等价地存在于所有工作项的执行路径上——否则SPMD语义无法折叠为循环。

2.3 选择策略:WorkitemHandlerChooser

切分完ParallelRegion之后,下一个问题是:如何将“在所有工作项上重复执行ParallelRegion”这一操作实现出来?

PoCL 在 WorkitemHandlerChooser.h[16] 文件中定义了两条技术路线:

// 来源:lib/llvmopencl/WorkitemHandlerChooser.h  
enum class WorkitemHandlerType { LOOPS, CBS, INVALID };  

class WorkitemHandlerChooser  
: public llvm::AnalysisInfoMixin<WorkitemHandlerChooser> {  
public:  
using Result = WorkitemHandlerResult;     // 仅承载枚举  
Result run(llvm::Function &F, llvm::FunctionAnalysisManager &AM);  
};  

WorkitemHandlerType ChooseWorkitemHandler(llvm::Function &F);  
  • LOOPS:通过 WorkitemLoops[17] Pass 实现。其原理是在每个 ParallelRegion 外层嵌套 1 至 3 维 for 循环(分别对应 X/Y/Z 维度的 local_id)。这是系统默认且最为成熟的策略。
  • CBS(Continuation-Based Synchronization):由 SubCFGFormation[18] 负责实现。该方案采用 sub-CFG 复制与状态机切换机制,在处理包含不规则控制流的内核时表现更佳,同时更有利于 LLVM 的循环向量化器(LoopVectorize)发挥优化能力。

Chooser 作为 LLVM Analysis 组件,并不会修改 IR。它的职责是将决策结果挂载到分析管理器上,供后续 Pass 按需取用。

2.4 主角登场:WorkitemLoops 如何包出循环

WorkitemLoops[19] 的实现类 WorkitemLoopsImpl 内部持有 LLVM 的 DominatorTree、LoopInfo、PostDominatorTree,以及 PoCL 自研的 VariableUniformityAnalysis

其核心操作是:针对每一个 ParallelRegion,调用 createLoopAround 在 X、Y、Z 维度上依次包裹一层循环。

// 来源:lib/llvmopencl/WorkitemLoops.cc  
class WorkitemLoopsImpl :public pocl::WorkitemHandler {  
// 关键状态  
llvm::DominatorTree &DT;  
llvm::LoopInfo &LI;  
llvm::PostDominatorTree &PDT;  
VariableUniformityAnalysisResult &VUA;         // 标记哪些变量对所有 WI 取值相同  
ParallelRegion::ParallelRegionVector OriginalParallelRegions; // 切分结果  
StrInstructionMap ContextArrays;               // 跨区域变量的"上下文数组"  
std::array<llvm::GlobalVariable *, 3> GlobalIdIterators;  
llvm::Value *LocalIdXFirstVar;                 // 第一次迭代起点(用于已 peeled 的 0,0,0)  

// 围绕 Region 在 Dim 维度上构建 [EntryBB, ExitBB] 之间的 for 循环  
std::pair<llvm::BasicBlock *, llvm::BasicBlock *>  
createLoopAround(ParallelRegion &Region,  
llvm::BasicBlock *EntryBB,  
llvm::BasicBlock *ExitBB,  
bool PeeledFirst, int Dim,  
bool AddIncBlock = true,  
llvm::Value *DynamicLocalSize = nullptr);  
};  

createLoopAround 在内部生成的 IR 模板(以下内容源自其在源文件中的英文注释翻译)大致呈现如下结构:

; 来源:lib/llvmopencl/WorkitemLoops.cc 中 createLoopAround 注释模板  
for.init:  
store i32 0, i32* %_local_id_x        ; PeeledFirst=false 时常规起步  
br label %for.cond  
for.cond:  
%lid = load i32, i32* %_local_id_x  
%cmp = icmp ult i32 %lid, %local_size_x  
br i1 %cmp, label %region.entry, label %for.end  
region.entry:  
... ; ParallelRegion 原本的代码  
br label %for.inc  
for.inc:  
%next = add i32 %lid, 1  
store i32 %next, i32* %_local_id_x  
br label %for.cond  
for.end:                                ; 即外层 ExitBB / 屏障所在 BB  

在 Y、Z 维度上再各套一层类似的循环后,整个工作组内 N = X·Y·Z 次工作项的执行就被串联成了顺序循环。LLVM 在识别出这种”标准 induction variable 循环”后,能够顺势启用 LoopVectorize、SLP 向量化、Loop Unroll 等通用优化——这正是 PoCL 让 OpenCL 内核在 CPU 上获得可观性能的核心秘诀。

2.5 跨区域变量的”上下文数组”

仅仅包裹好循环还不够。考虑以下简化代码:

int x = compute();  
barrier(CLK_LOCAL_MEM_FENCE);  
use(x);  

变量 x 在屏障前被定义、在屏障后被使用——这意味着屏障将代码切分成了两个 ParallelRegion,而每个工作项各自的 x 值必须被完整保留到第二个 Region 才能被正确使用。

PoCL 的解决方案是:将这种”跨区域活变量”提升为一个长度等于 local_size_x*y*z 的 alloca 数组(即代码中的 ContextArrays)。具体操作为:在第一个 Region 的循环内部,按 local_id 索引将值存入数组;在第二个 Region 的循环内部,再按相同的 local_id 索引将值取出。

WorkitemLoopsImpl::fixMultiRegionVariables 正是负责处理这一逻辑的函数。它充分利用了上一节预先构建的支配树和后支配树,以及 VariableUniformityAnalysis 的分析结果——后者能够识别出“对所有工作项而言值都相同的变量”(例如循环不变量和 kernel 参数),从而避免对这些变量进行不必要的上下文存储,有效节省栈空间和执行时间。

2.6 PHIsToAllocas、IsolateRegions 等辅助工具

为了确保上下文存储能够正确实现,PoCL 还提供了一系列“看似不起眼,实则不可或缺”的 Pass:

  • PHIsToAllocas[20]:将 PHI 节点降级为内存读写操作,方便在循环外部通过 alloca 数组统一管理变量的生命周期。
  • IsolateRegions[21]:在 Region 边界处插入空的基块,防止不同 Region 之间的边相互干扰。
  • AllocasToEntry[22]:将所有 alloca 指令集中到入口块,为后续的 mem2reg 优化创造便利。
  • BarrierTailReplication[23]:处理屏障后的“尾部”代码,将其在多个前驱块中进行复制,从而正确建模屏障的多入口语义。

这些 Pass 协同工作,将 LLVM IR 塑造成一种“对工作项循环非常友好”的形态。

2.7 终点:Workgroup 函数包装

经过上述一系列变换,每个 OpenCL kernel K 最终会被编译成一个等价的 C 函数 _pocl_kernel_K_workgroup,其签名大致如下:

// 来源:lib/llvmopencl/Workgroup.h(概念签名)
void _pocl_kernel_K_workgroup(uint8_t **args,
void *pocl_context,    // group_id, local_size, ...
uint32_t group_x,
uint32_t group_y,
uint32_t group_z);

Workgroup.cc[24] 负责生成这层包装代码:它从一个统一的指针数组中“拆包”出 OpenCL kernel 的参数,然后调用已经包含工作项循环的内核体。设备驱动程序只需在主机端串联起 for (group_x...) for (group_y...) for (group_z...) workgroup(...) 循环,而工作项级别的并行性已经在编译阶段被完全折叠处理。

三、Host Runtime:从 API 到设备命令

3.1 ICD 与 Platform Dispatch

OpenCL 应用程序通常通过 ICD Loader 来调用厂商的具体实现。

PoCL 在 clIcdGetFunctionAddressForPlatformKHR.c[25] 和 clIcdGetPlatformIDsKHR.c[26] 中实现了 ICD 入口点;pocl_intfn.h[27] 则将所有的 clXxx 入口函数集中起来,转化为可通过 dispatch 表索引的内部函数指针。

3.2 命令队列与事件机制

OpenCL 采用异步语义:clEnqueueXxx 系列函数将命令放入队列,并通过 cl_event 对象来串联依赖关系。

PoCL 在 pocl_util.c[28](73 KB,堪称 Runtime 层的“瑞士军刀”)和 pocl_mem_management.c[29] 中实现了以下核心机制:

  • pocl_create_eventpocl_create_command:用于构造命令节点和事件对象。
  • 事件状态机:遵循 SUBMITTED → READY → RUNNING → COMPLETE 的状态转换,跨设备时还需处理数据迁移。
  • 调度逻辑:将命令入队到设备驱动提供的 push_command 钩子函数中。

clEnqueueNDRangeKernel.c 主要负责参数检查,然后将任务转交给 pocl_ndrange_kernel.c。后者会完成 work-group 的切分、work_dim 的校正、SVM/USM 指针的登记等操作,最后构建一条 CL_COMMAND_NDRANGE_KERNEL 类型的命令,并将其送入设备队列。

3.3 程序构建:从源码/SPIR-V 到 LLVM bitcode

clBuildProgram 看似只是一个简单的包装函数,但其繁重的工作实际上由 pocl_build.c[30] 和 pocl_llvm_build.cc[31](41 KB)承担。

pocl_llvm_build.cc 直接调用 Clang 的 C++ API,完成从 OpenCL C 源码到 LLVM IR 的前端编译。pocl_llvm_spirv.cc[32] 则负责处理 SPIR-V 格式的输入和输出。生成的 bitcode 会通过 pocl_cache.c[33] 进行内容哈希缓存,从而避免重复构建——这也是 PoCL 在大型应用首次启动后能够“预热”并显著加速的原因。

四、Device Driver:统一接口下的多后端支持

4.1 pocl_devices_ops 函数表

每个设备驱动程序通过实现一组同构的回调函数来接入 Runtime:包括内存分配、命令调度、内核执行、镜像访问等。

  • CPU 驱动位于 lib/CL/devices/pthread/lib/CL/devices/basic/ 等子目录下。
  • GPU 后端则位于 lib/CL/devices/level0/lib/CL/devices/cuda/ 等目录中。

这种“瘦驱动 + 厚 Runtime”的设计使得 OpenCL 一致性测试(CTS)只需在 Runtime 层修复一次 bug,所有驱动程序便能自动受益。

4.2 CPU 驱动:将 Workgroup 函数作为函数指针调用

对于每个 NDRange 命令,CPU 驱动会在主机端执行三层 group 循环,每次直接调用 workgroup_fn(args, &ctx, gx, gy, gz)

由于 workgroup_fn 内部已经包含了工作项循环和 LLVM 向量化后的本地代码,其整体性能在处理标准 BLAS、卷积、reduce 等 kernel 时,能够逼近手写 OpenMP 实现的水平

此外,CPU 驱动还借助 hwloc[34] 自动识别 CPU 拓扑结构,将 group 调度到合适的核心上,并通过 pocl_threads.c[35] 实现的轻量级线程池来减少调度抖动。

4.3 GPU 驱动:编译期目标切换

对于 CUDA 和 Level Zero 后端,lib/llvmopencl/ 中的同一套 Pass 流水线同样适用,只是在 Workgroup.cc[36] 和 SPIR-V/PTX 生成阶段切换了 LLVM 的目标平台——这种“前端和中端通用、后端按设备分流”的策略,正是 PoCL 能够用一套代码覆盖如此多硬件平台的根本原因。

五、几个值得回味的工程细节

5.1 不变量驱动的 Pass 设计

回顾 lib/llvmopencl/ 下的 Pass 设计,我们可以看到一种朴素却高效的分层思想:首先通过预处理 Pass 维护语义不变量(例如,屏障在每个工作项上都是必经路径,包含屏障的循环必须同步),然后再让真正改写代码的 Pass(如 WorkitemLoops / SubCFGFormation)在干净的 IR 上工作。这种设计的好处是,每个 Pass 的复杂度可控,并且可以独立进行测试——这对于一个拥有 60 多万行代码、并需要同时支持 LLVM 18 到 22 等多个大版本的项目来说至关重要。

5.2 与 LLVM 上游生态的紧耦合

PoCL 大量直接通过 #include <llvm/...> 紧密对接 LLVM 内部 API,这种做法就像一把双刃剑:一方面它能充分利用最新优化手段(例如利用 MinLegalVecSize[37] 调整向量化阈值,以及通过 SanitizeUBofDivRem[38] 为未定义行为提供兜底处理);但另一方面,它每年都必须适配 LLVM 的大版本迭代——README 中关于 CUDA/LLVM 21 的 Clang Bug 兼容说明就是一个典型的例子。

5.3 一致性背后的代价

README 中提供的 CTS 通过率数据表明:CPU/x86_64 驱动能够 100% 通过 OpenCL 3.0 CTS 测试,CPU/RISCV 驱动也达到了 99% 以上。这些亮眼成绩的背后,是对大量边界情况的修复与打磨,例如:图像采样器的初始化逻辑(HandleSamplerInitialization.cc[39])、半精度浮点的模拟实现(pocl_cl_half_util.c[40])、以及 SVM 偏移量的修正(SVMOffset.cc[41])。正是这些看似“边角料”的细节工作,让 PoCL 得以跻身 Khronos Conformant Products 官方名单。

总结:PoCL 带给我们的启示

如果只能用一句话来概括 PoCL,那便是:它借助 LLVM IR 这一通用底层设施,将 OpenCL 的 SPMD 语义转化为 CPU 能够高效执行的循环逻辑。在这背后,是一条清晰得足以当作编译课程教材的 Pass 流水线:

  1. 标准化屏障处理
  2. 切分 ParallelRegion
  3. 选择处理策略(LOOPS / CBS)
  4. 插入工作项循环
  5. 跨区域变量上下文化
  6. 封装为 workgroup 函数
  7. 多后端代码生成

而 Host Runtime(位于 lib/CL/)则借助一套统一的 ICD、事件和命令缓冲机制,将 OpenCL API 与 Device Driver 成功解耦。这使得 CPU、CUDA、Level Zero、Vulkan、Remote 等差异巨大的后端,能够在同一套测试框架下被验证为“符合标准”。

  • 对于编译器领域的从业者来说,PoCL 是一本关于 SPMD-to-SIMD 转换、屏障语义降级以及 LLVM Pass 工程化的鲜活教材;
  • 对于系统软件开发者而言,它展示了“瘦驱动 + 厚 Runtime”这一架构如何在保证可移植性的同时,不牺牲一致性;
  • 对于硬件加速器的初创团队,它甚至提供了一条捷径——只需编写一个驱动并接入 pocl_devices_ops,就能立刻获得一个完整的 OpenCL 3.0 实现。

这或许正是 PoCL 在异构计算日益碎片化的今天,依然不可替代的根本价值所在。

参考资料
[1] pocl/pocl: https://github.com/pocl/pocl
[2] CPU: https://www.khronos.org/conformance/adopters/conformant-products/opencl#submission_450
[3] Level Zero GPU: https://www.khronos.org/conformance/adopters/conformant-products/opencl#submission_453
[4] 官方安装指南: http://portablecl.org/docs/html/install.html
[5] README.md: https://github.com/pocl/pocl/blob/main/README.md
[6] 构建文档: http://portablecl.org/docs/html/install.html
[7] clEnqueueNDRangeKernel.c: https://github.com/pocl/pocl/blob/main/lib/CL/clEnqueueNDRangeKernel.c
[8] clCreateBuffer.c: https://github.com/pocl/pocl/blob/main/lib/CL/clCreateBuffer.c
[9] pocl_cl.h: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_cl.h
[10] Barrier: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/Barrier.h
[11] ParallelRegion.h: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/ParallelRegion.h
[12] CanonicalizeBarriers: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/CanonicalizeBarriers.cc
[13] ImplicitLoopBarriers: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/ImplicitLoopBarriers.cc
[14] ImplicitConditionalBarriers: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/ImplicitConditionalBarriers.cc
[15] LoopBarriers: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/LoopBarriers.cc
[16] WorkitemHandlerChooser.h: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/WorkitemHandlerChooser.h
[17] WorkitemLoops: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/WorkitemLoops.cc
[18] SubCFGFormation: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/SubCFGFormation.cc
[19] WorkitemLoops: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/WorkitemLoops.cc
[20] PHIsToAllocas: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/PHIsToAllocas.cc
[21] IsolateRegions: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/IsolateRegions.cc
[22] AllocasToEntry: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/AllocasToEntry.cc

BarrierTailReplication: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/BarrierTailReplication.cc

[24]

Workgroup.cc: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/Workgroup.cc

[25]

clIcdGetFunctionAddressForPlatformKHR.c: https://github.com/pocl/pocl/blob/main/lib/CL/clIcdGetFunctionAddressForPlatformKHR.c

[26]

clIcdGetPlatformIDsKHR.c: https://github.com/pocl/pocl/blob/main/lib/CL/clIcdGetPlatformIDsKHR.c

[27]

pocl_intfn.h: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_intfn.h

[28]

pocl_util.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_util.c

[29]

pocl_mem_management.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_mem_management.c

[30]

pocl_build.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_build.c

[31]

pocl_llvm_build.cc: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_llvm_build.cc

[32]

pocl_llvm_spirv.cc: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_llvm_spirv.cc

[33]

pocl_cache.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_cache.c

[34]

hwloc: https://www.open-mpi.org/projects/hwloc/

[35]

pocl_threads.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_threads.c

[36]

Workgroup.cc: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/Workgroup.cc

[37]

MinLegalVecSize: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/MinLegalVecSize.cc

[38]

SanitizeUBofDivRem: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/SanitizeUBofDivRem.cc

[39]

HandleSamplerInitialization.cc: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/HandleSamplerInitialization.cc

[40]

pocl_cl_half_util.c: https://github.com/pocl/pocl/blob/main/lib/CL/pocl_cl_half_util.c

[41]

SVMOffset.cc: https://github.com/pocl/pocl/blob/main/lib/llvmopencl/SVMOffset.cc


关注“鲸栖”小程序,掌握最新AI资讯

本文来自网络搜集,不代表鲸林向海立场,如有侵权,联系删除。转载请注明出处:https://www.itsolotime.com/archives/35800

(0)
上一篇 14小时前
下一篇 14小时前

相关推荐

  • 解锁自动化新境界:n8n与飞书多维表格的完美融合,打造高效工作流

    解锁自动化新境界:n8n与飞书多维表格的完美融合,打造高效工作流 在利用开源工作流工具 n8n 构建自动化流程时,数据的归档与存储是关键环节。飞书多维表格为此提供了一个优秀的解决方案。它不仅可作为结构化数据的中转站,更能借助其「字段捷径」功能,与 AI 处理或后续自动化流程无缝衔接,实现效率的倍增。 值得一提的是,飞书多维表格现已支持网页端直接访问(base…

    2025年11月7日
    1.6K00
  • nncase:基于e-graph的端到端LLM编译器,突破异构存储架构性能瓶颈

    关键词:LLM 编译、 e-graph、异构存储架构、统一分布式编译、自动优化、端到端编译框架 本文转载自知乎账号:郑启航[1] 原文链接:https://zhuanlan.zhihu.com/p/1989088940733510928 nncase: An End-to-End Compiler for Efficient LLM Deployment o…

    2025年12月30日
    52900
  • AI视觉革命:5大开源项目让大模型像人类一样操控手机

    在过去,自动化操作手机通常需要依赖 Appium 或 Airtest 等工具,这要求开发者必须深入了解应用的底层元素标识,如 resource-id 或 xpath。一旦应用更新导致这些标识符发生变化,自动化脚本便会失效。 如今,随着 AI 大模型,尤其是视觉模型的发展,让 AI 像人类一样“看懂”并操控手机屏幕成为可能。本文将介绍几个热门的、利用 AI 实…

    2025年11月25日
    1.9K00
  • iPhone 17 Pro跑400B大模型!Flash-MoE让端侧AI突破物理极限

    有时候看到一些大模型项目,总会怀疑是不是真的有外星人在干预地球科技。 就比如今天这个。 刚看到这个 Demo 时确实有点想笑,已经很久没见过吐词如此缓慢的大模型了。观感上就像“闪电”老师。 尽管每秒只有 0.6 个 tokens 的输出速度,这依然是一项令人难以置信的成果。因为这是一个运行在 iPhone 17 Pro 上的 400B 大模型! 准确来说,这…

    2026年5月2日
    26000
  • 无需训练!开源Web Agent Avenir-Web刷新纪录,成功率53.7%逼近OpenAI Operator

    告别“网页操作翻车”:开源Web Agent Avenir-Web 刷新纪录,成功率飙至53.7% 伦敦大学学院(UCL)、普林斯顿大学与爱丁堡大学的联合研究团队,近日推出了名为 Avenir-Web 的开源框架。它让现有的多模态模型具备了像人类一样操作网页的能力,无需任何额外训练即可投入使用。 现有的Web Agent在处理复杂网页结构(例如 iframe…

    2026年4月29日
    35100