news 2026/10/12 1:37:52

Slang IR 参考索引导航:按家族检索 Slang IR 指令集、解读 opcode 溯源列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Slang IR 参考索引导航:按家族检索 Slang IR 指令集、解读 opcode 溯源列
  • 编译器
  • 图形学
  • 编程语言

【免费下载链接】slang

Making it easier to work with shaders

项目地址:https://gitcode.com/GitHub_Trending/sl/slang
点击查看免费下载

本指南围绕 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>()式的类型判断因此退化为一次整数区间比较),家族页的分类基本映射了这棵树的结构。十个家族页及其在指令集中的范围如下:

PageFamilyLua entry rootApprox. opcodes
types.mdType instructionsType(约 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.mdblock、参数、分支、函数退出、target / quad-executionRequire*标记block(约 line 944)、param(约 line 1170)、TerminatorInst(约 lines 1454-1535)、backend-hint 分组(约 lines 1537-1547)~30
generics-and-existentials.mdspecialize、witness 查找、existential 打包/解包、RTTI、类型流特化(sets、tagged unions、dispatchers)specialize(约 line 1047)、lookupWitness(约 line 1048);type-flowSetBase分组始于约 line 3129~50
resources-and-atomics.mdimage/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 占位符、DiffTypeInfoMakeDifferentialPairBase(约 lines 1016-1046)、DiffTypeInfo(约 line 1124)、TranslateBase(约 lines 2816-2855)~40
decorations.mdDecoration 家族(附加在指令上的元数据)Decoration(约 line 1756)~200
metadata.mdLayout、Attr、Debug*、SPIRVAsmOperandLayout(约 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),固定按以下顺序组织:

  1. # <Family>标题(如# Types、# Control Flow);
  2. ## Source——一段指出该家族在 Lua 文件中的条目区间、slang-ir-insts.h 中对应的IRFoo包装结构、以及 slang-lower-to-ir.cpp 中产出这些 opcode 的 visitor;依赖 slang-ir.h 基础设施(op flags、IRBuilder辅助)时一并引入;
  3. ## Family hierarchy——一个 mermaidflowchart TD,镜像 Lua 的嵌套结构,让读者看到例如BasicType是Type的一个子区间;抽象中间条目只出现在这里;
  4. ## Opcodes——一张或多张表格,每行一个具体 opcode,列固定为Opcode / C++ wrapper / Operands / Flags / AST origin / Summary;
  5. ## Notable opcodes——对表格难以承载语义的 opcode 的短段落说明;
  6. ## 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

项目地址:https://gitcode.com/GitHub_Trending/sl/slang
点击查看免费下载

相关推荐

上一篇:ClojureScript与WebGL:纹理映射技术
下一篇:终极命令行美化工具grc:10分钟让你的终端输出变得五彩斑斓

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:36:07

一条命令让 AI Agent 具备逆向工程能力:REA 快速上手

一条命令让 AI Agent 具备逆向工程能力&#xff1a;REA 快速上手 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA&#xff08;Reverse Engineer…

作者头像 李华
网站建设 2026/10/12 1:34:34

从LED看STM32寄存器编程:地址、位运算与外设配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:34:09

AI文档处理:从PDF/Word/Excel到Markdown的转换与优化实践

1. 为什么喂给 AI 的文档格式决定了回答质量的上限很多人用 AI 处理文档时都有过类似的体验&#xff1a;把一份排版精美的 PDF 直接丢进去&#xff0c;问它"帮我总结第三季度的销售数据"&#xff0c;结果 AI 要么答非所问&#xff0c;要么把表格里的数字张冠李戴&…

作者头像 李华
网站建设 2026/10/12 1:34:06

PTA天梯赛L2刷题复盘:模式匹配与数据结构要点

PTA天梯赛的题集&#xff0c;我断断续续刷过一遍之后&#xff0c;前阵子又完整回炉了一遍。这已经是这个系列的第二篇温故笔记&#xff0c;上一篇主要在处理L1的稳定拿分&#xff0c;这次把火力集中在L2区间。为什么单独拎出L2&#xff0c;原因很简单&#xff1a;在天梯赛里L1决…

作者头像 李华