当前位置: 首页 > news >正文

SPIR-V后端稳定性的推进工作报告总结

0 信息来源

https://llvm.org/devmtg/2024-10/slides/techtalk/Paszkowski-Levytskyy-AdvancingSPIR-V-BackendStability.pdf

1. 一段话总结

在2024年LLVM开发者会议上,Vyacheslav Levytskyy和Michal Paszkowski介绍了SPIR-V后端稳定性的推进工作,当前SPIR-V后端已实现显著提升,包括支持约500个LIT测试用例、达到OpenCL 3.0兼容性SYCL兼容性达93%-99%(依优化级别而定),还完成26个SPIR-V扩展的实现并提升了与Khronos LLVM/SPIR-V Translator的兼容性;同时分析了LLVM IR映射到SPIR-V的关键挑战,如类型不匹配、控制流差异、不透明指针问题等,提出了类型推断、聚合体降低机制、多通行证协同解决不透明指针等技术方案,通过LIT测试、spirv-val验证、spirv-sim工具保障正确性,未来计划推进SPIR-V消费者在LLVM代码库的集成及与后端共享代码,以助力SYCL/DPC++项目。

2. 思维导图(mindmap)

## 会议基础信息
- 会议名称:LLVM Developers' Meeting 2024
- 演讲者:Vyacheslav Levytskyy、Michal Paszkowski
- 主题:Advancing SPIR-V Backend Stability: Navigating GlobalISel Compromises
## SPIR-V后端现状与价值
- 核心价值- SPIR-V:IR+可移植二进制格式,异构加速器编程接口- 丰富生态:支持OpenCL、SYCL、GLSL、HLSL等语言/API- 跨厂商统一IR:Khronos定义核心规范,厂商扩展环境
- 后端成果- 测试与兼容性:500个LIT、OpenCL 3.0兼容、SYCL兼容93%-99%- 扩展与兼容:26个SPIR-V扩展,提升与Khronos LLVM/SPIR-V Translator兼容性- 其他:支持Vulkan/HLSL(进行中),集成外部工具库
## 关键挑战:LLVM IR映射到SPIR-V
- 语义与层级问题- SPIR-V语义丰富,与LLVM IR层级相当(甚至更高)- SPIR-V概念难用Machine IR表示,难循标准GlobalISel翻译模式
- 硬件与类型问题- 无实际加速器硬件,需反向翻译SPIR-V到硬件指令集- LLT类型不足以表达SPIR-V类型
- 具体技术问题- 类型与作用域:虚拟寄存器vs SPIRV标识符,LLVM 17前指针含元素类型而SPIR-V指针必带类型- 控制流:SPIR-V需结构化控制流(OpLoopMerge/OpSelectionMerge),无单独if逻辑,难优化分支- 指令语义:LLVM IR与SPIR-V、GISel’s MIR与SPIR-V存在细微差异(如phi、bitcast)- 不透明指针:LLVM 17过渡后,SPIR-V后端需重新推导指针类型
## 技术解决方案
- 类型推断- 模块级推断:识别指令模式(GEP、alloca等),推导复合类型、指针操作数类型,利用内置函数信息- 处理未知类型:记录未知类型,模块所有函数处理后重新检查
- 聚合体降低机制- 初始方案:替换函数签名中聚合体为i32,元数据记录类型以便后续恢复(存缺陷)- 升级方案:注册类型变更,访问原始函数类型;借助@llvm.fake.use/@llvm.spv.value.md沟通目标
- 不透明指针解决- 三通行证协同:SPIRVEmitIntrinsics(推断指针类型)→ SPIRVCallLowering(创建函数声明/降参数)→ SPIRVPreLegalizer(移除 intrinsic,存储类型映射)
- 控制流与示例问题解决- AsmPrinter标签问题:添加SPIR-V格式判断,避免函数体后生成标签- Machine Verifier问题:即时生成OpBitcast,复用通用操作码表示SPIR-V phi-node至最终编码
- TargetExtType应用- 替代不透明结构体表示SPIR-V特殊类型(如OpTypeImage)- 替代TypedPointerType表示推导的嵌套类型(LLVM无法创建TypedPointerType实例)
## 正确性维护
- 测试工具- LIT测试:核心回归测试手段,扩展后结合spirv-val验证SPIR-V二进制合规性- spirv-sim工具(Google贡献):测试SPIR-V结构化器,灵活且抗脆弱
- 避免测试问题:spirv-sim减少“连锁反应”,无需因后端变更频繁修改测试
## 未来工作
- 定位SPIR-V:作为目标更优,替代Khronos LLVM/SPIR-V Translator
- 代码共享与集成:在LLVM代码库集成SPIR-V消费者,共享后端与消费者代码
- 支持项目:助力上游SYCL/DPC++项目,作为测试与生产解决方案

