- 编译器
- 图形学
- 编程语言
【免费下载链接】slang
Making it easier to work with shaders
本文以 Slang 仓库中 ast-reference/modifiers.md 修复报告 为骨架,系统讲解 Slang 编译器前端 AST 中Modifier/Attribute两大语法族系的参考文档结构、其背后的类继承体系与源码落点,以及该文档所经历的"机器审查(review)— 机器修复(remediation)"质量保障流水线。读完本文,读者既能快速定位某个[attr]或关键字在 AST 中对应哪个类、字段如何声明、语义检查在哪里进行,也能理解生成式技术文档如何通过 finding 驱动的闭环迭代保证与源码一致。
一、这份参考文档解决什么问题
Modifiers and Attributes Reference(modifiers.md)是写给在 Slang 编译器的parser、checker 或 backend emit中工作的贡献者使用的:当他们在源码中看到一个[attr]或关键字修饰符时,需要知道它最终变成 AST 中的哪一个类、携带哪些字段、语义检查在哪里完成。该页面向导式地覆盖了slang-ast-modifier.h中每一个具体(concrete)Modifier子类,Modifier基类本身则在 base.md 中单独记录。
1.1 Modifier 与 Attribute 的分界
Slang 有意将两族语法区分开:
- Modifier(修饰符):不带参数列表的关键字标记,如
in、out、inout、const、static、uniform、globallycoherent、noperspective等,每个关键字对应一个独立的Modifier子类。 - Attribute(属性):采用
[name(args)]语法,派生自AttributeBase(而AttributeBase本身又派生自Modifier)。具体的属性类(UnrollAttribute、NumThreadsAttribute等)持有解析后的参数及检查阶段(checking)产生的元数据。
两者最终都以链接形式挂在ModifiableSyntaxNode::modifiers上,并通过findModifier<T>()/hasModifier<T>()遍历查找,因此大多数消费代码无需区分二者。
1.2 文档的五个组成部分
页面主体由五部分构成,这一骨架也是本文后续展开的脉络:
| 章节 | 内容 |
|---|---|
## Source | 类声明位置(头文件)、解析入口(parser)、表面拼写(spelling)的来源文件 |
## Family hierarchy | 一张 mermaid 继承关系图,覆盖全部具体类及其抽象中间层 |
## Nodes | 264 个具体类的编目:按 26 个功能分组表格列出,每行含 Class / Parent / Key fields / Grammar / Summary 五列 |
## Notable nodes | 对易混淆、易误解的重点节点组的深度讲解 |
## See also | 指向 base/declarations/expressions/types/values 及语法、能力系统等关联页面 |
二、文档的源码与拼写落点(Source)
参考文档明确指出,modifier 类声明于 slang-ast-modifier.h,建立在 slang-ast-base.h 中的Modifier/ModifiableSyntaxNode之上;解析发生在 slang-parser.cpp:
parseAttributeName与ParseSquareBracketAttributes处理[name(args)]形式;parseUncheckedGLSLLayoutAttribute处理 GLSLlayout(...)分组中的带值条目;- 属性通过
AttributeDecl表分发,该表记录于 keywords-and-builtins.md。
2.1 表面拼写(spelling)的四个来源
属性在源码中写成的表面拼写不声明在 C++ 头文件里,而是来自attribute_syntax声明。修复报告 F-006 澄清了这一点的精确表述:拼写来自四个源文件——三个核心模块.meta.slang文件加一个实验性标准模块:
| 文件 | 负责的属性族 |
|---|---|
| core.meta.slang | 主体(绝大多数属性) |
| diff.meta.slang | 可微性(differentiability)属性 |
| hlsl.meta.slang | Vulkan 指针属性 |
| workgraph.slang | 工作图(work-graph)节点属性 |
这一区分对维护者意义重大:workgraph.slang位于source/standard-modules/experimental/下,是实验性标准模块,而非核心模块源。修复前文档误称"四个核心模块源",现已更正。同时这四个文件都被页面列为 watched path——任何拼写改名都会使页面标记为 stale。
三、Nodes 编目:264 个具体类的组织方式
## Nodes是文档体量最大的部分。每个具体类占一行,统一五列格式:
- Class:类名;
- Parent:直接父类;
- Key fields:
name: Type形式的自有字段,若无则写(no additional state); - Grammar:对应语法(链接到 grammar.md#modifiers 或
#attributes-and-decorations),合成节点为(none); - Summary:一句话说明该类承载的语义。
3.1 继承自 AttributeBase 的公共字段
页面开篇有一段重要的统一说明:每个属性都从AttributeBase继承三个字段——attributeDecl: AttributeDecl*、originalIdentifierToken: Token、args: List<Expr*>;每个已检查(checked)的Attribute还额外带intArgVals: List<Val*>。许多属性类自身不声明任何成员,直接读取继承列表中的参数,这类行在 Key fields 列写继承的args: List<Expr*>,把每个参数的含义留给 Summary 列说明;而(no additional state)表示该类不携带任何数据。
这与源码完全吻合:slang-ast-modifier.h 第 809-820 行声明的AttributeBase正是:
// Base class for checked and unchecked `[name(arg0, ...)]` style attribute. FIDDLE(abstract) class AttributeBase : public Modifier { FIDDLE(...) FIDDLE() AttributeDecl* attributeDecl = nullptr; // The original identifier token representing the last part of the qualified name. Token originalIdentifierToken; FIDDLE() List<Expr*> args; };3.2 26 个功能分组一览
264 个具体类分布在 26 张表中,覆盖的范畴包括:参数方向与存储类修饰符、可见性修饰符、override/require/export/import 样板、HLSL 存储类修饰符、插值模式、矩阵布局、几何/网格着色器输入、HLSL 语义(: SV_*)、GLSL 预处理/布局/格式、类型修饰符(包裹类型而非声明)、内部/合成修饰符、内在函数与目标绑定修饰符、隐式参数组机制、属性基础(AttributeBase与UncheckedAttribute)、编译期提示属性(循环/分支/优化级别)、能力/目标属性、布局/绑定属性、未检查 GLSL 布局属性、阶段特定入口点属性、工作图节点属性、光线追踪属性、可变性/自动微分注解、可微性属性、继承控制属性、CUDA/Python/FFI 属性。
以下选取几组有代表性的表格行,展示其信息密度:
HLSL 语义(: SV_*):
| Class | Parent | Key fields | Summary |
|---|---|---|---|
HLSLLayoutSemantic | HLSLSemantic | registerName: Token,componentMask: Token | 影响布局(register / packoffset)的 HLSL 语义基类 |
HLSLRegisterSemantic | HLSLLayoutSemantic | spaceName: Token, plus inheritedregisterName | : register(...) |
HLSLPackOffsetSemantic | HLSLLayoutSemantic | uniformOffset: int | : packoffset(...) |
HLSLSimpleSemantic | HLSLSemantic | name: Token | : NAME(无括号参数) |
内在函数与目标绑定:
| Class | Parent | Key fields | Summary |
|---|---|---|---|
IntrinsicOpModifier | Modifier | opToken: Token,op: uint32_t | 将声明绑定到 Slang IR 操作码(核心模块内在函数) |
TargetIntrinsicModifier | Modifier | targetToken: Token,definitionString: String,predicateToken: Token,scrutineeDeclRef: DeclRef<Decl> | 将声明绑定到目标后端内在函数 |
SpecializedForTargetModifier | Modifier | targetToken: Token | 标记针对某目标特化的声明 |
NVAPISlotModifier | Modifier | registerName: String,spaceName: String | 来自NV_SHADER_EXTN_SLOT/NV_SHADER_EXTN_REGISTER_SPACE宏的 NVAPI 槽位绑定 |
编译期提示属性:UnrollAttribute([unroll(N)],提示转发给下游编译器,Slang 自身不改行为)、ForceUnrollAttribute([ForceUnroll(N)],Slang 自己完成展开,输出中不留循环)、LoopAttribute([loop])、FlattenAttribute([flatten])、NoInlineAttribute([noinline])等。
工作图节点属性:NodeLaunchAttribute([NodeLaunch("broadcasting" | "thread" | "coalescing")])、NodeMaxDispatchGridAttribute(动态网格上界)、NodeDispatchGridAttribute(固定网格大小)、MaxRecordsAttribute、NodeIDAttribute、NodeIsProgramEntryAttribute、AllowSparseNodesAttribute、NodeArraySizeAttribute——其拼写声明在实验性 workgraph.slang,而非core.meta.slang。
四、重点节点深入(Notable nodes)
## Notable nodes是全页最具解释力的部分,其中的知识点与修复报告的多项 finding 直接对应。
4.1 修饰符与属性的解析差异
解析器按语法区分二者:修饰符是 parser 按名字认知并立即构造对应子类的裸关键字;属性则是[ident(args)]结构,parser 先构建UncheckedAttribute,checker 再通过查找AttributeDecl将其解析为具体的Attribute子类(见 declarations.md)。
有几个容易遗漏的表面拼写规则:
- 一组可以写成
[a]或[[a]],多个属性可共享一组; - 属性之间的逗号是可选的,
[a, b]与[a b]解析结果相同; parseAttributeName会把::限定名拍平为单个标识符(每个::替换为_),所以用户写[vk::binding(0)]实际解析为注册名vk_binding的AttributeDecl;前导::变成前导_。
4.2 IntrinsicOpModifier:核心模块到 IR 的桥
IntrinsicOpModifier是 Slang 函数声明与它将要 lower 到的 IR 操作码之间的桥梁。例如core.meta.slang中sin的声明携带IntrinsicOpModifier,其op字段就是sin的 IR 操作码;IR lowering 阶段据此发出正确的操作码,而无需按函数名特判(见 core-module.md)。
4.3 TargetIntrinsicModifier 的 target 与 predicate 之辨(对应 F-005)
这是修复报告 F-005 澄清的核心概念。源码中 slang-ast-modifier.h 第 281-301 行的TargetIntrinsicModifier声明了:
// Token that names the target that the operation is an intrinsic for. FIDDLE() Token targetToken; // A custom definition for the operation, one of either an ident or a // string (the concatenation of several string literals) Token definitionIdent; FIDDLE() String definitionString; bool isString; // A predicate to be used on an identifier to guard this intrinsic Token predicateToken; NameLoc scrutinee; FIDDLE() DeclRef<Decl> scrutineeDeclRef;修复前的文档称谓词"仅在能力生效时应用",把目标选择错误地归因于谓词。事实上二者相互独立:targetToken命名目标后端,由它决定选择哪个目标的哪个能力;而可选的 predicate 是另一道独立守卫,通过scrutineeDeclRef解析到的声明来守卫该内在函数(例如 HLSL 的"fma"、SPIR-V 的"OpExtInst ..."这类按目标写的文本内在函数)。SpecializedForTargetModifier则是纯按目标标记的修饰符,checker 在 emit 时优先选用带它的函数声明。
4.4 GLSLLayout* 家族与绑定信息的表示(对应 F-004)
GLSL 的layout(...)限定符编译为一串布局修饰符:一个GLSLLayoutModifierGroupBegin、每个限定符一个条目(如UncheckedGLSLBindingLayoutAttribute、UncheckedGLSLLocationLayoutAttribute等具体子类)、最后跟一个GLSLLayoutModifierGroupEnd。"Unchecked"前缀表示 parser 阶段的表示,checker 会把每个条目解析为Attribute根系的等价类(如GLSLBindingAttribute、GLSLLocationAttribute)。
修复报告 F-004 指出:这些修饰符正是参数绑定在 AST 中的表示层。已检查的GLSLBindingAttribute持有解析后的binding: int32_t与set: int32_t对;HLSL 侧以 Token 形式存储同样的信息——HLSLLayoutSemantic持有registerName: Token与componentMask: Token,HLSLRegisterSemantic追加spaceName: Token表示寄存器空间。这些声明在 slang-ast-modifier.h 第 478-499 行附近可以得到确认。需要强调的是:修饰符只记录声明写了什么,把这些值变成实际绑定的规则属于 checking 与 layout 阶段,详见 03-semantic-check.md。
4.5 可见性与语言版本
声明是public、internal还是private,编码为挂在声明上的VisibilityModifier子类。默认值取决于模块的ModuleDecl::languageVersion与defaultVisibility:旧版 Slang 把一切视为public,现代语言默认internal。
4.6 可微性属性族:类到拼写并非一一对应
DifferentiableAttribute(头文件第 1715-1762 行)之下是一个小层级:标记属性([ForwardDifferentiable]、[BackwardDifferentiable])描述函数"可微"这一事实;UserDefinedDerivativeAttribute子类([ForwardDerivative(fn)]、[BackwardDerivative(fn)])显式绑定导数函数;DerivativeOf子类([ForwardDerivativeOf(fn)]等)声明某函数是另一函数的导数。
最容易踩坑的是类到拼写的映射不是一对一:DifferentiableAttribute是实践中的抽象基类,没有自己的attribute_syntax;用户代码里最常见的[Differentiable(order = 0)]映射到BackwardDifferentiableAttribute,与显式[BackwardDifferentiable(order = 0)]完全一致,只有[ForwardDifferentiable]映射到ForwardDifferentiableAttribute。此外,该属性是前端标记而非后续阶段回读的事实:checking 接受一个[Differentiable]函数f时,会合成extension __func_as_type(f) : IForwardDifferentiable<__func_as_type(f)>形式的 conformance,checker 的可微性查询(SemanticsVisitor::isFuncForwardDifferentiable及其 backward 对应物)返回的是SubtypeWitness*而非直接读修饰符的布尔值,下游 autodiff 工作基于该 witness 展开。
4.7 合成修饰符、GLSL 内存限定符聚合、缺失的基类
- 合成修饰符:
ToBeSynthesizedModifier、SynthesizedModifier、IgnoreForLookupModifier、VarReassignedModifier、ExistentialOpenedOnVarModifier等不来自用户语法,由 checker 添加、后续阶段检查。 - 内存限定符聚合:GLSL 允许对同一声明施加多个内存限定符(
coherent、volatile、readonly、writeonly、restrict),checker 可将其聚合为单个带标志位掩码的MemoryQualifierSetModifier(字段memoryQualifiers: uint32_t、memoryModifiers: List<Modifier*>),而独立的GLSLReadOnlyModifier等仍存在于 parse 阶段。 - 缺失的基类(F-007 保留的源码事实):不存在
HLSLAttribute、LayoutModifier或HLSLLayoutModifier类,这三个名字在source/下任何地方都查无此名。HLSL 着色器阶段属性(如NumThreadsAttribute、EntryPointAttribute)直接派生自Attribute,没有 HLSL 专用中间层;布局相关职责分散在HLSLLayoutSemantic(影响布局的语义)、MatrixLayoutModifier(矩阵存储布局)与GLSLLayout*Modifier组之间。
五、审查与修复流水线:从 review report 到 remediation report
modifiers.md是一份生成式文档(front matter 标注generated: true并有警告 "Auto-generated. May drift from source. Do not edit by hand."),其质量由一条"审查报告 → 修复报告"的机器流水线保障:
- review:审查报告(由
gpt-5.6-sol生成,2026-08-04)对照源码提交53b76e6…逐项核对,产出 8 个 finding(4 major、4 minor,无 critical),并按 checklist(factual_accuracy / cross_references / completeness / style_consistency / source_alignment / front_matter_validity)打分。 - remediation:修复报告(由
claude-opus-5生成)对每个 finding 给出动作与修复摘要。 - 结论汇总:5 个修复(fixed)、2 个推迟(deferred)、1 个超范围拒绝(rejected out-of-scope)、0 个升级。修复后页面 54,950 字节,仍在其 65,536 字节上限之下。
审查环节的核实力度值得参考:它对照了全部 273 个 FIDDLE 声明的类(264 个具体类恰好各出现一次,9 个抽象类全部被排除)、抽查了 10 余处事实断言、解析了全部 73 个相对链接(21 个唯一目标)、扫描了 627 个反引号标识符与 127 个括号属性拼写,并重新计算了七文件 watched-path digest。
六、八个 finding 逐一解读
下表汇总修复报告中的全部动作,随后逐项展开。
| Finding ID | 动作 | 严重级别 | 一句话摘要 |
|---|---|---|---|
| F-001 | deferred | major | 264 行跨 26 张表,未遵循"单表"契约;合并属整页级重写 |
| F-002 | deferred | major | Grammar 列大量(none)未区分解析产生与纯合成节点 |
| F-003 | fixed | major | 15 个 Key fields 单元格改为name: Type形式 |
| F-004 | fixed | major | 布局修饰符补充"绑定数据如何表示"的说明 |
| F-005 | fixed | minor | 纠正TargetIntrinsicModifier的 target/predicate 混淆 |
| F-006 | fixed | minor | workgraph.slang不再被称为核心模块源 |
| F-007 | fixed | minor | 删除生成历史叙述,保留三个类缺失的源码事实 |
| F-008 | rejected-out-of-scope | minor | watched_paths_digest归操作者的regenerate.py mark-fresh管理 |
6.1 F-001:单表合并(推迟)
AST 家族文档契约(prompts/_common.md 第 99 行附近)要求"一张表",而modifiers.md把 264 行拆在 26 张表中;同族的declarations.md、expressions.md、statements.md、types.md都只用一张表,本页是例外。推迟的理据很实在:合并涉及## Nodes整体重写、搬迁各分组间的过渡性说明文字、并丢失 26 个###锚点目标,绝非最小编辑;且它触及的行与 F-002 完全重叠。后续计划是让modifiers.md与values.md在同一周期内一起重新生成## Nodes。
6.2 F-002:全页 Grammar 审计(推迟)
prompt 契约(ast-reference-modifiers.md 第 21-25 行)规定(none)只保留给纯合成修饰符,但被解析产生的属性如ForceUnrollAttribute也被标为(none)。核查这些行的成本极高:264 行中有 217 行携带(none),每一行的判定都要交叉核对 126 个attribute_syntax声明(分布在core.meta.slang、diff.meta.slang、hlsl.meta.slang、workgraph.slang四个文件)外加 parser 的关键字修饰符映射表。因此 F-002 与 F-001 一样被推迟,计划并入 F-001 的表重写一并完成。
6.3 F-003:Key fields 统一为name: Type(已修复)
审查发现 15 行使用了无类型的概念性描述(如optional unroll count、opcode (in args)、binding、max (in args)),违反契约强制要求的name: Type形式。修复确认:AttributeBase::args在 slang-ast-modifier.h 第 804-815 行声明为List<Expr*>,且对这 15 个类做字段提取后确认没有一个类声明自有成员——继承列表是唯一真实存储。因此 15 个 Key fields 单元格(UnrollAttribute、SPIRVInstructionOpAttribute、八个UncheckedGLSL*布局行、五个细分曲面(tessellation)行)统一改为args: List<Expr*>(inherited),并同步重写了开头段落中"概念性参数约定"的句子。
6.4 F-004:布局修饰符补充绑定表示(已修复)
审查要求说明布局修饰符在参数绑定中的角色。修复在### GLSLLayout*Modifier family末尾新增一段:已检查的GLSLBindingAttribute持有binding: int32_t/set: int32_t对,HLSL 侧用 Token 存registerName/componentMask/spaceName(声明见 slang-ast-modifier.h 第 481-495 行与第 1080-1087 行附近),并把语义规则推迟到链接的语义检查页面,符合该 prompt 的 forbidden-content 条款。
6.5 F-005:TargetIntrinsicModifier 混淆纠正(已修复)
如 4.3 节所述,修复把"由 targetToken 选择目标能力"与"predicate 作为经 scrutineeDeclRef 解析的独立守卫"分开表述,删除了把目标选择归因于谓词的说法。
6.6 F-006:workgraph.slang 归类更正(已修复)
"四个核心模块源"改为"四个源——三个核心模块.meta.slang文件加一个实验性标准模块",并点名工作图子句指向实验性标准模块 workgraph.slang。
6.7 F-007:删除生成历史叙述(已修复)
"页面被要求覆盖什么""没有 manifest 路径能恢复这些名字""未来重新生成"这类生成过程评论没有源码依据,被删除(契约 prompts/_common.md 第 74-81 行禁止投机性与编辑性材料);小节更名为### Absent groupings: no HLSLAttribute or LayoutModifier base,仅保留"三个名字在source/下不存在"这一可验证事实。由于没有页面链接旧标题锚点,更名是安全的。
6.8 F-008:watched_paths_digest 拒绝处理(超范围)
watched_paths_digest是 front matter 中的校验摘要。契约(prompts/_remediate.md 第 97-100 行)把它保留给操作者的regenerate.py mark-fresh运行管理,禁止修复者编辑;审查报告自己也建议"review 期间不要编辑生成页面"。因此该 finding 被拒绝为 out-of-scope——由于本页在其他 finding 下被编辑过,mark-fresh会记录七文件摘要(当前 manifest 解析出七个 watched 文件,见 manifest.yaml)。
七、从这套流水线能学到什么
- 文档契约先行:
_common.md、ast-reference-modifiers.md、_remediate.md三份 prompt 分别约束文档格式(单表、name: Type、(none)语义)、页面专属要求(绑定角色、禁止内容)与修复者的权限边界。任何手动编辑生成页面的行为都会破坏 front matter 摘要与 digest 的一致性。 - 修复也要讲成本:F-001/F-002 的推迟不是因为"不重要",而是因为涉及 264 行中的同一批行、且属"重新生成"级别的操作——最小编辑原则与整页重写的边界在此得到清晰示范。
- 源码是唯一事实来源:每一次修复都落回头文件行号(如
AttributeBase::args在 804-815 行、TargetIntrinsicModifier在 281-301 行、HLSLLayoutSemantic在 481-495 行)与 parser/checker 的具体函数,保证了结论可复核、可追溯。
八、延伸阅读
- ast-reference/modifiers.md —— 被修复的参考文档本体(264 类编目 + 重点节点讲解)
- ast-reference/modifiers.md 审查报告 —— 8 个 finding 的完整证据链
- ast-reference/modifiers.md 修复报告 —— 本文的骨架文档
- ast-reference/base.md ——
Modifier基类 - ast-reference/declarations.md —— 携带修饰符的声明与
AttributeDecl - ast-reference/expressions.md ——
ModifiedTypeExpr携带内联Modifiers列表 - ast-reference/types.md ——
ModifiedTypeVal - ast-reference/values.md ——
ModifierVal族 - 语法参考 grammar.md —— 修饰符与属性的表面语法
- 能力系统 targets.md —— 解释
RequireCapabilityAttribute、TargetIntrinsicModifier等的能力系统 - 核心模块 core-module.md ——
IntrinsicOpModifier、BuiltinTypeModifier、MagicTypeModifier如何绑定核心模块声明 - 核心源码:slang-ast-modifier.h、slang-parser.cpp、core.meta.slang、diff.meta.slang、hlsl.meta.slang、workgraph.slang
- 编译器
- 图形学
- 编程语言
【免费下载链接】slang
Making it easier to work with shaders
相关推荐
Slang 编译器 Modifier/Attribute AST 参考文档的自动评审机制:`modifiers.md.review.md` 深度解析
Slang 编译器 Modifier/Attribute AST 参考文档的自动评审机制: modifiers.md.review.md 深度解析 本文聚焦 S
编译器图形学编程语言Slang 编译器 IR 参考文档的自动化质量审查:解读 generics-and-existentials 文档审查报告与修复闭环
Slang 编译器 IR 参考文档的自动化质量审查:解读 generics and existentials 文档审查报告与修复闭环 本篇技术指南围绕 Slan
编译器图形学编程语言Slang 编译器 Modifier 与 Attribute AST 家族参考:从语法关键字到语义检查的完整映射
Slang 编译器 Modifier 与 Attribute AST 家族参考:从语法关键字到语义检查的完整映射 本篇文章以 Slang 编译器( slang
编译器图形学编程语言
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考