- 编译器
- 图形学
- 编程语言
【免费下载链接】slang
Making it easier to work with shaders
本指南围绕 Slang 编译器的 IR 指令参考文档子树(docs/generated/design/ir-reference/)展开,说明该子树的组织契约、十个家族页的分类逻辑、页面总览表的计数口径,以及“AST origin”列(opcode 生产者溯源)的语义约定。读完本文,你可以掌握在数百个kIROp_*指令中快速定位目标 opcode 的方法,理解一个 opcode 是由 AST 降低、核心模块__intrinsic_op声明还是某个 IR pass 构造的判别依据,并能顺着源码路径(source/slang/slang-ir-insts.lua、source/slang/slang-lower-to-ir.cpp)自行核对与扩展这套参考文档。
子树的定位:一份面向编译器开发者的 IR opcode 目录
docs/generated/design/ir-reference/是 docs/generated/design/ir-reference/index.md 所描述的“按家族(per-family)的 Slang 中间表示(IR)参考”。其核心承诺是:凡是在 slang-ir-insts.lua 中声明的每一个具体 opcode,都会出现在下方某个家族页中,并以表格形式列出它的 C++ 包装结构体(IRFoo)、操作数形态(operand shape)、操作标志位(op-flags:Hhoistable、Pparent、Gglobal)、构造它的生产者(AST origin)以及一行摘要;语义复杂到表格行无法承载的 opcode,则在页面下方以短段落(Notable opcodes)单独说明。
它的目标读者是已经大致知道自己要找什么的编译器开发者:想查某个 opcode 是否存在、它的操作数形态是什么、它从哪里来。因此这个子树刻意保持“窄”范围——只描述形状与出处(shape and provenance),不描述消费 IR 的各 pass 的行为。后者分别由两份相邻文档承担:
- 约定层面(schema、flag bits、hoistable/global 去重、模块版本化、新增 opcode 的工作流):见 docs/generated/design/cross-cutting/ir-instructions.md;
- 时序层面(AST 节点何时降低为 IR、调用了哪些 lowering 辅助函数):见 docs/generated/design/pipeline/04-ast-to-ir.md;
- 后续处理(IR pass 对 IR 做合法化、特化与优化):见 docs/generated/design/pipeline/05-ir-passes.md。
Family taxonomy:十个家族页的分类逻辑
整个指令集以IRInst为根,先按语义分成五个大类,再细分出十个家族页。分类的目的是让读者在打开ir-reference/目录的瞬间就能判断自己该进哪个页面:
这一分类并非随意划分,而是贴合 Lua 声明文件中的嵌套分组:slang-ir-insts.lua本身就是一棵以父条目包裹子条目的树,父子层级同时决定了 opcode 枚举的连续区间(一个as<IRBasicType>()式的类型判断因此退化为一次整数区间比较),家族页的分类基本映射了这棵树的结构。十个家族页及其在指令集中的范围如下:
| Page | Family | Lua entry root | Approx. opcodes |
|---|---|---|---|
| types.md | Type instructions | Type(约 line 20),内含嵌套的BasicType(约 line 22)、TranslatedTypeBase(约 line 168)、WorkGraphRecordTypeBase(约 line 232)分组 | ~170 |
| values.md | 常量、算术、转换(含DescriptorHandle<T>转换)、内存、聚合构造器、reshape/pack 辅助、constexpr 算术/转换、字符串与原生指针辅助 | Constant(约 line 953)及顶层 value opcode;constexpr*簇始于约 line 3412 | ~150 |
| structure.md | 模块结构:函数、泛型、全局量、结构体、接口、witness 表 | GlobalValueWithCode(约 line 885)、module(约 line 942) | ~20 |
| control-flow.md | block、参数、分支、函数退出、target / quad-executionRequire*标记 | block(约 line 944)、param(约 line 1170)、TerminatorInst(约 lines 1454-1535)、backend-hint 分组(约 lines 1537-1547) | ~30 |
| generics-and-existentials.md | specialize、witness 查找、existential 打包/解包、RTTI、类型流特化(sets、tagged unions、dispatchers) | specialize(约 line 1047)、lookupWitness(约 line 1048);type-flowSetBase分组始于约 line 3129 | ~50 |
| resources-and-atomics.md | image/buffer/sampler 操作、shader IO、原子操作、barrier、fragment-shader interlocks、cooperative matrix/vector、wave intrinsics、光线追踪、descriptor-heap 加载,以及 natural-layout 的getNaturalStride/getNaturalAlignment对 | AtomicOperation(约 line 1186)及顶层 resource opcode | ~90 |
| differentiation.md | 自动微分:differential pairs、前向/反向 differentiate、reverse-mode 上下文、autodiff 占位符、DiffTypeInfo | MakeDifferentialPairBase(约 lines 1016-1046)、DiffTypeInfo(约 line 1124)、TranslateBase(约 lines 2816-2855) | ~40 |
| decorations.md | Decoration 家族(附加在指令上的元数据) | Decoration(约 line 1756) | ~200 |
| metadata.md | Layout、Attr、Debug*、SPIRVAsmOperand | Layout(约 line 2880)、Attr(约 line 2909)、Debug*簇(约 lines 2974-3009)、SPIRVAsmOperand(约 line 3016) | ~60 |
| misc.md | 系统 opcode(nop、Unrecognized)、pack/expansion、类型查询、编译期 size/align/count 查询、storage 转换、无类型 descriptor-heap handle 转换、liveness 标记、tensor/runtime 辅助、kernel launch | 顶层杂项 opcode,外加Undefined(约 line 972)、BindingQuery(约 line 1736)、CastStorageToLogicalBase(约 line 2763)、LiveRangeMarker(约 line 2961)分组 | ~70 |
Approx. opcodes 列的统计口径:向上/向下取整到最近的十位(prefix~)。计数方法是统计 slang-ir-insts.lua 中落入该家族的struct_name = "..."条目数,再加上落入该家族的裸 opcode 条目数。它还有第二个近似来源:部分页面存在“交叉链接行”——同一 opcode 的规范条目在另一页,本页以交叉链接形式重复出现,因此底层行数会对这类双角色 opcode 重复计数。
在动手翻页前,还有两处所有权划分容易猜错,值得先记住:
DescriptorHandle<T>的转换在 values.md,无类型descriptor-heap handle 转换在 misc.md(“untyped descriptor-heap handle casts”小节),而 descriptor-heap 的加载在 resources-and-atomics.md;getNaturalStride与getNaturalAlignment与其余 alignment/stride 家族一起记录在 resources-and-atomics.md(“getNaturalStride and getNaturalAlignment”小节),而不是放在 misc.md 的编译期sizeOf/alignOf查询里。
Lua entry root 列的行号均指 slang-ir-insts.lua 而言,该文件在生成导航页的提交(source_commit)下为 3609 行,当前 HEAD 实测为 3644 行——无论计数还是行号,都会随着 opcode 的增删、跨家族移动而漂移,应以提交时的实际文件为准。
家族页的内部契约:形状 + 出处,而非行为
每个家族页(如 types.md、control-flow.md)遵循_common.md中定义的IR-reference family contract(见 docs/generated/design/_meta/prompts/_common.md),固定按以下顺序组织:
# <Family>标题(如# Types、# Control Flow);## Source——一段指出该家族在 Lua 文件中的条目区间、slang-ir-insts.h 中对应的IRFoo包装结构、以及 slang-lower-to-ir.cpp 中产出这些 opcode 的 visitor;依赖 slang-ir.h 基础设施(op flags、IRBuilder辅助)时一并引入;## Family hierarchy——一个 mermaidflowchart TD,镜像 Lua 的嵌套结构,让读者看到例如BasicType是Type的一个子区间;抽象中间条目只出现在这里;## Opcodes——一张或多张表格,每行一个具体 opcode,列固定为Opcode / C++ wrapper / Operands / Flags / AST origin / Summary;## Notable opcodes——对表格难以承载语义的 opcode 的短段落说明;## See also——链接约定页、平级家族页、lowering 页、相关 ast-reference 页与词汇表。
覆盖规则是硬性的:Lua 文件中属于该家族的每个具体 opcode 条目都必须出现在## Opcodes表中,仅用于分组子条目的抽象父条目(如BasicType、TerminatorInst)只出现在## Family hierarchy图中;横跨两个家族的 opcode 归入更具体的一方,并从另一方交叉链接。
## Opcodes表的列语义有几处容易误读,逐条说明:
- Opcode:Lua 条目名(反引号包裹),同时也是
kIROp_<name>枚举标签的组成部分; - C++ wrapper:
IRFoo结构体名,必须带IR前缀。契约明确禁止在该列使用破折号——不存在没有包装结构体的 opcode。但并非全部 wrapper 都由生成器产出:getAllOtherInstStructsData()(slang-ir.h.lua 约 line 145 起)以if not Slang["IR" .. struct_name] then开头,会跳过已在 slang-ir-insts.h 中手写的结构体,只为其余条目生成结构;手写 wrapper 在表中以脚注标记(如‡)区分,并在正文中说明数量; - Operands:来自 Lua 条目的操作数名,如
elementType, count;变长操作用(variadic),无操作数用—。当 Lua 条目声明了min_operands = N却没有给出操作数名时,写作(N unnamed),不得自行发明名字,也不得误标为(variadic);若 slang-ir-insts.h 中的 C++ 访问器能揭示该未命名操作数实际是什么,则允许命名并加†标记与图例说明; - Flags:单字母连接、不加分隔符——
Hhoistable(可提升、会去重)、Pparent(父容器)、Gglobal(恒在模块作用域但不去重);均不适用时留空; - AST origin:见下一节;
- Summary:一行短句,除行内代码外不带其他标记。
AST origin 列:一个 opcode 究竟由谁构造
AST origin 列是这套参考文档最有信息量的一列,它要求写出实际的生产者(producer),而不是笼统的类别标签:
- 来自 AST 降低:标注 AST 类 + slang-lower-to-ir.cpp 中产出它的
visit*成员函数。整个文件约有 230 个(当前 HEAD 下 grep 到 285 处visit相关匹配,数量随提交漂移)visit*函数,例如visitVarDecl(当前 HEAD 约 line 11763)发射var。AST 一侧的对应关系记录在 docs/generated/design/ast-reference/ 子树,主要是 expressions.md、statements.md 与 declarations.md; - 不要因为解析层存在某个 AST 类就假定存在对应 visitor:例如并不存在
visitInfixExpr——parser 为a + b构建的InfixExpr在语义检查阶段已被解析成BuiltinOperatorExpr(由visitBuiltinOperatorExpr处理)或一个对核心模块中以__intrinsic_op声明的普通函数的InvokeExpr(由visitInvokeExpr处理)。导航页在源码中明确指出了这个“易错点”; - 由 pass 引入:标注 pass 或函数名,例如
lowerTypeLayout、前向自动微分 pass、PyTorch 绑定 pass——不再使用已退役的笼统标签(synthesized); - 由核心模块 intrinsic 声明:
__intrinsic_op声明本身没有visit*,其生产者是 source/slang/core.meta.slang、source/slang/hlsl.meta.slang 或 source/slang/diff.meta.slang 中的声明; - 没有任何
source/代码构造它:标注no producer at HEAD,并在 Summary 中说明。旧版文档使用的兜底写法(synthesized)与裸—已被废弃,因为它们掩盖了“由 pass 产生”与“完全未产生”之间的真实区别。
跨主题文档:IR 在更大编译流水线中的位置
导航页将 IR 相关的横向文档串成一张网,便于从任一入口跳到相邻主题:
- docs/generated/design/pipeline/04-ast-to-ir.md —— AST 到 IR 的 lowering 流水线;
IRBuilder、IRGenContext与visit*方法如何把 AST 翻译为 IR; - docs/generated/design/pipeline/05-ir-passes.md —— 对 IR 做合法化、特化与优化的各 IR pass;
- docs/generated/design/pipeline/06-emit.md —— 各目标发射器如何消费合法化后的 IR;
- docs/generated/design/cross-cutting/ir-instructions.md —— IR schema、op-flag 约定、hoistable/global 去重、模块版本化、新增 opcode 的工作流;
- docs/generated/design/cross-cutting/serialization.md —— IR 模块如何被序列化;
- docs/generated/design/cross-cutting/diagnostics.md —— IR 指令携带
SourceLoc贯穿诊断系统; - docs/generated/design/cross-cutting/targets.md —— 消费合法化 IR 的目标后端,以及塑造“哪些 opcode 能存活到发射阶段”的按目标合法化;
- docs/generated/design/glossary.md ——
IRInst、IROp、IRBuilder、IRModule、parent instruction、terminator instruction、block parameter、decoration、hoistable instruction、target intrinsic、differential pair、witness table、existential type、specialization、single static assignment (SSA) 等术语定义。
如何导航:先读约定还是直接进家族页
导航页给出的使用建议非常明确:
- 如果你是 IR 新手,先从 docs/generated/design/cross-cutting/ir-instructions.md 开始:它覆盖每个家族页都默认读者已知的 schema、op-flag 位与模块版本化;
- 如果你已有明确目标,直接跳到你关心的 opcode 所在家族页;每页以
## Source开头,链接其 Lua 条目区间并说明本页的表格约定; - AST origin 单元格的读法:把它当作“实际构造该 opcode 的生产者名称”——一个 slang-lower-to-ir.cpp visitor、一个核心模块
__intrinsic_op声明、或一个具名 IR pass;当source/中没有任何代码构造它时,读到的是no producer at HEAD; - 抽象条目只在图中:仅用于分组的 Lua 抽象条目永远不会作为
## Opcodes行出现,只出现在各页的## Family hierarchy图中。
导航页背后的基础设施:命名、包装与版本化
索引页引用的这些机制,实际上定义了整个 IR 参考子树的可信度边界,值得从源码层面理解:
kIROp_*枚举名来自struct_name而非 Lua key。instEnums模板(slang-ir.h.lua 约 line 269)发射kIROp_$(value.struct_name),所以 Lua keyVec变成kIROp_VectorType、Array变成kIROp_ArrayType、TextureShapeCubeDType变成kIROp_TextureShapeCubeType;Lua key 作为-dump-ir打印的助记符保留。当struct_name省略时,slang-ir-insts.lua 底部的process函数用to_pascal_case从 key 推导(对应 line 3452 附近)。
wrapper 的生成与手写边界:getAllOtherInstStructsData()只补发“未手写”的结构体;手写 wrapper 通常因为需要IRUse成员、索引算术或解释性访问器(如TerminatorInst家族、IRDebugFunction::getParentScope的“操作数个数 > 5 才读取第 6 个”写法)。生成的结构体获得isaImpl、kOp常量与每个具名 Lua 操作数一个访问器;IRBuilder::get<StructName>(...)便捷构造器则由getBasicTypesForBuilderMethods(slang-ir.h.lua 约 line 320)驱动,少数需要手写逻辑的类型(如StructType/ClassType采用createStructType/createClassType)被显式排除。
序列化与模块版本化:opcode 不会被序列化为其枚举值,而是经由 slang-ir-insts-stable-names.lua 中的稳定名(stable name,按条目点路径如Type.BasicType.Int分配、永不重用)转换;新增指令只提升k_maxSupportedModuleVersion,删除指令才同时抬高最小值(当前为 4~28),详见 docs/generated/design/cross-cutting/ir-instructions.md 的“Module versioning and opcode insertion”一节。新增 opcode 的完整工作流(在所属家族内插入 Lua 条目、运行 extras/check-ir-stable-names.lua 的update、提升版本常量、决定 flag、必要时手写 wrapper、补充 lowering 与发射后端、加测试)也在该文档中逐步列出,并由 extras/check-ir-stable-names-gh-actions.sh 与 extras/check-inst-version-changes.sh 在 CI 中强制。
结语:索引页是入口,家族页才是正文
ir-reference/index.md本身刻意不描述任何 opcode 细节——它是一份导航页,职责是把读者以最快路径送到正确的家族页,并交代清楚统计口径、所有权划分与 AST origin 的读法。真正逐 opcode 的目录在十个家族页中(合计约 7400 行,当前 HEAD),而支撑它们的事实基础是 slang-ir-insts.lua 这份“指令集的唯一权威声明”。无论你是要查一个 opcode 的形状、要确认它由谁构造、还是要新增一个 opcode,正确的起点分别是家族页、AST origin 列与 docs/generated/design/cross-cutting/ir-instructions.md 的“Adding a new opcode”工作流。
- 编译器
- 图形学
- 编程语言
【免费下载链接】slang
Making it easier to work with shaders
相关推荐
Windows热键冲突终极解决方案:Hotkey Detective一键定位占用程序
Windows热键冲突终极解决方案:Hotkey Detective一键定位占用程序 当你在Windows系统中设置的全局快捷键突然失效,却找不到哪个程序在占用
编译器图形学编程语言Slang IR 指令集全解析:从 `slang-ir-insts.lua` 到 opcode 定义、去重与版本管理
Slang IR 指令集全解析:从 slang ir insts.lua 到 opcode 定义、去重与版本管理 Slang(GitHub 推荐项目精选 / s
编译器图形学编程语言Slang 自动微分 IR Opcode 全解析:Differential Pair、翻译请求与 Checkpointing 家族参考
Slang 自动微分 IR Opcode 全解析:Differential Pair、翻译请求与 Checkpointing 家族参考 导读 本文面向编译器工程
编译器图形学编程语言
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考