3. 详细总结

一、会议与主题概述

本次内容来自2024年LLVM开发者会议(LLVM Developers’ Meeting 2024),由Vyacheslav Levytskyy和Michal Paszkowski主讲,核心主题为“推进SPIR-V后端稳定性:应对GlobalISel的妥协(Advancing SPIR-V Backend Stability: Navigating GlobalISel Compromises)”,围绕SPIR-V后端的现状、LLVM IR映射到SPIR-V的挑战、技术解决方案、正确性维护及未来规划展开。

二、SPIR-V与后端的核心价值及当前成果

1. SPIR-V的核心价值

  • 定位:既是中间表示(IR),也是可移植二进制格式,作为异构加速器(如GPU、FPGA、NPU)的编程接口。
  • 生态:支持丰富的高级语言与API,包括OpenCL、SYCL、GLSL、HLSL等。
  • 统一性:核心规范由Khronos Group定义,厂商可基于此扩展客户端API环境,成为跨厂商统一IR,由Intel、Microsoft、Google等Khronos成员公司共同开发。

2. SPIR-V后端的当前成果

成果类别具体内容关键数据/指标
测试与兼容性LIT测试用例支持、OpenCL兼容性、SYCL兼容性500个LIT测试用例OpenCL 3.0完全兼容、SYCL兼容性93%-99%(依优化级别变化)
扩展与协同SPIR-V扩展实现、与Khronos工具兼容性已实现26个SPIR-V扩展,提升与Khronos LLVM/SPIR-V Translator的兼容性
其他进展新API支持、工具集成正在推进Vulkan和HLSL支持,实现与外部工具和库的集成

三、LLVM IR映射到SPIR-V的关键挑战

1. 语义与层级差异挑战

  • SPIR-V语义丰富,其抽象层级与LLVM IR相当,甚至在部分场景下更高,导致难以通过标准GlobalISel翻译流程将其映射到Machine IR,且无法满足Machine Verifier的要求。

2. 硬件与类型基础挑战

  • 无实际硬件依赖:SPIR-V是硬件和厂商无关的格式,需通过反向翻译(如重新编码为LLVM IR)转换为具体加速器(FPGA、GPU、NPU)的指令集,且需进行硬件相关优化。
  • 类型表达不足:LLVM的LLT(Low-Level Type)类型无法完整表达SPIR-V的复杂类型需求。

3. 具体技术问题

问题类别具体表现
类型与作用域1. 虚拟寄存器与SPIRV标识符不匹配,GISel虚拟寄存器无法关联原始LLVM IR值,而SPIR-V后端需追踪完整类型(非LLT类型);
2. LLVM 17之前,IR指针包含元素类型,而SPIR-V指针必须带类型,存在“薛定谔的TypedPointerType”问题
控制流差异1. SPIR-V要求结构化控制流,需通过OpLoopMerge(循环)、OpSelectionMerge(选择)声明,而LLVM IR无此强制要求;
2. SPIR-V中标签是启动逻辑基本块的指令,无条件分支后不能删除指令(需为块内最后一条指令);
3. 无单独“if (Cond) then Stmt”逻辑,OpBranchConditional仅支持完整if-then-else,且无法进行分支折叠、If转换等优化
指令语义差异LLVM IR与SPIR-V、GISel’s MIR与SPIR-V在指令语义上存在细微差异,例如phi节点(SPIR-V OpPhi要求每个父块一个条目,LLVM phi可多个条目对应同一前驱)、bitcast指令的处理逻辑不同
不透明指针问题LLVM 17从“带类型指针”过渡到“不透明指针”,简化了LLVM IR,但SPIR-V后端需指针类型生成代码,导致需重新推导指针类型,影响类型声明、嵌套类型降低、内置函数调用解析等流程

四、核心技术解决方案

1. 类型推断方案

  • 推断范围与逻辑:在模块级(Module pass)进行类型推断,通过识别指令模式(如GEP的结果元素类型、alloca的分配类型、addrspacecast的指针操作数)推导类型;利用CallInst中内置函数(如OpGroupAsyncCopy、OpAtomic*)的已知信息,对齐ReturnInst、phi、ICmpInst的类型;分析函数调用点推导函数参数类型。
  • 未知类型处理:记录模块中的未知类型,待所有函数处理完成后重新访问并解决。

2. 聚合体降低机制

  • 初始方案(存在缺陷):移除函数调用中的聚合体以避免崩溃,将函数签名中的聚合体替换为i32,通过元数据记录类型变更以便后续恢复,但存在无法提前推导正确类型、spirv-val报无效SPIR-V、OpStore中类型不匹配等问题。
  • 升级方案与优化
    1. 注册类型变更,直接访问原始函数类型,避免类型丢失;
    2. 与IRTranslator协同:通过void @llvm.fake.use(...)映射虚拟寄存器到原始值,通过void @llvm.spv.value.md(metadata valAttrs)保留名称和数据类型,复用通用逻辑减少维护负担。

3. 不透明指针问题解决方案(三通行证协同)

通行证名称核心功能处理逻辑
SPIRVEmitIntrinsics推断并分配指针类型1. 从常见指令(GEP、Load等)推导类型,发射类型分配intrinsic;
2. 调整操作数类型,发射指针转换intrinsic;
3. 处理phi节点(不同入值类型取最频繁者),递归解决类型不一致;
4. 用assign_ptr_type为SSA值分配类型,或替换为ptrcast intrinsic
SPIRVCallLowering创建函数声明,降低形式参数1. 生成含类型的SPIR-V函数/参数声明(如OpFunction、OpFunctionParameter);
2. 优先从byval/byref属性、assign_type(SPIR-V内置类型)、assign_ptr_type获取指针类型;
3. 硬编码/通过TableGen(SPIRVBuiltins.td)解析OpenCL/GLSL内置函数类型(解决Itanium mangling无返回类型信息问题)
SPIRVPreLegalizer清理与类型存储1. 移除所有assign_ptr_type/assign_type intrinsic调用;
2. 为每个MIR寄存器分配相关类型,通过GlobalRegistry存储类型映射,确保模块内类型声明唯一

4. TargetExtType的应用

  • 用途:LLVM 16新增TargetExtType,用于保存目标相关且目标无关优化无法处理的类型,SPIR-V后端用其:
    1. 替代原不透明结构体(如%opencl.event_t = type opaque)表示SPIR-V特殊类型(OpTypeImage、OpTypeEvent等),格式为target("spirv.Event")
    2. 替代TypedPointerType表示SPIRVEmitIntrinsics中推导的嵌套类型(因LLVM无法创建TypedPointerType实例)。
  • 兼容性:导致SPIR-V后端与LLVM旧版本生成的IR不兼容(因不透明指针过渡)。

5. 典型问题解决案例

问题案例问题描述解决方案
AsmPrinter标签问题(#107013)含有效调试信息时,AsmPrinter强制生成函数结束符号,而SPIR-V标签仅允许在块内,不能在函数体后修改AsmPrinter代码,添加getObjectFormat() != Triple::SPIR判断,避免SPIR-V格式下函数体后生成标签
Machine Verifier与G_BITCAST/G_PHI(#110270)phi节点入值类型不同(如%r1为_ptr_Function_uchar,%r2为_ptr_Function_uint),G_BITCAST复用导致验证失败即时生成OpBitcast(非复用G_BITCAST),因OpBitcast非无操作bitcast;复用通用操作码表示SPIR-V phi-node,直至最终编码转为OpPhi
自定义OpPhi问题(#110019、#110507)SPIR-V OpPhi要求每个父块一个条目,LLVM phi可多个条目对应同一前驱;Machine Verifier不识别OpPhi指令选择阶段按SPIR-V规则生成OpPhi,遵循“复用GlobalISel共享代码”原则;用通用操作码标记OpPhi直至最终编码

五、正确性维护措施

1. 测试工具与流程

  • LIT测试:核心回归测试手段,优势是快速定位回归问题、测试用例简洁;扩展后结合Khronos的SPIR-V Tools(spirv-val)验证输出SPIR-V二进制是否符合规范,测试在ubuntu-latest环境中45分钟内可完成。
  • spirv-sim工具:由Google贡献,用于测试SPIR-V结构化器(控制流、跨通道交互),相比FileCheck更灵活、抗脆弱(无需匹配输出CFG顺序,避免后端变更导致测试“连锁修改”)。

六、未来工作规划

  1. SPIR-V目标定位:过去两年SPIR-V作为LLVM目标已成熟,常优于双向的Khronos LLVM/SPIR-V Translator,计划进一步强化其目标地位。
  2. 集成SPIR-V消费者:LLVM及依赖项目将受益于在代码库中集成SPIR-V消费者,实现后端与消费者的代码共享(如类型处理、控制流逻辑)。
  3. 支持SYCL/DPC++项目:将集成后的SPIR-V后端与消费者作为上游SYCL/DPC++项目的测试和生产解决方案,提升项目兼容性与稳定性。

4. 关键问题

问题1:SPIR-V后端当前已达成的核心成果有哪些?这些成果对实际开发有何意义?

答案

  • 核心成果:① 测试与兼容性方面,支持约500个LIT测试用例,达到OpenCL 3.0完全兼容,SYCL兼容性依优化级别达93%-99%;② 扩展与协同方面,已实现26个SPIR-V扩展,提升了与Khronos LLVM/SPIR-V Translator的兼容性;③ 功能扩展方面,正在推进Vulkan和HLSL的支持,且实现了与外部工具和库的集成。
  • 实际开发意义:① OpenCL 3.0和高比例SYCL兼容,确保基于OpenCL/SYCL的异构计算项目(如GPU加速的AI推理、科学计算)可无缝适配SPIR-V后端,减少兼容性调试成本;② 26个SPIR-V扩展覆盖更多硬件特性(如特殊指令、资源类型),满足复杂场景需求;③ 外部工具集成与Vulkan/HLSL支持,拓宽了SPIR-V后端的应用场景,可适配图形渲染(Vulkan)、游戏开发(HLSL)等领域。

问题2:LLVM 17引入的“不透明指针”给SPIR-V后端带来了哪些核心挑战?后端通过何种技术方案解决了这些挑战?

答案

  • 核心挑战:① 类型推导断层,LLVM 17前指针含元素类型,SPIR-V后端依赖该信息进行类型声明、嵌套类型(结构体/数组)降低、OpenCL内置函数调用解析等,不透明指针移除元素类型后,这些流程无法正常进行;② 类型一致性维护,函数调用(尤其是函数指针、间接调用)和phi节点入值可能存在不同类型,需确保推导后类型一致,避免SPIR-V二进制无效。
  • 解决方案:采用三通行证协同机制:① 第一阶段(SPIRVEmitIntrinsics):从常见指令(GEP、Load等)推导指针类型,发射类型分配/转换intrinsic,处理phi节点类型冲突(取最频繁类型),递归解决类型不一致;② 第二阶段(SPIRVCallLowering):生成含类型的SPIR-V函数/参数声明,优先从属性、内置类型映射获取指针类型,通过TableGen解析内置函数类型(解决mangling无返回类型问题);③ 第三阶段(SPIRVPreLegalizer):移除intrinsic,通过GlobalRegistry存储类型映射,确保模块内类型声明唯一,最终实现不透明指针到SPIR-V类型的正确映射。

问题3:SPIR-V后端在控制流处理上与LLVM IR存在哪些关键差异?针对这些差异,后端采取了哪些措施保障正确性?

答案

  • 关键差异:① 控制流结构要求不同,SPIR-V强制结构化控制流,需通过OpLoopMerge(循环)、OpSelectionMerge(选择)显式声明,而LLVM IR无此要求;② 分支逻辑表达不同,SPIR-V无单独“if (Cond) then Stmt”指令,仅通过OpBranchConditional实现完整if-then-else,且无法进行分支折叠、If转换等LLVM常见分支优化;③ 标签规则不同,SPIR-V中标签是启动基本块的指令,无条件分支后不能删除指令(需为块内最后一条),而LLVM IR标签无此严格位置限制。
  • 正确性保障措施:① 针对结构化控制流,开发spirv-sim工具(Google贡献)测试SPIR-V结构化器,验证控制流生成是否符合规范,避免非结构化控制流导致的SPIR-V无效;② 针对分支逻辑差异,在指令选择阶段严格按SPIR-V规则生成分支指令,放弃LLVM原生分支优化(如分支折叠),确保语义正确性;③ 针对标签规则,修改AsmPrinter代码(添加SPIR-V格式判断),避免在函数体后生成标签,同时在Machine Verifier阶段验证标签位置,确保符合SPIR-V规范;④ 复用GlobalISel共享代码时,通过通用操作码标记SPIR-V特有控制流指令(如OpPhi),直至最终编码阶段再转为标准SPIR-V指令,避免中间流程误判。

核心基础术语

  • SPIR-V:兼具中间表示(IR)和可移植二进制格式的特性,是异构加速器(如GPU、FPGA、NPU)的编程接口,由Khronos Group制定核心规范,支持跨厂商统一适配。
  • LLVM IR:LLVM编译器框架的中间表示,是连接高级语言与目标硬件指令集的关键环节,SPIR-V后端需将其映射为SPIR-V格式。
  • GlobalISel:LLVM的全局指令选择框架,负责将LLVM IR转换为Machine IR,SPIR-V后端在适配该框架时需解决多类兼容性问题。
  • SYCL/DPC++:SYCL是面向异构计算的编程模型,DPC++是基于SYCL的实现,SPIR-V后端需提供高兼容性支持(当前达93%-99%)。
  • OpenCL:开放计算语言,用于异构平台并行编程,SPIR-V后端已实现OpenCL 3.0完全兼容。
  • Khronos Group:制定SPIR-V、OpenCL、Vulkan等标准的行业联盟,主导跨厂商异构计算技术规范。

技术架构与工具术语

  • Machine IR(MIR):LLVM编译流程中介于LLVM IR和目标硬件指令集之间的中间表示,SPIR-V概念难以直接在其中表达。
  • SPIR-V Backend:LLVM中负责将LLVM IR转换为SPIR-V格式的模块,由Intel、Microsoft、Google等Khronos成员公司共同开发。
  • Khronos LLVM/SPIR-V Translator:双向转换LLVM IR与SPIR-V的工具,SPIR-V后端已提升与其的兼容性。
  • SPIR-V Tools:Khronos提供的SPIR-V工具集,含spirv-val(验证SPIR-V二进制合规性)等核心工具。
  • LIT:LLVM的集成测试框架,是SPIR-V后端的核心回归测试工具,支持快速定位代码回归问题。
  • spirv-sim:Google贡献的测试工具,用于验证SPIR-V结构化器的控制流和跨通道交互逻辑,抗脆弱性强。
  • AsmPrinter:LLVM中负责生成汇编代码(含SPIR-V二进制)的组件,需适配SPIR-V的标签指令规则。
  • Machine Verifier:LLVM的机器码验证工具,用于检查Machine IR的合法性,SPIR-V后端需适配其对类型、控制流的校验规则。

类型与指令相关术语

  • LLT(Low-Level Type):LLVM的底层类型系统,无法完整表达SPIR-V的复杂类型需求。
  • TypedPointerType:LLVM 17之前的带类型指针类型,包含指针指向的元素类型,SPIR-V指针强制要求带类型。
  • Opaque Pointers:LLVM 17引入的不透明指针类型,移除元素类型信息,给SPIR-V后端的类型推导带来挑战。
  • TargetExtType:LLVM 16新增的目标扩展类型,用于表示SPIR-V特殊类型(如OpTypeImage)和推导的嵌套类型。
  • OpLabel:SPIR-V中启动逻辑基本块的指令,是基本块的首个指令,位置有严格限制。
  • OpPhi:SPIR-V中的phi节点指令,要求为当前块的每个父块提供一个输入条目,与LLVM IR的phi节点语义存在差异。
  • OpBitcast:SPIR-V中的类型转换指令,非无操作转换,需在指令选择阶段即时生成。
  • OpLoopMerge:SPIR-V中声明结构化循环的指令,是结构化控制流的核心指令之一。
  • OpSelectionMerge:SPIR-V中声明结构化选择(if-then-else)的指令,确保控制流符合结构化要求。
  • OpBranchConditional:SPIR-V中的条件分支指令,仅支持完整的if-then-else逻辑,无单独的if逻辑表达。
  • Aggregate Types:聚合类型(如结构体、数组),SPIR-V中需显式保留在模块作用域,不能被GlobalISel拆解为底层类型。

编译流程与优化术语

  • IRTranslator:GlobalISel中负责将LLVM IR转换为Machine IR的组件,SPIR-V后端需与其协同处理聚合体、类型映射问题。
  • Call Lowering:函数调用的降低过程,将LLVM IR中的函数调用转换为Machine IR,SPIR-V后端需适配SPIR-V的函数声明和参数类型规则。
  • PreLegalizer:GlobalISel的预处理阶段,SPIR-V后端通过SPIRVPreLegalizer移除类型相关intrinsic并存储类型映射。
  • Intrinsic:LLVM的内置函数,SPIR-V后端通过自定义intrinsic(如assign_ptr_type)实现类型推导和映射。
  • SSA(Static Single Assignment):静态单赋值形式,LLVM IR和SPIR-V均基于SSA,要求每个变量仅赋值一次。
  • CFG(Control Flow Graph):控制流图,描述程序中基本块的跳转关系,SPIR-V要求CFG为结构化控制流图。
  • Branch Folding:分支折叠优化,通过合并冗余分支简化控制流,SPIR-V因结构化控制流要求不支持该优化。
  • If Conversion:if转换优化,将条件执行的代码转换为无分支代码,SPIR-V不支持该优化。

其他关键术语

  • SPIR-V Extensions:SPIR-V的扩展功能,当前SPIR-V后端已实现26个扩展,扩展核心规范的功能边界。
  • GlobalRegistry:SPIR-V后端的全局注册表,用于存储寄存器与类型的映射关系,确保模块内类型声明唯一。
  • TableGen:LLVM的代码生成工具,SPIR-V后端通过SPIRVBuiltins.td定义OpenCL/GLSL内置函数的类型规则。
  • Itanium Mangling:C++名字修饰规则,不包含函数返回类型信息,给SPIR-V后端解析内置函数类型带来挑战。
  • Byval/Byref Attributes:LLVM IR中的函数参数属性,用于标识参数传递方式,SPIR-V后端在类型推导时优先使用这些属性的类型信息。
http://www.dtcms.com/a/544606.html

相关文章:

  • MySQL逗号分隔字段-历史遗留原因兼容方案
  • Bun.js + Elysia 框架实现基于 SQLITE3 的简单 CURD 后端服务
  • 做网站 怎么赚钱吗网站数据分析课程
  • Rust——迭代器适配器深度解析:函数式编程的优雅实践
  • 理解PostgreSQL中的映射表
  • Java1029 抽象类:构造方法
  • 类和对象(中)——日期类的实现取地址运算符重载
  • Linux系统编程—线程同步与互斥
  • 【笔试真题】- 百度第一套-2025.09.23
  • notion模板 | 小胡的第二大脑[特殊字符]- 使用案例
  • notion模版 | 小胡的第二大脑[特殊字符]-介绍
  • 公司网站被百度转码了银川网站建设设计
  • 链式二叉树算法精讲:前中后序、层序与完全二叉树判断
  • 项目中遇到的特殊需求所作的特殊处理
  • 会所网站建设wordpress 怎样做模版
  • vue3使用ONLYOFFICE 实现在线Word,Excel等文档
  • Python数据分析自动化:从入门到精通
  • 零依赖一键多端!用纯 Node.js 打造“IP 可访、角色隔离”的轻量化 Mock 服务器
  • Azure 监控工具怎么选?从原生局限到第三方解决方案的效率跃升
  • 湖南省人力资源网夫唯seo
  • 佛山+网站建设品牌建设发展规划
  • 0009.STM32等单片机的RAM和FLASH使用情况查询
  • CloudFront分发安全优化指南:提升性能与用户体验的完整方案
  • 分享修改文件md5的工具
  • 拓展知识:了解grid、block、thread 关系
  • 打破视频壁垒:视频融合平台EasyCVR如何实现多路视频监控上屏的高效管理?
  • 仓颉原子操作封装:从底层原理到鸿蒙高并发实战
  • BIOS 设置PC 上电自启动
  • “自然搞懂”深度学习系列(基于Pytorch架构)——03渐入佳境
  • 网站建设及推广枣强怎么做汽车网站推广方案